WIP edit flows

This commit is contained in:
Pierre Jacquier
2025-07-03 19:45:52 -04:00
parent c035398ad7
commit a40ba06641
2 changed files with 127 additions and 49 deletions

View File

@ -9,6 +9,7 @@ import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
import { import {
codeRefFromRange, codeRefFromRange,
getArtifactOfTypes, getArtifactOfTypes,
getCodeRefsByArtifactId,
} from '@src/lang/std/artifactGraph' } from '@src/lang/std/artifactGraph'
import { getArgForEnd } from '@src/lang/std/sketch' import { getArgForEnd } from '@src/lang/std/sketch'
import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints' import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints'
@ -1202,6 +1203,61 @@ export function getSketchSelectionsFromOperation(
} }
} }
export function getObjectSelectionsFromOperation(
operation: Operation,
artifactGraph: ArtifactGraph
): Error | Selections {
const error = new Error("Couldn't retrieve sketches from operation")
if (operation.type !== 'StdLibCall' || !operation.unlabeledArg) {
return error
}
let artifactIds: string[] = []
if (
operation.unlabeledArg.value.type === 'Solid' ||
operation.unlabeledArg.value.type === 'Sketch'
) {
artifactIds = [operation.unlabeledArg.value.value.artifactId]
} else if (operation.unlabeledArg.value.type === 'ImportedGeometry') {
artifactIds = [operation.unlabeledArg.value.artifact_id]
} else if (operation.unlabeledArg.value.type === 'Array') {
artifactIds = operation.unlabeledArg.value.value
.filter((v) => v.type === 'Solid' || v.type === 'Sketch')
.map((v) => v.value.artifactId)
} else {
return error
}
const graphSelections: Selection[] = []
for (const artifactId of artifactIds) {
const artifact = artifactGraph.get(artifactId)
if (!artifact) {
continue
}
const codeRefs = getCodeRefsByArtifactId(artifactId, artifactGraph)
if (!codeRefs || codeRefs.length === 0) {
continue
}
graphSelections.push({
artifact,
codeRef: codeRefs[0], // TODO: figure out why two codeRefs could be possible?
})
}
if (graphSelections.length === 0) {
return error
}
const selections: Selections = {
graphSelections,
otherSelections: [],
}
console.log('massaged selections', selections)
return selections
}
export function locateVariableWithCallOrPipe( export function locateVariableWithCallOrPipe(
ast: Program, ast: Program,
pathToNode: PathToNode pathToNode: PathToNode

View File

@ -5,6 +5,7 @@ import {
getNodeFromPath, getNodeFromPath,
findPipesWithImportAlias, findPipesWithImportAlias,
getSketchSelectionsFromOperation, getSketchSelectionsFromOperation,
getObjectSelectionsFromOperation,
} from '@src/lang/queryAst' } from '@src/lang/queryAst'
import type { Artifact } from '@src/lang/std/artifactGraph' import type { Artifact } from '@src/lang/std/artifactGraph'
import { import {
@ -1523,67 +1524,88 @@ export async function enterAppearanceFlow({
) )
} }
async function prepareToEditTranslate({ operation }: EnterEditFlowProps) { async function prepareToEditTranslate({
operation,
artifact,
}: EnterEditFlowProps) {
const baseCommand = { const baseCommand = {
name: 'Translate', name: 'Translate',
groupId: 'modeling', groupId: 'modeling',
} }
const isModuleImport = operation.type === 'GroupBegin' // const isModuleImport = operation.type === 'GroupBegin'
const isSupportedStdLibCall = // const isSupportedStdLibCall =
operation.type === 'StdLibCall' && // operation.type === 'StdLibCall' &&
stdLibMap[operation.name]?.supportsTransform // stdLibMap[operation.name]?.supportsTransform
if (!isModuleImport && !isSupportedStdLibCall) { // if (!isModuleImport && !isSupportedStdLibCall) {
return { // return {
reason: 'Unsupported operation type. Please edit in the code editor.', // reason: 'Unsupported operation type. Please edit in the code editor.',
} // }
// }
if (operation.type !== 'StdLibCall') {
return { reason: 'Wrong operation type' }
} }
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath) // 1. Map the unlabeled arguments to solid2d selections
let x: KclExpression | undefined = undefined const objects = getObjectSelectionsFromOperation(
let y: KclExpression | undefined = undefined operation,
let z: KclExpression | undefined = undefined kclManager.artifactGraph
)
if (err(objects)) {
return { reason: "Couldn't retrieve objects" }
}
// 2. Convert the x y z arguments from a string to a KCL expression
const x = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.x?.sourceRange[0],
operation.labeledArgs.x?.sourceRange[1]
)
)
if (err(x) || 'errors' in x) {
return { reason: "Couldn't retrieve x argument" }
}
const y = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.y?.sourceRange[0],
operation.labeledArgs.y?.sourceRange[1]
)
)
if (err(y) || 'errors' in y) {
return { reason: "Couldn't retrieve y argument" }
}
const z = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.z?.sourceRange[0],
operation.labeledArgs.z?.sourceRange[1]
)
)
if (err(z) || 'errors' in z) {
return { reason: "Couldn't retrieve z argument" }
}
// symmetric argument from a string to boolean
let global: boolean | undefined let global: boolean | undefined
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>( if ('global' in operation.labeledArgs && operation.labeledArgs.global) {
kclManager.ast, global =
nodeToEdit, codeManager.code.slice(
'PipeExpression' operation.labeledArgs.global.sourceRange[0],
) operation.labeledArgs.global.sourceRange[1]
let pipe: PipeExpression | undefined ) === 'true'
const ast = kclManager.ast
if (
err(pipeLookupFromOperation) ||
pipeLookupFromOperation.node.type !== 'PipeExpression'
) {
// Look for the last pipe with the import alias and a call to translate
const pipes = findPipesWithImportAlias(ast, nodeToEdit, 'translate')
pipe = pipes.at(-1)?.expression
} else {
pipe = pipeLookupFromOperation.node
} }
if (pipe) { // 3. Assemble the default argument values for the command,
const translate = pipe.body.find( // with `nodeToEdit` set, which will let the actor know
(n) => n.type === 'CallExpressionKw' && n.callee.name.name === 'translate' // to edit the node that corresponds to the StdLibCall.
) const argDefaultValues: ModelingCommandSchema['Translate'] = {
if (translate?.type === 'CallExpressionKw') { objects,
x = await retrieveArgFromPipedCallExpression(translate, 'x') x,
y = await retrieveArgFromPipedCallExpression(translate, 'y') y,
z = await retrieveArgFromPipedCallExpression(translate, 'z') z,
global,
// optional global argument nodeToEdit: pathToNodeFromRustNodePath(operation.nodePath),
const result = await retrieveArgFromPipedCallExpression(
translate,
'global'
)
if (result) {
global = result.valueText === 'true'
} }
}
}
// Won't be used since we provide nodeToEdit
const selection: Selections = { graphSelections: [], otherSelections: [] }
const argDefaultValues = { nodeToEdit, selection, x, y, z, global }
return { return {
...baseCommand, ...baseCommand,
argDefaultValues, argDefaultValues,