1// deno-lint-ignore-file no-explicit-any
2// ^- NOTE: No idea for the typing of markdown-it plugin context, so we disable this check
3// Taken from https://github.com/pixeldesu/pixelde.su/blob/main/plugins/markdown-it/vscode.ts and edited for my use
4
5import "lume/types.ts";
6
7// Cache object for frontmatter line counts so we only reload files once
8const FRONTMATTER_LINE_CACHE: Record<string, number> = {};
9
10function markdownItOpenInEditor(md: any) {
11 // Override the default paragraph renderer
12 const defaultRender = md.renderer.rules.paragraph_open ||
13 function (
14 tokens: Record<number, any>,
15 idx: number,
16 options: any,
17 _env: any,
18 self: any,
19 ) {
20 return self.renderToken(tokens, idx, options);
21 };
22
23 md.renderer.rules.paragraph_open = function (
24 tokens: Record<number, any>,
25 idx: number,
26 options: any,
27 env: any,
28 self: any,
29 ) {
30 const token = tokens[idx];
31 const filePath = env.data.page.src.entry.src || "";
32
33 // FIXME: This is extremely ugly, having to reload the file to get the full context to
34 // calculate the line number offset for the frontmatter. For some reason this is
35 // pretty fast however, so I'll let it slide for now.
36 if (!FRONTMATTER_LINE_CACHE[filePath]) {
37 FRONTMATTER_LINE_CACHE[filePath] = countFrontMatterLines(
38 Deno.readTextFileSync(filePath),
39 );
40 }
41 const lineNumber = FRONTMATTER_LINE_CACHE[filePath] +
42 (token.map ? token.map[0] : 1);
43
44 // Create a wrapper element with context to open the file in the editor
45 const clickableElementStart = `<div data-editor-file="${filePath}:${lineNumber}">`;
46
47 return clickableElementStart +
48 defaultRender(tokens, idx, options, env, self);
49 };
50
51 md.renderer.rules.paragraph_close = function (
52 tokens: Record<number, any>,
53 idx: number,
54 options: any,
55 _env: any,
56 self: any,
57 ) {
58 return self.renderToken(tokens, idx, options) + "</div>";
59 };
60}
61
62function countFrontMatterLines(content: string) {
63 const frontMatterPattern = /^---\s*\n([\s\S]*?)\n---\s*\n/;
64 const match = content.match(frontMatterPattern);
65 if (match) {
66 return match[0].split("\n").length;
67 }
68 return 0;
69}
70
71export default function () {
72 return function (site: Lume.Site) {
73 site.hooks.addMarkdownItPlugin(markdownItOpenInEditor);
74 };
75}