feat: highlight repl session

This commit is contained in:
Maciej Jur 2024-02-16 21:54:53 +01:00
parent d86b028a06
commit c671506a0b
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
2 changed files with 62 additions and 23 deletions

View file

@ -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. Once we have this library we can load it inside Node (almost) like any other module.
``` ```node
Welcome to Node.js v21.6.1. Welcome to Node.js v21.6.1.
Type ".help" for more information. Type ".help" for more information.
> const treesitter = await import('./dist/index.js') > const treesitter = await import('./dist/index.js')

View file

@ -9,18 +9,74 @@ function text(value: string) {
} }
} }
function span(name: string) { function span(name: string, children: any[] = []) {
return { return {
type: 'element', type: 'element',
tagName: 'span', tagName: 'span',
properties: { properties: {
className: name.replace('.', '-'), 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() { export default function rehypeTreesitter() {
return function (tree: any) { return function (tree: any) {
visit(tree, null, (node, _, above) => { visit(tree, null, (node, _, above) => {
@ -36,27 +92,10 @@ export default function rehypeTreesitter() {
...!!lang && { "data-lang": lang }, ...!!lang && { "data-lang": lang },
}; };
const root = { children: [] }; switch (lang) {
const ptrs: any[] = [root]; case 'node': node.children = repl('>', 'js', code); break;
default: node.children = highlight(lang, code);
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;
}
} }
node.children = root.children;
}); });
}; };
} }