diff --git a/src/lang/modifyAst.ts b/src/lang/modifyAst.ts index b62b50122..fdfaf5815 100644 --- a/src/lang/modifyAst.ts +++ b/src/lang/modifyAst.ts @@ -335,12 +335,21 @@ export function sketchOnExtrudedFace( } } -export const getLastIndex = (pathToNode: PathToNode): number => { +export const getLastIndex = (pathToNode: PathToNode): number => + splitPathAtLastIndex(pathToNode).index + +export function splitPathAtLastIndex(pathToNode: PathToNode): { + path: PathToNode + index: number +} { const last = pathToNode[pathToNode.length - 1] if (typeof last === 'number') { - return last + return { + path: pathToNode.slice(0, -1), + index: last, + } } - return getLastIndex(pathToNode.slice(0, -1)) + return splitPathAtLastIndex(pathToNode.slice(0, -1)) } export function createLiteral(value: string | number): Literal { diff --git a/src/lang/queryAst.test.ts b/src/lang/queryAst.test.ts new file mode 100644 index 000000000..318493da1 --- /dev/null +++ b/src/lang/queryAst.test.ts @@ -0,0 +1,48 @@ +import { abstractSyntaxTree } from './abstractSyntaxTree' +import { findAllPreviousVariables } from './queryAst' +import { lexer } from './tokeniser' +import { initPromise } from './rust' +import { executor } from './executor' + +beforeAll(() => initPromise) + +describe('findAllPreviousVariables', () => { + it('should find all previous variables', () => { + const code = `const baseThick = 1 +const armAngle = 60 + +const baseThickHalf = baseThick / 2 +const halfArmAngle = armAngle / 2 + +const arrExpShouldNotBeIncluded = [1, 2, 3] +const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 } + +const part001 = startSketchAt([0, 0]) + |> yLineTo(1, %) + |> xLine(3.84, %) // selection-range-7ish-before-this + +const variableBelowShouldNotBeIncluded = 3 + +show(part001)` + const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7 + const ast = abstractSyntaxTree(lexer(code)) + const programMemory = executor(ast) + + const { variables, bodyPath, insertIndex } = findAllPreviousVariables( + ast, + programMemory, + [rangeStart, rangeStart] + ) + expect(variables).toEqual([ + { key: 'baseThick', value: 1 }, + { key: 'armAngle', value: 60 }, + { key: 'baseThickHalf', value: 0.5 }, + { key: 'halfArmAngle', value: 30 }, + // no arrExpShouldNotBeIncluded, variableBelowShouldNotBeIncluded etc + ]) + // there are 4 number variables and 2 non-number variables before the sketch var + // ∴ the insert index should be 6 + expect(insertIndex).toEqual(6) + expect(bodyPath).toEqual(['body']) + }) +}) diff --git a/src/lang/queryAst.ts b/src/lang/queryAst.ts index ef8c893c2..f0d1ff062 100644 --- a/src/lang/queryAst.ts +++ b/src/lang/queryAst.ts @@ -1,6 +1,7 @@ -import { PathToNode } from './executor' +import { PathToNode, ProgramMemory } from './executor' import { Range } from '../useStore' import { Program } from './abstractSyntaxTree' +import { splitPathAtLastIndex } from './modifyAst' export function getNodeFromPath( node: Program, @@ -133,3 +134,43 @@ export function getNodePathFromSourceRange( } return path } + +interface PrevVariable { + key: string + value: T +} + +export function findAllPreviousVariables( + ast: Program, + programMemory: ProgramMemory, + sourceRange: Range, + type: 'number' | 'string' = 'number' +): { + variables: PrevVariable[] + bodyPath: PathToNode + insertIndex: number +} { + const path = getNodePathFromSourceRange(ast, sourceRange) + const { path: pathToDec } = getNodeFromPath(ast, path, 'VariableDeclaration') + const { index: insertIndex, path: bodyPath } = splitPathAtLastIndex(pathToDec) + + const { node: bodyItems } = getNodeFromPath(ast, bodyPath) + + const variables: PrevVariable[] = [] + bodyItems.forEach((item) => { + if (item.type !== 'VariableDeclaration' || item.end > sourceRange[0]) return + const varName = item.declarations[0].id.name + const varValue = programMemory?.root[varName] + if (typeof varValue?.value !== type) return + variables.push({ + key: varName, + value: varValue.value, + }) + }) + + return { + insertIndex, + bodyPath: bodyPath, + variables, + } +}