|
|
|
|
@ -1,8 +1,13 @@
|
|
|
|
|
import { EditorView, Decoration, MatchDecorator, ViewPlugin, ViewUpdate } from "@codemirror/view";
|
|
|
|
|
import { StateEffect, Compartment } from "@codemirror/state";
|
|
|
|
|
import { StateEffect, Compartment, EditorSelection, RangeSet } from "@codemirror/state";
|
|
|
|
|
|
|
|
|
|
const searchMatchDecoration = Decoration.mark({ class: "cm-searchMatch" });
|
|
|
|
|
|
|
|
|
|
interface Match {
|
|
|
|
|
from: number;
|
|
|
|
|
to: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createSearchHighlighter(view: EditorView, searchTerm: string, matchCase: boolean, wholeWord: boolean) {
|
|
|
|
|
// Escape the search term for use in RegExp
|
|
|
|
|
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
|
|
|
@ -16,17 +21,49 @@ export function createSearchHighlighter(view: EditorView, searchTerm: string, ma
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return ViewPlugin.fromClass(class SearchHighlighter {
|
|
|
|
|
matches = matcher.createDeco(view);
|
|
|
|
|
totalFound = this.matches.size;
|
|
|
|
|
matches!: RangeSet<Decoration>;
|
|
|
|
|
totalFound: number;
|
|
|
|
|
private parsedMatches: Match[];
|
|
|
|
|
|
|
|
|
|
constructor(public view: EditorView) {
|
|
|
|
|
this.parsedMatches = [];
|
|
|
|
|
this.totalFound = 0;
|
|
|
|
|
this.updateSearchData(view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constructor(public view: EditorView) { }
|
|
|
|
|
updateSearchData(view: EditorView) {
|
|
|
|
|
const matches = matcher.createDeco(view);
|
|
|
|
|
const cursor = matches.iter();
|
|
|
|
|
while (cursor.value) {
|
|
|
|
|
this.parsedMatches.push({
|
|
|
|
|
from: cursor.from,
|
|
|
|
|
to: cursor.to
|
|
|
|
|
});
|
|
|
|
|
cursor.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.matches = matches;
|
|
|
|
|
this.totalFound = this.parsedMatches.length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update(update: ViewUpdate) {
|
|
|
|
|
if (update.docChanged || update.viewportChanged) {
|
|
|
|
|
this.matches = matcher.createDeco(update.view);
|
|
|
|
|
this.updateSearchData(update.view);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scrollToMatch(matchIndex: number) {
|
|
|
|
|
if (this.parsedMatches.length <= matchIndex) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const pos = this.parsedMatches[matchIndex];
|
|
|
|
|
this.view.dispatch({
|
|
|
|
|
effects: EditorView.scrollIntoView(pos.from, { y: "center" }),
|
|
|
|
|
scrollIntoView: true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
destroy() {
|
|
|
|
|
// Do nothing.
|
|
|
|
|
}
|
|
|
|
|
|