WIP edit flows
This commit is contained in:
@ -9,6 +9,7 @@ import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||
import {
|
||||
codeRefFromRange,
|
||||
getArtifactOfTypes,
|
||||
getCodeRefsByArtifactId,
|
||||
} from '@src/lang/std/artifactGraph'
|
||||
import { getArgForEnd } from '@src/lang/std/sketch'
|
||||
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(
|
||||
ast: Program,
|
||||
pathToNode: PathToNode
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
getNodeFromPath,
|
||||
findPipesWithImportAlias,
|
||||
getSketchSelectionsFromOperation,
|
||||
getObjectSelectionsFromOperation,
|
||||
} from '@src/lang/queryAst'
|
||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||
import {
|
||||
@ -1523,67 +1524,88 @@ export async function enterAppearanceFlow({
|
||||
)
|
||||
}
|
||||
|
||||
async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
|
||||
async function prepareToEditTranslate({
|
||||
operation,
|
||||
artifact,
|
||||
}: EnterEditFlowProps) {
|
||||
const baseCommand = {
|
||||
name: 'Translate',
|
||||
groupId: 'modeling',
|
||||
}
|
||||
const isModuleImport = operation.type === 'GroupBegin'
|
||||
const isSupportedStdLibCall =
|
||||
operation.type === 'StdLibCall' &&
|
||||
stdLibMap[operation.name]?.supportsTransform
|
||||
if (!isModuleImport && !isSupportedStdLibCall) {
|
||||
return {
|
||||
reason: 'Unsupported operation type. Please edit in the code editor.',
|
||||
}
|
||||
// const isModuleImport = operation.type === 'GroupBegin'
|
||||
// const isSupportedStdLibCall =
|
||||
// operation.type === 'StdLibCall' &&
|
||||
// stdLibMap[operation.name]?.supportsTransform
|
||||
// if (!isModuleImport && !isSupportedStdLibCall) {
|
||||
// return {
|
||||
// reason: 'Unsupported operation type. Please edit in the code editor.',
|
||||
// }
|
||||
// }
|
||||
if (operation.type !== 'StdLibCall') {
|
||||
return { reason: 'Wrong operation type' }
|
||||
}
|
||||
|
||||
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath)
|
||||
let x: KclExpression | undefined = undefined
|
||||
let y: KclExpression | undefined = undefined
|
||||
let z: KclExpression | undefined = undefined
|
||||
let global: boolean | undefined
|
||||
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>(
|
||||
kclManager.ast,
|
||||
nodeToEdit,
|
||||
'PipeExpression'
|
||||
// 1. Map the unlabeled arguments to solid2d selections
|
||||
const objects = getObjectSelectionsFromOperation(
|
||||
operation,
|
||||
kclManager.artifactGraph
|
||||
)
|
||||
let pipe: PipeExpression | undefined
|
||||
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 (err(objects)) {
|
||||
return { reason: "Couldn't retrieve objects" }
|
||||
}
|
||||
|
||||
if (pipe) {
|
||||
const translate = pipe.body.find(
|
||||
(n) => n.type === 'CallExpressionKw' && n.callee.name.name === 'translate'
|
||||
// 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 (translate?.type === 'CallExpressionKw') {
|
||||
x = await retrieveArgFromPipedCallExpression(translate, 'x')
|
||||
y = await retrieveArgFromPipedCallExpression(translate, 'y')
|
||||
z = await retrieveArgFromPipedCallExpression(translate, 'z')
|
||||
|
||||
// optional global argument
|
||||
const result = await retrieveArgFromPipedCallExpression(
|
||||
translate,
|
||||
'global'
|
||||
)
|
||||
if (result) {
|
||||
global = result.valueText === 'true'
|
||||
}
|
||||
}
|
||||
)
|
||||
if (err(x) || 'errors' in x) {
|
||||
return { reason: "Couldn't retrieve x argument" }
|
||||
}
|
||||
|
||||
// Won't be used since we provide nodeToEdit
|
||||
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
||||
const argDefaultValues = { nodeToEdit, selection, x, y, z, global }
|
||||
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
|
||||
if ('global' in operation.labeledArgs && operation.labeledArgs.global) {
|
||||
global =
|
||||
codeManager.code.slice(
|
||||
operation.labeledArgs.global.sourceRange[0],
|
||||
operation.labeledArgs.global.sourceRange[1]
|
||||
) === 'true'
|
||||
}
|
||||
|
||||
// 3. Assemble the default argument values for the command,
|
||||
// with `nodeToEdit` set, which will let the actor know
|
||||
// to edit the node that corresponds to the StdLibCall.
|
||||
const argDefaultValues: ModelingCommandSchema['Translate'] = {
|
||||
objects,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
global,
|
||||
nodeToEdit: pathToNodeFromRustNodePath(operation.nodePath),
|
||||
}
|
||||
return {
|
||||
...baseCommand,
|
||||
argDefaultValues,
|
||||
|
||||
Reference in New Issue
Block a user