mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
refactor(code/find): remove inner class
This commit is contained in:
parent
e5417827f4
commit
8a35e390f2
@ -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,90 +8,99 @@ 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);
|
||||
export class SearchHighlighter {
|
||||
matches: RangeSet<Decoration>;
|
||||
currentFound: number;
|
||||
totalFound: number;
|
||||
matcher?: MatchDecorator;
|
||||
private parsedMatches: Match[];
|
||||
|
||||
const matcher = new MatchDecorator({
|
||||
regexp: regex,
|
||||
decoration: searchMatchDecoration,
|
||||
});
|
||||
constructor(public view: EditorView) {
|
||||
this.parsedMatches = [];
|
||||
this.currentFound = 0;
|
||||
this.totalFound = 0;
|
||||
this.matches = (new RangeSetBuilder<Decoration>()).finish();
|
||||
}
|
||||
|
||||
return ViewPlugin.fromClass(class SearchHighlighter {
|
||||
matches!: RangeSet<Decoration>;
|
||||
currentFound: number;
|
||||
totalFound: number;
|
||||
private parsedMatches: Match[];
|
||||
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);
|
||||
|
||||
constructor(public view: EditorView) {
|
||||
this.parsedMatches = [];
|
||||
this.currentFound = 0;
|
||||
this.totalFound = 0;
|
||||
this.updateSearchData(view);
|
||||
this.matcher = new MatchDecorator({
|
||||
regexp: regex,
|
||||
decoration: searchMatchDecoration,
|
||||
});
|
||||
this.updateSearchData(this.view);
|
||||
}
|
||||
|
||||
updateSearchData(view: EditorView) {
|
||||
if (!this.matcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
const matches = this.matcher.createDeco(view);
|
||||
const cursor = matches.iter();
|
||||
while (cursor.value) {
|
||||
this.parsedMatches.push({
|
||||
from: cursor.from,
|
||||
to: cursor.to
|
||||
});
|
||||
cursor.next();
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
if (update.docChanged || update.viewportChanged) {
|
||||
this.updateSearchData(update.view);
|
||||
}
|
||||
this.matches = matches;
|
||||
this.totalFound = this.parsedMatches.length;
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
if (update.docChanged || update.viewportChanged) {
|
||||
this.updateSearchData(update.view);
|
||||
}
|
||||
}
|
||||
|
||||
scrollToMatch(matchIndex: number) {
|
||||
if (this.parsedMatches.length <= matchIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollToMatch(matchIndex: number) {
|
||||
if (this.parsedMatches.length <= matchIndex) {
|
||||
const match = this.parsedMatches[matchIndex];
|
||||
this.currentFound = matchIndex + 1;
|
||||
this.view.dispatch({
|
||||
effects: EditorView.scrollIntoView(match.from, { y: "center" }),
|
||||
scrollIntoView: true
|
||||
});
|
||||
}
|
||||
|
||||
scrollToMatchNearestSelection() {
|
||||
const cursorPos = this.view.state.selection.main.head;
|
||||
let index = 0;
|
||||
for (const match of this.parsedMatches) {
|
||||
if (match.from >= cursorPos) {
|
||||
this.scrollToMatch(index);
|
||||
return;
|
||||
}
|
||||
|
||||
const match = this.parsedMatches[matchIndex];
|
||||
this.currentFound = matchIndex + 1;
|
||||
this.view.dispatch({
|
||||
effects: EditorView.scrollIntoView(match.from, { y: "center" }),
|
||||
scrollIntoView: true
|
||||
});
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
scrollToMatchNearestSelection() {
|
||||
const cursorPos = this.view.state.selection.main.head;
|
||||
let index = 0;
|
||||
for (const match of this.parsedMatches) {
|
||||
if (match.from >= cursorPos) {
|
||||
this.scrollToMatch(index);
|
||||
return;
|
||||
}
|
||||
destroy() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
static deco = (v: SearchHighlighter) => v.matches;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
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)",
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user