mirror of
https://github.com/kotaindah55/extended-markdown-syntax.git
synced 2026-07-22 05:38:06 +00:00
refactor: check core activity using ActivityRecorder
Key-paired activity checking. So, checking only is done once in the same activity. The keys and the activity states will be reseted once the view or the activity have been updated.
This commit is contained in:
parent
9518006ff1
commit
076ed7d952
9 changed files with 68 additions and 30 deletions
|
|
@ -12,6 +12,7 @@ import { trimTag } from "src/utils";
|
|||
import { SelectionObserver } from "src/editor-mode/observer";
|
||||
import { editorLivePreviewField } from "obsidian";
|
||||
import { refresherAnnot } from "src/editor-mode/annotations";
|
||||
import { activityFacet } from "src/editor-mode/facets";
|
||||
|
||||
export class DecorationBuilder {
|
||||
readonly parser: Parser;
|
||||
|
|
@ -77,14 +78,13 @@ export class DecorationBuilder {
|
|||
onViewUpdate(update: ViewUpdate) {
|
||||
let state = update.state,
|
||||
view = update.view,
|
||||
isLivePreview = state.field(editorLivePreviewField);
|
||||
if (this.parser.isReparsing || this.parser.isInitializing || update.viewportMoved) {
|
||||
isLivePreview = state.field(editorLivePreviewField),
|
||||
activityRecorder = state.facet(activityFacet);
|
||||
if (activityRecorder.verify("builder-view-update", "parse", true) || update.viewportMoved) {
|
||||
this.buildMain(view, state);
|
||||
this.parser.isReparsing = this.parser.isInitializing = false;
|
||||
}
|
||||
if (this.selectionObserver.isObserving || update.viewportMoved) {
|
||||
if (activityRecorder.verify("builder-view-update", "observe", true) || update.viewportMoved) {
|
||||
this.buildSupplementary(isLivePreview);
|
||||
this.selectionObserver.isObserving = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
|
@ -102,8 +102,9 @@ export class DecorationBuilder {
|
|||
let state = transaction.state,
|
||||
isLivePreview = state.field(editorLivePreviewField),
|
||||
isRefreshed = transaction.annotation(refresherAnnot),
|
||||
isModeChanged = isEditorModeChanged(state, transaction.startState);
|
||||
if (this.parser.isReparsing || this.parser.isInitializing) {
|
||||
isModeChanged = isEditorModeChanged(state, transaction.startState),
|
||||
activityRecorder = state.facet(activityFacet);
|
||||
if (activityRecorder.verify("builder-state-update", "parse")) {
|
||||
this.replaceLineBreaks(transaction.newDoc, transaction.changes);
|
||||
}
|
||||
if (isModeChanged || isRefreshed) {
|
||||
|
|
@ -112,7 +113,7 @@ export class DecorationBuilder {
|
|||
}
|
||||
if (!isLivePreview) {
|
||||
this.removeOmitter();
|
||||
} else if (this.selectionObserver.isObserving || isModeChanged) {
|
||||
} else if (activityRecorder.verify("builder-state-update", "observe") || isModeChanged) {
|
||||
this.omitFencedDivOpening(transaction.changes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import { Extension, RangeSet } from "@codemirror/state";
|
|||
import { EditorView, ViewPlugin } from "@codemirror/view";
|
||||
import { builderField, decoSetField, parserField, selectionObserverField } from "src/editor-mode/state-fields";
|
||||
import { EditorPlugin } from "src/editor-mode/view-plugin";
|
||||
import { activityFacet } from "src/editor-mode/facets";
|
||||
|
||||
export const editorPlugin = ViewPlugin.fromClass(EditorPlugin);
|
||||
|
||||
export const editorExtendedSyntax: Extension = [
|
||||
activityFacet.of(null),
|
||||
parserField,
|
||||
selectionObserverField,
|
||||
builderField,
|
||||
|
|
|
|||
9
src/editor-mode/facets/activityFacet.ts
Normal file
9
src/editor-mode/facets/activityFacet.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Facet } from "@codemirror/state";
|
||||
import { ActivityRecorder } from "src/editor-mode/observer";
|
||||
|
||||
export const activityFacet = Facet.define({
|
||||
combine() {
|
||||
return new ActivityRecorder();
|
||||
},
|
||||
static: true
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./settingsFacet";
|
||||
export * from "./appFacet";
|
||||
export * from "./pluginFacet";
|
||||
export * from "./pluginFacet";
|
||||
export * from "./activityFacet";
|
||||
24
src/editor-mode/observer/ActivityRecorder.ts
Normal file
24
src/editor-mode/observer/ActivityRecorder.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
export class ActivityRecorder {
|
||||
private isParsing: boolean = false;
|
||||
private isObserving: boolean = false;
|
||||
private verifier = { parse: new Set(), observe: new Set() };
|
||||
enter(entry: { isParsing?: boolean, isObserving?: boolean }) {
|
||||
if (entry.isParsing) { this.isParsing = entry.isParsing }
|
||||
if (entry.isObserving) { this.isObserving = entry.isObserving }
|
||||
}
|
||||
verify(key: unknown, activity: "parse" | "observe", reset = false) {
|
||||
if (this.verifier[activity].has(key)) { return null }
|
||||
let acted = activity == "parse" ? this.isParsing : this.isObserving;
|
||||
if (reset) { this.reset(activity) }
|
||||
else { this.verifier[activity].add(key) }
|
||||
return acted;
|
||||
}
|
||||
private reset(activity: "parse" | "observe") {
|
||||
this.verifier[activity].clear();
|
||||
if (activity == "parse") {
|
||||
this.isParsing = false;
|
||||
} else {
|
||||
this.isObserving = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
export * from "./SelectionObserver";
|
||||
export * from "./SelectionObserver";
|
||||
export * from "./ActivityRecorder";
|
||||
|
|
@ -12,8 +12,6 @@ import { colorTag, customSpanTag, fencedDivTag } from "src/editor-mode/parser/to
|
|||
export class Parser {
|
||||
private state: ParserState;
|
||||
private queue: TokenQueue = new TokenQueue();
|
||||
isInitializing: boolean;
|
||||
isReparsing: boolean;
|
||||
inlineTokens: TokenGroup = [];
|
||||
blockTokens: TokenGroup = [];
|
||||
reparsedRanges: Record<TokenLevel, { from: number, initTo: number, changedTo: number }>;
|
||||
|
|
@ -41,16 +39,12 @@ export class Parser {
|
|||
this.blockTokens = [];
|
||||
this.defineState({ doc, tree, offset: 0, settings: this.settings });
|
||||
this.streamParse();
|
||||
this.isInitializing = true;
|
||||
this.isReparsing = false;
|
||||
this.reparsedRanges = {
|
||||
[TokenLevel.INLINE]: { from: 0, initTo: 0, changedTo: this.inlineTokens.length },
|
||||
[TokenLevel.BLOCK]: { from: 0, initTo: 0, changedTo: this.blockTokens.length }
|
||||
}
|
||||
}
|
||||
applyChange(doc: Text, tree: Tree, oldTree: Tree, changes: ChangeSet) {
|
||||
this.isReparsing = true;
|
||||
this.isInitializing = false;
|
||||
let changedRange = composeChanges(changes),
|
||||
offset = Math.min(oldTree.length, tree.length) + 1;
|
||||
if (changedRange) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { ParseContext, syntaxTree } from "@codemirror/language";
|
|||
import { Tree } from "@lezer/common";
|
||||
import { settingsFacet } from "src/editor-mode/facets";
|
||||
import { refresherAnnot } from "src/editor-mode/annotations";
|
||||
import { activityFacet } from "../facets/activityFacet";
|
||||
|
||||
/**
|
||||
* In the context of extending syntax extension, this field should be the
|
||||
|
|
@ -11,14 +12,17 @@ import { refresherAnnot } from "src/editor-mode/annotations";
|
|||
*/
|
||||
export const parserField = StateField.define({
|
||||
create(state) {
|
||||
let parser = new Parser(state.facet(settingsFacet.reader));
|
||||
let parser = new Parser(state.facet(settingsFacet.reader)),
|
||||
activityRecorder = state.facet(activityFacet);
|
||||
parser.initParse(state.doc, syntaxTree(state));
|
||||
activityRecorder.enter({ isParsing: true });
|
||||
return parser;
|
||||
},
|
||||
update(parser, transaction) {
|
||||
let oldTree = syntaxTree(transaction.startState),
|
||||
newTree = syntaxTree(transaction.state),
|
||||
isRefreshed = transaction.annotation(refresherAnnot);
|
||||
isRefreshed = transaction.annotation(refresherAnnot),
|
||||
activityRecorder = transaction.state.facet(activityFacet);
|
||||
// Sometimes, there is an effect containing ParseContext and the most
|
||||
// updated Tree along with the state update. If any, we should use it
|
||||
// instead of that was taken from the state.
|
||||
|
|
@ -30,16 +34,14 @@ export const parserField = StateField.define({
|
|||
});
|
||||
if (isRefreshed) {
|
||||
parser.initParse(transaction.newDoc, newTree);
|
||||
activityRecorder.enter({ isParsing: true });
|
||||
return parser;
|
||||
} else {
|
||||
parser.isInitializing = false;
|
||||
}
|
||||
// Also, do reparse when the Tree got to be reparsed.
|
||||
if (transaction.docChanged || oldTree.length != newTree.length) {
|
||||
parser.applyChange(transaction.newDoc, newTree, oldTree, transaction.changes);
|
||||
} else {
|
||||
parser.isReparsing = false;
|
||||
}
|
||||
activityRecorder.enter({ isParsing: true });
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
});
|
||||
|
|
@ -1,22 +1,26 @@
|
|||
import { StateField } from "@codemirror/state";
|
||||
import { SelectionObserver } from "src/editor-mode/observer";
|
||||
import { parserField } from "src/editor-mode/state-fields";
|
||||
import { activityFacet } from "src/editor-mode/facets";
|
||||
|
||||
export const selectionObserverField = StateField.define({
|
||||
create(state) {
|
||||
let observer = new SelectionObserver(state.field(parserField));
|
||||
observer.startObserve(state.selection, true);
|
||||
let observer = new SelectionObserver(state.field(parserField)),
|
||||
activityRecorder = state.facet(activityFacet);
|
||||
observer.observe(state.selection, true);
|
||||
activityRecorder.enter({ isObserving: true });
|
||||
return observer;
|
||||
},
|
||||
update(observer, transaction) {
|
||||
// Start observer only when the parser has run or the selection has been moved.
|
||||
// Start observer only when the parser has run or the selection has been
|
||||
// moved.
|
||||
let selectionMoved = !(transaction.selection && transaction.startState.selection.eq(transaction.selection)),
|
||||
isParsing = observer.parser.isReparsing || observer.parser.isInitializing;
|
||||
activityRecorder = transaction.state.facet(activityFacet),
|
||||
isParsing = activityRecorder.verify(observer, "parse");
|
||||
transaction.isUserEvent("select");
|
||||
if (isParsing || selectionMoved) {
|
||||
observer.startObserve(transaction.newSelection, isParsing);
|
||||
} else {
|
||||
observer.isObserving = false;
|
||||
observer.observe(transaction.newSelection, isParsing ?? false);
|
||||
activityRecorder.enter({ isObserving: true });
|
||||
}
|
||||
return observer;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue