tidal highlighting (single line only for now)

This commit is contained in:
Felix Roos 2024-05-18 00:44:47 +02:00
parent 5fb3aadad9
commit a94ddc7947
2 changed files with 76 additions and 7 deletions

View File

@ -28,11 +28,6 @@ function getInfixOperators() {
ops['~>'] = (l, r) => reify(l).late(reify(r));
ops['<~'] = (l, r) => reify(l).early(reify(r));
ops['<$>'] = (l, r) => reify(r).fmap(l).outerJoin(); // is this right?
ops['string'] = (node) => {
const str = node.text.slice(1, -1);
console.log('string node', node);
return m(str, 1);
};
return ops;
}
const ops = getInfixOperators();
@ -51,9 +46,30 @@ export async function initTidal() {
return loadParser();
}
export function tidal(code) {
// offset is expected to be passed in from transpiler
/*
1. acorn parses JS to find location of tidal call
2. haskell-tree-sitter calls "string" function with node (including location)
3. m function sets locations for individual mini notation atom
so the location for a mini notation atom is:
js offset + hs offset + atom offset
*/
export function tidal(code, offset = 0) {
if (Array.isArray(code)) {
code = code.join('');
}
return evaluate(code, window, ops);
return evaluate(code, window, {
...ops,
string: (node) => {
// parses strings as mini notation and passes location
const str = node.text.slice(1, -1);
const col = node.startIndex + offset;
// problem: node.startIndex is wrong because hs2js parser removes newlines
// for some reason, the parser doesn't like newlines
// this means highlighting only works in the first line for now
return m(str, col);
},
});
}

View File

@ -26,6 +26,16 @@ export function transpiler(input, options = {}) {
walk(ast, {
enter(node, parent /* , prop, index */) {
if (isTidalTeplateLiteral(node)) {
const raw = node.quasi.quasis[0].value.raw;
const offset = node.quasi.start + 1;
if (emitMiniLocations) {
const stringLocs = collectHaskellMiniLocations(raw, offset);
miniLocations = miniLocations.concat(stringLocs);
}
this.skip();
return this.replace(tidalWithLocation(raw, offset));
}
if (isBackTickString(node, parent)) {
const { quasis } = node;
const { raw } = quasis[0].value;
@ -207,3 +217,46 @@ function labelToP(node) {
},
};
}
// tidal highlighting
// this feels kind of stupid, when we also know the location inside the string op (tidal.mjs)
// but maybe it's the only way
function isTidalTeplateLiteral(node) {
return node.type === 'TaggedTemplateExpression' && node.tag.name === 'tidal';
}
function collectHaskellMiniLocations(haskellCode, offset) {
const stringLocations = haskellCode.split('').reduce((acc, char, i) => {
if (char !== '"') {
return acc;
}
if (!acc.length || acc[acc.length - 1].length > 1) {
acc.push([i + 1]);
} else {
acc[acc.length - 1].push(i);
}
return acc;
}, []);
return stringLocations
.map(([start, end]) => {
const miniString = haskellCode.slice(start, end);
return getLeafLocations(`"${miniString}"`, offset + start - 1);
})
.flat();
}
function tidalWithLocation(value, offset) {
return {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'tidal',
},
arguments: [
{ type: 'Literal', value },
{ type: 'Literal', value: offset },
],
optional: false,
};
}