mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 21:58:37 +00:00
tidal highlighting (single line only for now)
This commit is contained in:
parent
5fb3aadad9
commit
a94ddc7947
@ -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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user