refactor: rename "cm-extension" to "cm-extensions"

This commit is contained in:
kotaindah55 2025-04-13 11:57:44 +02:00
parent 87788eb6d5
commit f46c104879
5 changed files with 4 additions and 241 deletions

View file

@ -9,7 +9,7 @@ import { editorCommands } from "src/editor-mode/formatting/commands";
import { extendEditorCtxMenu } from "src/editor-mode/ui-components";
import { TagManager } from "src/tag-manager";
import { ReadingModeSyntaxExtender } from "src/preview-mode/post-processor/core";
import { editorSyntaxExtender, refreshCall } from "src/editor-mode/cm-extension";
import { editorSyntaxExtender, refreshCall } from "src/editor-mode/cm-extensions";
export default class ExtendedMarkdownSyntax extends Plugin {
settings: PluginSettings;

View file

@ -1,237 +0,0 @@
import ExtendedMarkdownSyntax from "main";
import { editorLivePreviewField } from "obsidian";
import { Annotation, EditorState, Extension, Facet, Prec, StateEffect, StateField, Transaction } from "@codemirror/state";
import { EditorView, PluginValue, ViewPlugin, ViewUpdate } from "@codemirror/view";
import { ParseContext, syntaxTree } from "@codemirror/language";
import { Tree } from "@lezer/common";
import { IndexCache } from "src/types";
import { EditorParser } from "src/editor-mode/preprocessor/parser";
import { SelectionObserver } from "src/editor-mode/preprocessor/observer";
import { DecorationBuilder } from "src/editor-mode/decorator/builder";
import { Formatter } from "src/editor-mode/formatting/formatter";
type TagMenuOptionCaches = Record<"colorMenuItem" | "spanTagMenuItem" | "divTagMenuItem", IndexCache>;
interface BuiltinParserState {
context: ParseContext,
tree: Tree
}
interface RefreshDesc {
deep?: boolean;
}
interface InternalInstances {
mainPlugin: ExtendedMarkdownSyntax;
parser: EditorParser;
observer: SelectionObserver;
builder: DecorationBuilder;
formatter: Formatter;
activities: ActivityRecord;
}
export interface ActivityRecord {
isParsing?: boolean;
isObserving?: boolean;
isSelectionMoved?: boolean;
isRefreshed?: boolean;
isDeepRefreshed?: boolean;
isModeChanged?: boolean;
}
function _isBuiltinParserStateEffect(effect: StateEffect<unknown>): effect is StateEffect<BuiltinParserState> {
let effectVal = effect;
return (
"context" in effectVal && effectVal.context instanceof ParseContext &&
"tree" in effectVal && effectVal.tree instanceof Tree
);
}
function _isSelectionMoved(transaction: Transaction): boolean {
return !(
transaction.selection &&
transaction.startState.selection.eq(transaction.selection)
);
}
function _ensureMostUpdatedTree(transaction: Transaction): Tree {
let currentTree = syntaxTree(transaction.state),
mostUpdatedTree = transaction.effects.reduce(
(tree: Tree, effect: StateEffect<unknown>) => {
if (_isBuiltinParserStateEffect(effect)) {
tree = effect.value.tree;
}
return tree;
},
currentTree
);
return mostUpdatedTree;
}
function _isEditorModeChanged(transaction: Transaction): boolean {
let isLivePreviewCurrently = transaction.state.field(editorLivePreviewField),
isLivePreviewPreviously = transaction.startState.field(editorLivePreviewField);
return isLivePreviewCurrently != isLivePreviewPreviously;
}
class _EditorPlugin implements PluginValue {
public readonly builder: DecorationBuilder;
public readonly mainPlugin: ExtendedMarkdownSyntax;
public root: DocumentOrShadowRoot;
public activities: ActivityRecord;
constructor(
plugin: ExtendedMarkdownSyntax,
view: EditorView,
builder: DecorationBuilder,
activities: ActivityRecord
) {
this.mainPlugin = plugin;
this.root = view.root;
this.builder = builder;
this.activities = activities;
this.builder.onViewInit(view);
}
public update(update: ViewUpdate) {
if (this.root !== update.view.root) {
this._onRootChanged(update.view.root);
}
this.builder.onViewUpdate(update, this.activities);
}
public destroy(): void {
this.mainPlugin.tagManager.colorsHandler.eject(this.root);
this.mainPlugin.opacityHandler.eject(this.root);
}
private _onRootChanged(newRoot: DocumentOrShadowRoot): void {
this.mainPlugin.tagManager.colorsHandler.eject(this.root);
this.mainPlugin.opacityHandler.eject(this.root);
this.mainPlugin.tagManager.colorsHandler.inject(newRoot);
this.mainPlugin.opacityHandler.inject(newRoot);
this.root = newRoot;
}
}
export const refreshCall = Annotation.define<RefreshDesc>();
export const instancesStore = Facet.define<ExtendedMarkdownSyntax, InternalInstances>({
combine(value) {
if (!value.length) return undefined as unknown as InternalInstances;
let mainPlugin = value[0],
parser = new EditorParser(mainPlugin.settings),
observer = new SelectionObserver(parser),
builder = new DecorationBuilder(parser, observer),
formatter = new Formatter(parser, observer);
return { mainPlugin, parser, observer, builder, formatter, activities: {} }
},
get static() {
return true;
}
});
export const tagMenuOptionCaches = Facet.define<TagMenuOptionCaches, TagMenuOptionCaches>({
combine(value) {
return value[0];
},
get static() {
return true;
}
});
export const editorSyntaxExtender = (plugin: ExtendedMarkdownSyntax): Extension => {
let preprocessField = StateField.define({
create(state: EditorState) {
let { parser, observer, builder, activities } = state.facet(instancesStore);
parser.initParse(state.doc, syntaxTree(state));
observer.observe(state.selection, true);
builder.onStateInit(state);
return {
parser, observer, builder, activities,
lineBreaksSet: builder.holder.lineBreaksSet,
blockOmittedSet: builder.holder.blockOmittedSet
}
},
update(prevField, transaction: Transaction) {
let newTree = _ensureMostUpdatedTree(transaction),
oldTree = syntaxTree(transaction.startState),
refreshDesc = transaction.annotation(refreshCall),
{ parser, observer, builder, activities } = prevField;
let isParsing = false,
isObserving = false,
isRefreshed = !!refreshDesc,
isDeepRefreshed = !!refreshDesc?.deep,
isModeChanged = _isEditorModeChanged(transaction);
if (isDeepRefreshed) {
parser.initParse(transaction.newDoc, newTree);
isParsing = true;
} else if (transaction.docChanged || oldTree.length != newTree.length) {
parser.applyChange(transaction.newDoc, newTree, oldTree, transaction.changes);
isParsing = true;
}
if (isDeepRefreshed || isModeChanged) {
observer.restartObserver(transaction.newSelection, transaction.docChanged);
isObserving = true;
} else if (isParsing || _isSelectionMoved(transaction)) {
observer.observe(transaction.newSelection, activities.isParsing ?? false);
isObserving = true;
}
builder.onStateUpdate(transaction, { isParsing, isObserving, isDeepRefreshed, isModeChanged });
activities.isParsing ||= isParsing;
activities.isObserving ||= isObserving;
activities.isRefreshed ||= isRefreshed;
activities.isModeChanged ||= isModeChanged;
if (
prevField.lineBreaksSet === builder.holder.lineBreaksSet &&
prevField.blockOmittedSet === builder.holder.blockOmittedSet
) return prevField;
return {
parser, observer, builder, activities,
lineBreaksSet: builder.holder.lineBreaksSet,
blockOmittedSet: builder.holder.blockOmittedSet
};
},
provide(field): Extension {
return [
EditorView.decorations.from(field, sets => sets.blockOmittedSet),
Prec.lowest(EditorView.decorations.from(field, sets => sets.lineBreaksSet))
]
}
});
let viewPlugin = ViewPlugin.define(
view => {
let { builder, activities } = view.state.facet(instancesStore);
return new _EditorPlugin(plugin, view, builder, activities);
},
{
provide: () => {
return [
tagMenuOptionCaches.of({
colorMenuItem: { number: 0 },
spanTagMenuItem: { number: 0 },
divTagMenuItem: { number: 0 },
}),
EditorView.decorations.of(view => view.state.facet(instancesStore).builder.holder.blockSet),
EditorView.decorations.of(view => view.state.facet(instancesStore).builder.holder.inlineOmittedSet),
EditorView.decorations.of(view => view.state.facet(instancesStore).builder.holder.colorBtnSet),
EditorView.decorations.of(view => view.state.facet(instancesStore).builder.holder.revealedSpoilerSet),
EditorView.outerDecorations.of(view => view.state.facet(instancesStore).builder.holder.inlineSet),
];
}
}
);
return [instancesStore.of(plugin), preprocessField, viewPlugin];
}

View file

@ -7,7 +7,7 @@ import { BlockRules, InlineRules } from "src/format-configs/rules";
import { trimTag } from "src/format-configs/format-utils";
import { EditorParser } from "src/editor-mode/preprocessor/parser";
import { SelectionObserver } from "src/editor-mode/preprocessor/observer";
import { ActivityRecord } from "src/editor-mode/cm-extension";
import { ActivityRecord } from "src/editor-mode/cm-extensions";
import { LineBreak, HiddenWidget, ColorButton } from "src/editor-mode/decorator/widgets";
import { REVEALED_SPOILER_DECO } from "src/editor-mode/decorator/decorations";
import { getTagRange, iterTokenGroup, provideTokenPartsRanges } from "src/editor-mode/utils/token-utils"

View file

@ -2,7 +2,7 @@ import { Command } from "obsidian";
import { EditorView } from "@codemirror/view";
import { Format } from "src/enums";
import { CtxMenuCommand } from "src/types";
import { instancesStore } from "src/editor-mode/cm-extension";
import { instancesStore } from "src/editor-mode/cm-extensions";
import { TagMenu } from "src/editor-mode/ui-components";
import { Formatter } from "src/editor-mode/formatting/formatter";

View file

@ -6,7 +6,7 @@ import { Format, TokenLevel, TokenStatus } from "src/enums";
import { getActiveCanvasNodeCoords } from "src/editor-mode/utils/canvas-utils";
import { Formatter } from "src/editor-mode/formatting/formatter";
import { supportTag } from "src/format-configs/format-utils";
import { tagMenuOptionCaches, instancesStore } from "src/editor-mode/cm-extension";
import { tagMenuOptionCaches, instancesStore } from "src/editor-mode/cm-extensions";
import { ctxMenuCommands } from "src/editor-mode/formatting/commands";
// eslint-disable-next-line @typescript-eslint/no-empty-object-type