@codemirror/search
Advanced tools
Comparing version 0.19.8 to 0.19.9
@@ -0,1 +1,9 @@ | ||
## 0.19.9 (2022-03-03) | ||
### New features | ||
The selection-matching extension now accepts a `wholeWords` option that makes it only highlight matches that span a whole word. Add SearchQuery.getCursor | ||
The `SearchQuery` class now has a `getCursor` method that allows external code to create a cursor for the query. | ||
## 0.19.8 (2022-02-14) | ||
@@ -2,0 +10,0 @@ |
@@ -152,2 +152,6 @@ import * as _codemirror_state from '@codemirror/state'; | ||
maxMatches?: number; | ||
/** | ||
Whether to only highlight whole words. | ||
*/ | ||
wholeWords?: boolean; | ||
}; | ||
@@ -252,2 +256,6 @@ /** | ||
eq(other: SearchQuery): boolean; | ||
getCursor(doc: Text, from?: number, to?: number): Iterator<{ | ||
from: number; | ||
to: number; | ||
}>; | ||
} | ||
@@ -254,0 +262,0 @@ /** |
@@ -390,3 +390,4 @@ import { EditorView, Decoration, ViewPlugin, runScopeHandlers } from '@codemirror/view'; | ||
minSelectionLength: 1, | ||
maxMatches: 100 | ||
maxMatches: 100, | ||
wholeWords: true | ||
}; | ||
@@ -416,2 +417,12 @@ const highlightConfig = /*@__PURE__*/Facet.define({ | ||
const mainMatchDeco = /*@__PURE__*/Decoration.mark({ class: "cm-selectionMatch cm-selectionMatch-main" }); | ||
// Whether the characters directly outside the given positions are non-word characters | ||
function insideWordBoundaries(check, state, from, to) { | ||
return (from == 0 || check(state.sliceDoc(from - 1, from)) != CharCategory.Word) && | ||
(to == state.doc.length || check(state.sliceDoc(to, to + 1)) != CharCategory.Word); | ||
} | ||
// Whether the characters directly at the given positions are word characters | ||
function insideWord(check, state, from, to) { | ||
return check(state.sliceDoc(from, from + 1)) == CharCategory.Word | ||
&& check(state.sliceDoc(to - 1, to)) == CharCategory.Word; | ||
} | ||
const matchHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class { | ||
@@ -444,5 +455,14 @@ constructor(view) { | ||
return Decoration.none; | ||
query = state.sliceDoc(range.from, range.to).trim(); | ||
if (!query) | ||
return Decoration.none; | ||
if (conf.wholeWords) { | ||
query = state.sliceDoc(range.from, range.to); // TODO: allow and include leading/trailing space? | ||
check = state.charCategorizer(range.head); | ||
if (!(insideWordBoundaries(check, state, range.from, range.to) | ||
&& insideWord(check, state, range.from, range.to))) | ||
return Decoration.none; | ||
} | ||
else { | ||
query = state.sliceDoc(range.from, range.to).trim(); | ||
if (!query) | ||
return Decoration.none; | ||
} | ||
} | ||
@@ -454,5 +474,4 @@ let deco = []; | ||
let { from, to } = cursor.value; | ||
if (!check || ((from == 0 || check(state.sliceDoc(from - 1, from)) != CharCategory.Word) && | ||
(to == state.doc.length || check(state.sliceDoc(to, to + 1)) != CharCategory.Word))) { | ||
if (check && from <= range.from && to >= range.to) | ||
if (!check || insideWordBoundaries(check, state, from, to)) { | ||
if (range.empty && from <= range.from && to >= range.to) | ||
deco.push(mainMatchDeco.range(from, to)); | ||
@@ -566,2 +585,3 @@ else if (from >= range.to || to <= range.from) | ||
this.valid = !!this.search && (!this.regexp || validRegExp(this.search)); | ||
this.unquoted = this.search.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\"); | ||
} | ||
@@ -581,2 +601,5 @@ /** | ||
} | ||
getCursor(doc, from = 0, to = doc.length) { | ||
return this.regexp ? regexpCursor(this, doc, from, to) : stringCursor(this, doc, from, to); | ||
} | ||
} | ||
@@ -588,14 +611,13 @@ class QueryType { | ||
} | ||
function stringCursor(spec, doc, from, to) { | ||
return new SearchCursor(doc, spec.unquoted, from, to, spec.caseSensitive ? undefined : x => x.toLowerCase()); | ||
} | ||
class StringQuery extends QueryType { | ||
constructor(spec) { | ||
super(spec); | ||
this.unquoted = spec.search.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\"); | ||
} | ||
cursor(doc, from = 0, to = doc.length) { | ||
return new SearchCursor(doc, this.unquoted, from, to, this.spec.caseSensitive ? undefined : x => x.toLowerCase()); | ||
} | ||
nextMatch(doc, curFrom, curTo) { | ||
let cursor = this.cursor(doc, curTo).nextOverlapping(); | ||
let cursor = stringCursor(this.spec, doc, curTo, doc.length).nextOverlapping(); | ||
if (cursor.done) | ||
cursor = this.cursor(doc, 0, curFrom).nextOverlapping(); | ||
cursor = stringCursor(this.spec, doc, 0, curFrom).nextOverlapping(); | ||
return cursor.done ? null : cursor.value; | ||
@@ -607,4 +629,4 @@ } | ||
for (let pos = to;;) { | ||
let start = Math.max(from, pos - 10000 /* ChunkSize */ - this.unquoted.length); | ||
let cursor = this.cursor(doc, start, pos), range = null; | ||
let start = Math.max(from, pos - 10000 /* ChunkSize */ - this.spec.unquoted.length); | ||
let cursor = stringCursor(this.spec, doc, start, pos), range = null; | ||
while (!cursor.nextOverlapping().done) | ||
@@ -625,3 +647,3 @@ range = cursor.value; | ||
matchAll(doc, limit) { | ||
let cursor = this.cursor(doc), ranges = []; | ||
let cursor = stringCursor(this.spec, doc, 0, doc.length), ranges = []; | ||
while (!cursor.next().done) { | ||
@@ -635,3 +657,3 @@ if (ranges.length >= limit) | ||
highlight(doc, from, to, add) { | ||
let cursor = this.cursor(doc, Math.max(0, from - this.unquoted.length), Math.min(to + this.unquoted.length, doc.length)); | ||
let cursor = stringCursor(this.spec, doc, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, doc.length)); | ||
while (!cursor.next().done) | ||
@@ -641,10 +663,10 @@ 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); | ||
} | ||
class RegExpQuery extends QueryType { | ||
cursor(doc, from = 0, to = doc.length) { | ||
return new RegExpCursor(doc, this.spec.search, this.spec.caseSensitive ? undefined : { ignoreCase: true }, from, to); | ||
} | ||
nextMatch(doc, curFrom, curTo) { | ||
let cursor = this.cursor(doc, curTo).next(); | ||
let cursor = regexpCursor(this.spec, doc, curTo, doc.length).next(); | ||
if (cursor.done) | ||
cursor = this.cursor(doc, 0, curFrom).next(); | ||
cursor = regexpCursor(this.spec, doc, 0, curFrom).next(); | ||
return cursor.done ? null : cursor.value; | ||
@@ -655,3 +677,3 @@ } | ||
let start = Math.max(from, to - size * 10000 /* ChunkSize */); | ||
let cursor = this.cursor(doc, start, to), range = null; | ||
let cursor = regexpCursor(this.spec, doc, start, to), range = null; | ||
while (!cursor.next().done) | ||
@@ -676,3 +698,3 @@ range = cursor.value; | ||
matchAll(doc, limit) { | ||
let cursor = this.cursor(doc), ranges = []; | ||
let cursor = regexpCursor(this.spec, doc, 0, doc.length), ranges = []; | ||
while (!cursor.next().done) { | ||
@@ -686,3 +708,3 @@ if (ranges.length >= limit) | ||
highlight(doc, from, to, add) { | ||
let cursor = this.cursor(doc, Math.max(0, from - 250 /* HighlightMargin */), Math.min(to + 250 /* HighlightMargin */, doc.length)); | ||
let cursor = regexpCursor(this.spec, doc, Math.max(0, from - 250 /* HighlightMargin */), Math.min(to + 250 /* HighlightMargin */, doc.length)); | ||
while (!cursor.next().done) | ||
@@ -689,0 +711,0 @@ add(cursor.value.from, cursor.value.to); |
{ | ||
"name": "@codemirror/search", | ||
"version": "0.19.8", | ||
"version": "0.19.9", | ||
"description": "Search functionality for the CodeMirror code editor", | ||
@@ -5,0 +5,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
105245
2555