Skip to content

Commit 45fe3f9

Browse files
committed
Extract view plugin
1 parent ed0cf2d commit 45fe3f9

File tree

2 files changed

+134
-131
lines changed

2 files changed

+134
-131
lines changed

main.ts

Lines changed: 3 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,7 @@ import {
1414
import { debounce, Debouncer, MarkdownView, Plugin, setIcon } from "obsidian";
1515
import { DEFAULT_SETTINGS, PluginSettings } from "./settings";
1616
import { SimpleEmbedPluginSettingTab } from "./settings-tab";
17-
import { RangeSetBuilder } from "@codemirror/rangeset";
18-
import {
19-
Decoration,
20-
DecorationSet,
21-
EditorView,
22-
ViewPlugin,
23-
ViewUpdate,
24-
WidgetType,
25-
} from "@codemirror/view";
17+
import { buildSimpleEmbedsViewPlugin } from "./view-plugin";
2618

2719
export default class SimpleEmbedsPlugin extends Plugin {
2820
settings: PluginSettings;
@@ -48,7 +40,7 @@ export default class SimpleEmbedsPlugin extends Plugin {
4840

4941
this.currentTheme = this._getCurrentTheme();
5042

51-
const ext = this.buildSimpleEmbedsViewPlugin(this);
43+
const ext = buildSimpleEmbedsViewPlugin(this);
5244
this.registerEditorExtension(ext);
5345

5446
this.processedMarkdown = debounce(() => {
@@ -142,7 +134,7 @@ export default class SimpleEmbedsPlugin extends Plugin {
142134
}
143135
}
144136

145-
private createEmbed(
137+
createEmbed(
146138
embedSource: EmbedSource,
147139
link: string,
148140
fullWidth: boolean,
@@ -181,124 +173,4 @@ export default class SimpleEmbedsPlugin extends Plugin {
181173
return (this.app.vault as any).config?.livePreview &&
182174
this.settings.enableInLivePreview;
183175
}
184-
185-
buildSimpleEmbedsViewPlugin(plugin: SimpleEmbedsPlugin) {
186-
class EmbedWidget extends WidgetType {
187-
constructor(
188-
readonly link: string,
189-
readonly embedSource: EmbedSource,
190-
readonly plugin: SimpleEmbedsPlugin,
191-
) {
192-
super();
193-
}
194-
195-
eq(other: EmbedWidget) {
196-
return other.link === this.link;
197-
}
198-
199-
toDOM() {
200-
const embed = this.plugin.createEmbed(
201-
this.embedSource,
202-
this.link,
203-
false,
204-
);
205-
return embed;
206-
}
207-
208-
ignoreEvent() {
209-
return true;
210-
}
211-
}
212-
213-
const viewPlugin = ViewPlugin.fromClass(
214-
class {
215-
decorations: DecorationSet;
216-
217-
constructor(view: EditorView) {
218-
this.decorations = this.buildDecorations(view);
219-
}
220-
221-
update(update: ViewUpdate) {
222-
if (
223-
update.docChanged || update.viewportChanged || update.selectionSet
224-
) {
225-
this.decorations = this.buildDecorations(update.view);
226-
}
227-
}
228-
229-
destroy() {
230-
}
231-
232-
buildDecorations(view: EditorView) {
233-
let builder = new RangeSetBuilder<Decoration>();
234-
235-
if (!plugin.isLivePreviewSupported) {
236-
return builder.finish();
237-
}
238-
239-
let lines: number[] = [];
240-
if (view.state.doc.length > 0) {
241-
lines = Array.from(
242-
{ length: view.state.doc.lines },
243-
(_, i) => i + 1,
244-
);
245-
}
246-
247-
const currentSelections = [...view.state.selection.ranges];
248-
249-
for (let n of lines) {
250-
const line = view.state.doc.line(n);
251-
const startOfLine = line.from;
252-
const endOfLine = line.to;
253-
254-
let currentLine = false;
255-
256-
currentSelections.forEach((r) => {
257-
if (r.from >= startOfLine && r.to <= endOfLine) {
258-
currentLine = true;
259-
return;
260-
}
261-
});
262-
263-
const mdLink = line.text.match(/\[.*\]\(\S*\)/)?.first().trim();
264-
if (!currentLine && mdLink) {
265-
const start = line.text.indexOf(mdLink) + startOfLine;
266-
const end = start + mdLink.length;
267-
let embedSource = plugin.embedSources.find((source) => {
268-
return plugin.settings[source.enabledKey] &&
269-
source.regex.test(line.text);
270-
});
271-
if (embedSource) {
272-
const link = line.text.match(embedSource.regex).first();
273-
const deco = Decoration.replace({
274-
widget: new EmbedWidget(
275-
link,
276-
embedSource,
277-
plugin,
278-
),
279-
inclusive: false,
280-
});
281-
if (plugin.settings.keepLinksInPreview) {
282-
if (plugin.settings.embedPlacement === "above") {
283-
builder.add(start, start, deco);
284-
} else if (plugin.settings.embedPlacement === "below") {
285-
builder.add(end, end, deco);
286-
}
287-
} else {
288-
builder.add(start, end, deco);
289-
}
290-
}
291-
}
292-
}
293-
294-
return builder.finish();
295-
}
296-
},
297-
{
298-
decorations: (v) => v.decorations,
299-
},
300-
);
301-
302-
return viewPlugin;
303-
}
304176
}

view-plugin.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { RangeSetBuilder } from "@codemirror/rangeset";
2+
import {
3+
Decoration,
4+
DecorationSet,
5+
EditorView,
6+
ViewPlugin,
7+
ViewUpdate,
8+
WidgetType,
9+
} from "@codemirror/view";
10+
import { EmbedSource } from "./embeds";
11+
import SimpleEmbedsPlugin from "./main";
12+
13+
export function buildSimpleEmbedsViewPlugin(plugin: SimpleEmbedsPlugin) {
14+
class EmbedWidget extends WidgetType {
15+
constructor(
16+
readonly link: string,
17+
readonly embedSource: EmbedSource,
18+
readonly plugin: SimpleEmbedsPlugin,
19+
) {
20+
super();
21+
}
22+
23+
eq(other: EmbedWidget) {
24+
return other.link === this.link;
25+
}
26+
27+
toDOM() {
28+
const embed = this.plugin.createEmbed(
29+
this.embedSource,
30+
this.link,
31+
false,
32+
);
33+
return embed;
34+
}
35+
36+
ignoreEvent() {
37+
return true;
38+
}
39+
}
40+
41+
const viewPlugin = ViewPlugin.fromClass(
42+
class {
43+
decorations: DecorationSet;
44+
45+
constructor(view: EditorView) {
46+
this.decorations = this.buildDecorations(view);
47+
}
48+
49+
update(update: ViewUpdate) {
50+
if (
51+
update.docChanged || update.viewportChanged || update.selectionSet
52+
) {
53+
this.decorations = this.buildDecorations(update.view);
54+
}
55+
}
56+
57+
destroy() {
58+
}
59+
60+
buildDecorations(view: EditorView) {
61+
let builder = new RangeSetBuilder<Decoration>();
62+
63+
if (!plugin.isLivePreviewSupported) {
64+
return builder.finish();
65+
}
66+
67+
let lines: number[] = [];
68+
if (view.state.doc.length > 0) {
69+
lines = Array.from(
70+
{ length: view.state.doc.lines },
71+
(_, i) => i + 1,
72+
);
73+
}
74+
75+
const currentSelections = [...view.state.selection.ranges];
76+
77+
for (let n of lines) {
78+
const line = view.state.doc.line(n);
79+
const startOfLine = line.from;
80+
const endOfLine = line.to;
81+
82+
let currentLine = false;
83+
84+
currentSelections.forEach((r) => {
85+
if (r.from >= startOfLine && r.to <= endOfLine) {
86+
currentLine = true;
87+
return;
88+
}
89+
});
90+
91+
const mdLink = line.text.match(/\[.*\]\(\S*\)/)?.first().trim();
92+
if (!currentLine && mdLink) {
93+
const start = line.text.indexOf(mdLink) + startOfLine;
94+
const end = start + mdLink.length;
95+
let embedSource = plugin.embedSources.find((source) => {
96+
return plugin.settings[source.enabledKey] &&
97+
source.regex.test(line.text);
98+
});
99+
if (embedSource) {
100+
const link = line.text.match(embedSource.regex).first();
101+
const deco = Decoration.replace({
102+
widget: new EmbedWidget(
103+
link,
104+
embedSource,
105+
plugin,
106+
),
107+
inclusive: false,
108+
});
109+
if (plugin.settings.keepLinksInPreview) {
110+
if (plugin.settings.embedPlacement === "above") {
111+
builder.add(start, start, deco);
112+
} else if (plugin.settings.embedPlacement === "below") {
113+
builder.add(end, end, deco);
114+
}
115+
} else {
116+
builder.add(start, end, deco);
117+
}
118+
}
119+
}
120+
}
121+
122+
return builder.finish();
123+
}
124+
},
125+
{
126+
decorations: (v) => v.decorations,
127+
},
128+
);
129+
130+
return viewPlugin;
131+
}

0 commit comments

Comments
 (0)