@type32/codemirror-rich-obsidian-editor 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/dist/module.d.mts +8 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +25 -0
- package/dist/runtime/assets/css/editor.css +1 -0
- package/dist/runtime/components/Editor/ImageEmbedComponent.vue +11 -0
- package/dist/runtime/components/Editor/ImageEmbedComponent.vue.d.ts +5 -0
- package/dist/runtime/components/Editor/TestCustomCodeBlock.vue +16 -0
- package/dist/runtime/components/Editor/TestCustomCodeBlock.vue.d.ts +5 -0
- package/dist/runtime/components/Editor.client.vue +182 -0
- package/dist/runtime/components/Editor.client.vue.d.ts +23 -0
- package/dist/runtime/editor/lezer-parsers/customOFMParsers.d.ts +1 -0
- package/dist/runtime/editor/lezer-parsers/customOFMParsers.js +23 -0
- package/dist/runtime/editor/lezer-parsers/lezerCalloutParser.d.ts +8 -0
- package/dist/runtime/editor/lezer-parsers/lezerCalloutParser.js +53 -0
- package/dist/runtime/editor/lezer-parsers/lezerHashtagParser.d.ts +6 -0
- package/dist/runtime/editor/lezer-parsers/lezerHashtagParser.js +35 -0
- package/dist/runtime/editor/lezer-parsers/lezerIndentationParser.d.ts +4 -0
- package/dist/runtime/editor/lezer-parsers/lezerIndentationParser.js +23 -0
- package/dist/runtime/editor/lezer-parsers/lezerInternalLinkParser.d.ts +10 -0
- package/dist/runtime/editor/lezer-parsers/lezerInternalLinkParser.js +110 -0
- package/dist/runtime/editor/lezer-parsers/lezerLatexParser.d.ts +7 -0
- package/dist/runtime/editor/lezer-parsers/lezerLatexParser.js +75 -0
- package/dist/runtime/editor/lezer-parsers/lezerYamlFrontmatterParser.d.ts +13 -0
- package/dist/runtime/editor/lezer-parsers/lezerYamlFrontmatterParser.js +55 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/customBracketClosingPlugin.d.ts +4 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/customBracketClosingPlugin.js +94 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorAttributesPlugin.d.ts +8 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorAttributesPlugin.js +136 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorInternalLinkAutocompletePlugin.d.ts +1 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorInternalLinkAutocompletePlugin.js +43 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorKeymapPlugin.d.ts +1 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorKeymapPlugin.js +95 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorLinkClickPlugin.d.ts +1 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorLinkClickPlugin.js +43 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationGuidesPlugin.d.ts +10 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationGuidesPlugin.js +121 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationListPlugin.d.ts +5 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationListPlugin.js +66 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCalloutPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCalloutPlugin.js +63 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.js +98 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.js +27 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.js +51 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.d.ts +6 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.js +112 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLatexCodemirrorViewPlugin.d.ts +2 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLatexCodemirrorViewPlugin.js +160 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLinkCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLinkCodemirrorViewPlugin.js +98 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseQuoteblockCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseQuoteblockCodemirrorViewPlugin.js +76 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseTaskListPlugin.d.ts +8 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseTaskListPlugin.js +106 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCalloutWidget.d.ts +13 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCalloutWidget.js +87 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCodeBlockWidgets.d.ts +15 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCodeBlockWidgets.js +53 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseLatexWidgets.d.ts +11 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseLatexWidgets.js +50 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseVueComponentEmbedWidget.d.ts +12 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseVueComponentEmbedWidget.js +32 -0
- package/dist/runtime/editor/plugins/customBracketClosingConfig.d.ts +2 -0
- package/dist/runtime/editor/plugins/customBracketClosingConfig.js +4 -0
- package/dist/runtime/editor/plugins/lezerStylesHighlightingPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/lezerStylesHighlightingPlugin.js +55 -0
- package/dist/runtime/editor/plugins/linkMappingConfig.d.ts +3 -0
- package/dist/runtime/editor/plugins/linkMappingConfig.js +4 -0
- package/dist/runtime/editor/plugins/richTextPlugin.d.ts +10 -0
- package/dist/runtime/editor/plugins/richTextPlugin.js +94 -0
- package/dist/runtime/editor/plugins/specialCodeBlockMappingConfig.d.ts +3 -0
- package/dist/runtime/editor/plugins/specialCodeBlockMappingConfig.js +4 -0
- package/dist/runtime/editor/types/editor-types.d.ts +17 -0
- package/dist/runtime/editor/utility/decorations.d.ts +9 -0
- package/dist/runtime/editor/utility/decorations.js +9 -0
- package/dist/runtime/editor/utility/tools.d.ts +12 -0
- package/dist/runtime/editor/utility/tools.js +32 -0
- package/dist/runtime/editor/wysiwyg.d.ts +4 -0
- package/dist/runtime/editor/wysiwyg.js +80 -0
- package/dist/types.d.mts +3 -0
- package/package.json +80 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EditorView } from "@codemirror/view";
|
|
2
|
+
import { syntaxTree } from "@codemirror/language";
|
|
3
|
+
export const editorLinkClickPlugin = EditorView.domEventHandlers({
|
|
4
|
+
mousedown(event, view) {
|
|
5
|
+
let target = event.target;
|
|
6
|
+
const imageEmbed = target.closest(".internal-embed");
|
|
7
|
+
if (imageEmbed) {
|
|
8
|
+
const posData = imageEmbed.dataset;
|
|
9
|
+
if (posData.embedPos) {
|
|
10
|
+
const from = posData.selectionFrom ? parseInt(posData.selectionFrom, 10) : parseInt(posData.embedPos, 10);
|
|
11
|
+
const to = posData.selectionTo ? parseInt(posData.selectionTo, 10) : syntaxTree(view.state).resolve(from, -1).to;
|
|
12
|
+
view.dispatch({
|
|
13
|
+
selection: { anchor: from, head: to }
|
|
14
|
+
});
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const anchor = target.closest("a.cm-link");
|
|
19
|
+
if (!anchor) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (anchor.dataset.internalLink === "true") {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
view.dom.dispatchEvent(new CustomEvent("internal-link-click", {
|
|
25
|
+
bubbles: true,
|
|
26
|
+
composed: true,
|
|
27
|
+
detail: {
|
|
28
|
+
path: anchor.dataset.path,
|
|
29
|
+
subpath: anchor.dataset.subpath,
|
|
30
|
+
display: anchor.dataset.display,
|
|
31
|
+
type: anchor.dataset.type
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
} else if (anchor.dataset.externalLink === "true") {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
const url = anchor.dataset.url;
|
|
37
|
+
if (url) {
|
|
38
|
+
window.open(url, "_blank");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Extension } from '@codemirror/state';
|
|
2
|
+
export interface IndentationGuidesSettings {
|
|
3
|
+
showActiveIndentationGroup: boolean;
|
|
4
|
+
lists: boolean;
|
|
5
|
+
previewLists: boolean;
|
|
6
|
+
uncategorizedIndents: boolean;
|
|
7
|
+
code: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_SETTINGS: IndentationGuidesSettings;
|
|
10
|
+
export declare const indentationGuides: (options?: Partial<IndentationGuidesSettings>) => Extension;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Decoration, MatchDecorator, ViewPlugin } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSetBuilder } from "@codemirror/state";
|
|
3
|
+
export const DEFAULT_SETTINGS = {
|
|
4
|
+
showActiveIndentationGroup: true,
|
|
5
|
+
lists: true,
|
|
6
|
+
previewLists: false,
|
|
7
|
+
uncategorizedIndents: false,
|
|
8
|
+
code: false
|
|
9
|
+
};
|
|
10
|
+
function getLineIndent(line) {
|
|
11
|
+
const match = line.text.match(/^((?:\t| {4})+)/);
|
|
12
|
+
if (!match) return 0;
|
|
13
|
+
return Math.max(match[1]?.split(/(?:\t| {4})/).length || 0, 1);
|
|
14
|
+
}
|
|
15
|
+
const tabMark = Decoration.mark({
|
|
16
|
+
class: "cm-indent"
|
|
17
|
+
});
|
|
18
|
+
const indentGroupMark = Decoration.mark({
|
|
19
|
+
class: "cm-indent cm-active-indent"
|
|
20
|
+
});
|
|
21
|
+
const indentationGroupDecoration = Decoration.line({
|
|
22
|
+
attributes: { class: "cm-indent-group" }
|
|
23
|
+
});
|
|
24
|
+
const activeIndentField = StateField.define({
|
|
25
|
+
create(state) {
|
|
26
|
+
if (!state.selection?.main) return 0;
|
|
27
|
+
return getLineIndent(state.doc.lineAt(state.selection.main.from));
|
|
28
|
+
},
|
|
29
|
+
update(_, tr) {
|
|
30
|
+
if (!tr.selection) return _.valueOf();
|
|
31
|
+
const state = tr.state;
|
|
32
|
+
return getLineIndent(state.doc.lineAt(state.selection.main.from));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const tabDecoration = (getSettings) => {
|
|
36
|
+
return ViewPlugin.fromClass(
|
|
37
|
+
class {
|
|
38
|
+
constructor(view) {
|
|
39
|
+
this.view = view;
|
|
40
|
+
this.decorator = new MatchDecorator({
|
|
41
|
+
regexp: new RegExp(/(?:\t| {4})/g),
|
|
42
|
+
decoration: (match, view2) => {
|
|
43
|
+
if (!getSettings().showActiveIndentationGroup) {
|
|
44
|
+
return tabMark;
|
|
45
|
+
}
|
|
46
|
+
const currentIndent = Math.max(view2.state.field(activeIndentField), 1);
|
|
47
|
+
const thisIndent = match.index / match[0].length + 1;
|
|
48
|
+
return thisIndent === currentIndent ? indentGroupMark : tabMark;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.decorations = this.decorator.createDeco(view);
|
|
52
|
+
}
|
|
53
|
+
decorator;
|
|
54
|
+
decorations = Decoration.none;
|
|
55
|
+
update(update) {
|
|
56
|
+
if (!getSettings().showActiveIndentationGroup) {
|
|
57
|
+
this.decorations = this.decorator.updateDeco(update, this.decorations);
|
|
58
|
+
} else {
|
|
59
|
+
this.decorations = this.decorator.createDeco(update.view);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
decorations: (v) => v.decorations
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
function tagIndentationGroup(view) {
|
|
69
|
+
const builder = new RangeSetBuilder();
|
|
70
|
+
const state = view.state;
|
|
71
|
+
if (!state.selection?.main) return builder.finish();
|
|
72
|
+
const currentLine = state.doc.lineAt(state.selection.main.from);
|
|
73
|
+
const currentIndent = view.state.field(activeIndentField);
|
|
74
|
+
if (currentIndent === 0) return builder.finish();
|
|
75
|
+
const indentationGroup2 = [currentLine];
|
|
76
|
+
let from = currentLine.from;
|
|
77
|
+
let to = currentLine.to;
|
|
78
|
+
while (from > 0) {
|
|
79
|
+
const prevLine = state.doc.lineAt(from - 1);
|
|
80
|
+
const prevIndent = getLineIndent(prevLine);
|
|
81
|
+
if (prevIndent >= currentIndent) {
|
|
82
|
+
indentationGroup2.push(prevLine);
|
|
83
|
+
from = prevLine.from;
|
|
84
|
+
} else {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
while (to < state.doc.length - 1) {
|
|
89
|
+
const nextLine = state.doc.lineAt(to + 1);
|
|
90
|
+
const nextIndent = getLineIndent(nextLine);
|
|
91
|
+
if (nextIndent >= currentIndent) {
|
|
92
|
+
indentationGroup2.push(nextLine);
|
|
93
|
+
to = nextLine.to;
|
|
94
|
+
} else {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
indentationGroup2.sort((a, b) => a.from - b.from).forEach((line) => {
|
|
99
|
+
builder.add(line.from, line.from, indentationGroupDecoration);
|
|
100
|
+
});
|
|
101
|
+
return builder.finish();
|
|
102
|
+
}
|
|
103
|
+
const indentationGroup = ViewPlugin.fromClass(
|
|
104
|
+
class {
|
|
105
|
+
decorations;
|
|
106
|
+
constructor(view) {
|
|
107
|
+
this.decorations = tagIndentationGroup(view);
|
|
108
|
+
}
|
|
109
|
+
update(update) {
|
|
110
|
+
this.decorations = tagIndentationGroup(update.view);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
decorations: (v) => v.decorations
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
export const indentationGuides = (options = {}) => {
|
|
118
|
+
const settings = { ...DEFAULT_SETTINGS, ...options };
|
|
119
|
+
const getSettings = () => settings;
|
|
120
|
+
return [activeIndentField, indentationGroup, tabDecoration(getSettings)];
|
|
121
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Decoration, ViewPlugin } from "@codemirror/view";
|
|
2
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
3
|
+
import { syntaxTree } from "@codemirror/language";
|
|
4
|
+
function getLineIndent(line) {
|
|
5
|
+
const match = line.text.match(/^(?:\t| {4})*/);
|
|
6
|
+
if (!match) return 0;
|
|
7
|
+
return match[0].length;
|
|
8
|
+
}
|
|
9
|
+
function decorate(view) {
|
|
10
|
+
const builder = new RangeSetBuilder();
|
|
11
|
+
for (const { from, to } of view.visibleRanges) {
|
|
12
|
+
syntaxTree(view.state).iterate({
|
|
13
|
+
from,
|
|
14
|
+
to,
|
|
15
|
+
enter: (node) => {
|
|
16
|
+
if (node.name === "ListItem") {
|
|
17
|
+
const line = view.state.doc.lineAt(node.from);
|
|
18
|
+
const indentLevel = getLineIndent(line) + 1;
|
|
19
|
+
builder.add(
|
|
20
|
+
line.from,
|
|
21
|
+
line.from,
|
|
22
|
+
Decoration.line({
|
|
23
|
+
attributes: {
|
|
24
|
+
class: `cm-list-line cm-list-line-${indentLevel}`,
|
|
25
|
+
style: `--indent-level: ${indentLevel}`
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
let contentFrom = -1, contentTo = -1;
|
|
30
|
+
const listMark = node.node.getChild("ListMark");
|
|
31
|
+
if (listMark) {
|
|
32
|
+
contentFrom = listMark.to + 1;
|
|
33
|
+
contentTo = node.to;
|
|
34
|
+
}
|
|
35
|
+
if (contentFrom !== -1) {
|
|
36
|
+
builder.add(
|
|
37
|
+
contentFrom,
|
|
38
|
+
contentTo,
|
|
39
|
+
Decoration.mark({
|
|
40
|
+
class: `cm-list-internal cm-list-${indentLevel}`,
|
|
41
|
+
style: `--indent-level: ${indentLevel}`
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return builder.finish();
|
|
50
|
+
}
|
|
51
|
+
export const indentationListPlugin = ViewPlugin.fromClass(
|
|
52
|
+
class {
|
|
53
|
+
decorations;
|
|
54
|
+
constructor(view) {
|
|
55
|
+
this.decorations = decorate(view);
|
|
56
|
+
}
|
|
57
|
+
update(update) {
|
|
58
|
+
if (update.docChanged || update.viewportChanged) {
|
|
59
|
+
this.decorations = decorate(update.view);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
decorations: (v) => v.decorations
|
|
65
|
+
}
|
|
66
|
+
);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Decoration, EditorView } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
|
+
import { syntaxTree } from "@codemirror/language";
|
|
4
|
+
import { CalloutWidget } from "../codemirror-widgets/proseCalloutWidget.js";
|
|
5
|
+
function isNodeRangeActive(state, nodeFrom, nodeTo) {
|
|
6
|
+
const cursor = state.selection.main;
|
|
7
|
+
if (cursor.empty) {
|
|
8
|
+
return cursor.from >= nodeFrom && cursor.from <= nodeTo;
|
|
9
|
+
}
|
|
10
|
+
return Math.max(nodeFrom, cursor.from) < Math.min(nodeTo, cursor.to);
|
|
11
|
+
}
|
|
12
|
+
function getCalloutNode(blockquoteNode) {
|
|
13
|
+
let calloutNode = null;
|
|
14
|
+
let found = false;
|
|
15
|
+
blockquoteNode.cursor().iterate((node) => {
|
|
16
|
+
if (found) return false;
|
|
17
|
+
if (node.name === "Paragraph") {
|
|
18
|
+
node.node.cursor().iterate((child) => {
|
|
19
|
+
if (child.name === "Callout") {
|
|
20
|
+
calloutNode = child.node;
|
|
21
|
+
found = true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return calloutNode;
|
|
29
|
+
}
|
|
30
|
+
function buildCalloutWidgetDecorations(state) {
|
|
31
|
+
const decorations = [];
|
|
32
|
+
syntaxTree(state).iterate({
|
|
33
|
+
enter(node) {
|
|
34
|
+
if (node.name === "Blockquote") {
|
|
35
|
+
const calloutNode = getCalloutNode(node.node);
|
|
36
|
+
if (calloutNode && node.node.parent?.name !== "Blockquote") {
|
|
37
|
+
const from = node.from;
|
|
38
|
+
const to = node.to;
|
|
39
|
+
if (!isNodeRangeActive(state, from, to)) {
|
|
40
|
+
decorations.push(Decoration.replace({
|
|
41
|
+
widget: new CalloutWidget(from, to),
|
|
42
|
+
block: true
|
|
43
|
+
}).range(from, to));
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return decorations.sort((a, b) => a.from - b.from);
|
|
51
|
+
}
|
|
52
|
+
export const proseCalloutPlugin = StateField.define({
|
|
53
|
+
create(state) {
|
|
54
|
+
return RangeSet.of(buildCalloutWidgetDecorations(state), true);
|
|
55
|
+
},
|
|
56
|
+
update(value, tr) {
|
|
57
|
+
if (tr.docChanged || tr.selection) {
|
|
58
|
+
return RangeSet.of(buildCalloutWidgetDecorations(tr.state), true);
|
|
59
|
+
}
|
|
60
|
+
return value.map(tr.changes);
|
|
61
|
+
},
|
|
62
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
63
|
+
});
|
package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Decoration, EditorView } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
|
+
import { syntaxTree } from "@codemirror/language";
|
|
4
|
+
import { EndFenceWidget, LanguageFlairWidget } from "../codemirror-widgets/proseCodeBlockWidgets.js";
|
|
5
|
+
import { specialCodeBlockMapFacet } from "../specialCodeBlockMappingConfig.js";
|
|
6
|
+
import { ProseVueComponentEmbedWidget } from "../codemirror-widgets/proseVueComponentEmbedWidget.js";
|
|
7
|
+
import { cursorSelectionCoveredNode, isNodeRangeActive, toCursorNodePositions } from "../../utility/tools.js";
|
|
8
|
+
function buildCodeBlockDecorations(state) {
|
|
9
|
+
const decorations = [];
|
|
10
|
+
const specialCodeBlocks = state.facet(specialCodeBlockMapFacet);
|
|
11
|
+
syntaxTree(state).iterate({
|
|
12
|
+
enter(node) {
|
|
13
|
+
if (node.name === "FencedCode") {
|
|
14
|
+
const codeInfoNode = node.node.getChild("CodeInfo");
|
|
15
|
+
let language = "";
|
|
16
|
+
if (codeInfoNode) {
|
|
17
|
+
language = state.doc.sliceString(codeInfoNode.from, codeInfoNode.to);
|
|
18
|
+
}
|
|
19
|
+
const specialMapping = specialCodeBlocks.find((m) => m.codeInfo === language);
|
|
20
|
+
const firstLineNode = state.doc.lineAt(node.from);
|
|
21
|
+
let scanPos = node.to;
|
|
22
|
+
while (scanPos > node.from && /\s/.test(state.doc.sliceString(scanPos - 1, scanPos))) {
|
|
23
|
+
scanPos--;
|
|
24
|
+
}
|
|
25
|
+
const lastLineNode = state.doc.lineAt(scanPos > node.from ? scanPos : node.from);
|
|
26
|
+
let codeText = "";
|
|
27
|
+
const firstContentLineNum = firstLineNode.number + 1;
|
|
28
|
+
const lastContentLineNum = lastLineNode.number - 1;
|
|
29
|
+
if (firstContentLineNum <= lastContentLineNum) {
|
|
30
|
+
const contentStartOffset = state.doc.line(firstContentLineNum).from;
|
|
31
|
+
const contentEndOffset = state.doc.line(lastContentLineNum).to;
|
|
32
|
+
codeText = state.doc.sliceString(contentStartOffset, contentEndOffset);
|
|
33
|
+
}
|
|
34
|
+
const poses = toCursorNodePositions(state, node);
|
|
35
|
+
if (specialMapping != void 0 && !(isNodeRangeActive(state, node.from, node.to) || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo))) {
|
|
36
|
+
if (specialMapping)
|
|
37
|
+
decorations.push(
|
|
38
|
+
Decoration.replace({
|
|
39
|
+
widget: new ProseVueComponentEmbedWidget(
|
|
40
|
+
specialMapping.component,
|
|
41
|
+
{ codeContent: codeText },
|
|
42
|
+
node.from
|
|
43
|
+
),
|
|
44
|
+
block: true
|
|
45
|
+
}).range(node.from, node.to)
|
|
46
|
+
);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const cursor = state.selection.main;
|
|
50
|
+
for (let currentLineNum = firstLineNode.number; currentLineNum <= lastLineNode.number; currentLineNum++) {
|
|
51
|
+
const line = state.doc.line(currentLineNum);
|
|
52
|
+
let lineClasses = ["cm-codeblock"];
|
|
53
|
+
const cursorFocusedOnThisLine = cursor.anchor >= line.from && cursor.anchor <= line.to || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo);
|
|
54
|
+
if (currentLineNum === firstLineNode.number) {
|
|
55
|
+
lineClasses.push("cm-line-codeblock-begin");
|
|
56
|
+
if (!cursorFocusedOnThisLine) {
|
|
57
|
+
decorations.push(
|
|
58
|
+
Decoration.replace({
|
|
59
|
+
widget: new LanguageFlairWidget(language, codeText)
|
|
60
|
+
}).range(line.from, line.to)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
} else if (currentLineNum === lastLineNode.number) {
|
|
64
|
+
lineClasses.push("cm-line-codeblock-end");
|
|
65
|
+
if (!cursorFocusedOnThisLine) {
|
|
66
|
+
decorations.push(
|
|
67
|
+
Decoration.replace({
|
|
68
|
+
widget: new EndFenceWidget()
|
|
69
|
+
}).range(line.from, line.to)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
lineClasses.push("cm-line-codeblock-content");
|
|
74
|
+
}
|
|
75
|
+
decorations.push(
|
|
76
|
+
Decoration.line({
|
|
77
|
+
attributes: { class: lineClasses.join(" ") }
|
|
78
|
+
}).range(line.from)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return decorations;
|
|
86
|
+
}
|
|
87
|
+
export const proseCodeBlockCodemirrorViewPlugin = StateField.define({
|
|
88
|
+
create(state) {
|
|
89
|
+
return RangeSet.of(buildCodeBlockDecorations(state), true);
|
|
90
|
+
},
|
|
91
|
+
update(value, tr) {
|
|
92
|
+
if (tr.docChanged || tr.selection) {
|
|
93
|
+
return RangeSet.of(buildCodeBlockDecorations(tr.state), true);
|
|
94
|
+
}
|
|
95
|
+
return value.map(tr.changes);
|
|
96
|
+
},
|
|
97
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
98
|
+
});
|
package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { EditorView } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
|
+
import { syntaxTree } from "@codemirror/language";
|
|
4
|
+
import { decorationProseHashtag } from "../../utility/decorations.js";
|
|
5
|
+
function buildHashtagWrappers(state) {
|
|
6
|
+
const decorations = [];
|
|
7
|
+
syntaxTree(state).iterate({
|
|
8
|
+
enter(node) {
|
|
9
|
+
if (node.name === "HashtagTag") {
|
|
10
|
+
decorations.push(decorationProseHashtag.range(node.from, node.to));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
return decorations;
|
|
15
|
+
}
|
|
16
|
+
export const proseHashtagCodemirrorViewPlugin = StateField.define({
|
|
17
|
+
create(state) {
|
|
18
|
+
return RangeSet.of(buildHashtagWrappers(state));
|
|
19
|
+
},
|
|
20
|
+
update(value, tr) {
|
|
21
|
+
if (tr.docChanged) {
|
|
22
|
+
return RangeSet.of(buildHashtagWrappers(tr.state));
|
|
23
|
+
}
|
|
24
|
+
return value.map(tr.changes);
|
|
25
|
+
},
|
|
26
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
27
|
+
});
|
package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Decoration, EditorView } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
|
+
import { syntaxTree } from "@codemirror/language";
|
|
4
|
+
import { cursorSelectionCoveredNode, isNodeRangeActive, toCursorNodePositions } from "../../utility/tools.js";
|
|
5
|
+
function buildHighlightDecorations(state) {
|
|
6
|
+
const decorations = [];
|
|
7
|
+
syntaxTree(state).iterate({
|
|
8
|
+
enter(node) {
|
|
9
|
+
if (node.name === "Mark") {
|
|
10
|
+
const markers = node.node.getChildren("MarkMarker");
|
|
11
|
+
if (markers.length < 2) return;
|
|
12
|
+
const openMark = state.doc.sliceString(markers[0]?.from || 0, markers[0]?.to || 0);
|
|
13
|
+
if (openMark !== "==") return;
|
|
14
|
+
const closeMark = state.doc.sliceString(
|
|
15
|
+
markers[markers.length - 1]?.from || 0,
|
|
16
|
+
markers[markers.length - 1]?.to || 0
|
|
17
|
+
);
|
|
18
|
+
if (closeMark !== "==") return;
|
|
19
|
+
const poses = toCursorNodePositions(state, node);
|
|
20
|
+
if (isNodeRangeActive(state, node.from, node.to) || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const startContent = markers[0]?.to || 0;
|
|
24
|
+
const endContent = markers[markers.length - 1]?.from || 0;
|
|
25
|
+
decorations.push(
|
|
26
|
+
Decoration.mark({
|
|
27
|
+
class: "cm-highlighted",
|
|
28
|
+
tagName: "span"
|
|
29
|
+
}).range(startContent, endContent)
|
|
30
|
+
);
|
|
31
|
+
decorations.push(Decoration.replace({}).range(markers[0]?.from || 0, markers[0]?.to || 0));
|
|
32
|
+
decorations.push(
|
|
33
|
+
Decoration.replace({}).range(markers[markers.length - 1]?.from || 0, markers[markers.length - 1]?.to || 0)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return decorations;
|
|
39
|
+
}
|
|
40
|
+
export const proseHighlightCodemirrorViewPlugin = StateField.define({
|
|
41
|
+
create(state) {
|
|
42
|
+
return RangeSet.of(buildHighlightDecorations(state), true);
|
|
43
|
+
},
|
|
44
|
+
update(value, tr) {
|
|
45
|
+
if (tr.docChanged || tr.selection) {
|
|
46
|
+
return RangeSet.of(buildHighlightDecorations(tr.state), true);
|
|
47
|
+
}
|
|
48
|
+
return value.map(tr.changes);
|
|
49
|
+
},
|
|
50
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
51
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type DecorationSet } from '@codemirror/view';
|
|
2
|
+
import { StateField } from '@codemirror/state';
|
|
3
|
+
import type { EditorState } from '@codemirror/state';
|
|
4
|
+
declare function isNodeRangeActive(state: EditorState, nodeFrom: number, nodeTo: number): boolean;
|
|
5
|
+
export { isNodeRangeActive };
|
|
6
|
+
export declare const proseInternalLinkCodemirrorViewPlugin: StateField<DecorationSet>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Decoration, EditorView } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
|
+
import { syntaxTree } from "@codemirror/language";
|
|
4
|
+
import { internalLinkMapFacet } from "../linkMappingConfig.js";
|
|
5
|
+
import { ProseVueComponentEmbedWidget } from "../codemirror-widgets/proseVueComponentEmbedWidget.js";
|
|
6
|
+
import { cursorSelectionCoveredNode, toCursorNodePositions } from "../../utility/tools.js";
|
|
7
|
+
function isNodeRangeActive(state, nodeFrom, nodeTo) {
|
|
8
|
+
const cursor = state.selection.main;
|
|
9
|
+
if (cursor.empty) {
|
|
10
|
+
return cursor.from >= nodeFrom && cursor.from <= nodeTo;
|
|
11
|
+
} else {
|
|
12
|
+
return Math.max(nodeFrom, cursor.from) < Math.min(nodeTo, cursor.to);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function buildInternalLinkDecorations(state) {
|
|
16
|
+
const decorations = [];
|
|
17
|
+
const linkMap = state.facet(internalLinkMapFacet);
|
|
18
|
+
const widgets = [];
|
|
19
|
+
syntaxTree(state).iterate({
|
|
20
|
+
enter(node) {
|
|
21
|
+
if (node.name === "Embed") {
|
|
22
|
+
const pathNode = node.node.getChild("InternalLink")?.getChild("InternalPath");
|
|
23
|
+
if (pathNode) {
|
|
24
|
+
const path = state.doc.sliceString(pathNode.from, pathNode.to);
|
|
25
|
+
const linkInfo = linkMap.find((l) => l.internalLinkName === path);
|
|
26
|
+
if (linkInfo?.embedComponent) {
|
|
27
|
+
const line = state.doc.lineAt(node.from);
|
|
28
|
+
const props = { linkData: linkInfo };
|
|
29
|
+
if (linkInfo.filePath) {
|
|
30
|
+
props.filePath = linkInfo.filePath;
|
|
31
|
+
}
|
|
32
|
+
widgets.push(Decoration.widget({
|
|
33
|
+
widget: new ProseVueComponentEmbedWidget(linkInfo.embedComponent, props, node.from),
|
|
34
|
+
block: true,
|
|
35
|
+
side: 1
|
|
36
|
+
}).range(line.to));
|
|
37
|
+
const poses = toCursorNodePositions(state, node);
|
|
38
|
+
if (!(isNodeRangeActive(state, node.from, node.to) || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo))) {
|
|
39
|
+
decorations.push(Decoration.replace({}).range(node.from, node.to));
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (node.name === "InternalLink" || node.name === "Embed") {
|
|
46
|
+
const mainNode = node.node;
|
|
47
|
+
const isActive = isNodeRangeActive(state, mainNode.from, mainNode.to);
|
|
48
|
+
if (isActive) return;
|
|
49
|
+
const contentContainerNode = node.name === "Embed" ? mainNode.getChild("InternalLink") : mainNode;
|
|
50
|
+
if (!contentContainerNode) return;
|
|
51
|
+
const pathNode = contentContainerNode.getChild("InternalPath");
|
|
52
|
+
if (!pathNode) return;
|
|
53
|
+
const path = state.doc.sliceString(pathNode.from, pathNode.to);
|
|
54
|
+
const subpathNode = contentContainerNode.getChild("InternalSubpath");
|
|
55
|
+
const aliasNode = contentContainerNode.getChild("InternalDisplay");
|
|
56
|
+
const subpath = subpathNode ? state.doc.sliceString(subpathNode.from, subpathNode.to) : void 0;
|
|
57
|
+
const alias = aliasNode ? state.doc.sliceString(aliasNode.from, aliasNode.to) : void 0;
|
|
58
|
+
const linkInfo = linkMap.find((l) => l.internalLinkName === path);
|
|
59
|
+
if (node.name === "Embed" && linkInfo?.embedComponent) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const linkAttributes = {
|
|
63
|
+
"class": "cm-link",
|
|
64
|
+
"href": "#",
|
|
65
|
+
"data-internal-link": "true",
|
|
66
|
+
"data-path": path,
|
|
67
|
+
"data-type": node.name === "Embed" ? "embed" : "internal-link"
|
|
68
|
+
};
|
|
69
|
+
if (!linkInfo) {
|
|
70
|
+
linkAttributes["class"] += " cm-unresolved-link";
|
|
71
|
+
}
|
|
72
|
+
if (subpath) linkAttributes["data-subpath"] = subpath;
|
|
73
|
+
if (alias) linkAttributes["data-display"] = alias;
|
|
74
|
+
if (node.name === "Embed") {
|
|
75
|
+
const embedMark = mainNode.getChild("EmbedMark");
|
|
76
|
+
if (embedMark) {
|
|
77
|
+
decorations.push(Decoration.replace({}).range(embedMark.from, embedMark.to));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (aliasNode) {
|
|
81
|
+
decorations.push(Decoration.mark({ tagName: "a", attributes: linkAttributes }).range(aliasNode.from, aliasNode.to));
|
|
82
|
+
decorations.push(Decoration.replace({}).range(pathNode.from, pathNode.to));
|
|
83
|
+
if (subpathNode) {
|
|
84
|
+
decorations.push(Decoration.replace({}).range(subpathNode.from, subpathNode.to));
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
const linkStart = pathNode.from;
|
|
88
|
+
const linkEnd = subpathNode ? subpathNode.to : pathNode.to;
|
|
89
|
+
decorations.push(Decoration.mark({ tagName: "a", attributes: linkAttributes }).range(linkStart, linkEnd));
|
|
90
|
+
}
|
|
91
|
+
contentContainerNode.getChildren("InternalMark").forEach((mark) => {
|
|
92
|
+
decorations.push(Decoration.replace({}).range(mark.from, mark.to));
|
|
93
|
+
});
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return [...decorations, ...widgets];
|
|
99
|
+
}
|
|
100
|
+
export { isNodeRangeActive };
|
|
101
|
+
export const proseInternalLinkCodemirrorViewPlugin = StateField.define({
|
|
102
|
+
create(state) {
|
|
103
|
+
return RangeSet.of(buildInternalLinkDecorations(state), true);
|
|
104
|
+
},
|
|
105
|
+
update(value, tr) {
|
|
106
|
+
if (tr.docChanged || tr.selection) {
|
|
107
|
+
return RangeSet.of(buildInternalLinkDecorations(tr.state), true);
|
|
108
|
+
}
|
|
109
|
+
return value.map(tr.changes);
|
|
110
|
+
},
|
|
111
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
112
|
+
});
|