Compare commits
	
		
			7 Commits
		
	
	
		
			achalmers/
			...
			jtran/type
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 602f27737f | |||
| 38adaf8c68 | |||
| 971471f45d | |||
| 92b3f6192d | |||
| 2ff1d1167f | |||
| b2fe72f677 | |||
| 8e5d13315d | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 36 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 39 KiB | 
| @ -5,7 +5,7 @@ import { cameraMouseDragGuards } from 'lib/cameraControls' | ||||
| import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' | ||||
| import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra' | ||||
| import { ReactCameraProperties } from './CameraControls' | ||||
| import { throttle } from 'lib/utils' | ||||
| import { isArray, throttle } from 'lib/utils' | ||||
| import { | ||||
|   sceneInfra, | ||||
|   kclManager, | ||||
| @ -20,13 +20,17 @@ import { | ||||
|   getParentGroup, | ||||
| } from './sceneEntities' | ||||
| import { SegmentOverlay, SketchDetails } from 'machines/modelingMachine' | ||||
| import { findUsesOfTagInPipe, getNodeFromPath } from 'lang/queryAst' | ||||
| import { | ||||
|   expectNodeOnPath, | ||||
|   findUsesOfTagInPipe, | ||||
|   getLastNodeFromPath, | ||||
|   getNodeFromPath, | ||||
| } from 'lang/queryAst' | ||||
| import { | ||||
|   CallExpression, | ||||
|   PathToNode, | ||||
|   Program, | ||||
|   SourceRange, | ||||
|   Value, | ||||
|   parse, | ||||
|   recast, | ||||
| } from 'lang/wasm' | ||||
| @ -186,13 +190,12 @@ const Overlay = ({ | ||||
|   let xAlignment = overlay.angle < 0 ? '0%' : '-100%' | ||||
|   let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%' | ||||
|  | ||||
|   const _node1 = getNodeFromPath<CallExpression>( | ||||
|   const callExpression = expectNodeOnPath<CallExpression>( | ||||
|     kclManager.ast, | ||||
|     overlay.pathToNode, | ||||
|     'CallExpression' | ||||
|   ) | ||||
|   if (err(_node1)) return | ||||
|   const callExpression = _node1.node | ||||
|   if (err(callExpression)) return | ||||
|  | ||||
|   const constraints = getConstraintInfo( | ||||
|     callExpression, | ||||
| @ -549,13 +552,13 @@ const ConstraintSymbol = ({ | ||||
|     varNameMap[_type as LineInputsType]?.implicitConstraintDesc | ||||
|  | ||||
|   const _node = useMemo( | ||||
|     () => getNodeFromPath<Value>(kclManager.ast, pathToNode), | ||||
|     () => getLastNodeFromPath(kclManager.ast, pathToNode), | ||||
|     [kclManager.ast, pathToNode] | ||||
|   ) | ||||
|   if (err(_node)) return | ||||
|   const node = _node.node | ||||
|  | ||||
|   const range: SourceRange = node ? [node.start, node.end] : [0, 0] | ||||
|   const range: SourceRange = !isArray(node) ? [node.start, node.end] : [0, 0] | ||||
|  | ||||
|   if (_type === 'intersectionTag') return null | ||||
|  | ||||
|  | ||||
| @ -61,7 +61,12 @@ import { | ||||
|   codeManager, | ||||
|   editorManager, | ||||
| } from 'lib/singletons' | ||||
| import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' | ||||
| import { | ||||
|   isNodeType, | ||||
|   expectNodeOnPath, | ||||
|   getNodeFromPath, | ||||
|   getNodePathFromSourceRange, | ||||
| } from 'lang/queryAst' | ||||
| import { executeAst } from 'lang/langHelpers' | ||||
| import { | ||||
|   createArcGeometry, | ||||
| @ -77,7 +82,13 @@ import { | ||||
|   changeSketchArguments, | ||||
|   updateStartProfileAtArgs, | ||||
| } from 'lang/std/sketch' | ||||
| import { isOverlap, normaliseAngle, roundOff, throttle } from 'lib/utils' | ||||
| import { | ||||
|   isArray, | ||||
|   isOverlap, | ||||
|   normaliseAngle, | ||||
|   roundOff, | ||||
|   throttle, | ||||
| } from 'lib/utils' | ||||
| import { | ||||
|   addStartProfileAt, | ||||
|   createArrayExpression, | ||||
| @ -463,13 +474,18 @@ export class SceneEntities { | ||||
|       ) | ||||
|  | ||||
|       let seg: Group | ||||
|       const _node1 = getNodeFromPath<CallExpression>( | ||||
|       const callExp = getNodeFromPath<CallExpression>( | ||||
|         maybeModdedAst, | ||||
|         segPathToNode, | ||||
|         'CallExpression' | ||||
|       ) | ||||
|       if (err(_node1)) return | ||||
|       const callExpName = _node1.node?.callee?.name | ||||
|       if (err(callExp)) return | ||||
|       const callExpName = isNodeType<CallExpression>( | ||||
|         callExp.node, | ||||
|         'CallExpression' | ||||
|       ) | ||||
|         ? callExp.node.callee.name | ||||
|         : '' | ||||
|  | ||||
|       if (segment.type === 'TangentialArcTo') { | ||||
|         seg = tangentialArcToSegment({ | ||||
| @ -590,8 +606,12 @@ export class SceneEntities { | ||||
|       'VariableDeclaration' | ||||
|     ) | ||||
|     if (trap(_node1)) return Promise.reject(_node1) | ||||
|     const variableDeclarationName = | ||||
|       _node1.node?.declarations?.[0]?.id?.name || '' | ||||
|     const variableDeclarationName = isNodeType<VariableDeclaration>( | ||||
|       _node1.node, | ||||
|       'VariableDeclaration' | ||||
|     ) | ||||
|       ? _node1.node.declarations[0]?.id?.name || '' | ||||
|       : '' | ||||
|  | ||||
|     const sg = kclManager.programMemory.get( | ||||
|       variableDeclarationName | ||||
| @ -728,15 +748,14 @@ export class SceneEntities { | ||||
|   ) => { | ||||
|     let _ast = structuredClone(kclManager.ast) | ||||
|  | ||||
|     const _node1 = getNodeFromPath<VariableDeclaration>( | ||||
|     const varDec = expectNodeOnPath<VariableDeclaration>( | ||||
|       _ast, | ||||
|       sketchPathToNode || [], | ||||
|       'VariableDeclaration' | ||||
|     ) | ||||
|     if (trap(_node1)) return Promise.reject(_node1) | ||||
|     const variableDeclarationName = | ||||
|       _node1.node?.declarations?.[0]?.id?.name || '' | ||||
|     const startSketchOn = _node1.node?.declarations | ||||
|     if (trap(varDec)) return Promise.reject(varDec) | ||||
|     const variableDeclarationName = varDec.declarations?.[0]?.id?.name || '' | ||||
|     const startSketchOn = varDec.declarations | ||||
|     const startSketchOnInit = startSketchOn?.[0]?.init | ||||
|  | ||||
|     const tags: [string, string, string] = [ | ||||
| @ -775,12 +794,17 @@ export class SceneEntities { | ||||
|           'VariableDeclaration' | ||||
|         ) | ||||
|         if (trap(_node)) return Promise.reject(_node) | ||||
|         const sketchInit = _node.node?.declarations?.[0]?.init | ||||
|         const sketchInit = isNodeType<VariableDeclaration>( | ||||
|           _node.node, | ||||
|           'VariableDeclaration' | ||||
|         ) | ||||
|           ? _node.node.declarations[0]?.init | ||||
|           : null | ||||
|  | ||||
|         const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0] | ||||
|         const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1] | ||||
|  | ||||
|         if (sketchInit.type === 'PipeExpression') { | ||||
|         if (sketchInit?.type === 'PipeExpression') { | ||||
|           updateRectangleSketch(sketchInit, x, y, tags[0]) | ||||
|         } | ||||
|  | ||||
| @ -823,9 +847,14 @@ export class SceneEntities { | ||||
|           'VariableDeclaration' | ||||
|         ) | ||||
|         if (trap(_node)) return Promise.reject(_node) | ||||
|         const sketchInit = _node.node?.declarations?.[0]?.init | ||||
|         const sketchInit = isNodeType<VariableDeclaration>( | ||||
|           _node.node, | ||||
|           'VariableDeclaration' | ||||
|         ) | ||||
|           ? _node.node.declarations[0]?.init | ||||
|           : null | ||||
|  | ||||
|         if (sketchInit.type === 'PipeExpression') { | ||||
|         if (sketchInit?.type === 'PipeExpression') { | ||||
|           updateRectangleSketch(sketchInit, x, y, tags[0]) | ||||
|  | ||||
|           let _recastAst = parse(recast(_ast)) | ||||
| @ -1061,7 +1090,7 @@ export class SceneEntities { | ||||
|     if (trap(_node)) return | ||||
|     const node = _node.node | ||||
|  | ||||
|     if (node.type !== 'CallExpression') return | ||||
|     if (isArray(node) || node.type !== 'CallExpression') return | ||||
|  | ||||
|     let modded: | ||||
|       | { | ||||
| @ -1666,7 +1695,9 @@ export class SceneEntities { | ||||
|           ) | ||||
|           if (trap(_node, { suppress: true })) return | ||||
|           const node = _node.node | ||||
|           editorManager.setHighlightRange([node.start, node.end]) | ||||
|           editorManager.setHighlightRange( | ||||
|             !isArray(node) ? [node.start, node.end] : [0, 0] | ||||
|           ) | ||||
|           const yellow = 0xffff00 | ||||
|           colorSegment(selected, yellow) | ||||
|           const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE) | ||||
| @ -1796,7 +1827,14 @@ function prepareTruncatedMemoryAndAst( | ||||
|     'VariableDeclaration' | ||||
|   ) | ||||
|   if (err(_node)) return _node | ||||
|   const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || '' | ||||
|   if (isArray(_node.node)) | ||||
|     return new Error('Expected node to be an object, but found Array') | ||||
|   const variableDeclarationName = isNodeType<VariableDeclaration>( | ||||
|     _node.node, | ||||
|     'VariableDeclaration' | ||||
|   ) | ||||
|     ? _node.node.declarations[0]?.id?.name || '' | ||||
|     : '' | ||||
|   const lastSeg = ( | ||||
|     programMemory.get(variableDeclarationName) as SketchGroup | ||||
|   ).value.slice(-1)[0] | ||||
| @ -1919,7 +1957,12 @@ export function sketchGroupFromPathToNode({ | ||||
|   ) | ||||
|   if (err(_varDec)) return _varDec | ||||
|   const varDec = _varDec.node | ||||
|   const result = programMemory.get(varDec?.id?.name || '') | ||||
|   if (isArray(varDec)) | ||||
|     return new Error('Expected node to be an object, but found Array') | ||||
|   const varName = isNodeType<VariableDeclarator>(varDec, 'VariableDeclarator') | ||||
|     ? varDec.id.name | ||||
|     : '' | ||||
|   const result = programMemory.get(varName) | ||||
|   if (result?.type === 'ExtrudeGroup') { | ||||
|     return result.sketchGroup | ||||
|   } | ||||
|  | ||||
| @ -1,9 +1,12 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { Program, Value, VariableDeclarator } from '../../lang/wasm' | ||||
| import { CallExpression, Program, VariableDeclarator } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   getLastNodeFromPath, | ||||
|   DynamicNode, | ||||
|   isNodeType, | ||||
|   expectNodeOnPath, | ||||
| } from '../../lang/queryAst' | ||||
| import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' | ||||
| import { | ||||
| @ -14,6 +17,7 @@ import { | ||||
| } from '../../lang/std/sketchcombos' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| export function equalAngleInfo({ | ||||
|   selectionRanges, | ||||
| @ -29,26 +33,33 @@ export function equalAngleInfo({ | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: DynamicNode[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const _varDecs = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<VariableDeclarator>( | ||||
|     const tmp = expectNodeOnPath<VariableDeclarator>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.node | ||||
|     return tmp | ||||
|   }) | ||||
|   const _err2 = _varDecs.find(err) | ||||
|   if (err(_err2)) return _err2 | ||||
|   const varDecs = _varDecs as VariableDeclarator[] | ||||
|   const varDecs: VariableDeclarator[] = [] | ||||
|   for (const varDec of _varDecs) { | ||||
|     if (err(varDec)) return varDec | ||||
|     varDecs.push(varDec) | ||||
|   } | ||||
|  | ||||
|   const primaryLine = varDecs[0] | ||||
|   const secondaryVarDecs = varDecs.slice(1) | ||||
| @ -57,7 +68,7 @@ export function equalAngleInfo({ | ||||
|   ) | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       toolTips.includes(node.callee.name as any) | ||||
|   ) | ||||
|  | ||||
|  | ||||
| @ -1,9 +1,12 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { Program, Value, VariableDeclarator } from '../../lang/wasm' | ||||
| import { CallExpression, Program, VariableDeclarator } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   getLastNodeFromPath, | ||||
|   DynamicNode, | ||||
|   isNodeType, | ||||
|   expectNodeOnPath, | ||||
| } from '../../lang/queryAst' | ||||
| import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' | ||||
| import { | ||||
| @ -14,6 +17,7 @@ import { | ||||
| } from '../../lang/std/sketchcombos' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| export function setEqualLengthInfo({ | ||||
|   selectionRanges, | ||||
| @ -29,26 +33,33 @@ export function setEqualLengthInfo({ | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: DynamicNode[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const _varDecs = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<VariableDeclarator>( | ||||
|     const varDec = expectNodeOnPath<VariableDeclarator>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.node | ||||
|     if (err(varDec)) return varDec | ||||
|     return varDec | ||||
|   }) | ||||
|   const _err2 = _varDecs.find(err) | ||||
|   if (err(_err2)) return _err2 | ||||
|   const varDecs = _varDecs as VariableDeclarator[] | ||||
|   const varDecs: VariableDeclarator[] = [] | ||||
|   for (const varDec of _varDecs) { | ||||
|     if (err(varDec)) return varDec | ||||
|     varDecs.push(varDec) | ||||
|   } | ||||
|  | ||||
|   const primaryLine = varDecs[0] | ||||
|   const secondaryVarDecs = varDecs.slice(1) | ||||
| @ -57,7 +68,7 @@ export function setEqualLengthInfo({ | ||||
|   ) | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       toolTips.includes(node.callee.name as any) | ||||
|   ) | ||||
|  | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { Program, ProgramMemory, Value } from '../../lang/wasm' | ||||
| import { CallExpression, Program, ProgramMemory } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   getLastNodeFromPath, | ||||
|   DynamicNode, | ||||
|   isNodeType, | ||||
| } from '../../lang/queryAst' | ||||
| import { | ||||
|   PathToNodeMap, | ||||
| @ -13,6 +15,7 @@ import { | ||||
| } from '../../lang/std/sketchcombos' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| export function horzVertInfo( | ||||
|   selectionRanges: Selections, | ||||
| @ -27,17 +30,22 @@ export function horzVertInfo( | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: DynamicNode[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       toolTips.includes(node.callee.name as any) | ||||
|   ) | ||||
|  | ||||
|  | ||||
| @ -1,10 +1,18 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm' | ||||
| import { | ||||
|   BinaryPart, | ||||
|   CallExpression, | ||||
|   Program, | ||||
|   VariableDeclarator, | ||||
| } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   isLinesParallelAndConstrained, | ||||
|   getLastNodeFromPath, | ||||
|   expectNodeOnPath, | ||||
|   DynamicNode, | ||||
|   isNodeType, | ||||
| } from '../../lang/queryAst' | ||||
| import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' | ||||
| import { | ||||
| @ -18,6 +26,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst' | ||||
| import { removeDoubleNegatives } from '../AvailableVarsHelpers' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| const getModalInfo = createInfoModal(GetInfoModal) | ||||
|  | ||||
| @ -72,26 +81,33 @@ export function intersectInfo({ | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: DynamicNode[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const _varDecs = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<VariableDeclarator>( | ||||
|     const varDec = expectNodeOnPath<VariableDeclarator>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.node | ||||
|     if (err(varDec)) return varDec | ||||
|     return varDec | ||||
|   }) | ||||
|   const _err2 = _varDecs.find(err) | ||||
|   if (err(_err2)) return _err2 | ||||
|   const varDecs = _varDecs as VariableDeclarator[] | ||||
|   const varDecs: VariableDeclarator[] = [] | ||||
|   for (const varDec of _varDecs) { | ||||
|     if (err(varDec)) return varDec | ||||
|     varDecs.push(varDec) | ||||
|   } | ||||
|  | ||||
|   const primaryLine = varDecs[0] | ||||
|   const secondaryVarDecs = varDecs.slice(1) | ||||
| @ -100,7 +116,7 @@ export function intersectInfo({ | ||||
|   ) | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       [ | ||||
|         ...toolTips, | ||||
|         'startSketchAt', // TODO probably a better place for this to live | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selection, Selections } from 'lib/selections' | ||||
| import { PathToNode, Program, Value } from '../../lang/wasm' | ||||
| import { CallExpression, PathToNode, Program } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   getLastNodeFromPath, | ||||
|   DynamicNode, | ||||
|   isNodeType, | ||||
| } from '../../lang/queryAst' | ||||
| import { | ||||
|   PathToNodeMap, | ||||
| @ -13,6 +15,7 @@ import { | ||||
| } from '../../lang/std/sketchcombos' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| export function removeConstrainingValuesInfo({ | ||||
|   selectionRanges, | ||||
| @ -33,13 +36,18 @@ export function removeConstrainingValuesInfo({ | ||||
|       getNodePathFromSourceRange(kclManager.ast, range) | ||||
|     ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: DynamicNode[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const updatedSelectionRanges = pathToNodes | ||||
|     ? { | ||||
| @ -54,7 +62,7 @@ export function removeConstrainingValuesInfo({ | ||||
|     : selectionRanges | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       toolTips.includes(node.callee.name as any) | ||||
|   ) | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { BinaryPart, Program, Value } from '../../lang/wasm' | ||||
| import { BinaryPart, CallExpression, Program, Value } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
| @ -49,22 +49,22 @@ export function absDistanceInfo({ | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>( | ||||
|     const tmp = getNodeFromPath<CallExpression>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.node | ||||
|     return tmp.stopAtNode | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: (CallExpression | null)[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       toolTips.includes(node.callee.name as any) | ||||
|     (node) => node && toolTips.includes(node.callee.name as any) | ||||
|   ) | ||||
|  | ||||
|   const transforms = getTransformInfos(selectionRanges, kclManager.ast, disType) | ||||
|  | ||||
| @ -1,9 +1,17 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm' | ||||
| import { | ||||
|   BinaryPart, | ||||
|   CallExpression, | ||||
|   Program, | ||||
|   VariableDeclarator, | ||||
| } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   getLastNodeFromPath, | ||||
|   DynamicNode, | ||||
|   expectNodeOnPath, | ||||
|   isNodeType, | ||||
| } from '../../lang/queryAst' | ||||
| import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' | ||||
| import { | ||||
| @ -17,6 +25,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst' | ||||
| import { removeDoubleNegatives } from '../AvailableVarsHelpers' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| const getModalInfo = createInfoModal(GetInfoModal) | ||||
|  | ||||
| @ -35,26 +44,33 @@ export function angleBetweenInfo({ | ||||
|   ) | ||||
|  | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const _err1 = _nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|   const nodes = _nodes as Value[] | ||||
|   const nodes: DynamicNode[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const _varDecs = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<VariableDeclarator>( | ||||
|     const varDec = expectNodeOnPath<VariableDeclarator>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.node | ||||
|     if (err(varDec)) return varDec | ||||
|     return varDec | ||||
|   }) | ||||
|   const _err2 = _varDecs.find(err) | ||||
|   if (err(_err2)) return _err2 | ||||
|   const varDecs = _varDecs as VariableDeclarator[] | ||||
|   const varDecs: VariableDeclarator[] = [] | ||||
|   for (const varDec of _varDecs) { | ||||
|     if (err(varDec)) return varDec | ||||
|     varDecs.push(varDec) | ||||
|   } | ||||
|  | ||||
|   const primaryLine = varDecs[0] | ||||
|   const secondaryVarDecs = varDecs.slice(1) | ||||
| @ -63,7 +79,7 @@ export function angleBetweenInfo({ | ||||
|   ) | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       toolTips.includes(node.callee.name as any) | ||||
|   ) | ||||
|  | ||||
|  | ||||
| @ -1,8 +1,15 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm' | ||||
| import { | ||||
|   BinaryPart, | ||||
|   CallExpression, | ||||
|   Program, | ||||
|   VariableDeclarator, | ||||
| } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
|   getLastNodeFromPath, | ||||
|   expectNodeOnPath, | ||||
|   isNodeType, | ||||
| } from '../../lang/queryAst' | ||||
| import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' | ||||
| import { | ||||
| @ -17,6 +24,7 @@ import { removeDoubleNegatives } from '../AvailableVarsHelpers' | ||||
| import { kclManager } from 'lib/singletons' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { cleanErrs, err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| const getModalInfo = createInfoModal(GetInfoModal) | ||||
|  | ||||
| @ -36,27 +44,31 @@ export function horzVertDistanceInfo({ | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<Value>(kclManager.ast, pathToNode) | ||||
|     const tmp = getLastNodeFromPath(kclManager.ast, pathToNode) | ||||
|     if (err(tmp)) return tmp | ||||
|     if (isArray(tmp.node)) { | ||||
|       return new Error('Expected value node, but found array') | ||||
|     } | ||||
|     return tmp.node | ||||
|   }) | ||||
|   const [hasErr, , nodesWErrs] = cleanErrs(_nodes) | ||||
|   const [hasErr, nodes, nodesWErrs] = cleanErrs(_nodes) | ||||
|  | ||||
|   if (hasErr) return nodesWErrs[0] | ||||
|   const nodes = _nodes as Value[] | ||||
|  | ||||
|   const _varDecs = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<VariableDeclarator>( | ||||
|     const varDec = expectNodeOnPath<VariableDeclarator>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.node | ||||
|     if (err(varDec)) return varDec | ||||
|     return varDec | ||||
|   }) | ||||
|   const _err2 = _varDecs.find(err) | ||||
|   if (err(_err2)) return _err2 | ||||
|   const varDecs = _varDecs as VariableDeclarator[] | ||||
|   const varDecs: VariableDeclarator[] = [] | ||||
|   for (const varDec of _varDecs) { | ||||
|     if (err(varDec)) return varDec | ||||
|     varDecs.push(varDec) | ||||
|   } | ||||
|  | ||||
|   const primaryLine = varDecs[0] | ||||
|   const secondaryVarDecs = varDecs.slice(1) | ||||
| @ -65,7 +77,7 @@ export function horzVertDistanceInfo({ | ||||
|   ) | ||||
|   const isAllTooltips = nodes.every( | ||||
|     (node) => | ||||
|       node?.type === 'CallExpression' && | ||||
|       isNodeType<CallExpression>(node, 'CallExpression') && | ||||
|       [ | ||||
|         ...toolTips, | ||||
|         'startSketchAt', // TODO probably a better place for this to live | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { toolTips } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { BinaryPart, Program, Value } from '../../lang/wasm' | ||||
| import { BinaryPart, CallExpression, Program } from '../../lang/wasm' | ||||
| import { | ||||
|   getNodePathFromSourceRange, | ||||
|   getNodeFromPath, | ||||
| @ -43,18 +43,24 @@ export function angleLengthInfo({ | ||||
|     getNodePathFromSourceRange(kclManager.ast, range) | ||||
|   ) | ||||
|  | ||||
|   const nodes = paths.map((pathToNode) => | ||||
|     getNodeFromPath<Value>(kclManager.ast, pathToNode, 'CallExpression') | ||||
|   ) | ||||
|   const _err1 = nodes.find(err) | ||||
|   if (err(_err1)) return _err1 | ||||
|  | ||||
|   const isAllTooltips = nodes.every((meta) => { | ||||
|     if (err(meta)) return false | ||||
|     return ( | ||||
|       meta.node?.type === 'CallExpression' && | ||||
|       toolTips.includes(meta.node.callee.name as any) | ||||
|   const _nodes = paths.map((pathToNode) => { | ||||
|     const tmp = getNodeFromPath<CallExpression>( | ||||
|       kclManager.ast, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(tmp)) return tmp | ||||
|     return tmp.stopAtNode | ||||
|   }) | ||||
|   const nodes: (CallExpression | null)[] = [] | ||||
|   for (const node of _nodes) { | ||||
|     if (err(node)) return node | ||||
|     nodes.push(node) | ||||
|   } | ||||
|  | ||||
|   const isAllTooltips = nodes.every((node) => { | ||||
|     if (err(node)) return false | ||||
|     return node && toolTips.includes(node.callee.name as any) | ||||
|   }) | ||||
|  | ||||
|   const transforms = getTransformInfos( | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { executeAst, lintAst } from 'lang/langHelpers' | ||||
| import { Selections } from 'lib/selections' | ||||
| import { KCLError, kclErrorsToDiagnostics } from './errors' | ||||
| import { uuidv4 } from 'lib/utils' | ||||
| import { isArray, uuidv4 } from 'lib/utils' | ||||
| import { EngineCommandManager } from './std/engineConnection' | ||||
| import { err } from 'lib/trap' | ||||
|  | ||||
| @ -15,12 +15,17 @@ import { | ||||
|   recast, | ||||
|   SourceRange, | ||||
| } from 'lang/wasm' | ||||
| import { getNodeFromPath } from './queryAst' | ||||
| import { | ||||
|   expectNodeOnPath, | ||||
|   getLastNodeFromPath, | ||||
|   getNodeFromPath, | ||||
| } from './queryAst' | ||||
| import { codeManager, editorManager, sceneInfra } from 'lib/singletons' | ||||
| import { Diagnostic } from '@codemirror/lint' | ||||
|  | ||||
| export class KclManager { | ||||
|   private _ast: Program = { | ||||
|     type: 'Program', | ||||
|     body: [], | ||||
|     start: 0, | ||||
|     end: 0, | ||||
| @ -156,6 +161,7 @@ export class KclManager { | ||||
|  | ||||
|   clearAst() { | ||||
|     this._ast = { | ||||
|       type: 'Program', | ||||
|       body: [], | ||||
|       start: 0, | ||||
|       end: 0, | ||||
| @ -313,14 +319,14 @@ export class KclManager { | ||||
|     Object.entries(this.engineCommandManager.artifactMap).forEach( | ||||
|       ([commandId, artifact]) => { | ||||
|         if (!artifact.pathToNode) return | ||||
|         const _node1 = getNodeFromPath<CallExpression>( | ||||
|         const _node = getNodeFromPath<CallExpression>( | ||||
|           this.ast, | ||||
|           artifact.pathToNode, | ||||
|           'CallExpression' | ||||
|         ) | ||||
|         if (err(_node1)) return | ||||
|         const { node } = _node1 | ||||
|         if (node.type !== 'CallExpression') return | ||||
|         if (err(_node)) return | ||||
|         const { node } = _node | ||||
|         if (isArray(node) || node.type !== 'CallExpression') return | ||||
|         const [oldStart, oldEnd] = artifact.range | ||||
|         if (oldStart === 0 && oldEnd === 0) return | ||||
|         if (oldStart === node.start && oldEnd === node.end) return | ||||
| @ -392,12 +398,15 @@ export class KclManager { | ||||
|     let returnVal: Selections | undefined = undefined | ||||
|  | ||||
|     if (optionalParams?.focusPath) { | ||||
|       const _node1 = getNodeFromPath<any>( | ||||
|       const _node1 = getLastNodeFromPath( | ||||
|         astWithUpdatedSource, | ||||
|         optionalParams?.focusPath | ||||
|       ) | ||||
|       if (err(_node1)) return Promise.reject(_node1) | ||||
|       const { node } = _node1 | ||||
|       if (isArray(node)) { | ||||
|         return Promise.reject(new Error('Expected node to not be an array')) | ||||
|       } | ||||
|  | ||||
|       const { start, end } = node | ||||
|       if (!start || !end) | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst' | ||||
| import { isArray } from 'lib/utils' | ||||
| import { getNodePathFromSourceRange, getLastNodeFromPath } from './queryAst' | ||||
| import { Identifier, parse, initPromise, Parameter } from './wasm' | ||||
| import { err } from 'lib/trap' | ||||
|  | ||||
| @ -25,10 +26,13 @@ const sk3 = startSketchAt([0, 0]) | ||||
|     const ast = parse(code) | ||||
|     if (err(ast)) throw ast | ||||
|     const nodePath = getNodePathFromSourceRange(ast, sourceRange) | ||||
|     const _node = getNodeFromPath<any>(ast, nodePath) | ||||
|     const _node = getLastNodeFromPath(ast, nodePath) | ||||
|     if (err(_node)) throw _node | ||||
|     const { node } = _node | ||||
|  | ||||
|     if (isArray(node)) { | ||||
|       throw new Error('Expected call expression node, but found array') | ||||
|     } | ||||
|     expect([node.start, node.end]).toEqual(sourceRange) | ||||
|     expect(node.type).toBe('CallExpression') | ||||
|   }) | ||||
| @ -53,7 +57,7 @@ const b1 = cube([0,0], 10)` | ||||
|     const ast = parse(code) | ||||
|     if (err(ast)) throw ast | ||||
|     const nodePath = getNodePathFromSourceRange(ast, sourceRange) | ||||
|     const _node = getNodeFromPath<Parameter>(ast, nodePath) | ||||
|     const _node = getLastNodeFromPath(ast, nodePath) | ||||
|     if (err(_node)) throw _node | ||||
|     const node = _node.node | ||||
|  | ||||
| @ -66,8 +70,12 @@ const b1 = cube([0,0], 10)` | ||||
|       ['params', 'FunctionExpression'], | ||||
|       [0, 'index'], | ||||
|     ]) | ||||
|     if (isArray(node)) { | ||||
|       throw new Error('Expected parameter node, but found array') | ||||
|     } | ||||
|     expect(node.type).toBe('Parameter') | ||||
|     expect(node.identifier.name).toBe('pos') | ||||
|     const param = node as any as Parameter | ||||
|     expect(param.identifier.name).toBe('pos') | ||||
|   }) | ||||
|   it('gets path right for deep within function definition body', () => { | ||||
|     const code = `fn cube = (pos, scale) => { | ||||
| @ -90,7 +98,7 @@ const b1 = cube([0,0], 10)` | ||||
|     const ast = parse(code) | ||||
|     if (err(ast)) throw ast | ||||
|     const nodePath = getNodePathFromSourceRange(ast, sourceRange) | ||||
|     const _node = getNodeFromPath<Identifier>(ast, nodePath) | ||||
|     const _node = getLastNodeFromPath(ast, nodePath) | ||||
|     if (err(_node)) throw _node | ||||
|     const node = _node.node | ||||
|     expect(nodePath).toEqual([ | ||||
| @ -112,7 +120,11 @@ const b1 = cube([0,0], 10)` | ||||
|       ['elements', 'ArrayExpression'], | ||||
|       [0, 'index'], | ||||
|     ]) | ||||
|     if (isArray(node)) { | ||||
|       throw new Error('Expected identifier node, but found array') | ||||
|     } | ||||
|     expect(node.type).toBe('Identifier') | ||||
|     expect(node.name).toBe('scale') | ||||
|     const ident = node as any as Identifier | ||||
|     expect(ident.name).toBe('scale') | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -119,6 +119,7 @@ describe('Testing addSketchTo', () => { | ||||
|   it('should add a sketch to a program', () => { | ||||
|     const result = addSketchTo( | ||||
|       { | ||||
|         type: 'Program', | ||||
|         body: [], | ||||
|         start: 0, | ||||
|         end: 0, | ||||
|  | ||||
| @ -27,6 +27,8 @@ import { | ||||
|   getNodePathFromSourceRange, | ||||
|   isNodeSafeToReplace, | ||||
|   traverse, | ||||
|   getLastNodeFromPath, | ||||
|   expectNodeOnPath, | ||||
| } from './queryAst' | ||||
| import { addTagForSketchOnFace, getConstraintInfo } from './std/sketch' | ||||
| import { | ||||
| @ -36,7 +38,7 @@ import { | ||||
|   transformAstSketchLines, | ||||
| } from './std/sketchcombos' | ||||
| import { DefaultPlaneStr } from 'clientSideScene/sceneEntities' | ||||
| import { isOverlap, roundOff } from 'lib/utils' | ||||
| import { isArray, isOverlap, roundOff } from 'lib/utils' | ||||
| import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants' | ||||
| import { ConstrainInfo } from './std/stdTypes' | ||||
| import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator' | ||||
| @ -79,16 +81,12 @@ export function addStartProfileAt( | ||||
|   pathToNode: PathToNode, | ||||
|   at: [number, number] | ||||
| ): { modifiedAst: Program; pathToNode: PathToNode } | Error { | ||||
|   const _node1 = getNodeFromPath<VariableDeclaration>( | ||||
|   const variableDeclaration = expectNodeOnPath<VariableDeclaration>( | ||||
|     node, | ||||
|     pathToNode, | ||||
|     'VariableDeclaration' | ||||
|   ) | ||||
|   if (err(_node1)) return _node1 | ||||
|   const variableDeclaration = _node1.node | ||||
|   if (variableDeclaration.type !== 'VariableDeclaration') { | ||||
|     return new Error('variableDeclaration.init.type !== PipeExpression') | ||||
|   } | ||||
|   if (err(variableDeclaration)) return variableDeclaration | ||||
|   const _node = { ...node } | ||||
|   const init = variableDeclaration.declarations[0].init | ||||
|   const startProfileAt = createCallExpressionStdLib('startProfileAt', [ | ||||
| @ -263,7 +261,7 @@ export function extrudeSketch( | ||||
|     } | ||||
|   | Error { | ||||
|   const _node = { ...node } | ||||
|   const _node1 = getNodeFromPath(_node, pathToNode) | ||||
|   const _node1 = getLastNodeFromPath(_node, pathToNode) | ||||
|   if (err(_node1)) return _node1 | ||||
|   const { node: sketchExpression } = _node1 | ||||
|  | ||||
| @ -274,9 +272,9 @@ export function extrudeSketch( | ||||
|     'PipeExpression' | ||||
|   ) | ||||
|   if (err(_node2)) return _node2 | ||||
|   const { node: pipeExpression } = _node2 | ||||
|   const { stopAtNode: pipeExpression } = _node2 | ||||
|  | ||||
|   const isInPipeExpression = pipeExpression.type === 'PipeExpression' | ||||
|   const isInPipeExpression = !!pipeExpression | ||||
|  | ||||
|   const _node3 = getNodeFromPath<VariableDeclarator>( | ||||
|     _node, | ||||
| @ -284,7 +282,11 @@ export function extrudeSketch( | ||||
|     'VariableDeclarator' | ||||
|   ) | ||||
|   if (err(_node3)) return _node3 | ||||
|   const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3 | ||||
|   const { stopAtNode: variableDeclarator, shallowPath: pathToDecleration } = | ||||
|     _node3 | ||||
|   if (!variableDeclarator) { | ||||
|     return new Error('VariableDeclarator not found') | ||||
|   } | ||||
|  | ||||
|   const extrudeCall = createCallExpressionStdLib('extrude', [ | ||||
|     distance, | ||||
| @ -356,34 +358,34 @@ export function sketchOnExtrudedFace( | ||||
|     node, | ||||
|     KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH | ||||
|   ) | ||||
|   const _node1 = getNodeFromPath<VariableDeclarator>( | ||||
|   const oldSketchNode = expectNodeOnPath<VariableDeclarator>( | ||||
|     _node, | ||||
|     sketchPathToNode, | ||||
|     'VariableDeclarator', | ||||
|     true | ||||
|     { | ||||
|       firstFound: true, | ||||
|       message: 'Old sketch node not found', | ||||
|     } | ||||
|   ) | ||||
|   if (err(_node1)) return _node1 | ||||
|   const { node: oldSketchNode } = _node1 | ||||
|   if (err(oldSketchNode)) return oldSketchNode | ||||
|  | ||||
|   const oldSketchName = oldSketchNode.id.name | ||||
|   const _node2 = getNodeFromPath<CallExpression>( | ||||
|   const expression = expectNodeOnPath<CallExpression>( | ||||
|     _node, | ||||
|     sketchPathToNode, | ||||
|     'CallExpression' | ||||
|   ) | ||||
|   if (err(_node2)) return _node2 | ||||
|   const { node: expression } = _node2 | ||||
|   if (err(expression)) return expression | ||||
|  | ||||
|   const _node3 = getNodeFromPath<VariableDeclarator>( | ||||
|   const extrudeVarDec = expectNodeOnPath<VariableDeclarator>( | ||||
|     _node, | ||||
|     extrudePathToNode, | ||||
|     'VariableDeclarator' | ||||
|   ) | ||||
|   if (err(_node3)) return _node3 | ||||
|   const { node: extrudeVarDec } = _node3 | ||||
|   const extrudeName = extrudeVarDec.id?.name | ||||
|   if (err(extrudeVarDec)) return extrudeVarDec | ||||
|   const extrudeName = extrudeVarDec.id.name | ||||
|  | ||||
|   let _tag = null | ||||
|   let _tag: Identifier | Literal | null = null | ||||
|   if (cap === 'none') { | ||||
|     const __tag = addTagForSketchOnFace( | ||||
|       { | ||||
| @ -678,9 +680,12 @@ export function giveSketchFnCallTag( | ||||
|     } | ||||
|   | Error { | ||||
|   const path = getNodePathFromSourceRange(ast, range) | ||||
|   const _node1 = getNodeFromPath<CallExpression>(ast, path, 'CallExpression') | ||||
|   if (err(_node1)) return _node1 | ||||
|   const { node: primaryCallExp } = _node1 | ||||
|   const primaryCallExp = expectNodeOnPath<CallExpression>( | ||||
|     ast, | ||||
|     path, | ||||
|     'CallExpression' | ||||
|   ) | ||||
|   if (err(primaryCallExp)) return primaryCallExp | ||||
|  | ||||
|   // Tag is always 3rd expression now, using arg index feels brittle | ||||
|   // but we can come up with a better way to identify tag later. | ||||
| @ -784,27 +789,35 @@ export function deleteSegmentFromPipeExpression( | ||||
| ): Program | Error { | ||||
|   let _modifiedAst = structuredClone(modifiedAst) | ||||
|  | ||||
|   dependentRanges.forEach((range) => { | ||||
|   for (const range of dependentRanges) { | ||||
|     const path = getNodePathFromSourceRange(_modifiedAst, range) | ||||
|  | ||||
|     const callExp = getNodeFromPath<CallExpression>( | ||||
|     const _callExp = getNodeFromPath<CallExpression>( | ||||
|       _modifiedAst, | ||||
|       path, | ||||
|       'CallExpression', | ||||
|       true | ||||
|     ) | ||||
|     if (err(callExp)) return callExp | ||||
|     if (err(_callExp)) return _callExp | ||||
|     const callExp = _callExp.stopAtNode | ||||
|     if (!callExp) { | ||||
|       return new Error('Call Expression not found') | ||||
|     } | ||||
|  | ||||
|     const constraintInfo = getConstraintInfo(callExp.node, code, path).find( | ||||
|     const constraintInfo = getConstraintInfo(callExp, code, path).find( | ||||
|       ({ sourceRange }) => isOverlap(sourceRange, range) | ||||
|     ) | ||||
|     if (!constraintInfo) return | ||||
|     if (!constraintInfo) { | ||||
|       return new Error('Constraint Info not found') | ||||
|     } | ||||
|  | ||||
|     const input = makeRemoveSingleConstraintInput( | ||||
|       constraintInfo.argPosition, | ||||
|       callExp.shallowPath | ||||
|       _callExp.shallowPath | ||||
|     ) | ||||
|     if (!input) return | ||||
|     if (!input) { | ||||
|       return new Error('Input not found') | ||||
|     } | ||||
|     const transform = removeSingleConstraintInfo( | ||||
|       { | ||||
|         ...input, | ||||
| @ -812,11 +825,13 @@ export function deleteSegmentFromPipeExpression( | ||||
|       _modifiedAst, | ||||
|       programMemory | ||||
|     ) | ||||
|     if (!transform) return | ||||
|     if (!transform) { | ||||
|       return new Error('Transform not found') | ||||
|     } | ||||
|     _modifiedAst = transform.modifiedAst | ||||
|   }) | ||||
|   } | ||||
|  | ||||
|   const pipeExpression = getNodeFromPath<PipeExpression>( | ||||
|   const pipeExpression = expectNodeOnPath<PipeExpression>( | ||||
|     _modifiedAst, | ||||
|     pathToNode, | ||||
|     'PipeExpression' | ||||
| @ -827,7 +842,7 @@ export function deleteSegmentFromPipeExpression( | ||||
|     ([_, desc]) => desc === 'PipeExpression' | ||||
|   ) | ||||
|   const segmentIndexInPipe = pathToNode[pipeInPathIndex + 1] | ||||
|   pipeExpression.node.body.splice(segmentIndexInPipe[0] as number, 1) | ||||
|   pipeExpression.body.splice(segmentIndexInPipe[0] as number, 1) | ||||
|  | ||||
|   // Move up to the next segment. | ||||
|   segmentIndexInPipe[0] = Math.max((segmentIndexInPipe[0] as number) - 1, 0) | ||||
| @ -902,19 +917,23 @@ export async function deleteFromSelection( | ||||
|   const astClone = structuredClone(ast) | ||||
|   const range = selection.range | ||||
|   const path = getNodePathFromSourceRange(ast, range) | ||||
|   const varDec = getNodeFromPath<VariableDeclarator>( | ||||
|   const _varDec = getNodeFromPath<VariableDeclarator>( | ||||
|     ast, | ||||
|     path, | ||||
|     'VariableDeclarator' | ||||
|   ) | ||||
|   if (err(varDec)) return varDec | ||||
|   if (err(_varDec)) return _varDec | ||||
|   const { stopAtNode: varDec } = _varDec | ||||
|   if (!varDec) { | ||||
|     return new Error('VariableDeclarator not found') | ||||
|   } | ||||
|   if ( | ||||
|     (selection.type === 'extrude-wall' || | ||||
|       selection.type === 'end-cap' || | ||||
|       selection.type === 'start-cap') && | ||||
|     varDec.node.init.type === 'PipeExpression' | ||||
|     varDec.init.type === 'PipeExpression' | ||||
|   ) { | ||||
|     const varDecName = varDec.node.id.name | ||||
|     const varDecName = varDec.id.name | ||||
|     let pathToNode: PathToNode | null = null | ||||
|     let extrudeNameToDelete = '' | ||||
|     traverse(astClone, { | ||||
| @ -976,13 +995,16 @@ export async function deleteFromSelection( | ||||
|           lastKey: number | ||||
|         }[] = [] | ||||
|         for (const { path, sketchName } of pathsDependingOnExtrude) { | ||||
|           const parent = getNodeFromPath<PipeExpression['body']>( | ||||
|             astClone, | ||||
|             path.slice(0, -1) | ||||
|           ) | ||||
|           if (err(parent)) { | ||||
|           const _parent = getLastNodeFromPath(astClone, path.slice(0, -1)) | ||||
|           if (err(_parent)) { | ||||
|             return | ||||
|           } | ||||
|           const { node: parent } = _parent | ||||
|           if (!isArray(parent)) { | ||||
|             console.error(`Parent is not an array: ${parent}`) | ||||
|             return | ||||
|           } | ||||
|           const pipeBodyItems = parent as PipeExpression['body'] | ||||
|           const sketchToPreserve = programMemory.get(sketchName) as SketchGroup | ||||
|           console.log('sketchName', sketchName) | ||||
|           // Can't kick off multiple requests at once as getFaceDetails | ||||
| @ -1000,7 +1022,7 @@ export async function deleteFromSelection( | ||||
|           } | ||||
|           const lastKey = Number(path.slice(-1)[0][0]) | ||||
|           modificationDetails.push({ | ||||
|             parent: parent.node, | ||||
|             parent: pipeBodyItems, | ||||
|             faceDetails, | ||||
|             lastKey, | ||||
|           }) | ||||
| @ -1048,14 +1070,14 @@ export async function deleteFromSelection( | ||||
|     } | ||||
|     // await prom | ||||
|     return astClone | ||||
|   } else if (varDec.node.init.type === 'PipeExpression') { | ||||
|     const pipeBody = varDec.node.init.body | ||||
|   } else if (varDec.init.type === 'PipeExpression') { | ||||
|     const pipeBody = varDec.init.body | ||||
|     if ( | ||||
|       pipeBody[0].type === 'CallExpression' && | ||||
|       pipeBody[0].callee.name === 'startSketchOn' | ||||
|     ) { | ||||
|       // remove varDec | ||||
|       const varDecIndex = varDec.shallowPath[1][0] as number | ||||
|       const varDecIndex = _varDec.shallowPath[1][0] as number | ||||
|       astClone.body.splice(varDecIndex, 1) | ||||
|       return astClone | ||||
|     } | ||||
|  | ||||
| @ -12,7 +12,7 @@ import { | ||||
|   hasValidFilletSelection, | ||||
|   isTagUsedInFillet, | ||||
| } from './addFillet' | ||||
| import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst' | ||||
| import { expectNodeOnPath, getNodePathFromSourceRange } from '../queryAst' | ||||
| import { createLiteral } from 'lang/modifyAst' | ||||
| import { err } from 'lib/trap' | ||||
| import { Selections } from 'lib/selections' | ||||
| @ -270,13 +270,13 @@ const extrude001 = extrude(-5, sketch001) | ||||
|     ] | ||||
|     const pathToNode = getNodePathFromSourceRange(ast, range) | ||||
|     if (err(pathToNode)) return | ||||
|     const callExp = getNodeFromPath<CallExpression>( | ||||
|     const callExp = expectNodeOnPath<CallExpression>( | ||||
|       ast, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(callExp)) return | ||||
|     const edges = isTagUsedInFillet({ ast, callExp: callExp.node }) | ||||
|     const edges = isTagUsedInFillet({ ast, callExp }) | ||||
|     expect(edges).toEqual(['getOppositeEdge', 'baseEdge']) | ||||
|   }) | ||||
|   it('should correctly identify getPreviousAdjacentEdge edges', () => { | ||||
| @ -289,13 +289,13 @@ const extrude001 = extrude(-5, sketch001) | ||||
|     ] | ||||
|     const pathToNode = getNodePathFromSourceRange(ast, range) | ||||
|     if (err(pathToNode)) return | ||||
|     const callExp = getNodeFromPath<CallExpression>( | ||||
|     const callExp = expectNodeOnPath<CallExpression>( | ||||
|       ast, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(callExp)) return | ||||
|     const edges = isTagUsedInFillet({ ast, callExp: callExp.node }) | ||||
|     const edges = isTagUsedInFillet({ ast, callExp }) | ||||
|     expect(edges).toEqual(['getPreviousAdjacentEdge']) | ||||
|   }) | ||||
|   it('should correctly identify no edges', () => { | ||||
| @ -308,13 +308,13 @@ const extrude001 = extrude(-5, sketch001) | ||||
|     ] | ||||
|     const pathToNode = getNodePathFromSourceRange(ast, range) | ||||
|     if (err(pathToNode)) return | ||||
|     const callExp = getNodeFromPath<CallExpression>( | ||||
|     const callExp = expectNodeOnPath<CallExpression>( | ||||
|       ast, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(callExp)) return | ||||
|     const edges = isTagUsedInFillet({ ast, callExp: callExp.node }) | ||||
|     const edges = isTagUsedInFillet({ ast, callExp }) | ||||
|     expect(edges).toEqual([]) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -18,6 +18,7 @@ import { | ||||
|   createPipeExpression, | ||||
| } from '../modifyAst' | ||||
| import { | ||||
|   expectNodeOnPath, | ||||
|   getNodeFromPath, | ||||
|   getNodePathFromSourceRange, | ||||
|   hasSketchPipeBeenExtruded, | ||||
| @ -46,15 +47,12 @@ export function addFillet( | ||||
|    */ | ||||
|  | ||||
|   // Find the specific sketch segment to tag with the new tag | ||||
|   const sketchSegmentChunk = getNodeFromPath( | ||||
|   const sketchSegmentNode = expectNodeOnPath<CallExpression>( | ||||
|     _node, | ||||
|     pathToSegmentNode, | ||||
|     'CallExpression' | ||||
|   ) | ||||
|   if (err(sketchSegmentChunk)) return sketchSegmentChunk | ||||
|   const { node: sketchSegmentNode } = sketchSegmentChunk as { | ||||
|     node: CallExpression | ||||
|   } | ||||
|   if (err(sketchSegmentNode)) return sketchSegmentNode | ||||
|  | ||||
|   // Check whether selection is a valid segment from sketchLineHelpersMap | ||||
|   if (!(sketchSegmentNode.callee.name in sketchLineHelperMap)) { | ||||
| @ -93,13 +91,12 @@ export function addFillet( | ||||
|   ]) | ||||
|  | ||||
|   // Locate the extrude call | ||||
|   const extrudeChunk = getNodeFromPath<VariableDeclaration>( | ||||
|   const extrudeVarDecl = expectNodeOnPath<VariableDeclaration>( | ||||
|     _node, | ||||
|     pathToExtrudeNode, | ||||
|     'VariableDeclaration' | ||||
|   ) | ||||
|   if (err(extrudeChunk)) return extrudeChunk | ||||
|   const { node: extrudeVarDecl } = extrudeChunk | ||||
|   if (err(extrudeVarDecl)) return extrudeVarDecl | ||||
|  | ||||
|   const extrudeDeclarator = extrudeVarDecl.declarations[0] | ||||
|   const extrudeInit = extrudeDeclarator.init | ||||
| @ -281,12 +278,12 @@ export const hasValidFilletSelection = ({ | ||||
|         'CallExpression' | ||||
|       ) | ||||
|       if (err(segmentNode)) return false | ||||
|       if (segmentNode.node.type === 'CallExpression') { | ||||
|         const segmentName = segmentNode.node.callee.name | ||||
|       if (segmentNode.stopAtNode) { | ||||
|         const segmentName = segmentNode.stopAtNode.callee.name | ||||
|         if (segmentName in sketchLineHelperMap) { | ||||
|           const edges = isTagUsedInFillet({ | ||||
|             ast, | ||||
|             callExp: segmentNode.node, | ||||
|             callExp: segmentNode.stopAtNode, | ||||
|           }) | ||||
|           // edge has already been filleted | ||||
|           if ( | ||||
|  | ||||
| @ -19,13 +19,47 @@ import { | ||||
| } from './wasm' | ||||
| import { createIdentifier, splitPathAtLastIndex } from './modifyAst' | ||||
| import { getSketchSegmentFromSourceRange } from './std/sketchConstraints' | ||||
| import { getAngle } from '../lib/utils' | ||||
| import { getAngle, isArray } from '../lib/utils' | ||||
| import { getFirstArg } from './std/sketch' | ||||
| import { | ||||
|   getConstraintLevelFromSourceRange, | ||||
|   getConstraintType, | ||||
| } from './std/sketchcombos' | ||||
| import { err } from 'lib/trap' | ||||
| import { BodyItem } from 'wasm-lib/kcl/bindings/BodyItem' | ||||
|  | ||||
| export interface DynamicNode { | ||||
|   type: SyntaxType | ||||
|   // Source range of the node. | ||||
|   start: number | ||||
|   end: number | ||||
|   [index: string]: unknown | ||||
| } | ||||
|  | ||||
| function isAstNode( | ||||
|   node: any | ||||
| ): node is { type: SyntaxType; start: number; end: number } { | ||||
|   // TODO: Should we check for start and end also? | ||||
|   return node && typeof node === 'object' && 'type' in node | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Given T and its corresponding SyntaxType, narrow the node to type T if | ||||
|  * node.type matches. | ||||
|  */ | ||||
| export function isNodeType<T extends DynamicNode>( | ||||
|   node: unknown, | ||||
|   syntaxType: SyntaxType | SyntaxType[] | ||||
| ): node is T { | ||||
|   return ( | ||||
|     !!node && | ||||
|     typeof node === 'object' && | ||||
|     'type' in node && | ||||
|     (isArray(syntaxType) | ||||
|       ? syntaxType.includes(node.type as SyntaxType) | ||||
|       : node.type === syntaxType) | ||||
|   ) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type. | ||||
| @ -33,51 +67,64 @@ import { err } from 'lib/trap' | ||||
|  * and return the node at the end of this path. | ||||
|  * By default it will return the node of the deepest "stopAt" type encountered, or the node at the end of the path if no "stopAt" type is provided. | ||||
|  * If the "returnEarly" flag is set to true, the function will return as soon as a node of the specified type is found. | ||||
|  * | ||||
|  * If stopAt is provided, it must match T's type property. | ||||
|  */ | ||||
| export function getNodeFromPath<T>( | ||||
| export function getNodeFromPath<T extends DynamicNode>( | ||||
|   node: Program, | ||||
|   path: PathToNode, | ||||
|   stopAt?: SyntaxType | SyntaxType[], | ||||
|   returnEarly = false | ||||
| ): | ||||
|   | { | ||||
|       node: T | ||||
|       node: DynamicNode | unknown[] | ||||
|       stopAtNode: T | null | ||||
|       shallowPath: PathToNode | ||||
|       deepPath: PathToNode | ||||
|     } | ||||
|   | Error { | ||||
|   let currentNode = node as any | ||||
|   let stopAtNode = null | ||||
|   let currentNode: DynamicNode | unknown[] = node | ||||
|   let stopAtNode: T | null = null | ||||
|   let successfulPaths: PathToNode = [] | ||||
|   let pathsExplored: PathToNode = [] | ||||
|   for (const pathItem of path) { | ||||
|     if (typeof currentNode[pathItem[0]] !== 'object') { | ||||
|       if (stopAtNode) { | ||||
|     const pathIndex = pathItem[0] | ||||
|     let nextNode: unknown | ||||
|     if (currentNode && typeof pathIndex === 'number' && isArray(currentNode)) { | ||||
|       nextNode = currentNode[pathIndex] | ||||
|     } | ||||
|     if ( | ||||
|       currentNode && | ||||
|       typeof pathIndex === 'string' && | ||||
|       typeof currentNode === 'object' && | ||||
|       !isArray(currentNode) | ||||
|     ) { | ||||
|       nextNode = currentNode[pathIndex] | ||||
|     } | ||||
|     if (!isArray(nextNode) && !isAstNode(nextNode)) { | ||||
|       if (isAstNode(stopAtNode)) { | ||||
|         return { | ||||
|           node: stopAtNode, | ||||
|           stopAtNode, | ||||
|           shallowPath: pathsExplored, | ||||
|           deepPath: successfulPaths, | ||||
|         } | ||||
|       } | ||||
|       return new Error('not an object') | ||||
|     } | ||||
|     currentNode = currentNode?.[pathItem[0]] | ||||
|     currentNode = nextNode | ||||
|     successfulPaths.push(pathItem) | ||||
|     if (!stopAtNode) { | ||||
|       pathsExplored.push(pathItem) | ||||
|     } | ||||
|     if ( | ||||
|       typeof stopAt !== 'undefined' && | ||||
|       (Array.isArray(stopAt) | ||||
|         ? stopAt.includes(currentNode.type) | ||||
|         : currentNode.type === stopAt) | ||||
|     ) { | ||||
|     if (stopAt && isNodeType<T>(currentNode, stopAt)) { | ||||
|       // it will match the deepest node of the type | ||||
|       // instead of returning at the first match | ||||
|       stopAtNode = currentNode | ||||
|       if (returnEarly) { | ||||
|         return { | ||||
|           node: stopAtNode, | ||||
|           stopAtNode, | ||||
|           shallowPath: pathsExplored, | ||||
|           deepPath: successfulPaths, | ||||
|         } | ||||
| @ -86,32 +133,104 @@ export function getNodeFromPath<T>( | ||||
|   } | ||||
|   return { | ||||
|     node: stopAtNode || currentNode, | ||||
|     stopAtNode, | ||||
|     shallowPath: pathsExplored, | ||||
|     deepPath: successfulPaths, | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns the terminal node in the given path.  Like getNodeFromPath, but no | ||||
|  * stopAt parameter. | ||||
|  */ | ||||
| export function getLastNodeFromPath( | ||||
|   node: Program, | ||||
|   path: PathToNode | ||||
| ): | ||||
|   | { | ||||
|       node: DynamicNode | unknown[] | ||||
|     } | ||||
|   | Error { | ||||
|   const _result = getNodeFromPath<DynamicNode>(node, path) | ||||
|   if (err(_result)) return _result | ||||
|   return { | ||||
|     node: _result.node, | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns the terminal node in the given path, and asserts that it's the given | ||||
|  * type. | ||||
|  */ | ||||
| export function expectLastNodeFromPath<T extends DynamicNode>( | ||||
|   node: Program, | ||||
|   path: PathToNode, | ||||
|   syntaxType: SyntaxType | SyntaxType[] | ||||
| ): T | Error { | ||||
|   const result = getLastNodeFromPath(node, path) | ||||
|   if (err(result)) return result | ||||
|   if (!isNodeType<T>(result.node, syntaxType)) { | ||||
|     return new Error( | ||||
|       `Expected node of type ${syntaxType}: found ${JSON.stringify(result)}` | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   return result.node | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns the node in the path with the given type.  Like getNodeFromPath, but | ||||
|  * if no stopAt node is found, an error is returned instead of null. | ||||
|  * | ||||
|  * @param {boolean} [options.firstFound=false] if true, return the first node of | ||||
|  *   the type found.  The default returns the last node found. | ||||
|  * @param {boolean} [options.message] the error message to return if the node is | ||||
|  *   not found. | ||||
|  */ | ||||
| export function expectNodeOnPath<T extends DynamicNode>( | ||||
|   node: Program, | ||||
|   path: PathToNode, | ||||
|   syntaxType: SyntaxType, | ||||
|   options: { firstFound?: boolean; message?: string } = { | ||||
|     firstFound: false, | ||||
|   } | ||||
| ): T | Error { | ||||
|   const result = getNodeFromPath<T>(node, path, syntaxType, options.firstFound) | ||||
|   if (err(result)) return result | ||||
|   if (!result.stopAtNode) { | ||||
|     return new Error( | ||||
|       options.message ?? `Node of type ${syntaxType} not found in path` | ||||
|     ) | ||||
|   } | ||||
|   return result.stopAtNode | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Functions the same as getNodeFromPath, but returns a curried function that can be called with the stopAt and returnEarly arguments. | ||||
|  */ | ||||
| export function getNodeFromPathCurry( | ||||
|   node: Program, | ||||
|   path: PathToNode | ||||
| ): <T>( | ||||
|   stopAt?: SyntaxType | SyntaxType[], | ||||
| ): <T extends DynamicNode>( | ||||
|   stopAt: SyntaxType | SyntaxType[], | ||||
|   returnEarly?: boolean | ||||
| ) => | ||||
|   | { | ||||
|       node: T | ||||
|       node: DynamicNode | unknown[] | ||||
|       stopAtNode: T | null | ||||
|       path: PathToNode | ||||
|     } | ||||
|   | Error { | ||||
|   return <T>(stopAt?: SyntaxType | SyntaxType[], returnEarly = false) => { | ||||
|   return <T extends DynamicNode>( | ||||
|     stopAt: SyntaxType | SyntaxType[], | ||||
|     returnEarly = false | ||||
|   ) => { | ||||
|     const _node1 = getNodeFromPath<T>(node, path, stopAt, returnEarly) | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: _node, shallowPath } = _node1 | ||||
|     const { node: _node, stopAtNode, shallowPath } = _node1 | ||||
|     return { | ||||
|       node: _node, | ||||
|       stopAtNode, | ||||
|       path: shallowPath, | ||||
|     } | ||||
|   } | ||||
| @ -459,7 +578,7 @@ export function findAllPreviousVariablesPath( | ||||
|  | ||||
|   const { index: insertIndex, path: bodyPath } = splitPathAtLastIndex(pathToDec) | ||||
|  | ||||
|   const _node2 = getNodeFromPath<Program['body']>(ast, bodyPath) | ||||
|   const _node2 = getLastNodeFromPath(ast, bodyPath) | ||||
|   if (err(_node2)) { | ||||
|     console.error(_node2) | ||||
|     return { | ||||
| @ -468,19 +587,30 @@ export function findAllPreviousVariablesPath( | ||||
|       insertIndex: 0, | ||||
|     } | ||||
|   } | ||||
|   if (!isArray(_node2.node)) { | ||||
|     console.error( | ||||
|       `Expected node of type array, but found: ${JSON.stringify(_node2)}` | ||||
|     ) | ||||
|     return { | ||||
|       variables: [], | ||||
|       bodyPath: [], | ||||
|       insertIndex: 0, | ||||
|     } | ||||
|   } | ||||
|   const { node: bodyItems } = _node2 | ||||
|  | ||||
|   const variables: PrevVariable<any>[] = [] | ||||
|   bodyItems?.forEach?.((item) => { | ||||
|     if (item.type !== 'VariableDeclaration' || item.end > startRange) return | ||||
|   for (const unknownItem of bodyItems) { | ||||
|     const item = unknownItem as BodyItem | ||||
|     if (item.type !== 'VariableDeclaration' || item.end > startRange) continue | ||||
|     const varName = item.declarations[0].id.name | ||||
|     const varValue = programMemory?.get(varName) | ||||
|     if (!varValue || typeof varValue?.value !== type) return | ||||
|     if (!varValue || typeof varValue?.value !== type) continue | ||||
|     variables.push({ | ||||
|       key: varName, | ||||
|       value: varValue.value, | ||||
|     }) | ||||
|   }) | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     insertIndex, | ||||
| @ -554,7 +684,7 @@ export function isNodeSafeToReplacePath( | ||||
|     } | ||||
|     pathToReplaced[1][0] = index + 1 | ||||
|     const startPath = finPath.slice(0, -1) | ||||
|     const _nodeToReplace = getNodeFromPath(_ast, startPath) | ||||
|     const _nodeToReplace = getLastNodeFromPath(_ast, startPath) | ||||
|     if (err(_nodeToReplace)) return _nodeToReplace | ||||
|     const nodeToReplace = _nodeToReplace.node as any | ||||
|     nodeToReplace[last[0]] = identifier | ||||
| @ -656,7 +786,8 @@ export function isLinesParallelAndConstrained( | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(_secondaryNode)) return _secondaryNode | ||||
|     const secondaryNode = _secondaryNode.node | ||||
|     const secondaryNode = _secondaryNode.stopAtNode | ||||
|     if (!secondaryNode) return new Error('no secondary node found') | ||||
|     const _varDec = getNodeFromPath(ast, primaryPath, 'VariableDeclaration') | ||||
|     if (err(_varDec)) return _varDec | ||||
|     const varDec = _varDec.node | ||||
| @ -682,7 +813,7 @@ export function isLinesParallelAndConstrained( | ||||
|       Math.abs(primaryAngle - secondaryAngle) < EPSILON || | ||||
|       Math.abs(primaryAngle - secondaryAngleAlt) < EPSILON | ||||
|  | ||||
|     // is secordary line fully constrain, or has constrain type of 'angle' | ||||
|     // is secondary line fully constrain, or has constrain type of 'angle' | ||||
|     const secondaryFirstArg = getFirstArg(secondaryNode) | ||||
|     if (err(secondaryFirstArg)) return secondaryFirstArg | ||||
|  | ||||
| @ -747,8 +878,8 @@ export function doesPipeHaveCallExp({ | ||||
|     console.error(pipeExpressionMeta) | ||||
|     return false | ||||
|   } | ||||
|   const pipeExpression = pipeExpressionMeta.node | ||||
|   if (pipeExpression.type !== 'PipeExpression') return false | ||||
|   const pipeExpression = pipeExpressionMeta.stopAtNode | ||||
|   if (!pipeExpression) return false | ||||
|   return pipeExpression.body.some( | ||||
|     (expression) => | ||||
|       expression.type === 'CallExpression' && | ||||
| @ -775,8 +906,8 @@ export function hasExtrudeSketchGroup({ | ||||
|     console.error(varDecMeta) | ||||
|     return false | ||||
|   } | ||||
|   const varDec = varDecMeta.node | ||||
|   if (varDec.type !== 'VariableDeclaration') return false | ||||
|   const varDec = varDecMeta.stopAtNode | ||||
|   if (!varDec) return false | ||||
|   const varName = varDec.declarations[0].id.name | ||||
|   const varValue = programMemory?.get(varName) | ||||
|   return varValue?.type === 'ExtrudeGroup' || varValue?.type === 'SketchGroup' | ||||
| @ -814,8 +945,8 @@ export function findUsesOfTagInPipe( | ||||
|     console.error(nodeMeta) | ||||
|     return [] | ||||
|   } | ||||
|   const node = nodeMeta.node | ||||
|   if (node.type !== 'CallExpression') return [] | ||||
|   const node = nodeMeta.stopAtNode | ||||
|   if (!node) return [] | ||||
|   const tagIndex = node.callee.name === 'close' ? 1 : 2 | ||||
|   const thirdParam = node.arguments[tagIndex] | ||||
|   if ( | ||||
| @ -836,9 +967,14 @@ export function findUsesOfTagInPipe( | ||||
|     console.error(varDec) | ||||
|     return [] | ||||
|   } | ||||
|   const varDecNode = varDec.stopAtNode | ||||
|   if (!varDecNode) { | ||||
|     console.error('varDecNode not found') | ||||
|     return [] | ||||
|   } | ||||
|   const dependentRanges: SourceRange[] = [] | ||||
|  | ||||
|   traverse(varDec.node, { | ||||
|   traverse(varDecNode, { | ||||
|     enter: (node) => { | ||||
|       if ( | ||||
|         node.type !== 'CallExpression' || | ||||
| @ -860,16 +996,16 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) { | ||||
|   const path = getNodePathFromSourceRange(ast, selection.range) | ||||
|   const _node = getNodeFromPath<PipeExpression>(ast, path, 'PipeExpression') | ||||
|   if (err(_node)) return false | ||||
|   const { node: pipeExpression } = _node | ||||
|   if (pipeExpression.type !== 'PipeExpression') return false | ||||
|   const { stopAtNode: pipeExpression } = _node | ||||
|   if (!pipeExpression) return false | ||||
|   const _varDec = getNodeFromPath<VariableDeclarator>( | ||||
|     ast, | ||||
|     path, | ||||
|     'VariableDeclarator' | ||||
|   ) | ||||
|   if (err(_varDec)) return false | ||||
|   const varDec = _varDec.node | ||||
|   if (varDec.type !== 'VariableDeclarator') return false | ||||
|   const varDec = _varDec.stopAtNode | ||||
|   if (!varDec) return false | ||||
|   let extruded = false | ||||
|   traverse(ast as any, { | ||||
|     enter(node) { | ||||
|  | ||||
| @ -14,7 +14,11 @@ import { | ||||
|   SourceRange, | ||||
|   CallExpression, | ||||
| } from '../wasm' | ||||
| import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst' | ||||
| import { | ||||
|   expectNodeOnPath, | ||||
|   getNodeFromPath, | ||||
|   getNodePathFromSourceRange, | ||||
| } from '../queryAst' | ||||
| import { enginelessExecutor } from '../../lib/testHelpers' | ||||
| import { err } from 'lib/trap' | ||||
|  | ||||
| @ -600,13 +604,13 @@ describe('testing getConstraintInfo', () => { | ||||
|       ] | ||||
|       if (err(ast)) return ast | ||||
|       const pathToNode = getNodePathFromSourceRange(ast, sourceRange) | ||||
|       const callExp = getNodeFromPath<CallExpression>( | ||||
|       const callExp = expectNodeOnPath<CallExpression>( | ||||
|         ast, | ||||
|         pathToNode, | ||||
|         'CallExpression' | ||||
|       ) | ||||
|       if (err(callExp)) return callExp | ||||
|       const result = getConstraintInfo(callExp.node, code, pathToNode) | ||||
|       const result = getConstraintInfo(callExp, code, pathToNode) | ||||
|       expect(result).toEqual(expected) | ||||
|     }) | ||||
|   }) | ||||
| @ -754,13 +758,13 @@ describe('testing getConstraintInfo', () => { | ||||
|       ] | ||||
|       if (err(ast)) return ast | ||||
|       const pathToNode = getNodePathFromSourceRange(ast, sourceRange) | ||||
|       const callExp = getNodeFromPath<CallExpression>( | ||||
|       const callExp = expectNodeOnPath<CallExpression>( | ||||
|         ast, | ||||
|         pathToNode, | ||||
|         'CallExpression' | ||||
|       ) | ||||
|       if (err(callExp)) return callExp | ||||
|       const result = getConstraintInfo(callExp.node, code, pathToNode) | ||||
|       const result = getConstraintInfo(callExp, code, pathToNode) | ||||
|       expect(result).toEqual(expected) | ||||
|     }) | ||||
|   }) | ||||
| @ -1110,14 +1114,14 @@ describe('testing getConstraintInfo', () => { | ||||
|       ] | ||||
|       if (err(ast)) return ast | ||||
|       const pathToNode = getNodePathFromSourceRange(ast, sourceRange) | ||||
|       const callExp = getNodeFromPath<CallExpression>( | ||||
|       const callExp = expectNodeOnPath<CallExpression>( | ||||
|         ast, | ||||
|         pathToNode, | ||||
|         'CallExpression' | ||||
|       ) | ||||
|       if (err(callExp)) return callExp | ||||
|  | ||||
|       const result = getConstraintInfo(callExp.node, code, pathToNode) | ||||
|       const result = getConstraintInfo(callExp, code, pathToNode) | ||||
|       expect(result).toEqual(expected) | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
| @ -14,8 +14,10 @@ import { | ||||
|   Identifier, | ||||
| } from 'lang/wasm' | ||||
| import { | ||||
|   isNodeType, | ||||
|   expectLastNodeFromPath, | ||||
|   expectNodeOnPath, | ||||
|   getNodeFromPath, | ||||
|   getNodeFromPathCurry, | ||||
|   getNodePathFromSourceRange, | ||||
| } from 'lang/queryAst' | ||||
| import { | ||||
| @ -50,7 +52,7 @@ import { | ||||
|   mutateObjExpProp, | ||||
|   findUniqueName, | ||||
| } from 'lang/modifyAst' | ||||
| import { roundOff, getLength, getAngle } from 'lib/utils' | ||||
| import { roundOff, getLength, getAngle, isArray } from 'lib/utils' | ||||
| import { err } from 'lib/trap' | ||||
| import { perpendicularDistance } from 'sketch-helpers' | ||||
| import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator' | ||||
| @ -330,13 +332,12 @@ export const lineTo: SketchLineHelper = { | ||||
|     referencedSegment, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression>( | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: pipe } = nodeMeta | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const newVals: [Value, Value] = [ | ||||
|       createLiteral(roundOff(to[0], 2)), | ||||
| @ -373,7 +374,7 @@ export const lineTo: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath(_node, pathToNode) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|  | ||||
| @ -382,8 +383,13 @@ export const lineTo: SketchLineHelper = { | ||||
|       createLiteral(to[1]), | ||||
|     ]) | ||||
|  | ||||
|     mutateArrExp(callExpression.arguments?.[0], toArrExp) || | ||||
|       mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to') | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         mutateArrExp(firstArg, toArrExp) || | ||||
|           mutateObjExpProp(firstArg, toArrExp, 'to') | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
|       pathToNode, | ||||
| @ -414,7 +420,7 @@ export const line: SketchLineHelper = { | ||||
|     spliceBetween, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression | CallExpression>( | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
| @ -427,12 +433,16 @@ export const line: SketchLineHelper = { | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(nodeMeta2)) return nodeMeta2 | ||||
|     const { node: varDec } = nodeMeta2 | ||||
|     const { stopAtNode: varDec } = nodeMeta2 | ||||
|  | ||||
|     const newXVal = createLiteral(roundOff(to[0] - from[0], 2)) | ||||
|     const newYVal = createLiteral(roundOff(to[1] - from[1], 2)) | ||||
|  | ||||
|     if (spliceBetween && !createCallback && pipe.type === 'PipeExpression') { | ||||
|     if ( | ||||
|       spliceBetween && | ||||
|       !createCallback && | ||||
|       isNodeType<PipeExpression>(pipe, 'PipeExpression') | ||||
|     ) { | ||||
|       const callExp = createCallExpression('line', [ | ||||
|         createArrayExpression([newXVal, newYVal]), | ||||
|         createPipeSubstitution(), | ||||
| @ -455,7 +465,11 @@ export const line: SketchLineHelper = { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (replaceExisting && createCallback && pipe.type !== 'CallExpression') { | ||||
|     if ( | ||||
|       replaceExisting && | ||||
|       createCallback && | ||||
|       isNodeType<PipeExpression>(pipe, 'PipeExpression') | ||||
|     ) { | ||||
|       const { index: callIndex } = splitPathAtPipeExpression(pathToNode) | ||||
|       const { callExp, valueUsedInTransform } = createCallback( | ||||
|         [newXVal, newYVal], | ||||
| @ -477,7 +491,7 @@ export const line: SketchLineHelper = { | ||||
|       createArrayExpression([newXVal, newYVal]), | ||||
|       createPipeSubstitution(), | ||||
|     ]) | ||||
|     if (pipe.type === 'PipeExpression') { | ||||
|     if (isNodeType<PipeExpression>(pipe, 'PipeExpression')) { | ||||
|       pipe.body = [...pipe.body, callExp] | ||||
|       return { | ||||
|         modifiedAst: _node, | ||||
| @ -488,6 +502,7 @@ export const line: SketchLineHelper = { | ||||
|         ], | ||||
|       } | ||||
|     } else { | ||||
|       if (!varDec) return new Error('Variable declaration not found') | ||||
|       varDec.init = createPipeExpression([varDec.init, callExp]) | ||||
|     } | ||||
|     return { | ||||
| @ -497,7 +512,11 @@ export const line: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|  | ||||
| @ -506,10 +525,15 @@ export const line: SketchLineHelper = { | ||||
|       createLiteral(roundOff(to[1] - from[1], 2)), | ||||
|     ]) | ||||
|  | ||||
|     if (callExpression.arguments?.[0].type === 'ObjectExpression') { | ||||
|       mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to') | ||||
|     } else { | ||||
|       mutateArrExp(callExpression.arguments?.[0], toArrExp) | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         if (firstArg.type === 'ObjectExpression') { | ||||
|           mutateObjExpProp(firstArg, toArrExp, 'to') | ||||
|         } else { | ||||
|           mutateArrExp(firstArg, toArrExp) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -531,10 +555,12 @@ export const line: SketchLineHelper = { | ||||
| export const xLineTo: SketchLineHelper = { | ||||
|   add: ({ node, pathToNode, to, replaceExisting, createCallback }) => { | ||||
|     const _node = { ...node } | ||||
|     const getNode = getNodeFromPathCurry(_node, pathToNode) | ||||
|     const _node1 = getNode<PipeExpression>('PipeExpression') | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: pipe } = _node1 | ||||
|     const pipe = expectLastNodeFromPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const newVal = createLiteral(roundOff(to[0], 2)) | ||||
|  | ||||
| @ -563,14 +589,24 @@ export const xLineTo: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const newX = createLiteral(roundOff(to[0], 2)) | ||||
|     if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) { | ||||
|       callExpression.arguments[0] = newX | ||||
|     } else { | ||||
|       mutateObjExpProp(callExpression.arguments?.[0], newX, 'to') | ||||
|  | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const newX = createLiteral(roundOff(to[0], 2)) | ||||
|         if (isLiteralArrayOrStatic(firstArg)) { | ||||
|           callExpression.arguments[0] = newX | ||||
|         } else { | ||||
|           mutateObjExpProp(firstArg, newX, 'to') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -592,10 +628,12 @@ export const xLineTo: SketchLineHelper = { | ||||
| export const yLineTo: SketchLineHelper = { | ||||
|   add: ({ node, pathToNode, to, replaceExisting, createCallback }) => { | ||||
|     const _node = { ...node } | ||||
|     const getNode = getNodeFromPathCurry(_node, pathToNode) | ||||
|     const _node1 = getNode<PipeExpression>('PipeExpression') | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: pipe } = _node1 | ||||
|     const pipe = expectLastNodeFromPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const newVal = createLiteral(roundOff(to[1], 2)) | ||||
|  | ||||
| @ -624,14 +662,24 @@ export const yLineTo: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const newY = createLiteral(roundOff(to[1], 2)) | ||||
|     if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) { | ||||
|       callExpression.arguments[0] = newY | ||||
|     } else { | ||||
|       mutateObjExpProp(callExpression.arguments?.[0], newY, 'to') | ||||
|  | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const newY = createLiteral(roundOff(to[1], 2)) | ||||
|         if (isLiteralArrayOrStatic(firstArg)) { | ||||
|           callExpression.arguments[0] = newY | ||||
|         } else { | ||||
|           mutateObjExpProp(firstArg, newY, 'to') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -653,10 +701,12 @@ export const yLineTo: SketchLineHelper = { | ||||
| export const xLine: SketchLineHelper = { | ||||
|   add: ({ node, pathToNode, to, from, replaceExisting, createCallback }) => { | ||||
|     const _node = { ...node } | ||||
|     const getNode = getNodeFromPathCurry(_node, pathToNode) | ||||
|     const _node1 = getNode<PipeExpression>('PipeExpression') | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: pipe } = _node1 | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const newVal = createLiteral(roundOff(to[0] - from[0], 2)) | ||||
|     const firstArg = newVal | ||||
| @ -684,14 +734,24 @@ export const xLine: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const newX = createLiteral(roundOff(to[0] - from[0], 2)) | ||||
|     if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) { | ||||
|       callExpression.arguments[0] = newX | ||||
|     } else { | ||||
|       mutateObjExpProp(callExpression.arguments?.[0], newX, 'length') | ||||
|  | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const newX = createLiteral(roundOff(to[0] - from[0], 2)) | ||||
|         if (isLiteralArrayOrStatic(firstArg)) { | ||||
|           callExpression.arguments[0] = newX | ||||
|         } else { | ||||
|           mutateObjExpProp(firstArg, newX, 'length') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -713,10 +773,12 @@ export const xLine: SketchLineHelper = { | ||||
| export const yLine: SketchLineHelper = { | ||||
|   add: ({ node, pathToNode, to, from, replaceExisting, createCallback }) => { | ||||
|     const _node = { ...node } | ||||
|     const getNode = getNodeFromPathCurry(_node, pathToNode) | ||||
|     const _node1 = getNode<PipeExpression>('PipeExpression') | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: pipe } = _node1 | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(pipe)) return pipe | ||||
|     const newVal = createLiteral(roundOff(to[1] - from[1], 2)) | ||||
|     if (replaceExisting && createCallback) { | ||||
|       const { index: callIndex } = splitPathAtPipeExpression(pathToNode) | ||||
| @ -741,14 +803,24 @@ export const yLine: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const newY = createLiteral(roundOff(to[1] - from[1], 2)) | ||||
|     if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) { | ||||
|       callExpression.arguments[0] = newY | ||||
|     } else { | ||||
|       mutateObjExpProp(callExpression.arguments?.[0], newY, 'length') | ||||
|  | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const newY = createLiteral(roundOff(to[1] - from[1], 2)) | ||||
|         if (isLiteralArrayOrStatic(firstArg)) { | ||||
|           callExpression.arguments[0] = newY | ||||
|         } else { | ||||
|           mutateObjExpProp(firstArg, newY, 'length') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -777,8 +849,11 @@ export const tangentialArcTo: SketchLineHelper = { | ||||
|     referencedSegment, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const getNode = getNodeFromPathCurry(_node, pathToNode) | ||||
|     const _node1 = getNode<PipeExpression | CallExpression>('PipeExpression') | ||||
|     const _node1 = getNodeFromPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: pipe } = _node1 | ||||
|     const _node2 = getNodeFromPath<VariableDeclarator>( | ||||
| @ -787,12 +862,16 @@ export const tangentialArcTo: SketchLineHelper = { | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(_node2)) return _node2 | ||||
|     const { node: varDec } = _node2 | ||||
|     const { stopAtNode: varDec } = _node2 | ||||
|  | ||||
|     const toX = createLiteral(roundOff(to[0], 2)) | ||||
|     const toY = createLiteral(roundOff(to[1], 2)) | ||||
|  | ||||
|     if (replaceExisting && createCallback && pipe.type !== 'CallExpression') { | ||||
|     if ( | ||||
|       replaceExisting && | ||||
|       createCallback && | ||||
|       isNodeType<PipeExpression>(pipe, 'PipeExpression') | ||||
|     ) { | ||||
|       const { index: callIndex } = splitPathAtPipeExpression(pathToNode) | ||||
|       const { callExp, valueUsedInTransform } = createCallback( | ||||
|         [toX, toY], | ||||
| @ -813,7 +892,7 @@ export const tangentialArcTo: SketchLineHelper = { | ||||
|       createArrayExpression([toX, toY]), | ||||
|       createPipeSubstitution(), | ||||
|     ]) | ||||
|     if (pipe.type === 'PipeExpression') { | ||||
|     if (isNodeType<PipeExpression>(pipe, 'PipeExpression')) { | ||||
|       pipe.body = [...pipe.body, newLine] | ||||
|       return { | ||||
|         modifiedAst: _node, | ||||
| @ -824,6 +903,7 @@ export const tangentialArcTo: SketchLineHelper = { | ||||
|         ], | ||||
|       } | ||||
|     } else { | ||||
|       if (!varDec) return new Error('Variable declaration not found') | ||||
|       varDec.init = createPipeExpression([varDec.init, newLine]) | ||||
|     } | ||||
|     return { | ||||
| @ -833,15 +913,23 @@ export const tangentialArcTo: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const x = createLiteral(roundOff(to[0], 2)) | ||||
|     const y = createLiteral(roundOff(to[1], 2)) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
|     if (!mutateArrExp(firstArg, createArrayExpression([x, y]))) { | ||||
|       mutateObjExpProp(firstArg, createArrayExpression([x, y]), 'to') | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         if (!mutateArrExp(firstArg, createArrayExpression([x, y]))) { | ||||
|           mutateObjExpProp(firstArg, createArrayExpression([x, y]), 'to') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -909,10 +997,12 @@ export const angledLine: SketchLineHelper = { | ||||
|     referencedSegment, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const getNode = getNodeFromPathCurry(_node, pathToNode) | ||||
|     const _node1 = getNode<PipeExpression>('PipeExpression') | ||||
|     if (err(_node1)) return _node1 | ||||
|     const { node: pipe } = _node1 | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const newAngleVal = createLiteral(roundOff(getAngle(from, to), 0)) | ||||
|     const newLengthVal = createLiteral(roundOff(getLength(from, to), 2)) | ||||
| @ -947,7 +1037,11 @@ export const angledLine: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const angle = roundOff(getAngle(from, to), 0) | ||||
| @ -956,10 +1050,16 @@ export const angledLine: SketchLineHelper = { | ||||
|     const angleLit = createLiteral(angle) | ||||
|     const lengthLit = createLiteral(lineLength) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
|     if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) { | ||||
|       mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|       mutateObjExpProp(firstArg, lengthLit, 'length') | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         if ( | ||||
|           !mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit])) | ||||
|         ) { | ||||
|           mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|           mutateObjExpProp(firstArg, lengthLit, 'length') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
| @ -993,20 +1093,18 @@ export const angledLineOfXLength: SketchLineHelper = { | ||||
|     replaceExisting, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression>( | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: pipe } = nodeMeta | ||||
|     const nodeMeta2 = getNodeFromPath<VariableDeclarator>( | ||||
|     if (err(pipe)) return pipe | ||||
|     const varDec = expectNodeOnPath<VariableDeclarator>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(nodeMeta2)) return nodeMeta2 | ||||
|     const { node: varDec } = nodeMeta2 | ||||
|     if (err(varDec)) return varDec | ||||
|  | ||||
|     const variableName = varDec.id.name | ||||
|     const sketch = previousProgramMemory?.get(variableName) | ||||
| @ -1040,23 +1138,33 @@ export const angledLineOfXLength: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const angle = roundOff(getAngle(from, to), 0) | ||||
|     const xLength = roundOff(Math.abs(to[0] - from[0]), 2) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
|     const adjustedXLength = isAngleLiteral(firstArg) | ||||
|       ? Math.abs(xLength) | ||||
|       : xLength // todo make work for variable angle > 180 | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const adjustedXLength = isAngleLiteral(firstArg) | ||||
|           ? Math.abs(xLength) | ||||
|           : xLength // todo make work for variable angle > 180 | ||||
|  | ||||
|     const angleLit = createLiteral(angle) | ||||
|     const lengthLit = createLiteral(adjustedXLength) | ||||
|         const angleLit = createLiteral(angle) | ||||
|         const lengthLit = createLiteral(adjustedXLength) | ||||
|  | ||||
|     if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) { | ||||
|       mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|       mutateObjExpProp(firstArg, lengthLit, 'length') | ||||
|         if ( | ||||
|           !mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit])) | ||||
|         ) { | ||||
|           mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|           mutateObjExpProp(firstArg, lengthLit, 'length') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
| @ -1090,20 +1198,18 @@ export const angledLineOfYLength: SketchLineHelper = { | ||||
|     replaceExisting, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression>( | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: pipe } = nodeMeta | ||||
|     const nodeMeta2 = getNodeFromPath<VariableDeclarator>( | ||||
|     if (err(pipe)) return pipe | ||||
|     const varDec = expectNodeOnPath<VariableDeclarator>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'VariableDeclarator' | ||||
|     ) | ||||
|     if (err(nodeMeta2)) return nodeMeta2 | ||||
|     const { node: varDec } = nodeMeta2 | ||||
|     if (err(varDec)) return varDec | ||||
|     const variableName = varDec.id.name | ||||
|     const sketch = previousProgramMemory?.get(variableName) | ||||
|     if (!sketch || sketch.type !== 'SketchGroup') { | ||||
| @ -1137,23 +1243,33 @@ export const angledLineOfYLength: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const angle = roundOff(getAngle(from, to), 0) | ||||
|     const yLength = roundOff(to[1] - from[1], 2) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
|     const adjustedYLength = isAngleLiteral(firstArg) | ||||
|       ? Math.abs(yLength) | ||||
|       : yLength // todo make work for variable angle > 180 | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const adjustedYLength = isAngleLiteral(firstArg) | ||||
|           ? Math.abs(yLength) | ||||
|           : yLength // todo make work for variable angle > 180 | ||||
|  | ||||
|     const angleLit = createLiteral(angle) | ||||
|     const lengthLit = createLiteral(adjustedYLength) | ||||
|         const angleLit = createLiteral(angle) | ||||
|         const lengthLit = createLiteral(adjustedYLength) | ||||
|  | ||||
|     if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) { | ||||
|       mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|       mutateObjExpProp(firstArg, lengthLit, 'length') | ||||
|         if ( | ||||
|           !mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit])) | ||||
|         ) { | ||||
|           mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|           mutateObjExpProp(firstArg, lengthLit, 'length') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
| @ -1187,14 +1303,13 @@ export const angledLineToX: SketchLineHelper = { | ||||
|     referencedSegment, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression>( | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const { node: pipe } = nodeMeta | ||||
|     const angle = createLiteral(roundOff(getAngle(from, to), 0)) | ||||
|     const xArg = createLiteral(roundOff(to[0], 2)) | ||||
|     if (replaceExisting && createCallback) { | ||||
| @ -1227,22 +1342,32 @@ export const angledLineToX: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|  | ||||
|     const { node: callExpression } = nodeMeta | ||||
|  | ||||
|     const angle = roundOff(getAngle(from, to), 0) | ||||
|     const xLength = roundOff(to[0], 2) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
|     const adjustedXLength = xLength | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const adjustedXLength = xLength | ||||
|  | ||||
|     const angleLit = createLiteral(angle) | ||||
|     const lengthLit = createLiteral(adjustedXLength) | ||||
|         const angleLit = createLiteral(angle) | ||||
|         const lengthLit = createLiteral(adjustedXLength) | ||||
|  | ||||
|     if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) { | ||||
|       mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|       mutateObjExpProp(firstArg, lengthLit, 'to') | ||||
|         if ( | ||||
|           !mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit])) | ||||
|         ) { | ||||
|           mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|           mutateObjExpProp(firstArg, lengthLit, 'to') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -1275,14 +1400,12 @@ export const angledLineToY: SketchLineHelper = { | ||||
|     referencedSegment, | ||||
|   }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<PipeExpression>( | ||||
|     const pipe = expectNodeOnPath<PipeExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'PipeExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|  | ||||
|     const { node: pipe } = nodeMeta | ||||
|     if (err(pipe)) return pipe | ||||
|  | ||||
|     const angle = createLiteral(roundOff(getAngle(from, to), 0)) | ||||
|     const yArg = createLiteral(roundOff(to[1], 2)) | ||||
| @ -1317,22 +1440,32 @@ export const angledLineToY: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|  | ||||
|     const { node: callExpression } = nodeMeta | ||||
|  | ||||
|     const angle = roundOff(getAngle(from, to), 0) | ||||
|     const xLength = roundOff(to[1], 2) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
|     const adjustedXLength = xLength | ||||
|     if (isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|       const firstArg = callExpression.arguments[0] | ||||
|       if (firstArg) { | ||||
|         const adjustedXLength = xLength | ||||
|  | ||||
|     const angleLit = createLiteral(angle) | ||||
|     const lengthLit = createLiteral(adjustedXLength) | ||||
|         const angleLit = createLiteral(angle) | ||||
|         const lengthLit = createLiteral(adjustedXLength) | ||||
|  | ||||
|     if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) { | ||||
|       mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|       mutateObjExpProp(firstArg, lengthLit, 'to') | ||||
|         if ( | ||||
|           !mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit])) | ||||
|         ) { | ||||
|           mutateObjExpProp(firstArg, angleLit, 'angle') | ||||
|           mutateObjExpProp(firstArg, lengthLit, 'to') | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       modifiedAst: _node, | ||||
| @ -1372,7 +1505,7 @@ export const angledLineThatIntersects: SketchLineHelper = { | ||||
|     ) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|  | ||||
|     const { node: pipe } = nodeMeta | ||||
|     const { stopAtNode: pipe } = nodeMeta | ||||
|  | ||||
|     const angle = createLiteral(roundOff(getAngle(from, to), 0)) | ||||
|     if (!referencedSegment) { | ||||
| @ -1409,6 +1542,7 @@ export const angledLineThatIntersects: SketchLineHelper = { | ||||
|         ] | ||||
|       ) | ||||
|       const { index: callIndex } = splitPathAtPipeExpression(pathToNode) | ||||
|       if (!pipe) return new Error('Pipe expression not found') | ||||
|       pipe.body[callIndex] = callExp | ||||
|       return { | ||||
|         modifiedAst: _node, | ||||
| @ -1420,10 +1554,13 @@ export const angledLineThatIntersects: SketchLineHelper = { | ||||
|   }, | ||||
|   updateArgs: ({ node, pathToNode, to, from, previousProgramMemory }) => { | ||||
|     const _node = { ...node } | ||||
|     const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|     if (err(nodeMeta)) return nodeMeta | ||||
|     const callExpression = expectLastNodeFromPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(callExpression)) return callExpression | ||||
|  | ||||
|     const { node: callExpression } = nodeMeta | ||||
|     const angle = roundOff(getAngle(from, to), 0) | ||||
|  | ||||
|     const firstArg = callExpression.arguments?.[0] | ||||
| @ -1434,14 +1571,13 @@ export const angledLineThatIntersects: SketchLineHelper = { | ||||
|         : createLiteral('') | ||||
|     const intersectTagName = | ||||
|       intersectTag.type === 'Identifier' ? intersectTag.name : '' | ||||
|     const nodeMeta2 = getNodeFromPath<VariableDeclaration>( | ||||
|     const varDec = expectNodeOnPath<VariableDeclaration>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'VariableDeclaration' | ||||
|     ) | ||||
|     if (err(nodeMeta2)) return nodeMeta2 | ||||
|     if (err(varDec)) return varDec | ||||
|  | ||||
|     const { node: varDec } = nodeMeta2 | ||||
|     const varName = varDec.declarations[0].id.name | ||||
|     const sketchGroup = previousProgramMemory.get(varName) as SketchGroup | ||||
|     const intersectPath = sketchGroup.value.find( | ||||
| @ -1553,11 +1689,16 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({ | ||||
|   to, | ||||
| }) => { | ||||
|   const _node = { ...node } | ||||
|   const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode) | ||||
|   if (err(nodeMeta)) { | ||||
|     console.error(nodeMeta) | ||||
|   const callExpression = expectLastNodeFromPath<CallExpression>( | ||||
|     _node, | ||||
|     pathToNode, | ||||
|     'CallExpression' | ||||
|   ) | ||||
|   if (err(callExpression)) { | ||||
|     console.error(callExpression) | ||||
|     return { | ||||
|       modifiedAst: { | ||||
|         type: 'Program', | ||||
|         start: 0, | ||||
|         end: 0, | ||||
|         body: [], | ||||
| @ -1572,7 +1713,6 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({ | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const { node: callExpression } = nodeMeta | ||||
|   const toArrExp = createArrayExpression([ | ||||
|     createLiteral(roundOff(to[0])), | ||||
|     createLiteral(roundOff(to[1])), | ||||
| @ -1615,8 +1755,14 @@ export function changeSketchArguments( | ||||
|   if (err(nodeMeta)) return nodeMeta | ||||
|  | ||||
|   const { node: callExpression, shallowPath } = nodeMeta | ||||
|   if (isArray(callExpression)) { | ||||
|     return new Error('Expected call expression but found array') | ||||
|   } | ||||
|   if (!isNodeType<CallExpression>(callExpression, 'CallExpression')) { | ||||
|     return new Error('Call expression not found') | ||||
|   } | ||||
|  | ||||
|   if (callExpression?.callee?.name in sketchLineHelperMap) { | ||||
|   if (callExpression.callee.name in sketchLineHelperMap) { | ||||
|     const { updateArgs } = sketchLineHelperMap[callExpression.callee.name] | ||||
|     if (!updateArgs) { | ||||
|       return new Error('not a sketch line helper') | ||||
| @ -1631,7 +1777,7 @@ export function changeSketchArguments( | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   return new Error(`not a sketch line helper: ${callExpression?.callee?.name}`) | ||||
|   return new Error(`not a sketch line helper: ${callExpression.callee.name}`) | ||||
| } | ||||
|  | ||||
| export function getConstraintInfo( | ||||
| @ -1737,10 +1883,13 @@ export function addCallExpressionsToPipe({ | ||||
|   ) | ||||
|   if (err(pipeExpression)) return pipeExpression | ||||
|  | ||||
|   if (pipeExpression.node.type !== 'PipeExpression') { | ||||
|   if (!pipeExpression.stopAtNode) { | ||||
|     return new Error('not a pipe expression') | ||||
|   } | ||||
|   pipeExpression.node.body = [...pipeExpression.node.body, ...expressions] | ||||
|   pipeExpression.stopAtNode.body = [ | ||||
|     ...pipeExpression.stopAtNode.body, | ||||
|     ...expressions, | ||||
|   ] | ||||
|   return _node | ||||
| } | ||||
|  | ||||
| @ -1763,10 +1912,13 @@ export function addCloseToPipe({ | ||||
|   ) | ||||
|   if (err(pipeExpression)) return pipeExpression | ||||
|  | ||||
|   if (pipeExpression.node.type !== 'PipeExpression') { | ||||
|   if (!pipeExpression.stopAtNode) { | ||||
|     return new Error('not a pipe expression') | ||||
|   } | ||||
|   pipeExpression.node.body = [...pipeExpression.node.body, closeExpression] | ||||
|   pipeExpression.stopAtNode.body = [ | ||||
|     ...pipeExpression.stopAtNode.body, | ||||
|     closeExpression, | ||||
|   ] | ||||
|   return _node | ||||
| } | ||||
|  | ||||
| @ -1854,18 +2006,16 @@ type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error | ||||
| function addTag(tagIndex = 2): addTagFn { | ||||
|   return ({ node, pathToNode }) => { | ||||
|     const _node = { ...node } | ||||
|     const callExpr = getNodeFromPath<CallExpression>( | ||||
|     const primaryCallExp = expectNodeOnPath<CallExpression>( | ||||
|       _node, | ||||
|       pathToNode, | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(callExpr)) return callExpr | ||||
|  | ||||
|     const { node: primaryCallExp } = callExpr | ||||
|     if (err(primaryCallExp)) return primaryCallExp | ||||
|  | ||||
|     // Tag is always 3rd expression now, using arg index feels brittle | ||||
|     // but we can come up with a better way to identify tag later. | ||||
|     const thirdArg = primaryCallExp.arguments?.[tagIndex] | ||||
|     const thirdArg = primaryCallExp.arguments[tagIndex] | ||||
|     const tagDeclarator = | ||||
|       thirdArg || | ||||
|       (createTagDeclarator(findUniqueName(_node, 'seg', 2)) as TagDeclarator) | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { getNodeFromPath } from 'lang/queryAst' | ||||
| import { getLastNodeFromPath } from 'lang/queryAst' | ||||
| import { ToolTip, toolTips } from 'lang/langHelpers' | ||||
| import { | ||||
|   Program, | ||||
| @ -8,9 +8,9 @@ import { | ||||
|   SourceRange, | ||||
|   Path, | ||||
|   PathToNode, | ||||
|   Value, | ||||
| } from '../wasm' | ||||
| import { err } from 'lib/trap' | ||||
| import { isArray } from 'lib/utils' | ||||
|  | ||||
| export function getSketchSegmentFromPathToNode( | ||||
|   sketchGroup: SketchGroup, | ||||
| @ -25,11 +25,14 @@ export function getSketchSegmentFromPathToNode( | ||||
|   // TODO: once pathTodNode is stored on program memory as part of execution, | ||||
|   // we can check if the pathToNode matches the pathToNode of the sketchGroup. | ||||
|   // For now we fall back to the sourceRange | ||||
|   const nodeMeta = getNodeFromPath<Value>(ast, pathToNode) | ||||
|   const nodeMeta = getLastNodeFromPath(ast, pathToNode) | ||||
|   if (err(nodeMeta)) return nodeMeta | ||||
|   const { node } = nodeMeta | ||||
|   if (isArray(node)) { | ||||
|     return new Error('Value node expected, but found array') | ||||
|   } | ||||
|  | ||||
|   const node = nodeMeta.node | ||||
|   if (!node || typeof node.start !== 'number' || !node.end) | ||||
|   if (typeof node.start !== 'number' || !node.end) | ||||
|     return new Error('no node found') | ||||
|   const sourceRange: SourceRange = [node.start, node.end] | ||||
|   return getSketchSegmentFromSourceRange(sketchGroup, sourceRange) | ||||
|  | ||||
| @ -12,6 +12,9 @@ import { | ||||
|   ProgramMemory, | ||||
| } from '../wasm' | ||||
| import { | ||||
|   isNodeType, | ||||
|   expectNodeOnPath, | ||||
|   getLastNodeFromPath, | ||||
|   getNodeFromPath, | ||||
|   getNodeFromPathCurry, | ||||
|   getNodePathFromSourceRange, | ||||
| @ -39,7 +42,7 @@ import { | ||||
|   getSketchSegmentFromPathToNode, | ||||
|   getSketchSegmentFromSourceRange, | ||||
| } from './sketchConstraints' | ||||
| import { getAngle, roundOff, normaliseAngle } from '../../lib/utils' | ||||
| import { getAngle, roundOff, normaliseAngle, isArray } from '../../lib/utils' | ||||
|  | ||||
| export type LineInputsType = | ||||
|   | 'xAbsolute' | ||||
| @ -1225,22 +1228,19 @@ export function removeSingleConstraint({ | ||||
|   objectProperty?: string | ||||
|   ast: Program | ||||
| }): TransformInfo | false { | ||||
|   const callExp = getNodeFromPath<CallExpression>( | ||||
|   const callExp = expectNodeOnPath<CallExpression>( | ||||
|     ast, | ||||
|     pathToCallExp, | ||||
|     'CallExpression' | ||||
|     'CallExpression', | ||||
|     { message: 'Invalid node type' } | ||||
|   ) | ||||
|   if (err(callExp)) { | ||||
|     console.error(callExp) | ||||
|     return false | ||||
|   } | ||||
|   if (callExp.node.type !== 'CallExpression') { | ||||
|     console.error(new Error('Invalid node type')) | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   const transform: TransformInfo = { | ||||
|     tooltip: callExp.node.callee.name as any, | ||||
|     tooltip: callExp.callee.name as any, | ||||
|     createNode: | ||||
|       ({ tag, referenceSegName, varValues }) => | ||||
|       (_, rawValues) => { | ||||
| @ -1264,7 +1264,7 @@ export function removeSingleConstraint({ | ||||
|           }) | ||||
|           const objExp = createObjectExpression(expression) | ||||
|           return createStdlibCallExpression( | ||||
|             callExp.node.callee.name as any, | ||||
|             callExp.callee.name as any, | ||||
|             objExp, | ||||
|             tag | ||||
|           ) | ||||
| @ -1289,7 +1289,7 @@ export function removeSingleConstraint({ | ||||
|             return varValue.value | ||||
|           }) | ||||
|           return createStdlibCallExpression( | ||||
|             callExp.node.callee.name as any, | ||||
|             callExp.callee.name as any, | ||||
|             createArrayExpression(values), | ||||
|             tag | ||||
|           ) | ||||
| @ -1298,7 +1298,7 @@ export function removeSingleConstraint({ | ||||
|         // if (typeof arrayIndex !== 'number' || !objectProperty) must be single value input xLine, yLineTo etc | ||||
|  | ||||
|         return createCallWrapper( | ||||
|           callExp.node.callee.name as any, | ||||
|           callExp.callee.name as any, | ||||
|           rawValues[0].value, | ||||
|           tag | ||||
|         ) | ||||
| @ -1416,7 +1416,7 @@ export function getTransformInfos( | ||||
|     getNodePathFromSourceRange(ast, range) | ||||
|   ) | ||||
|   const nodes = paths.map((pathToNode) => | ||||
|     getNodeFromPath<Value>(ast, pathToNode, 'CallExpression') | ||||
|     getNodeFromPath<CallExpression>(ast, pathToNode, 'CallExpression') | ||||
|   ) | ||||
|  | ||||
|   try { | ||||
| @ -1426,9 +1426,8 @@ export function getTransformInfos( | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       const node = nodeMeta.node | ||||
|       if (node?.type === 'CallExpression') | ||||
|         return getTransformInfo(node, constraintType) | ||||
|       const node = nodeMeta.stopAtNode | ||||
|       if (node) return getTransformInfo(node, constraintType) | ||||
|  | ||||
|       return false | ||||
|     }) as TransformInfo[] | ||||
| @ -1448,9 +1447,7 @@ export function getRemoveConstraintsTransforms( | ||||
|   const paths = selectionRanges.codeBasedSelections.map((selectionRange) => | ||||
|     getNodePathFromSourceRange(ast, selectionRange.range) | ||||
|   ) | ||||
|   const nodes = paths.map((pathToNode) => | ||||
|     getNodeFromPath<Value>(ast, pathToNode) | ||||
|   ) | ||||
|   const nodes = paths.map((pathToNode) => getLastNodeFromPath(ast, pathToNode)) | ||||
|  | ||||
|   const theTransforms = nodes.map((nodeMeta) => { | ||||
|     // Typescript is not smart enough to know node will never be Error | ||||
| @ -1461,7 +1458,11 @@ export function getRemoveConstraintsTransforms( | ||||
|     } | ||||
|  | ||||
|     const node = nodeMeta.node | ||||
|     if (node?.type === 'CallExpression') | ||||
|     if (isArray(node)) { | ||||
|       console.error('Expected node, but found Array') | ||||
|       return false | ||||
|     } | ||||
|     if (isNodeType<CallExpression>(node, 'CallExpression')) | ||||
|       return getRemoveConstraintsTransform(node, constraintType) | ||||
|  | ||||
|     return false | ||||
| @ -1579,15 +1580,17 @@ export function transformAstSketchLines({ | ||||
|  | ||||
|     const callExp = getNode<CallExpression>('CallExpression') | ||||
|     if (err(callExp)) return callExp | ||||
|     if (!callExp.stopAtNode) return new Error('Call expression not found') | ||||
|     const varDec = getNode<VariableDeclarator>('VariableDeclarator') | ||||
|     if (err(varDec)) return varDec | ||||
|     if (!varDec.stopAtNode) return new Error('Variable declaration not found') | ||||
|  | ||||
|     const firstArg = getFirstArg(callExp.node) | ||||
|     const firstArg = getFirstArg(callExp.stopAtNode) | ||||
|     if (err(firstArg)) return firstArg | ||||
|     const callBackTag = callExp.node.arguments[2] | ||||
|     const callBackTag = callExp.stopAtNode.arguments[2] | ||||
|     const _referencedSegmentNameVal = | ||||
|       callExp.node.arguments[0]?.type === 'ObjectExpression' && | ||||
|       callExp.node.arguments[0].properties?.find( | ||||
|       callExp.stopAtNode.arguments[0]?.type === 'ObjectExpression' && | ||||
|       callExp.stopAtNode.arguments[0].properties?.find( | ||||
|         (prop) => prop.key.name === 'intersectTag' | ||||
|       )?.value | ||||
|     const _referencedSegmentName = | ||||
| @ -1601,7 +1604,7 @@ export function transformAstSketchLines({ | ||||
|  | ||||
|     const varValues: VarValues = [] | ||||
|  | ||||
|     getConstraintInfo(callExp.node, '', _pathToNode).forEach((a) => { | ||||
|     getConstraintInfo(callExp.stopAtNode, '', _pathToNode).forEach((a) => { | ||||
|       if ( | ||||
|         a.type === 'tangentialWithPrevious' || | ||||
|         a.type === 'horizontal' || | ||||
| @ -1609,33 +1612,46 @@ export function transformAstSketchLines({ | ||||
|       ) | ||||
|         return | ||||
|  | ||||
|       const nodeMeta = getNodeFromPath<Value>(ast, a.pathToNode) | ||||
|       const nodeMeta = getLastNodeFromPath(ast, a.pathToNode) | ||||
|       if (err(nodeMeta)) return | ||||
|  | ||||
|       // TODO: Assert that the node is a valid value. | ||||
|       if (a?.argPosition?.type === 'arrayItem') { | ||||
|         if (isArray(nodeMeta.node)) { | ||||
|           console.log('Expected Value, but found Array') | ||||
|           return | ||||
|         } | ||||
|         varValues.push({ | ||||
|           type: 'arrayItem', | ||||
|           index: a.argPosition.index, | ||||
|           value: nodeMeta.node, | ||||
|           value: nodeMeta.node as Value, | ||||
|           argType: a.type, | ||||
|         }) | ||||
|       } else if (a?.argPosition?.type === 'objectProperty') { | ||||
|         if (isArray(nodeMeta.node)) { | ||||
|           console.log('Expected Value, but found Array') | ||||
|           return | ||||
|         } | ||||
|         varValues.push({ | ||||
|           type: 'objectProperty', | ||||
|           key: a.argPosition.key, | ||||
|           value: nodeMeta.node, | ||||
|           value: nodeMeta.node as Value, | ||||
|           argType: a.type, | ||||
|         }) | ||||
|       } else if (a?.argPosition?.type === 'singleValue') { | ||||
|         if (isArray(nodeMeta.node)) { | ||||
|           console.log('Expected Value, but found Array') | ||||
|           return | ||||
|         } | ||||
|         varValues.push({ | ||||
|           type: 'singleValue', | ||||
|           argType: a.type, | ||||
|           value: nodeMeta.node, | ||||
|           value: nodeMeta.node as Value, | ||||
|         }) | ||||
|       } | ||||
|     }) | ||||
|  | ||||
|     const varName = varDec.node.id.name | ||||
|     const varName = varDec.stopAtNode.id.name | ||||
|     let sketchGroup = programMemory.get(varName) | ||||
|     if (sketchGroup?.type === 'ExtrudeGroup') { | ||||
|       sketchGroup = sketchGroup.sketchGroup | ||||
| @ -1669,7 +1685,7 @@ export function transformAstSketchLines({ | ||||
|       programMemory, | ||||
|       pathToNode: _pathToNode, | ||||
|       referencedSegment, | ||||
|       fnName: transformTo || (callExp.node.callee.name as ToolTip), | ||||
|       fnName: transformTo || (callExp.stopAtNode.callee.name as ToolTip), | ||||
|       to, | ||||
|       from, | ||||
|       createCallback: callBack({ | ||||
| @ -1743,15 +1759,14 @@ export function getConstraintLevelFromSourceRange( | ||||
|   ast: Program | Error | ||||
| ): Error | { range: [number, number]; level: ConstraintLevel } { | ||||
|   if (err(ast)) return ast | ||||
|   const nodeMeta = getNodeFromPath<CallExpression>( | ||||
|   const sketchFnExp = expectNodeOnPath<CallExpression>( | ||||
|     ast, | ||||
|     getNodePathFromSourceRange(ast, cursorRange), | ||||
|     'CallExpression' | ||||
|   ) | ||||
|   if (err(nodeMeta)) return nodeMeta | ||||
|   if (err(sketchFnExp)) return sketchFnExp | ||||
|  | ||||
|   const { node: sketchFnExp } = nodeMeta | ||||
|   const name = sketchFnExp?.callee?.name as ToolTip | ||||
|   const name = sketchFnExp.callee.name as ToolTip | ||||
|   const range: [number, number] = [sketchFnExp.start, sketchFnExp.end] | ||||
|   if (!toolTips.includes(name)) return { level: 'free', range: range } | ||||
|  | ||||
|  | ||||
| @ -5,15 +5,16 @@ import { | ||||
|   kclManager, | ||||
|   sceneEntitiesManager, | ||||
| } from 'lib/singletons' | ||||
| import { CallExpression, SourceRange, Value, parse, recast } from 'lang/wasm' | ||||
| import { CallExpression, SourceRange, parse, recast } from 'lang/wasm' | ||||
| import { ModelingMachineEvent } from 'machines/modelingMachine' | ||||
| import { uuidv4 } from 'lib/utils' | ||||
| import { isArray, uuidv4 } from 'lib/utils' | ||||
| import { EditorSelection, SelectionRange } from '@codemirror/state' | ||||
| import { getNormalisedCoordinates, isOverlap } from 'lib/utils' | ||||
| import { isCursorInSketchCommandRange } from 'lang/util' | ||||
| import { Program } from 'lang/wasm' | ||||
| import { | ||||
|   doesPipeHaveCallExp, | ||||
|   getLastNodeFromPath, | ||||
|   getNodeFromPath, | ||||
|   hasSketchPipeBeenExtruded, | ||||
|   isSingleCursorInPipe, | ||||
| @ -177,7 +178,11 @@ export function getEventForSegmentSelection( | ||||
|   ) | ||||
|   if (err(nodeMeta)) return null | ||||
|  | ||||
|   const node = nodeMeta.node | ||||
|   const node = nodeMeta.stopAtNode | ||||
|   if (!node) { | ||||
|     console.error('Call expression not found') | ||||
|     return null | ||||
|   } | ||||
|   const range: SourceRange = [node.start, node.end] | ||||
|   return { | ||||
|     type: 'Set selection', | ||||
| @ -300,7 +305,11 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) { | ||||
|       'CallExpression' | ||||
|     ) | ||||
|     if (err(nodeMeta)) return | ||||
|     const node = nodeMeta.node | ||||
|     const node = nodeMeta.stopAtNode | ||||
|     if (!node) { | ||||
|       console.error('Call expression not found') | ||||
|       return | ||||
|     } | ||||
|     const groupHasCursor = codeBasedSelections.some((selection) => { | ||||
|       return isOverlap(selection.range, [node.start, node.end]) | ||||
|     }) | ||||
| @ -597,9 +606,10 @@ export function updateSelections( | ||||
|  | ||||
|   const newSelections = Object.entries(pathToNodeMap) | ||||
|     .map(([index, pathToNode]): Selection | undefined => { | ||||
|       const nodeMeta = getNodeFromPath<Value>(ast, pathToNode) | ||||
|       const nodeMeta = getLastNodeFromPath(ast, pathToNode) | ||||
|       if (err(nodeMeta)) return undefined | ||||
|       const node = nodeMeta.node | ||||
|       if (isArray(node)) return undefined | ||||
|       return { | ||||
|         range: [node.start, node.end], | ||||
|         type: prevSelectionRanges.codeBasedSelections[Number(index)]?.type, | ||||
|  | ||||
| @ -4,6 +4,13 @@ import { v4 } from 'uuid' | ||||
|  | ||||
| export const uuidv4 = v4 | ||||
|  | ||||
| /** | ||||
|  * A safer type guard for arrays since the built-in Array.isArray() asserts `any[]`. | ||||
|  */ | ||||
| export function isArray(val: any): val is unknown[] { | ||||
|   return Array.isArray(val) | ||||
| } | ||||
|  | ||||
| export function isOverlap(a: SourceRange, b: SourceRange) { | ||||
|   const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a] | ||||
|   const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]] | ||||
|  | ||||
| @ -11,6 +11,7 @@ import { SidebarType } from 'components/ModelingSidebar/ModelingPanes' | ||||
| import { | ||||
|   isNodeSafeToReplacePath, | ||||
|   getNodePathFromSourceRange, | ||||
|   expectNodeOnPath, | ||||
| } from 'lang/queryAst' | ||||
| import { | ||||
|   kclManager, | ||||
| @ -879,8 +880,8 @@ export const modelingMachine = createMachine( | ||||
|           'VariableDeclarator' | ||||
|         ) | ||||
|         if (err(variableDeclaration)) return false | ||||
|         if (variableDeclaration.node.type !== 'VariableDeclarator') return false | ||||
|         const pipeExpression = variableDeclaration.node.init | ||||
|         if (!variableDeclaration.stopAtNode) return false | ||||
|         const pipeExpression = variableDeclaration.stopAtNode.init | ||||
|         if (pipeExpression.type !== 'PipeExpression') return false | ||||
|         const hasStartSketchOn = pipeExpression.body.some( | ||||
|           (item) => | ||||
| @ -1134,13 +1135,13 @@ export const modelingMachine = createMachine( | ||||
|           selection.codeBasedSelections[0].range | ||||
|         ) | ||||
|  | ||||
|         const varDecNode = getNodeFromPath<VariableDeclaration>( | ||||
|         const varDecNode = expectNodeOnPath<VariableDeclaration>( | ||||
|           ast, | ||||
|           pathToSegmentNode, | ||||
|           'VariableDeclaration' | ||||
|         ) | ||||
|         if (err(varDecNode)) return | ||||
|         const sketchVar = varDecNode.node.declarations[0].id.name | ||||
|         const sketchVar = varDecNode.declarations[0].id.name | ||||
|         const sketchGroup = kclManager.programMemory.get(sketchVar) | ||||
|         if (sketchGroup?.type !== 'SketchGroup') return | ||||
|         const idArtifact = engineCommandManager.artifactMap[sketchGroup.id] | ||||
| @ -1625,14 +1626,13 @@ export function isEditingExistingSketch({ | ||||
|   // should check that the variable declaration is a pipeExpression | ||||
|   // and that the pipeExpression contains a "startProfileAt" callExpression | ||||
|   if (!sketchDetails?.sketchPathToNode) return false | ||||
|   const variableDeclaration = getNodeFromPath<VariableDeclarator>( | ||||
|   const variableDeclaration = expectNodeOnPath<VariableDeclarator>( | ||||
|     kclManager.ast, | ||||
|     sketchDetails.sketchPathToNode, | ||||
|     'VariableDeclarator' | ||||
|   ) | ||||
|   if (err(variableDeclaration)) return false | ||||
|   if (variableDeclaration.node.type !== 'VariableDeclarator') return false | ||||
|   const pipeExpression = variableDeclaration.node.init | ||||
|   const pipeExpression = variableDeclaration.init | ||||
|   if (pipeExpression.type !== 'PipeExpression') return false | ||||
|   const hasStartProfileAt = pipeExpression.body.some( | ||||
|     (item) => | ||||
| @ -1654,5 +1654,5 @@ export function canRectangleTool({ | ||||
|   // This should not be returning false, and it should be caught | ||||
|   // but we need to simulate old behavior to move on. | ||||
|   if (err(node)) return false | ||||
|   return node.node?.declarations?.[0]?.init.type !== 'PipeExpression' | ||||
|   return node.stopAtNode?.declarations?.[0]?.init.type !== 'PipeExpression' | ||||
| } | ||||
|  | ||||
| @ -37,6 +37,7 @@ fn basic() { | ||||
|             digest: None, | ||||
|         })], | ||||
|         non_code_meta: NonCodeMeta::default(), | ||||
|         ts_type: (), | ||||
|         digest: None, | ||||
|     }; | ||||
|     assert_eq!(expected, actual); | ||||
|  | ||||
| @ -41,6 +41,17 @@ pub type Digest = [u8; 32]; | ||||
| #[ts(export)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct Program { | ||||
|     // This is so that it can be used with other AST nodes in TypeScript and the | ||||
|     // node type can be determined at runtime. | ||||
|     #[serde( | ||||
|         default, | ||||
|         rename = "type", | ||||
|         serialize_with = "Program::serialize_ts_type", | ||||
|         skip_deserializing | ||||
|     )] | ||||
|     #[ts(rename = "type", type = "\"Program\"")] | ||||
|     pub ts_type: (), | ||||
|  | ||||
|     pub start: usize, | ||||
|     pub end: usize, | ||||
|     pub body: Vec<BodyItem>, | ||||
| @ -82,6 +93,13 @@ impl Program { | ||||
|         hasher.update(slf.non_code_meta.compute_digest()); | ||||
|     }); | ||||
|  | ||||
|     fn serialize_ts_type<S>(_: &(), serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: serde::Serializer, | ||||
|     { | ||||
|         serializer.serialize_str("Program") | ||||
|     } | ||||
|  | ||||
|     pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> { | ||||
|         // Check if we are in the non code meta. | ||||
|         if let Some(meta) = self.get_non_code_meta_for_position(pos) { | ||||
| @ -5695,6 +5713,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; | ||||
|                         end: 0, | ||||
|                         body: Vec::new(), | ||||
|                         non_code_meta: Default::default(), | ||||
|                         ts_type: (), | ||||
|                         digest: None, | ||||
|                     }, | ||||
|                     return_type: None, | ||||
| @ -5723,6 +5742,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; | ||||
|                         end: 0, | ||||
|                         body: Vec::new(), | ||||
|                         non_code_meta: Default::default(), | ||||
|                         ts_type: (), | ||||
|                         digest: None, | ||||
|                     }, | ||||
|                     return_type: None, | ||||
| @ -5751,6 +5771,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; | ||||
|                         end: 0, | ||||
|                         body: Vec::new(), | ||||
|                         non_code_meta: Default::default(), | ||||
|                         ts_type: (), | ||||
|                         digest: None, | ||||
|                     }, | ||||
|                     return_type: None, | ||||
| @ -5792,6 +5813,7 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; | ||||
|                         end: 0, | ||||
|                         body: Vec::new(), | ||||
|                         non_code_meta: Default::default(), | ||||
|                         ts_type: (), | ||||
|                         digest: None, | ||||
|                     }, | ||||
|                     return_type: None, | ||||
|  | ||||
| @ -2910,6 +2910,7 @@ let w = f() + f() | ||||
|                     end: 0, | ||||
|                     body: Vec::new(), | ||||
|                     non_code_meta: Default::default(), | ||||
|                     ts_type: (), | ||||
|                     digest: None, | ||||
|                 }, | ||||
|                 return_type: None, | ||||
|  | ||||
| @ -872,6 +872,7 @@ pub fn function_body(i: TokenSlice) -> PResult<Program> { | ||||
|         end, | ||||
|         body, | ||||
|         non_code_meta, | ||||
|         ts_type: (), | ||||
|         digest: None, | ||||
|     }) | ||||
| } | ||||
| @ -1746,6 +1747,7 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|                         }], | ||||
|                         digest: None, | ||||
|                     }, | ||||
|                     ts_type: (), | ||||
|                     digest: None, | ||||
|                 }, | ||||
|                 return_type: None, | ||||
| @ -2400,6 +2402,7 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|                 digest: None, | ||||
|             })], | ||||
|             non_code_meta: NonCodeMeta::default(), | ||||
|             ts_type: (), | ||||
|             digest: None, | ||||
|         }; | ||||
|  | ||||
| @ -2864,6 +2867,7 @@ e | ||||
|                 digest: None, | ||||
|             })], | ||||
|             non_code_meta: NonCodeMeta::default(), | ||||
|             ts_type: (), | ||||
|             digest: None, | ||||
|         }; | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	![github-actions[bot]](/assets/img/avatar_default.png)