diff --git a/src/content/posts/treesitter-on-the-web.md b/src/content/posts/treesitter-on-the-web.md index 3252436..2eb2fac 100644 --- a/src/content/posts/treesitter-on-the-web.md +++ b/src/content/posts/treesitter-on-the-web.md @@ -180,7 +180,7 @@ export const { hl } = require('./treesitter.linux-x64-gnu.node'); Once we have this library we can load it inside Node (almost) like any other module. -``` +```node Welcome to Node.js v21.6.1. Type ".help" for more information. > const treesitter = await import('./dist/index.js') diff --git a/src/utils/treesitter.ts b/src/utils/treesitter.ts index fedbcef..a27fb44 100644 --- a/src/utils/treesitter.ts +++ b/src/utils/treesitter.ts @@ -9,18 +9,74 @@ function text(value: string) { } } -function span(name: string) { +function span(name: string, children: any[] = []) { return { type: 'element', tagName: 'span', properties: { className: name.replace('.', '-'), }, - children: [] + children, } } +function highlight(lang: string, code: string) { + const root: any[] = []; + const ptrs: any[] = [{ children: root }]; + + for (const event of treesitter.hl(lang, code)) { + switch (event.kind) { + case 'text': { + const node = text(event.text); + ptrs.at(-1).children.push(node); + } break; + case 'open': { + const node = span(event.name); + ptrs.at(-1).children.push(node); + ptrs.push(node); + } break; + case 'close': { + ptrs.pop(); + } break; + } + } + + return root; +} + +function repl(prompt: string, lang: string, code: string) { + const chunks = [{ i: [] as any[], o: [] as any[] }]; + + for (const line of code.split('\n')) { + if (line.startsWith(prompt)) { + chunks.push({ i: [line], o: [] }); + } else { + chunks.at(-1)!.o.push(line); + } + } + + const out: any[] = []; + for (const { i, o } of chunks) { + if (i.length) { + out.push({ + type: 'element', + tagName: 'div', + children: [ + span('keyword-return', [text(prompt)]), + ...highlight(lang, i[0].replace(prompt, '')), + ] + }); + } + if (o.length) { + out.push(text(o.join('\n'))); + } + } + + return out; +} + + export default function rehypeTreesitter() { return function (tree: any) { visit(tree, null, (node, _, above) => { @@ -36,27 +92,10 @@ export default function rehypeTreesitter() { ...!!lang && { "data-lang": lang }, }; - const root = { children: [] }; - const ptrs: any[] = [root]; - - for (const event of treesitter.hl(lang, code)) { - switch (event.kind) { - case 'text': { - const inserted = text(event.text); - ptrs.at(-1).children.push(inserted); - } break; - case 'open': { - const inserted = span(event.name); - ptrs.at(-1).children.push(inserted); - ptrs.push(inserted); - } break; - case 'close': { - ptrs.pop(); - } break; - } + switch (lang) { + case 'node': node.children = repl('>', 'js', code); break; + default: node.children = highlight(lang, code); } - - node.children = root.children; }); }; }