fix: updated the LiteralValue dereferencing and added some type narrowing helpers

This commit is contained in:
Kevin Nadro
2025-01-28 15:15:48 -06:00
parent c6d563f08f
commit 519041c8fa
5 changed files with 111 additions and 7 deletions

View File

@ -612,8 +612,8 @@ export class SceneEntities {
segment.type === 'TangentialArcTo' segment.type === 'TangentialArcTo'
? segmentUtils.tangentialArcTo.init ? segmentUtils.tangentialArcTo.init
: segment.type === 'Circle' : segment.type === 'Circle'
? segmentUtils.circle.init ? segmentUtils.circle.init
: segmentUtils.straight.init : segmentUtils.straight.init
const input: SegmentInputs = const input: SegmentInputs =
segment.type === 'Circle' segment.type === 'Circle'
? { ? {

View File

@ -6,6 +6,8 @@ import {
ArrayExpression, ArrayExpression,
BinaryExpression, BinaryExpression,
ArtifactGraph, ArtifactGraph,
LiteralValue,
NumericSuffix,
} from './wasm' } from './wasm'
import { filterArtifacts } from 'lang/std/artifactGraph' import { filterArtifacts } from 'lang/std/artifactGraph'
import { isOverlap } from 'lib/utils' import { isOverlap } from 'lib/utils'
@ -69,3 +71,9 @@ export function isLiteral(e: any): e is Literal {
export function isBinaryExpression(e: any): e is BinaryExpression { export function isBinaryExpression(e: any): e is BinaryExpression {
return e && e.type === 'BinaryExpression' return e && e.type === 'BinaryExpression'
} }
export function isLiteralValueNotStringAndBoolean(
e: any
): e is { value: number; suffix: NumericSuffix } {
return e && 'value' in e && 'suffix' in e
}

View File

@ -90,6 +90,7 @@ export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
export type { LiteralValue } from '../wasm-lib/kcl/bindings/LiteralValue' export type { LiteralValue } from '../wasm-lib/kcl/bindings/LiteralValue'
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression' export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
export type { SourceRange } from 'wasm-lib/kcl/bindings/SourceRange' export type { SourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
export type { NumericSuffix } from 'wasm-lib/kcl/bindings/NumericSuffix'
export type SyntaxType = export type SyntaxType =
| 'Program' | 'Program'

View File

@ -0,0 +1,87 @@
import { expect } from 'vitest'
import {
recast,
assertParse,
topLevelRange,
VariableDeclaration,
} from 'lang/wasm'
import { updateCenterRectangleSketch } from './rectangleTool'
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
import { getNodeFromPath } from 'lang/queryAst'
import { findUniqueName } from 'lang/modifyAst'
import { err, trap } from './trap'
describe('library rectangleTool helper functions', () => {
describe('updateCenterRectangleSketch', () => {
// regression test for https://github.com/KittyCAD/modeling-app/issues/5157
test('should update AST and source code', async () => {
// Base source code that will be edited in place
const sourceCode = `sketch001 = startSketchOn('XZ')
|> startProfileAt([120.37, 162.76], %)
|> angledLine([0, 0], %, $rectangleSegmentA001)
|> angledLine([segAng(rectangleSegmentA001) + 90, 0], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
`
// Create ast
const _ast = assertParse(sourceCode)
let ast = structuredClone(_ast)
// Find some nodes and paths to reference
const sketchSnippet = `startProfileAt([120.37, 162.76], %)`
const sketchRange = topLevelRange(
sourceCode.indexOf(sketchSnippet),
sourceCode.indexOf(sketchSnippet) + sketchSnippet.length
)
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
const _node = getNodeFromPath<VariableDeclaration>(
ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node)) return
const sketchInit = _node.node?.declaration.init
// Hard code inputs that a user would have taken with their mouse
const x = 40
const y = 60
const rectangleOrigin = [120, 180]
const tags: [string, string, string] = [
'rectangleSegmentA001',
'rectangleSegmentB001',
'rectangleSegmentC001',
]
// Update the ast
if (sketchInit.type === 'PipeExpression') {
updateCenterRectangleSketch(
sketchInit,
x,
y,
tags[0],
rectangleOrigin[0],
rectangleOrigin[1]
)
}
// ast is edited in place from the updateCenterRectangleSketch
const expectedSourceCode = `sketch001 = startSketchOn('XZ')
|> startProfileAt([80, 120], %)
|> angledLine([0, 80], %, $rectangleSegmentA001)
|> angledLine([segAng(rectangleSegmentA001) + 90, 120], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
`
const recasted = recast(ast)
expect(recasted).toEqual(expectedSourceCode)
})
})
})

View File

@ -8,13 +8,19 @@ import {
createTagDeclarator, createTagDeclarator,
createUnaryExpression, createUnaryExpression,
} from 'lang/modifyAst' } from 'lang/modifyAst'
import { ArrayExpression, CallExpression, PipeExpression } from 'lang/wasm' import {
ArrayExpression,
CallExpression,
PipeExpression,
recast,
} from 'lang/wasm'
import { roundOff } from 'lib/utils' import { roundOff } from 'lib/utils'
import { import {
isCallExpression, isCallExpression,
isArrayExpression, isArrayExpression,
isLiteral, isLiteral,
isBinaryExpression, isBinaryExpression,
isLiteralValueNotStringAndBoolean,
} from 'lang/util' } from 'lang/util'
/** /**
@ -140,10 +146,12 @@ export function updateCenterRectangleSketch(
if (isArrayExpression(arrayExpression)) { if (isArrayExpression(arrayExpression)) {
const literal = arrayExpression.elements[0] const literal = arrayExpression.elements[0]
if (isLiteral(literal)) { if (isLiteral(literal)) {
callExpression.arguments[0] = createArrayExpression([ if (isLiteralValueNotStringAndBoolean(literal.value)) {
createLiteral(literal.value.value), callExpression.arguments[0] = createArrayExpression([
createLiteral(Math.abs(twoX)), createLiteral(literal.value.value),
]) createLiteral(Math.abs(twoX)),
])
}
} }
} }
} }