mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-06 03:50:04 +02:00
add p2p support for HLS https://github.com/Novage/p2p-media-loader
This commit is contained in:
parent
64c36d9f4e
commit
0d0338876d
1197 changed files with 121461 additions and 179724 deletions
2
node_modules/@codemirror/search/.github/workflows/dispatch.yml
generated
vendored
2
node_modules/@codemirror/search/.github/workflows/dispatch.yml
generated
vendored
|
@ -11,6 +11,6 @@ jobs:
|
|||
with:
|
||||
# You should create a personal access token and store it in your repository
|
||||
token: ${{ secrets.DISPATCH_AUTH }}
|
||||
repo: codemirror.next
|
||||
repo: dev
|
||||
owner: codemirror
|
||||
event_type: push
|
||||
|
|
48
node_modules/@codemirror/search/CHANGELOG.md
generated
vendored
48
node_modules/@codemirror/search/CHANGELOG.md
generated
vendored
|
@ -1,3 +1,51 @@
|
|||
## 6.2.3 (2022-11-14)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix a bug that hid the search dialog's close button when the editor was read-only.
|
||||
|
||||
## 6.2.2 (2022-10-18)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
When `literal` is off, \n, \r, and \t escapes are now also supported in replacement text.
|
||||
|
||||
Make sure search dialog inputs don't get treated as form fields when the editor is created inside a form.
|
||||
|
||||
Fix a bug in `RegExpCursor` that would cause it to stop matching in the middle of a line when its current match position was equal to the length of the line.
|
||||
|
||||
## 6.2.1 (2022-09-26)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
By-word search queries will now skip any result that had word characters both before and after a match boundary.
|
||||
|
||||
## 6.2.0 (2022-08-25)
|
||||
|
||||
### New features
|
||||
|
||||
A new `wholeWord` search query flag can be used to limit matches to whole words.
|
||||
|
||||
`SearchCursor` and `RegExpCursor` now support a `test` parameter that can be used to ignore certain matches.
|
||||
|
||||
## 6.1.0 (2022-08-16)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fix an infinite loop when the match position of a `RegExpCursor` ended up in the middle of an UTF16 surrogate pair.
|
||||
|
||||
### New features
|
||||
|
||||
The `literal` search option can now be set to make literal queries the default.
|
||||
|
||||
The new `searchPanelOpen` function can be used to find out whether the search panel is open for a given state.
|
||||
|
||||
## 6.0.1 (2022-07-22)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
`findNext` and `findPrevious` will now return to the current result (and scroll it into view) if no other matches are found.
|
||||
|
||||
## 6.0.0 (2022-06-08)
|
||||
|
||||
### Bug fixes
|
||||
|
|
10
node_modules/@codemirror/search/README.md
generated
vendored
10
node_modules/@codemirror/search/README.md
generated
vendored
|
@ -1,13 +1,13 @@
|
|||
# @codemirror/search [](https://www.npmjs.org/package/@codemirror/search)
|
||||
|
||||
[ [**WEBSITE**](https://codemirror.net/6/) | [**DOCS**](https://codemirror.net/6/docs/ref/#search) | [**ISSUES**](https://github.com/codemirror/codemirror.next/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/search/blob/main/CHANGELOG.md) ]
|
||||
[ [**WEBSITE**](https://codemirror.net/) | [**DOCS**](https://codemirror.net/docs/ref/#search) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/search/blob/main/CHANGELOG.md) ]
|
||||
|
||||
This package implements search functionality for the
|
||||
[CodeMirror](https://codemirror.net/6/) code editor.
|
||||
[CodeMirror](https://codemirror.net/) code editor.
|
||||
|
||||
The [project page](https://codemirror.net/6/) has more information, a
|
||||
number of [examples](https://codemirror.net/6/examples/) and the
|
||||
[documentation](https://codemirror.net/6/docs/).
|
||||
The [project page](https://codemirror.net/) has more information, a
|
||||
number of [examples](https://codemirror.net/examples/) and the
|
||||
[documentation](https://codemirror.net/docs/).
|
||||
|
||||
This code is released under an
|
||||
[MIT license](https://github.com/codemirror/search/tree/main/LICENSE).
|
||||
|
|
260
node_modules/@codemirror/search/dist/index.cjs
generated
vendored
260
node_modules/@codemirror/search/dist/index.cjs
generated
vendored
|
@ -30,7 +30,8 @@ class SearchCursor {
|
|||
[`.normalize("NFKD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
|
||||
(when supported).
|
||||
*/
|
||||
constructor(text, query, from = 0, to = text.length, normalize) {
|
||||
constructor(text, query, from = 0, to = text.length, normalize, test) {
|
||||
this.test = test;
|
||||
/**
|
||||
The current match (only holds a meaningful value after
|
||||
[`next`](https://codemirror.net/6/docs/ref/#search.SearchCursor.next) has been called and when
|
||||
|
@ -124,6 +125,8 @@ class SearchCursor {
|
|||
else
|
||||
this.matches.push(1, pos);
|
||||
}
|
||||
if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferPos))
|
||||
match = null;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +147,7 @@ class RegExpCursor {
|
|||
`new RegExp`).
|
||||
*/
|
||||
constructor(text, query, options, from = 0, to = text.length) {
|
||||
this.text = text;
|
||||
this.to = to;
|
||||
this.curLine = "";
|
||||
/**
|
||||
|
@ -160,10 +164,11 @@ class RegExpCursor {
|
|||
if (/\\[sWDnr]|\n|\r|\[\^/.test(query))
|
||||
return new MultilineRegExpCursor(text, query, options, from, to);
|
||||
this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
|
||||
this.test = options === null || options === void 0 ? void 0 : options.test;
|
||||
this.iter = text.iter();
|
||||
let startLine = text.lineAt(from);
|
||||
this.curLineStart = startLine.from;
|
||||
this.matchPos = from;
|
||||
this.matchPos = toCharEnd(text, from);
|
||||
this.getLine(this.curLineStart);
|
||||
}
|
||||
getLine(skip) {
|
||||
|
@ -194,10 +199,10 @@ class RegExpCursor {
|
|||
let match = this.matchPos <= this.to && this.re.exec(this.curLine);
|
||||
if (match) {
|
||||
let from = this.curLineStart + match.index, to = from + match[0].length;
|
||||
this.matchPos = to + (from == to ? 1 : 0);
|
||||
if (from == this.curLine.length)
|
||||
this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
|
||||
if (from == this.curLineStart + this.curLine.length)
|
||||
this.nextLine();
|
||||
if (from < to || from > this.value.to) {
|
||||
if ((from < to || from > this.value.to) && (!this.test || this.test(from, to, match))) {
|
||||
this.value = { from, to, match };
|
||||
return this;
|
||||
}
|
||||
|
@ -248,9 +253,10 @@ class MultilineRegExpCursor {
|
|||
this.to = to;
|
||||
this.done = false;
|
||||
this.value = empty;
|
||||
this.matchPos = from;
|
||||
this.matchPos = toCharEnd(text, from);
|
||||
this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
|
||||
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Base */));
|
||||
this.test = options === null || options === void 0 ? void 0 : options.test;
|
||||
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Chunk.Base */));
|
||||
}
|
||||
chunkEnd(pos) {
|
||||
return pos >= this.to ? this.to : this.text.lineAt(pos).to;
|
||||
|
@ -264,24 +270,23 @@ class MultilineRegExpCursor {
|
|||
this.re.lastIndex = off + 1;
|
||||
match = this.re.exec(this.flat.text);
|
||||
}
|
||||
// If a match goes almost to the end of a noncomplete chunk, try
|
||||
// again, since it'll likely be able to match more
|
||||
if (match && this.flat.to < this.to && match.index + match[0].length > this.flat.text.length - 10)
|
||||
match = null;
|
||||
if (match) {
|
||||
let from = this.flat.from + match.index, to = from + match[0].length;
|
||||
this.value = { from, to, match };
|
||||
this.matchPos = to + (from == to ? 1 : 0);
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
if (this.flat.to == this.to) {
|
||||
this.done = true;
|
||||
// If a match goes almost to the end of a noncomplete chunk, try
|
||||
// again, since it'll likely be able to match more
|
||||
if ((this.flat.to >= this.to || match.index + match[0].length <= this.flat.text.length - 10) &&
|
||||
(!this.test || this.test(from, to, match))) {
|
||||
this.value = { from, to, match };
|
||||
this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
// Grow the flattened doc
|
||||
this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2));
|
||||
}
|
||||
if (this.flat.to == this.to) {
|
||||
this.done = true;
|
||||
return this;
|
||||
}
|
||||
// Grow the flattened doc
|
||||
this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,6 +303,14 @@ function validRegExp(source) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
function toCharEnd(text, pos) {
|
||||
if (pos >= text.length)
|
||||
return pos;
|
||||
let line = text.lineAt(pos), next;
|
||||
while (pos < line.to && (next = line.text.charCodeAt(pos - line.from)) >= 0xDC00 && next < 0xE000)
|
||||
pos++;
|
||||
return pos;
|
||||
}
|
||||
|
||||
function createLineDialog(view) {
|
||||
let input = elt__default["default"]("input", { class: "cm-textfield", name: "line" });
|
||||
|
@ -549,12 +562,13 @@ const selectNextOccurrence = ({ state: state$1, dispatch }) => {
|
|||
|
||||
const searchConfigFacet = state.Facet.define({
|
||||
combine(configs) {
|
||||
var _a;
|
||||
return {
|
||||
top: configs.reduce((val, conf) => val !== null && val !== void 0 ? val : conf.top, undefined) || false,
|
||||
caseSensitive: configs.reduce((val, conf) => val !== null && val !== void 0 ? val : conf.caseSensitive, undefined) || false,
|
||||
createPanel: ((_a = configs.find(c => c.createPanel)) === null || _a === void 0 ? void 0 : _a.createPanel) || (view => new SearchPanel(view))
|
||||
};
|
||||
return state.combineConfig(configs, {
|
||||
top: false,
|
||||
caseSensitive: false,
|
||||
literal: false,
|
||||
wholeWord: false,
|
||||
createPanel: view => new SearchPanel(view)
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
|
@ -576,17 +590,27 @@ class SearchQuery {
|
|||
constructor(config) {
|
||||
this.search = config.search;
|
||||
this.caseSensitive = !!config.caseSensitive;
|
||||
this.literal = !!config.literal;
|
||||
this.regexp = !!config.regexp;
|
||||
this.replace = config.replace || "";
|
||||
this.valid = !!this.search && (!this.regexp || validRegExp(this.search));
|
||||
this.unquoted = config.literal ? this.search : this.search.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\");
|
||||
this.unquoted = this.unquote(this.search);
|
||||
this.wholeWord = !!config.wholeWord;
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
*/
|
||||
unquote(text) {
|
||||
return this.literal ? text :
|
||||
text.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\");
|
||||
}
|
||||
/**
|
||||
Compare this query to another query.
|
||||
*/
|
||||
eq(other) {
|
||||
return this.search == other.search && this.replace == other.replace &&
|
||||
this.caseSensitive == other.caseSensitive && this.regexp == other.regexp;
|
||||
this.caseSensitive == other.caseSensitive && this.regexp == other.regexp &&
|
||||
this.wholeWord == other.wholeWord;
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
|
@ -596,10 +620,13 @@ class SearchQuery {
|
|||
}
|
||||
/**
|
||||
Get a search cursor for this query, searching through the given
|
||||
range in the given document.
|
||||
range in the given state.
|
||||
*/
|
||||
getCursor(doc, from = 0, to = doc.length) {
|
||||
return this.regexp ? regexpCursor(this, doc, from, to) : stringCursor(this, doc, from, to);
|
||||
getCursor(state$1, from = 0, to) {
|
||||
let st = state$1.doc ? state$1 : state.EditorState.create({ doc: state$1 });
|
||||
if (to == null)
|
||||
to = st.doc.length;
|
||||
return this.regexp ? regexpCursor(this, st, from, to) : stringCursor(this, st, from, to);
|
||||
}
|
||||
}
|
||||
class QueryType {
|
||||
|
@ -607,41 +634,53 @@ class QueryType {
|
|||
this.spec = spec;
|
||||
}
|
||||
}
|
||||
function stringCursor(spec, doc, from, to) {
|
||||
return new SearchCursor(doc, spec.unquoted, from, to, spec.caseSensitive ? undefined : x => x.toLowerCase());
|
||||
function stringCursor(spec, state, from, to) {
|
||||
return new SearchCursor(state.doc, spec.unquoted, from, to, spec.caseSensitive ? undefined : x => x.toLowerCase(), spec.wholeWord ? stringWordTest(state.doc, state.charCategorizer(state.selection.main.head)) : undefined);
|
||||
}
|
||||
function stringWordTest(doc, categorizer) {
|
||||
return (from, to, buf, bufPos) => {
|
||||
if (bufPos > from || bufPos + buf.length < to) {
|
||||
bufPos = Math.max(0, from - 2);
|
||||
buf = doc.sliceString(bufPos, Math.min(doc.length, to + 2));
|
||||
}
|
||||
return (categorizer(charBefore(buf, from - bufPos)) != state.CharCategory.Word ||
|
||||
categorizer(charAfter(buf, from - bufPos)) != state.CharCategory.Word) &&
|
||||
(categorizer(charAfter(buf, to - bufPos)) != state.CharCategory.Word ||
|
||||
categorizer(charBefore(buf, to - bufPos)) != state.CharCategory.Word);
|
||||
};
|
||||
}
|
||||
class StringQuery extends QueryType {
|
||||
constructor(spec) {
|
||||
super(spec);
|
||||
}
|
||||
nextMatch(doc, curFrom, curTo) {
|
||||
let cursor = stringCursor(this.spec, doc, curTo, doc.length).nextOverlapping();
|
||||
nextMatch(state, curFrom, curTo) {
|
||||
let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping();
|
||||
if (cursor.done)
|
||||
cursor = stringCursor(this.spec, doc, 0, curFrom).nextOverlapping();
|
||||
cursor = stringCursor(this.spec, state, 0, curFrom).nextOverlapping();
|
||||
return cursor.done ? null : cursor.value;
|
||||
}
|
||||
// Searching in reverse is, rather than implementing inverted search
|
||||
// cursor, done by scanning chunk after chunk forward.
|
||||
prevMatchInRange(doc, from, to) {
|
||||
prevMatchInRange(state, from, to) {
|
||||
for (let pos = to;;) {
|
||||
let start = Math.max(from, pos - 10000 /* ChunkSize */ - this.spec.unquoted.length);
|
||||
let cursor = stringCursor(this.spec, doc, start, pos), range = null;
|
||||
let start = Math.max(from, pos - 10000 /* FindPrev.ChunkSize */ - this.spec.unquoted.length);
|
||||
let cursor = stringCursor(this.spec, state, start, pos), range = null;
|
||||
while (!cursor.nextOverlapping().done)
|
||||
range = cursor.value;
|
||||
if (range)
|
||||
return range;
|
||||
if (start == from)
|
||||
return null;
|
||||
pos -= 10000 /* ChunkSize */;
|
||||
pos -= 10000 /* FindPrev.ChunkSize */;
|
||||
}
|
||||
}
|
||||
prevMatch(doc, curFrom, curTo) {
|
||||
return this.prevMatchInRange(doc, 0, curFrom) ||
|
||||
this.prevMatchInRange(doc, curTo, doc.length);
|
||||
prevMatch(state, curFrom, curTo) {
|
||||
return this.prevMatchInRange(state, 0, curFrom) ||
|
||||
this.prevMatchInRange(state, curTo, state.doc.length);
|
||||
}
|
||||
getReplacement(_result) { return this.spec.replace; }
|
||||
matchAll(doc, limit) {
|
||||
let cursor = stringCursor(this.spec, doc, 0, doc.length), ranges = [];
|
||||
getReplacement(_result) { return this.spec.unquote(this.spec.replace); }
|
||||
matchAll(state, limit) {
|
||||
let cursor = stringCursor(this.spec, state, 0, state.doc.length), ranges = [];
|
||||
while (!cursor.next().done) {
|
||||
if (ranges.length >= limit)
|
||||
return null;
|
||||
|
@ -649,26 +688,42 @@ class StringQuery extends QueryType {
|
|||
}
|
||||
return ranges;
|
||||
}
|
||||
highlight(doc, from, to, add) {
|
||||
let cursor = stringCursor(this.spec, doc, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, doc.length));
|
||||
highlight(state, from, to, add) {
|
||||
let cursor = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length));
|
||||
while (!cursor.next().done)
|
||||
add(cursor.value.from, cursor.value.to);
|
||||
}
|
||||
}
|
||||
function regexpCursor(spec, doc, from, to) {
|
||||
return new RegExpCursor(doc, spec.search, spec.caseSensitive ? undefined : { ignoreCase: true }, from, to);
|
||||
function regexpCursor(spec, state, from, to) {
|
||||
return new RegExpCursor(state.doc, spec.search, {
|
||||
ignoreCase: !spec.caseSensitive,
|
||||
test: spec.wholeWord ? regexpWordTest(state.charCategorizer(state.selection.main.head)) : undefined
|
||||
}, from, to);
|
||||
}
|
||||
function charBefore(str, index) {
|
||||
return str.slice(state.findClusterBreak(str, index, false), index);
|
||||
}
|
||||
function charAfter(str, index) {
|
||||
return str.slice(index, state.findClusterBreak(str, index));
|
||||
}
|
||||
function regexpWordTest(categorizer) {
|
||||
return (_from, _to, match) => !match[0].length ||
|
||||
(categorizer(charBefore(match.input, match.index)) != state.CharCategory.Word ||
|
||||
categorizer(charAfter(match.input, match.index)) != state.CharCategory.Word) &&
|
||||
(categorizer(charAfter(match.input, match.index + match[0].length)) != state.CharCategory.Word ||
|
||||
categorizer(charBefore(match.input, match.index + match[0].length)) != state.CharCategory.Word);
|
||||
}
|
||||
class RegExpQuery extends QueryType {
|
||||
nextMatch(doc, curFrom, curTo) {
|
||||
let cursor = regexpCursor(this.spec, doc, curTo, doc.length).next();
|
||||
nextMatch(state, curFrom, curTo) {
|
||||
let cursor = regexpCursor(this.spec, state, curTo, state.doc.length).next();
|
||||
if (cursor.done)
|
||||
cursor = regexpCursor(this.spec, doc, 0, curFrom).next();
|
||||
cursor = regexpCursor(this.spec, state, 0, curFrom).next();
|
||||
return cursor.done ? null : cursor.value;
|
||||
}
|
||||
prevMatchInRange(doc, from, to) {
|
||||
prevMatchInRange(state, from, to) {
|
||||
for (let size = 1;; size++) {
|
||||
let start = Math.max(from, to - size * 10000 /* ChunkSize */);
|
||||
let cursor = regexpCursor(this.spec, doc, start, to), range = null;
|
||||
let start = Math.max(from, to - size * 10000 /* FindPrev.ChunkSize */);
|
||||
let cursor = regexpCursor(this.spec, state, start, to), range = null;
|
||||
while (!cursor.next().done)
|
||||
range = cursor.value;
|
||||
if (range && (start == from || range.from > start + 10))
|
||||
|
@ -677,18 +732,18 @@ class RegExpQuery extends QueryType {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
prevMatch(doc, curFrom, curTo) {
|
||||
return this.prevMatchInRange(doc, 0, curFrom) ||
|
||||
this.prevMatchInRange(doc, curTo, doc.length);
|
||||
prevMatch(state, curFrom, curTo) {
|
||||
return this.prevMatchInRange(state, 0, curFrom) ||
|
||||
this.prevMatchInRange(state, curTo, state.doc.length);
|
||||
}
|
||||
getReplacement(result) {
|
||||
return this.spec.replace.replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
|
||||
return this.spec.unquote(this.spec.replace.replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
|
||||
: i == "&" ? result.match[0]
|
||||
: i != "0" && +i < result.match.length ? result.match[i]
|
||||
: m);
|
||||
: m));
|
||||
}
|
||||
matchAll(doc, limit) {
|
||||
let cursor = regexpCursor(this.spec, doc, 0, doc.length), ranges = [];
|
||||
matchAll(state, limit) {
|
||||
let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = [];
|
||||
while (!cursor.next().done) {
|
||||
if (ranges.length >= limit)
|
||||
return null;
|
||||
|
@ -696,8 +751,8 @@ class RegExpQuery extends QueryType {
|
|||
}
|
||||
return ranges;
|
||||
}
|
||||
highlight(doc, from, to, add) {
|
||||
let cursor = regexpCursor(this.spec, doc, Math.max(0, from - 250 /* HighlightMargin */), Math.min(to + 250 /* HighlightMargin */, doc.length));
|
||||
highlight(state, from, to, add) {
|
||||
let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250 /* RegExp.HighlightMargin */), Math.min(to + 250 /* RegExp.HighlightMargin */, state.doc.length));
|
||||
while (!cursor.next().done)
|
||||
add(cursor.value.from, cursor.value.to);
|
||||
}
|
||||
|
@ -733,6 +788,13 @@ function getSearchQuery(state) {
|
|||
let curState = state.field(searchState, false);
|
||||
return curState ? curState.query.spec : defaultQuery(state);
|
||||
}
|
||||
/**
|
||||
Query whether the search panel is open in the given editor state.
|
||||
*/
|
||||
function searchPanelOpen(state) {
|
||||
var _a;
|
||||
return ((_a = state.field(searchState, false)) === null || _a === void 0 ? void 0 : _a.panel) != null;
|
||||
}
|
||||
class SearchState {
|
||||
constructor(query, panel) {
|
||||
this.query = query;
|
||||
|
@ -757,9 +819,9 @@ const searchHighlighter = view.ViewPlugin.fromClass(class {
|
|||
let builder = new state.RangeSetBuilder();
|
||||
for (let i = 0, ranges = view$1.visibleRanges, l = ranges.length; i < l; i++) {
|
||||
let { from, to } = ranges[i];
|
||||
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* HighlightMargin */)
|
||||
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* RegExp.HighlightMargin */)
|
||||
to = ranges[++i].to;
|
||||
query.highlight(view$1.state.doc, from, to, (from, to) => {
|
||||
query.highlight(view$1.state, from, to, (from, to) => {
|
||||
let selected = view$1.state.selection.ranges.some(r => r.from == from && r.to == to);
|
||||
builder.add(from, to, selected ? selectedMatchMark : matchMark);
|
||||
});
|
||||
|
@ -782,9 +844,9 @@ Will wrap around to the start of the document when it reaches the
|
|||
end.
|
||||
*/
|
||||
const findNext = searchCommand((view, { query }) => {
|
||||
let { from, to } = view.state.selection.main;
|
||||
let next = query.nextMatch(view.state.doc, from, to);
|
||||
if (!next || next.from == from && next.to == to)
|
||||
let { to } = view.state.selection.main;
|
||||
let next = query.nextMatch(view.state, to, to);
|
||||
if (!next)
|
||||
return false;
|
||||
view.dispatch({
|
||||
selection: { anchor: next.from, head: next.to },
|
||||
|
@ -800,8 +862,8 @@ before the current main selection. Will wrap past the start
|
|||
of the document to start searching at the end again.
|
||||
*/
|
||||
const findPrevious = searchCommand((view, { query }) => {
|
||||
let { state } = view, { from, to } = state.selection.main;
|
||||
let range = query.prevMatch(state.doc, from, to);
|
||||
let { state } = view, { from } = state.selection.main;
|
||||
let range = query.prevMatch(state, from, from);
|
||||
if (!range)
|
||||
return false;
|
||||
view.dispatch({
|
||||
|
@ -816,7 +878,7 @@ const findPrevious = searchCommand((view, { query }) => {
|
|||
Select all instances of the search query.
|
||||
*/
|
||||
const selectMatches = searchCommand((view, { query }) => {
|
||||
let ranges = query.matchAll(view.state.doc, 1000);
|
||||
let ranges = query.matchAll(view.state, 1000);
|
||||
if (!ranges || !ranges.length)
|
||||
return false;
|
||||
view.dispatch({
|
||||
|
@ -854,7 +916,7 @@ const replaceNext = searchCommand((view$1, { query }) => {
|
|||
let { state } = view$1, { from, to } = state.selection.main;
|
||||
if (state.readOnly)
|
||||
return false;
|
||||
let next = query.nextMatch(state.doc, from, from);
|
||||
let next = query.nextMatch(state, from, from);
|
||||
if (!next)
|
||||
return false;
|
||||
let changes = [], selection, replacement;
|
||||
|
@ -862,7 +924,7 @@ const replaceNext = searchCommand((view$1, { query }) => {
|
|||
if (next.from == from && next.to == to) {
|
||||
replacement = state.toText(query.getReplacement(next));
|
||||
changes.push({ from: next.from, to: next.to, insert: replacement });
|
||||
next = query.nextMatch(state.doc, next.from, next.to);
|
||||
next = query.nextMatch(state, next.from, next.to);
|
||||
announce.push(view.EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + "."));
|
||||
}
|
||||
if (next) {
|
||||
|
@ -885,7 +947,7 @@ replacement.
|
|||
const replaceAll = searchCommand((view$1, { query }) => {
|
||||
if (view$1.state.readOnly)
|
||||
return false;
|
||||
let changes = query.matchAll(view$1.state.doc, 1e9).map(match => {
|
||||
let changes = query.matchAll(view$1.state, 1e9).map(match => {
|
||||
let { from, to } = match;
|
||||
return { from, to, insert: query.getReplacement(match) };
|
||||
});
|
||||
|
@ -903,11 +965,18 @@ function createSearchPanel(view) {
|
|||
return view.state.facet(searchConfigFacet).createPanel(view);
|
||||
}
|
||||
function defaultQuery(state, fallback) {
|
||||
var _a;
|
||||
var _a, _b, _c, _d;
|
||||
let sel = state.selection.main;
|
||||
let selText = sel.empty || sel.to > sel.from + 100 ? "" : state.sliceDoc(sel.from, sel.to);
|
||||
let caseSensitive = (_a = fallback === null || fallback === void 0 ? void 0 : fallback.caseSensitive) !== null && _a !== void 0 ? _a : state.facet(searchConfigFacet).caseSensitive;
|
||||
return fallback && !selText ? fallback : new SearchQuery({ search: selText.replace(/\n/g, "\\n"), caseSensitive });
|
||||
if (fallback && !selText)
|
||||
return fallback;
|
||||
let config = state.facet(searchConfigFacet);
|
||||
return new SearchQuery({
|
||||
search: ((_a = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _a !== void 0 ? _a : config.literal) ? selText : selText.replace(/\n/g, "\\n"),
|
||||
caseSensitive: (_b = fallback === null || fallback === void 0 ? void 0 : fallback.caseSensitive) !== null && _b !== void 0 ? _b : config.caseSensitive,
|
||||
literal: (_c = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _c !== void 0 ? _c : config.literal,
|
||||
wholeWord: (_d = fallback === null || fallback === void 0 ? void 0 : fallback.wholeWord) !== null && _d !== void 0 ? _d : config.wholeWord
|
||||
});
|
||||
}
|
||||
/**
|
||||
Make sure the search panel is open and focused.
|
||||
|
@ -977,6 +1046,7 @@ class SearchPanel {
|
|||
"aria-label": phrase(view, "Find"),
|
||||
class: "cm-textfield",
|
||||
name: "search",
|
||||
form: "",
|
||||
"main-field": "true",
|
||||
onchange: this.commit,
|
||||
onkeyup: this.commit
|
||||
|
@ -987,21 +1057,31 @@ class SearchPanel {
|
|||
"aria-label": phrase(view, "Replace"),
|
||||
class: "cm-textfield",
|
||||
name: "replace",
|
||||
form: "",
|
||||
onchange: this.commit,
|
||||
onkeyup: this.commit
|
||||
});
|
||||
this.caseField = elt__default["default"]("input", {
|
||||
type: "checkbox",
|
||||
name: "case",
|
||||
form: "",
|
||||
checked: query.caseSensitive,
|
||||
onchange: this.commit
|
||||
});
|
||||
this.reField = elt__default["default"]("input", {
|
||||
type: "checkbox",
|
||||
name: "re",
|
||||
form: "",
|
||||
checked: query.regexp,
|
||||
onchange: this.commit
|
||||
});
|
||||
this.wordField = elt__default["default"]("input", {
|
||||
type: "checkbox",
|
||||
name: "word",
|
||||
form: "",
|
||||
checked: query.wholeWord,
|
||||
onchange: this.commit
|
||||
});
|
||||
function button(name, onclick, content) {
|
||||
return elt__default["default"]("button", { class: "cm-button", name, onclick, type: "button" }, content);
|
||||
}
|
||||
|
@ -1012,18 +1092,19 @@ class SearchPanel {
|
|||
button("select", () => selectMatches(view), [phrase(view, "all")]),
|
||||
elt__default["default"]("label", null, [this.caseField, phrase(view, "match case")]),
|
||||
elt__default["default"]("label", null, [this.reField, phrase(view, "regexp")]),
|
||||
elt__default["default"]("label", null, [this.wordField, phrase(view, "by word")]),
|
||||
...view.state.readOnly ? [] : [
|
||||
elt__default["default"]("br"),
|
||||
this.replaceField,
|
||||
button("replace", () => replaceNext(view), [phrase(view, "replace")]),
|
||||
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")]),
|
||||
elt__default["default"]("button", {
|
||||
name: "close",
|
||||
onclick: () => closeSearchPanel(view),
|
||||
"aria-label": phrase(view, "close"),
|
||||
type: "button"
|
||||
}, ["×"])
|
||||
]
|
||||
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
|
||||
],
|
||||
elt__default["default"]("button", {
|
||||
name: "close",
|
||||
onclick: () => closeSearchPanel(view),
|
||||
"aria-label": phrase(view, "close"),
|
||||
type: "button"
|
||||
}, ["×"])
|
||||
]);
|
||||
}
|
||||
commit() {
|
||||
|
@ -1031,7 +1112,8 @@ class SearchPanel {
|
|||
search: this.searchField.value,
|
||||
caseSensitive: this.caseField.checked,
|
||||
regexp: this.reField.checked,
|
||||
replace: this.replaceField.value
|
||||
wholeWord: this.wordField.checked,
|
||||
replace: this.replaceField.value,
|
||||
});
|
||||
if (!query.eq(this.query)) {
|
||||
this.query = query;
|
||||
|
@ -1064,6 +1146,7 @@ class SearchPanel {
|
|||
this.replaceField.value = query.replace;
|
||||
this.caseField.checked = query.caseSensitive;
|
||||
this.reField.checked = query.regexp;
|
||||
this.wordField.checked = query.wholeWord;
|
||||
}
|
||||
mount() {
|
||||
this.searchField.select();
|
||||
|
@ -1144,6 +1227,7 @@ exports.replaceAll = replaceAll;
|
|||
exports.replaceNext = replaceNext;
|
||||
exports.search = search;
|
||||
exports.searchKeymap = searchKeymap;
|
||||
exports.searchPanelOpen = searchPanelOpen;
|
||||
exports.selectMatches = selectMatches;
|
||||
exports.selectNextOccurrence = selectNextOccurrence;
|
||||
exports.selectSelectionMatches = selectSelectionMatches;
|
||||
|
|
52
node_modules/@codemirror/search/dist/index.d.ts
generated
vendored
52
node_modules/@codemirror/search/dist/index.d.ts
generated
vendored
|
@ -10,6 +10,7 @@ declare class SearchCursor implements Iterator<{
|
|||
from: number;
|
||||
to: number;
|
||||
}> {
|
||||
private test?;
|
||||
private iter;
|
||||
/**
|
||||
The current match (only holds a meaningful value after
|
||||
|
@ -43,7 +44,7 @@ declare class SearchCursor implements Iterator<{
|
|||
[`.normalize("NFKD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
|
||||
(when supported).
|
||||
*/
|
||||
constructor(text: Text, query: string, from?: number, to?: number, normalize?: (string: string) => string);
|
||||
constructor(text: Text, query: string, from?: number, to?: number, normalize?: (string: string) => string, test?: ((from: number, to: number, buffer: string, bufferPos: number) => boolean) | undefined);
|
||||
private peek;
|
||||
/**
|
||||
Look for the next match. Updates the iterator's
|
||||
|
@ -65,6 +66,10 @@ declare class SearchCursor implements Iterator<{
|
|||
}>;
|
||||
}
|
||||
|
||||
interface RegExpCursorOptions {
|
||||
ignoreCase?: boolean;
|
||||
test?: (from: number, to: number, match: RegExpExecArray) => boolean;
|
||||
}
|
||||
/**
|
||||
This class is similar to [`SearchCursor`](https://codemirror.net/6/docs/ref/#search.SearchCursor)
|
||||
but searches for a regular expression pattern instead of a plain
|
||||
|
@ -75,9 +80,11 @@ declare class RegExpCursor implements Iterator<{
|
|||
to: number;
|
||||
match: RegExpExecArray;
|
||||
}> {
|
||||
private text;
|
||||
private to;
|
||||
private iter;
|
||||
private re;
|
||||
private test?;
|
||||
private curLine;
|
||||
private curLineStart;
|
||||
private matchPos;
|
||||
|
@ -101,9 +108,7 @@ declare class RegExpCursor implements Iterator<{
|
|||
document. `query` should be the raw pattern (as you'd pass it to
|
||||
`new RegExp`).
|
||||
*/
|
||||
constructor(text: Text, query: string, options?: {
|
||||
ignoreCase?: boolean;
|
||||
}, from?: number, to?: number);
|
||||
constructor(text: Text, query: string, options?: RegExpCursorOptions, from?: number, to?: number);
|
||||
private getLine;
|
||||
private nextLine;
|
||||
/**
|
||||
|
@ -177,6 +182,15 @@ interface SearchConfig {
|
|||
*/
|
||||
caseSensitive?: boolean;
|
||||
/**
|
||||
Whether to treat string searches literally by default (defaults to false).
|
||||
*/
|
||||
literal?: boolean;
|
||||
/**
|
||||
Controls whether the default query has by-word matching enabled.
|
||||
Defaults to false.
|
||||
*/
|
||||
wholeWord?: boolean;
|
||||
/**
|
||||
Can be used to override the way the search panel is implemented.
|
||||
Should create a [Panel](https://codemirror.net/6/docs/ref/#view.Panel) that contains a form
|
||||
which lets the user:
|
||||
|
@ -188,6 +202,9 @@ interface SearchConfig {
|
|||
- Notice external changes to the query by reacting to the
|
||||
appropriate [state effect](https://codemirror.net/6/docs/ref/#search.setSearchQuery).
|
||||
- Run some of the search commands.
|
||||
|
||||
The field that should be focused when opening the panel must be
|
||||
tagged with a `main-field=true` DOM attribute.
|
||||
*/
|
||||
createPanel?: (view: EditorView) => Panel;
|
||||
}
|
||||
|
@ -211,7 +228,13 @@ declare class SearchQuery {
|
|||
*/
|
||||
readonly caseSensitive: boolean;
|
||||
/**
|
||||
Then true, the search string is interpreted as a regular
|
||||
By default, string search will replace `\n`, `\r`, and `\t` in
|
||||
the query with newline, return, and tab characters. When this
|
||||
is set to true, that behavior is disabled.
|
||||
*/
|
||||
readonly literal: boolean;
|
||||
/**
|
||||
When true, the search string is interpreted as a regular
|
||||
expression.
|
||||
*/
|
||||
readonly regexp: boolean;
|
||||
|
@ -226,6 +249,11 @@ declare class SearchQuery {
|
|||
*/
|
||||
readonly valid: boolean;
|
||||
/**
|
||||
When true, matches that contain words are ignored when there are
|
||||
further word characters around them.
|
||||
*/
|
||||
readonly wholeWord: boolean;
|
||||
/**
|
||||
Create a query object.
|
||||
*/
|
||||
constructor(config: {
|
||||
|
@ -251,6 +279,10 @@ declare class SearchQuery {
|
|||
The replace text.
|
||||
*/
|
||||
replace?: string;
|
||||
/**
|
||||
Enable whole-word matching.
|
||||
*/
|
||||
wholeWord?: boolean;
|
||||
});
|
||||
/**
|
||||
Compare this query to another query.
|
||||
|
@ -258,9 +290,9 @@ declare class SearchQuery {
|
|||
eq(other: SearchQuery): boolean;
|
||||
/**
|
||||
Get a search cursor for this query, searching through the given
|
||||
range in the given document.
|
||||
range in the given state.
|
||||
*/
|
||||
getCursor(doc: Text, from?: number, to?: number): Iterator<{
|
||||
getCursor(state: EditorState | Text, from?: number, to?: number): Iterator<{
|
||||
from: number;
|
||||
to: number;
|
||||
}>;
|
||||
|
@ -278,6 +310,10 @@ Get the current search query from an editor state.
|
|||
*/
|
||||
declare function getSearchQuery(state: EditorState): SearchQuery;
|
||||
/**
|
||||
Query whether the search panel is open in the given editor state.
|
||||
*/
|
||||
declare function searchPanelOpen(state: EditorState): boolean;
|
||||
/**
|
||||
Open the search panel if it isn't already open, and move the
|
||||
selection to the first match after the current main selection.
|
||||
Will wrap around to the start of the document when it reaches the
|
||||
|
@ -326,4 +362,4 @@ Default search-related key bindings.
|
|||
*/
|
||||
declare const searchKeymap: readonly KeyBinding[];
|
||||
|
||||
export { RegExpCursor, SearchCursor, SearchQuery, closeSearchPanel, findNext, findPrevious, getSearchQuery, gotoLine, highlightSelectionMatches, openSearchPanel, replaceAll, replaceNext, search, searchKeymap, selectMatches, selectNextOccurrence, selectSelectionMatches, setSearchQuery };
|
||||
export { RegExpCursor, SearchCursor, SearchQuery, closeSearchPanel, findNext, findPrevious, getSearchQuery, gotoLine, highlightSelectionMatches, openSearchPanel, replaceAll, replaceNext, search, searchKeymap, searchPanelOpen, selectMatches, selectNextOccurrence, selectSelectionMatches, setSearchQuery };
|
||||
|
|
263
node_modules/@codemirror/search/dist/index.js
generated
vendored
263
node_modules/@codemirror/search/dist/index.js
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
import { showPanel, EditorView, getPanel, Decoration, ViewPlugin, runScopeHandlers } from '@codemirror/view';
|
||||
import { codePointAt, fromCodePoint, codePointSize, StateEffect, StateField, EditorSelection, Facet, combineConfig, CharCategory, RangeSetBuilder, Prec } from '@codemirror/state';
|
||||
import { codePointAt, fromCodePoint, codePointSize, StateEffect, StateField, EditorSelection, Facet, combineConfig, CharCategory, RangeSetBuilder, Prec, EditorState, findClusterBreak } from '@codemirror/state';
|
||||
import elt from 'crelt';
|
||||
|
||||
const basicNormalize = typeof String.prototype.normalize == "function"
|
||||
|
@ -22,7 +22,8 @@ class SearchCursor {
|
|||
[`.normalize("NFKD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
|
||||
(when supported).
|
||||
*/
|
||||
constructor(text, query, from = 0, to = text.length, normalize) {
|
||||
constructor(text, query, from = 0, to = text.length, normalize, test) {
|
||||
this.test = test;
|
||||
/**
|
||||
The current match (only holds a meaningful value after
|
||||
[`next`](https://codemirror.net/6/docs/ref/#search.SearchCursor.next) has been called and when
|
||||
|
@ -116,6 +117,8 @@ class SearchCursor {
|
|||
else
|
||||
this.matches.push(1, pos);
|
||||
}
|
||||
if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferPos))
|
||||
match = null;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +139,7 @@ class RegExpCursor {
|
|||
`new RegExp`).
|
||||
*/
|
||||
constructor(text, query, options, from = 0, to = text.length) {
|
||||
this.text = text;
|
||||
this.to = to;
|
||||
this.curLine = "";
|
||||
/**
|
||||
|
@ -152,10 +156,11 @@ class RegExpCursor {
|
|||
if (/\\[sWDnr]|\n|\r|\[\^/.test(query))
|
||||
return new MultilineRegExpCursor(text, query, options, from, to);
|
||||
this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
|
||||
this.test = options === null || options === void 0 ? void 0 : options.test;
|
||||
this.iter = text.iter();
|
||||
let startLine = text.lineAt(from);
|
||||
this.curLineStart = startLine.from;
|
||||
this.matchPos = from;
|
||||
this.matchPos = toCharEnd(text, from);
|
||||
this.getLine(this.curLineStart);
|
||||
}
|
||||
getLine(skip) {
|
||||
|
@ -186,10 +191,10 @@ class RegExpCursor {
|
|||
let match = this.matchPos <= this.to && this.re.exec(this.curLine);
|
||||
if (match) {
|
||||
let from = this.curLineStart + match.index, to = from + match[0].length;
|
||||
this.matchPos = to + (from == to ? 1 : 0);
|
||||
if (from == this.curLine.length)
|
||||
this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
|
||||
if (from == this.curLineStart + this.curLine.length)
|
||||
this.nextLine();
|
||||
if (from < to || from > this.value.to) {
|
||||
if ((from < to || from > this.value.to) && (!this.test || this.test(from, to, match))) {
|
||||
this.value = { from, to, match };
|
||||
return this;
|
||||
}
|
||||
|
@ -240,9 +245,10 @@ class MultilineRegExpCursor {
|
|||
this.to = to;
|
||||
this.done = false;
|
||||
this.value = empty;
|
||||
this.matchPos = from;
|
||||
this.matchPos = toCharEnd(text, from);
|
||||
this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
|
||||
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Base */));
|
||||
this.test = options === null || options === void 0 ? void 0 : options.test;
|
||||
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Chunk.Base */));
|
||||
}
|
||||
chunkEnd(pos) {
|
||||
return pos >= this.to ? this.to : this.text.lineAt(pos).to;
|
||||
|
@ -256,24 +262,23 @@ class MultilineRegExpCursor {
|
|||
this.re.lastIndex = off + 1;
|
||||
match = this.re.exec(this.flat.text);
|
||||
}
|
||||
// If a match goes almost to the end of a noncomplete chunk, try
|
||||
// again, since it'll likely be able to match more
|
||||
if (match && this.flat.to < this.to && match.index + match[0].length > this.flat.text.length - 10)
|
||||
match = null;
|
||||
if (match) {
|
||||
let from = this.flat.from + match.index, to = from + match[0].length;
|
||||
this.value = { from, to, match };
|
||||
this.matchPos = to + (from == to ? 1 : 0);
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
if (this.flat.to == this.to) {
|
||||
this.done = true;
|
||||
// If a match goes almost to the end of a noncomplete chunk, try
|
||||
// again, since it'll likely be able to match more
|
||||
if ((this.flat.to >= this.to || match.index + match[0].length <= this.flat.text.length - 10) &&
|
||||
(!this.test || this.test(from, to, match))) {
|
||||
this.value = { from, to, match };
|
||||
this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
// Grow the flattened doc
|
||||
this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2));
|
||||
}
|
||||
if (this.flat.to == this.to) {
|
||||
this.done = true;
|
||||
return this;
|
||||
}
|
||||
// Grow the flattened doc
|
||||
this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -290,6 +295,14 @@ function validRegExp(source) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
function toCharEnd(text, pos) {
|
||||
if (pos >= text.length)
|
||||
return pos;
|
||||
let line = text.lineAt(pos), next;
|
||||
while (pos < line.to && (next = line.text.charCodeAt(pos - line.from)) >= 0xDC00 && next < 0xE000)
|
||||
pos++;
|
||||
return pos;
|
||||
}
|
||||
|
||||
function createLineDialog(view) {
|
||||
let input = elt("input", { class: "cm-textfield", name: "line" });
|
||||
|
@ -541,12 +554,13 @@ const selectNextOccurrence = ({ state, dispatch }) => {
|
|||
|
||||
const searchConfigFacet = /*@__PURE__*/Facet.define({
|
||||
combine(configs) {
|
||||
var _a;
|
||||
return {
|
||||
top: configs.reduce((val, conf) => val !== null && val !== void 0 ? val : conf.top, undefined) || false,
|
||||
caseSensitive: configs.reduce((val, conf) => val !== null && val !== void 0 ? val : conf.caseSensitive, undefined) || false,
|
||||
createPanel: ((_a = configs.find(c => c.createPanel)) === null || _a === void 0 ? void 0 : _a.createPanel) || (view => new SearchPanel(view))
|
||||
};
|
||||
return combineConfig(configs, {
|
||||
top: false,
|
||||
caseSensitive: false,
|
||||
literal: false,
|
||||
wholeWord: false,
|
||||
createPanel: view => new SearchPanel(view)
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
|
@ -568,17 +582,27 @@ class SearchQuery {
|
|||
constructor(config) {
|
||||
this.search = config.search;
|
||||
this.caseSensitive = !!config.caseSensitive;
|
||||
this.literal = !!config.literal;
|
||||
this.regexp = !!config.regexp;
|
||||
this.replace = config.replace || "";
|
||||
this.valid = !!this.search && (!this.regexp || validRegExp(this.search));
|
||||
this.unquoted = config.literal ? this.search : this.search.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\");
|
||||
this.unquoted = this.unquote(this.search);
|
||||
this.wholeWord = !!config.wholeWord;
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
*/
|
||||
unquote(text) {
|
||||
return this.literal ? text :
|
||||
text.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\");
|
||||
}
|
||||
/**
|
||||
Compare this query to another query.
|
||||
*/
|
||||
eq(other) {
|
||||
return this.search == other.search && this.replace == other.replace &&
|
||||
this.caseSensitive == other.caseSensitive && this.regexp == other.regexp;
|
||||
this.caseSensitive == other.caseSensitive && this.regexp == other.regexp &&
|
||||
this.wholeWord == other.wholeWord;
|
||||
}
|
||||
/**
|
||||
@internal
|
||||
|
@ -588,10 +612,13 @@ class SearchQuery {
|
|||
}
|
||||
/**
|
||||
Get a search cursor for this query, searching through the given
|
||||
range in the given document.
|
||||
range in the given state.
|
||||
*/
|
||||
getCursor(doc, from = 0, to = doc.length) {
|
||||
return this.regexp ? regexpCursor(this, doc, from, to) : stringCursor(this, doc, from, to);
|
||||
getCursor(state, from = 0, to) {
|
||||
let st = state.doc ? state : EditorState.create({ doc: state });
|
||||
if (to == null)
|
||||
to = st.doc.length;
|
||||
return this.regexp ? regexpCursor(this, st, from, to) : stringCursor(this, st, from, to);
|
||||
}
|
||||
}
|
||||
class QueryType {
|
||||
|
@ -599,41 +626,53 @@ class QueryType {
|
|||
this.spec = spec;
|
||||
}
|
||||
}
|
||||
function stringCursor(spec, doc, from, to) {
|
||||
return new SearchCursor(doc, spec.unquoted, from, to, spec.caseSensitive ? undefined : x => x.toLowerCase());
|
||||
function stringCursor(spec, state, from, to) {
|
||||
return new SearchCursor(state.doc, spec.unquoted, from, to, spec.caseSensitive ? undefined : x => x.toLowerCase(), spec.wholeWord ? stringWordTest(state.doc, state.charCategorizer(state.selection.main.head)) : undefined);
|
||||
}
|
||||
function stringWordTest(doc, categorizer) {
|
||||
return (from, to, buf, bufPos) => {
|
||||
if (bufPos > from || bufPos + buf.length < to) {
|
||||
bufPos = Math.max(0, from - 2);
|
||||
buf = doc.sliceString(bufPos, Math.min(doc.length, to + 2));
|
||||
}
|
||||
return (categorizer(charBefore(buf, from - bufPos)) != CharCategory.Word ||
|
||||
categorizer(charAfter(buf, from - bufPos)) != CharCategory.Word) &&
|
||||
(categorizer(charAfter(buf, to - bufPos)) != CharCategory.Word ||
|
||||
categorizer(charBefore(buf, to - bufPos)) != CharCategory.Word);
|
||||
};
|
||||
}
|
||||
class StringQuery extends QueryType {
|
||||
constructor(spec) {
|
||||
super(spec);
|
||||
}
|
||||
nextMatch(doc, curFrom, curTo) {
|
||||
let cursor = stringCursor(this.spec, doc, curTo, doc.length).nextOverlapping();
|
||||
nextMatch(state, curFrom, curTo) {
|
||||
let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping();
|
||||
if (cursor.done)
|
||||
cursor = stringCursor(this.spec, doc, 0, curFrom).nextOverlapping();
|
||||
cursor = stringCursor(this.spec, state, 0, curFrom).nextOverlapping();
|
||||
return cursor.done ? null : cursor.value;
|
||||
}
|
||||
// Searching in reverse is, rather than implementing inverted search
|
||||
// cursor, done by scanning chunk after chunk forward.
|
||||
prevMatchInRange(doc, from, to) {
|
||||
prevMatchInRange(state, from, to) {
|
||||
for (let pos = to;;) {
|
||||
let start = Math.max(from, pos - 10000 /* ChunkSize */ - this.spec.unquoted.length);
|
||||
let cursor = stringCursor(this.spec, doc, start, pos), range = null;
|
||||
let start = Math.max(from, pos - 10000 /* FindPrev.ChunkSize */ - this.spec.unquoted.length);
|
||||
let cursor = stringCursor(this.spec, state, start, pos), range = null;
|
||||
while (!cursor.nextOverlapping().done)
|
||||
range = cursor.value;
|
||||
if (range)
|
||||
return range;
|
||||
if (start == from)
|
||||
return null;
|
||||
pos -= 10000 /* ChunkSize */;
|
||||
pos -= 10000 /* FindPrev.ChunkSize */;
|
||||
}
|
||||
}
|
||||
prevMatch(doc, curFrom, curTo) {
|
||||
return this.prevMatchInRange(doc, 0, curFrom) ||
|
||||
this.prevMatchInRange(doc, curTo, doc.length);
|
||||
prevMatch(state, curFrom, curTo) {
|
||||
return this.prevMatchInRange(state, 0, curFrom) ||
|
||||
this.prevMatchInRange(state, curTo, state.doc.length);
|
||||
}
|
||||
getReplacement(_result) { return this.spec.replace; }
|
||||
matchAll(doc, limit) {
|
||||
let cursor = stringCursor(this.spec, doc, 0, doc.length), ranges = [];
|
||||
getReplacement(_result) { return this.spec.unquote(this.spec.replace); }
|
||||
matchAll(state, limit) {
|
||||
let cursor = stringCursor(this.spec, state, 0, state.doc.length), ranges = [];
|
||||
while (!cursor.next().done) {
|
||||
if (ranges.length >= limit)
|
||||
return null;
|
||||
|
@ -641,26 +680,42 @@ class StringQuery extends QueryType {
|
|||
}
|
||||
return ranges;
|
||||
}
|
||||
highlight(doc, from, to, add) {
|
||||
let cursor = stringCursor(this.spec, doc, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, doc.length));
|
||||
highlight(state, from, to, add) {
|
||||
let cursor = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length));
|
||||
while (!cursor.next().done)
|
||||
add(cursor.value.from, cursor.value.to);
|
||||
}
|
||||
}
|
||||
function regexpCursor(spec, doc, from, to) {
|
||||
return new RegExpCursor(doc, spec.search, spec.caseSensitive ? undefined : { ignoreCase: true }, from, to);
|
||||
function regexpCursor(spec, state, from, to) {
|
||||
return new RegExpCursor(state.doc, spec.search, {
|
||||
ignoreCase: !spec.caseSensitive,
|
||||
test: spec.wholeWord ? regexpWordTest(state.charCategorizer(state.selection.main.head)) : undefined
|
||||
}, from, to);
|
||||
}
|
||||
function charBefore(str, index) {
|
||||
return str.slice(findClusterBreak(str, index, false), index);
|
||||
}
|
||||
function charAfter(str, index) {
|
||||
return str.slice(index, findClusterBreak(str, index));
|
||||
}
|
||||
function regexpWordTest(categorizer) {
|
||||
return (_from, _to, match) => !match[0].length ||
|
||||
(categorizer(charBefore(match.input, match.index)) != CharCategory.Word ||
|
||||
categorizer(charAfter(match.input, match.index)) != CharCategory.Word) &&
|
||||
(categorizer(charAfter(match.input, match.index + match[0].length)) != CharCategory.Word ||
|
||||
categorizer(charBefore(match.input, match.index + match[0].length)) != CharCategory.Word);
|
||||
}
|
||||
class RegExpQuery extends QueryType {
|
||||
nextMatch(doc, curFrom, curTo) {
|
||||
let cursor = regexpCursor(this.spec, doc, curTo, doc.length).next();
|
||||
nextMatch(state, curFrom, curTo) {
|
||||
let cursor = regexpCursor(this.spec, state, curTo, state.doc.length).next();
|
||||
if (cursor.done)
|
||||
cursor = regexpCursor(this.spec, doc, 0, curFrom).next();
|
||||
cursor = regexpCursor(this.spec, state, 0, curFrom).next();
|
||||
return cursor.done ? null : cursor.value;
|
||||
}
|
||||
prevMatchInRange(doc, from, to) {
|
||||
prevMatchInRange(state, from, to) {
|
||||
for (let size = 1;; size++) {
|
||||
let start = Math.max(from, to - size * 10000 /* ChunkSize */);
|
||||
let cursor = regexpCursor(this.spec, doc, start, to), range = null;
|
||||
let start = Math.max(from, to - size * 10000 /* FindPrev.ChunkSize */);
|
||||
let cursor = regexpCursor(this.spec, state, start, to), range = null;
|
||||
while (!cursor.next().done)
|
||||
range = cursor.value;
|
||||
if (range && (start == from || range.from > start + 10))
|
||||
|
@ -669,18 +724,18 @@ class RegExpQuery extends QueryType {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
prevMatch(doc, curFrom, curTo) {
|
||||
return this.prevMatchInRange(doc, 0, curFrom) ||
|
||||
this.prevMatchInRange(doc, curTo, doc.length);
|
||||
prevMatch(state, curFrom, curTo) {
|
||||
return this.prevMatchInRange(state, 0, curFrom) ||
|
||||
this.prevMatchInRange(state, curTo, state.doc.length);
|
||||
}
|
||||
getReplacement(result) {
|
||||
return this.spec.replace.replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
|
||||
return this.spec.unquote(this.spec.replace.replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
|
||||
: i == "&" ? result.match[0]
|
||||
: i != "0" && +i < result.match.length ? result.match[i]
|
||||
: m);
|
||||
: m));
|
||||
}
|
||||
matchAll(doc, limit) {
|
||||
let cursor = regexpCursor(this.spec, doc, 0, doc.length), ranges = [];
|
||||
matchAll(state, limit) {
|
||||
let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = [];
|
||||
while (!cursor.next().done) {
|
||||
if (ranges.length >= limit)
|
||||
return null;
|
||||
|
@ -688,8 +743,8 @@ class RegExpQuery extends QueryType {
|
|||
}
|
||||
return ranges;
|
||||
}
|
||||
highlight(doc, from, to, add) {
|
||||
let cursor = regexpCursor(this.spec, doc, Math.max(0, from - 250 /* HighlightMargin */), Math.min(to + 250 /* HighlightMargin */, doc.length));
|
||||
highlight(state, from, to, add) {
|
||||
let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250 /* RegExp.HighlightMargin */), Math.min(to + 250 /* RegExp.HighlightMargin */, state.doc.length));
|
||||
while (!cursor.next().done)
|
||||
add(cursor.value.from, cursor.value.to);
|
||||
}
|
||||
|
@ -725,6 +780,13 @@ function getSearchQuery(state) {
|
|||
let curState = state.field(searchState, false);
|
||||
return curState ? curState.query.spec : defaultQuery(state);
|
||||
}
|
||||
/**
|
||||
Query whether the search panel is open in the given editor state.
|
||||
*/
|
||||
function searchPanelOpen(state) {
|
||||
var _a;
|
||||
return ((_a = state.field(searchState, false)) === null || _a === void 0 ? void 0 : _a.panel) != null;
|
||||
}
|
||||
class SearchState {
|
||||
constructor(query, panel) {
|
||||
this.query = query;
|
||||
|
@ -749,9 +811,9 @@ const searchHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|||
let builder = new RangeSetBuilder();
|
||||
for (let i = 0, ranges = view.visibleRanges, l = ranges.length; i < l; i++) {
|
||||
let { from, to } = ranges[i];
|
||||
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* HighlightMargin */)
|
||||
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* RegExp.HighlightMargin */)
|
||||
to = ranges[++i].to;
|
||||
query.highlight(view.state.doc, from, to, (from, to) => {
|
||||
query.highlight(view.state, from, to, (from, to) => {
|
||||
let selected = view.state.selection.ranges.some(r => r.from == from && r.to == to);
|
||||
builder.add(from, to, selected ? selectedMatchMark : matchMark);
|
||||
});
|
||||
|
@ -774,9 +836,9 @@ Will wrap around to the start of the document when it reaches the
|
|||
end.
|
||||
*/
|
||||
const findNext = /*@__PURE__*/searchCommand((view, { query }) => {
|
||||
let { from, to } = view.state.selection.main;
|
||||
let next = query.nextMatch(view.state.doc, from, to);
|
||||
if (!next || next.from == from && next.to == to)
|
||||
let { to } = view.state.selection.main;
|
||||
let next = query.nextMatch(view.state, to, to);
|
||||
if (!next)
|
||||
return false;
|
||||
view.dispatch({
|
||||
selection: { anchor: next.from, head: next.to },
|
||||
|
@ -792,8 +854,8 @@ before the current main selection. Will wrap past the start
|
|||
of the document to start searching at the end again.
|
||||
*/
|
||||
const findPrevious = /*@__PURE__*/searchCommand((view, { query }) => {
|
||||
let { state } = view, { from, to } = state.selection.main;
|
||||
let range = query.prevMatch(state.doc, from, to);
|
||||
let { state } = view, { from } = state.selection.main;
|
||||
let range = query.prevMatch(state, from, from);
|
||||
if (!range)
|
||||
return false;
|
||||
view.dispatch({
|
||||
|
@ -808,7 +870,7 @@ const findPrevious = /*@__PURE__*/searchCommand((view, { query }) => {
|
|||
Select all instances of the search query.
|
||||
*/
|
||||
const selectMatches = /*@__PURE__*/searchCommand((view, { query }) => {
|
||||
let ranges = query.matchAll(view.state.doc, 1000);
|
||||
let ranges = query.matchAll(view.state, 1000);
|
||||
if (!ranges || !ranges.length)
|
||||
return false;
|
||||
view.dispatch({
|
||||
|
@ -846,7 +908,7 @@ const replaceNext = /*@__PURE__*/searchCommand((view, { query }) => {
|
|||
let { state } = view, { from, to } = state.selection.main;
|
||||
if (state.readOnly)
|
||||
return false;
|
||||
let next = query.nextMatch(state.doc, from, from);
|
||||
let next = query.nextMatch(state, from, from);
|
||||
if (!next)
|
||||
return false;
|
||||
let changes = [], selection, replacement;
|
||||
|
@ -854,7 +916,7 @@ const replaceNext = /*@__PURE__*/searchCommand((view, { query }) => {
|
|||
if (next.from == from && next.to == to) {
|
||||
replacement = state.toText(query.getReplacement(next));
|
||||
changes.push({ from: next.from, to: next.to, insert: replacement });
|
||||
next = query.nextMatch(state.doc, next.from, next.to);
|
||||
next = query.nextMatch(state, next.from, next.to);
|
||||
announce.push(EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + "."));
|
||||
}
|
||||
if (next) {
|
||||
|
@ -877,7 +939,7 @@ replacement.
|
|||
const replaceAll = /*@__PURE__*/searchCommand((view, { query }) => {
|
||||
if (view.state.readOnly)
|
||||
return false;
|
||||
let changes = query.matchAll(view.state.doc, 1e9).map(match => {
|
||||
let changes = query.matchAll(view.state, 1e9).map(match => {
|
||||
let { from, to } = match;
|
||||
return { from, to, insert: query.getReplacement(match) };
|
||||
});
|
||||
|
@ -895,11 +957,18 @@ function createSearchPanel(view) {
|
|||
return view.state.facet(searchConfigFacet).createPanel(view);
|
||||
}
|
||||
function defaultQuery(state, fallback) {
|
||||
var _a;
|
||||
var _a, _b, _c, _d;
|
||||
let sel = state.selection.main;
|
||||
let selText = sel.empty || sel.to > sel.from + 100 ? "" : state.sliceDoc(sel.from, sel.to);
|
||||
let caseSensitive = (_a = fallback === null || fallback === void 0 ? void 0 : fallback.caseSensitive) !== null && _a !== void 0 ? _a : state.facet(searchConfigFacet).caseSensitive;
|
||||
return fallback && !selText ? fallback : new SearchQuery({ search: selText.replace(/\n/g, "\\n"), caseSensitive });
|
||||
if (fallback && !selText)
|
||||
return fallback;
|
||||
let config = state.facet(searchConfigFacet);
|
||||
return new SearchQuery({
|
||||
search: ((_a = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _a !== void 0 ? _a : config.literal) ? selText : selText.replace(/\n/g, "\\n"),
|
||||
caseSensitive: (_b = fallback === null || fallback === void 0 ? void 0 : fallback.caseSensitive) !== null && _b !== void 0 ? _b : config.caseSensitive,
|
||||
literal: (_c = fallback === null || fallback === void 0 ? void 0 : fallback.literal) !== null && _c !== void 0 ? _c : config.literal,
|
||||
wholeWord: (_d = fallback === null || fallback === void 0 ? void 0 : fallback.wholeWord) !== null && _d !== void 0 ? _d : config.wholeWord
|
||||
});
|
||||
}
|
||||
/**
|
||||
Make sure the search panel is open and focused.
|
||||
|
@ -969,6 +1038,7 @@ class SearchPanel {
|
|||
"aria-label": phrase(view, "Find"),
|
||||
class: "cm-textfield",
|
||||
name: "search",
|
||||
form: "",
|
||||
"main-field": "true",
|
||||
onchange: this.commit,
|
||||
onkeyup: this.commit
|
||||
|
@ -979,21 +1049,31 @@ class SearchPanel {
|
|||
"aria-label": phrase(view, "Replace"),
|
||||
class: "cm-textfield",
|
||||
name: "replace",
|
||||
form: "",
|
||||
onchange: this.commit,
|
||||
onkeyup: this.commit
|
||||
});
|
||||
this.caseField = elt("input", {
|
||||
type: "checkbox",
|
||||
name: "case",
|
||||
form: "",
|
||||
checked: query.caseSensitive,
|
||||
onchange: this.commit
|
||||
});
|
||||
this.reField = elt("input", {
|
||||
type: "checkbox",
|
||||
name: "re",
|
||||
form: "",
|
||||
checked: query.regexp,
|
||||
onchange: this.commit
|
||||
});
|
||||
this.wordField = elt("input", {
|
||||
type: "checkbox",
|
||||
name: "word",
|
||||
form: "",
|
||||
checked: query.wholeWord,
|
||||
onchange: this.commit
|
||||
});
|
||||
function button(name, onclick, content) {
|
||||
return elt("button", { class: "cm-button", name, onclick, type: "button" }, content);
|
||||
}
|
||||
|
@ -1004,18 +1084,19 @@ class SearchPanel {
|
|||
button("select", () => selectMatches(view), [phrase(view, "all")]),
|
||||
elt("label", null, [this.caseField, phrase(view, "match case")]),
|
||||
elt("label", null, [this.reField, phrase(view, "regexp")]),
|
||||
elt("label", null, [this.wordField, phrase(view, "by word")]),
|
||||
...view.state.readOnly ? [] : [
|
||||
elt("br"),
|
||||
this.replaceField,
|
||||
button("replace", () => replaceNext(view), [phrase(view, "replace")]),
|
||||
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")]),
|
||||
elt("button", {
|
||||
name: "close",
|
||||
onclick: () => closeSearchPanel(view),
|
||||
"aria-label": phrase(view, "close"),
|
||||
type: "button"
|
||||
}, ["×"])
|
||||
]
|
||||
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
|
||||
],
|
||||
elt("button", {
|
||||
name: "close",
|
||||
onclick: () => closeSearchPanel(view),
|
||||
"aria-label": phrase(view, "close"),
|
||||
type: "button"
|
||||
}, ["×"])
|
||||
]);
|
||||
}
|
||||
commit() {
|
||||
|
@ -1023,7 +1104,8 @@ class SearchPanel {
|
|||
search: this.searchField.value,
|
||||
caseSensitive: this.caseField.checked,
|
||||
regexp: this.reField.checked,
|
||||
replace: this.replaceField.value
|
||||
wholeWord: this.wordField.checked,
|
||||
replace: this.replaceField.value,
|
||||
});
|
||||
if (!query.eq(this.query)) {
|
||||
this.query = query;
|
||||
|
@ -1056,6 +1138,7 @@ class SearchPanel {
|
|||
this.replaceField.value = query.replace;
|
||||
this.caseField.checked = query.caseSensitive;
|
||||
this.reField.checked = query.regexp;
|
||||
this.wordField.checked = query.wholeWord;
|
||||
}
|
||||
mount() {
|
||||
this.searchField.select();
|
||||
|
@ -1122,4 +1205,4 @@ const searchExtensions = [
|
|||
baseTheme
|
||||
];
|
||||
|
||||
export { RegExpCursor, SearchCursor, SearchQuery, closeSearchPanel, findNext, findPrevious, getSearchQuery, gotoLine, highlightSelectionMatches, openSearchPanel, replaceAll, replaceNext, search, searchKeymap, selectMatches, selectNextOccurrence, selectSelectionMatches, setSearchQuery };
|
||||
export { RegExpCursor, SearchCursor, SearchQuery, closeSearchPanel, findNext, findPrevious, getSearchQuery, gotoLine, highlightSelectionMatches, openSearchPanel, replaceAll, replaceNext, search, searchKeymap, searchPanelOpen, selectMatches, selectNextOccurrence, selectSelectionMatches, setSearchQuery };
|
||||
|
|
2
node_modules/@codemirror/search/package.json
generated
vendored
2
node_modules/@codemirror/search/package.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@codemirror/search",
|
||||
"version": "6.0.0",
|
||||
"version": "6.2.3",
|
||||
"description": "Search functionality for the CodeMirror code editor",
|
||||
"scripts": {
|
||||
"test": "cm-runtests",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue