Add edit flow for named constants / parameters (#5911)

* Add support for forcing kcl input create variable

* Command palette padding tweak

* Make traverse function work for ExpressionStatements

* Add utilities for getting earliest safe index in AST

* Fix the insertIndex logic to not be based on the selection anymore

* Add workflow to create a named constant

* Fix bug with nameEndInDigits matcher

* Tweak command config

* Add a three-dot menu to feature tree pane to create parameters

* Add E2E test for create parameter flow

* Remove edit flow oops

* Fix tsc error

* Fix E2E test

* Update named constant position in edit flow test

* Add tags into consideration for safe insert index

Per @Irev-dev's helpful feedback, with unit tests!

* Fix tsc by removing a generic type

* Remove unused imports

* Fix lints

* A snapshot a day keeps the bugs away! 📷🐛

* Add utilities for working with variable declarations

* Add "edit parameter" user flow

* Add edit flow config

* WIP working on de-bloating useCalculateKclExpreesion

* Add the ability to specify a `displayName` for an arg

* Add utility to type check on SourceRanges

* Review step design tweak fixes

* Refactor useCalculateKclExpression to take a sourceRange

* Make option arg validation work for objects and arrays

Using an admittedly dumb stringification approach

* Make edit flow never move the constant to be edited

* Add E2E test section

* Fix lints

* Remove lying comment, tiny CSS tweak

* A snapshot a day keeps the bugs away! 📷🐛

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Frank Noirot
2025-03-20 16:41:09 -04:00
committed by GitHub
parent 2c6404f671
commit 9da8574103
18 changed files with 301 additions and 29 deletions

View File

@ -1,12 +1,17 @@
import { Models } from '@kittycad/lib'
import { angleLengthInfo } from 'components/Toolbar/setAngleLength'
import { transformAstSketchLines } from 'lang/std/sketchcombos'
import { PathToNode } from 'lang/wasm'
import {
isPathToNode,
PathToNode,
SourceRange,
VariableDeclarator,
} from 'lang/wasm'
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
import { components } from 'lib/machine-api'
import { Selections } from 'lib/selections'
import { kclManager } from 'lib/singletons'
import { codeManager, kclManager } from 'lib/singletons'
import { err } from 'lib/trap'
import { modelingMachine, SketchTool } from 'machines/modelingMachine'
import {
@ -15,6 +20,9 @@ import {
shellValidator,
sweepValidator,
} from './validators'
import { getVariableDeclaration } from 'lang/queryAst/getVariableDeclaration'
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
import { getNodeFromPath } from 'lang/queryAst'
type OutputFormat = Models['OutputFormat_type']
type OutputTypeKey = OutputFormat['type']
@ -95,6 +103,10 @@ export type ModelingCommandSchema = {
'event.parameter.create': {
value: KclCommandValue
}
'event.parameter.edit': {
nodeToEdit: PathToNode
value: KclCommandValue
}
'change tool': {
tool: SketchTool
}
@ -601,6 +613,77 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
},
},
},
'event.parameter.edit': {
displayName: 'Edit parameter',
description: 'Edit the value of a named constant',
icon: 'make-variable',
status: 'development',
needsReview: false,
args: {
nodeToEdit: {
displayName: 'Name',
inputType: 'options',
valueSummary: (nodeToEdit: PathToNode) => {
const node = getNodeFromPath<VariableDeclarator>(
kclManager.ast,
nodeToEdit
)
if (err(node) || node.node.type !== 'VariableDeclarator')
return 'Error'
return node.node.id.name || ''
},
required: true,
options() {
return (
Object.entries(kclManager.execState.variables)
// TODO: @franknoirot && @jtran would love to make this go away soon 🥺
.filter(([_, variable]) => variable?.type === 'Number')
.map(([name, variable]) => {
const node = getVariableDeclaration(kclManager.ast, name)
if (node === undefined) return
const range: SourceRange = [node.start, node.end, node.moduleId]
const pathToNode = getNodePathFromSourceRange(
kclManager.ast,
range
)
return {
name,
value: pathToNode,
}
})
.filter((a) => !!a) || []
)
},
},
value: {
inputType: 'kcl',
required: true,
defaultValue(commandBarContext) {
const nodeToEdit = commandBarContext.argumentsToSubmit.nodeToEdit
if (!nodeToEdit || !isPathToNode(nodeToEdit)) return '5'
const node = getNodeFromPath<VariableDeclarator>(
kclManager.ast,
nodeToEdit
)
if (err(node) || node.node.type !== 'VariableDeclarator')
return 'Error'
const variableName = node.node.id.name || ''
if (typeof variableName !== 'string') return '5'
const variableNode = getVariableDeclaration(
kclManager.ast,
variableName
)
if (!variableNode) return '5'
const code = codeManager.code.slice(
variableNode.declaration.init.start,
variableNode.declaration.init.end
)
return code
},
createVariable: 'disallow',
},
},
},
'Constrain length': {
description: 'Constrain the length of one or more segments.',
icon: 'dimension',