refactor(code/find): remove inner class

This commit is contained in:
Elian Doran 2025-05-12 20:59:46 +03:00
parent e5417827f4
commit 8a35e390f2
No known key found for this signature in database
2 changed files with 75 additions and 65 deletions

View File

@ -1,5 +1,5 @@
import { EditorView, Decoration, MatchDecorator, ViewPlugin, ViewUpdate } from "@codemirror/view";
import { StateEffect, Compartment, EditorSelection, RangeSet } from "@codemirror/state";
import { RangeSet, RangeSetBuilder } from "@codemirror/state";
const searchMatchDecoration = Decoration.mark({ class: "cm-searchMatch" });
@ -8,33 +8,40 @@ interface Match {
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, '\\$&');
const wordBoundary = wholeWord ? "\\b" : "";
const flags = matchCase ? "g" : "gi";
const regex = new RegExp(`${wordBoundary}${escapedTerm}${wordBoundary}`, flags);
const matcher = new MatchDecorator({
regexp: regex,
decoration: searchMatchDecoration,
});
return ViewPlugin.fromClass(class SearchHighlighter {
matches!: RangeSet<Decoration>;
export class SearchHighlighter {
matches: RangeSet<Decoration>;
currentFound: number;
totalFound: number;
matcher?: MatchDecorator;
private parsedMatches: Match[];
constructor(public view: EditorView) {
this.parsedMatches = [];
this.currentFound = 0;
this.totalFound = 0;
this.updateSearchData(view);
this.matches = (new RangeSetBuilder<Decoration>()).finish();
}
searchFor(searchTerm: string, matchCase: boolean, wholeWord: boolean) {
// Escape the search term for use in RegExp
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const wordBoundary = wholeWord ? "\\b" : "";
const flags = matchCase ? "g" : "gi";
const regex = new RegExp(`${wordBoundary}${escapedTerm}${wordBoundary}`, flags);
this.matcher = new MatchDecorator({
regexp: regex,
decoration: searchMatchDecoration,
});
this.updateSearchData(this.view);
}
updateSearchData(view: EditorView) {
const matches = matcher.createDeco(view);
if (!this.matcher) {
return;
}
const matches = this.matcher.createDeco(view);
const cursor = matches.iter();
while (cursor.value) {
this.parsedMatches.push({
@ -85,13 +92,15 @@ export function createSearchHighlighter(view: EditorView, searchTerm: string, ma
}
static deco = (v: SearchHighlighter) => v.matches;
}, {
}
export function createSearchHighlighter() {
return ViewPlugin.fromClass(SearchHighlighter, {
decorations: v => v.matches,
provide: (plugin) => plugin
});
}
export const searchMatchHighlightTheme = EditorView.baseTheme({
".cm-searchMatch": {
backgroundColor: "rgba(255, 255, 0, 0.4)",

View File

@ -172,7 +172,7 @@ export default class CodeMirror extends EditorView {
}
async performFind(searchTerm: string, matchCase: boolean, wholeWord: boolean) {
const plugin = createSearchHighlighter(this, searchTerm, matchCase, wholeWord);
const plugin = createSearchHighlighter();
this.dispatch({
effects: this.searchHighlightCompartment.reconfigure(plugin)
});
@ -180,6 +180,7 @@ export default class CodeMirror extends EditorView {
// Wait for the plugin to activate in the next render cycle
await new Promise(requestAnimationFrame);
const instance = this.plugin(plugin);
instance?.searchFor(searchTerm, matchCase, wholeWord);
if (instance) {
instance.scrollToMatchNearestSelection();
}