focus on extrude literal when extruding sketch
This commit is contained in:
@ -62,8 +62,11 @@ export const Toolbar = () => {
|
||||
onClick={() => {
|
||||
if (!ast) return
|
||||
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
||||
const { modifiedAst } = extrudeSketch(ast, pathToNode)
|
||||
updateAst(modifiedAst)
|
||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||||
ast,
|
||||
pathToNode
|
||||
)
|
||||
updateAst(modifiedAst, pathToExtrudeArg)
|
||||
}}
|
||||
className="border m-1 px-1 rounded"
|
||||
>
|
||||
@ -73,8 +76,12 @@ export const Toolbar = () => {
|
||||
onClick={() => {
|
||||
if (!ast) return
|
||||
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
||||
const { modifiedAst } = extrudeSketch(ast, pathToNode, false)
|
||||
updateAst(modifiedAst)
|
||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||||
ast,
|
||||
pathToNode,
|
||||
false
|
||||
)
|
||||
updateAst(modifiedAst, pathToExtrudeArg)
|
||||
}}
|
||||
className="border m-1 px-1 rounded"
|
||||
>
|
||||
|
@ -55,7 +55,10 @@ function MovingSphere({
|
||||
const { originalXY } = useMemo(() => {
|
||||
if (ast) {
|
||||
const thePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExpression = getNodeFromPath(ast, thePath) as CallExpression
|
||||
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||
ast,
|
||||
thePath
|
||||
)
|
||||
const [xArg, yArg] = callExpression?.arguments || []
|
||||
const x = xArg?.type === 'Literal' ? xArg.value : -1
|
||||
const y = yArg?.type === 'Literal' ? yArg.value : -1
|
||||
|
@ -1,82 +1,82 @@
|
||||
import { PathToNode } from './executor'
|
||||
import { Token } from './tokeniser'
|
||||
|
||||
type syntaxType =
|
||||
| 'Program'
|
||||
| 'ExpressionStatement'
|
||||
| 'BinaryExpression'
|
||||
| 'NumberLiteral'
|
||||
| 'StringLiteral'
|
||||
| 'CallExpression'
|
||||
| 'Identifier'
|
||||
| 'BlockStatement'
|
||||
| 'IfStatement'
|
||||
| 'WhileStatement'
|
||||
| 'FunctionDeclaration'
|
||||
| 'ReturnStatement'
|
||||
| 'VariableDeclaration'
|
||||
| 'VariableDeclarator'
|
||||
| 'AssignmentExpression'
|
||||
| 'UnaryExpression'
|
||||
| 'MemberExpression'
|
||||
| 'ArrayExpression'
|
||||
| 'ObjectExpression'
|
||||
| 'ObjectProperty'
|
||||
| 'Property'
|
||||
| 'LogicalExpression'
|
||||
| 'ConditionalExpression'
|
||||
| 'ForStatement'
|
||||
| 'ForInStatement'
|
||||
| 'ForOfStatement'
|
||||
| 'BreakStatement'
|
||||
| 'ContinueStatement'
|
||||
| 'SwitchStatement'
|
||||
| 'SwitchCase'
|
||||
| 'ThrowStatement'
|
||||
| 'TryStatement'
|
||||
| 'CatchClause'
|
||||
| 'ClassDeclaration'
|
||||
| 'ClassBody'
|
||||
| 'MethodDefinition'
|
||||
| 'NewExpression'
|
||||
| 'ThisExpression'
|
||||
| 'UpdateExpression'
|
||||
// | "ArrowFunctionExpression"
|
||||
| 'FunctionExpression'
|
||||
| 'SketchExpression'
|
||||
| 'PipeExpression'
|
||||
| 'PipeSubstitution'
|
||||
| 'YieldExpression'
|
||||
| 'AwaitExpression'
|
||||
| 'ImportDeclaration'
|
||||
| 'ImportSpecifier'
|
||||
| 'ImportDefaultSpecifier'
|
||||
| 'ImportNamespaceSpecifier'
|
||||
| 'ExportNamedDeclaration'
|
||||
| 'ExportDefaultDeclaration'
|
||||
| 'ExportAllDeclaration'
|
||||
| 'ExportSpecifier'
|
||||
| 'TaggedTemplateExpression'
|
||||
| 'TemplateLiteral'
|
||||
| 'TemplateElement'
|
||||
| 'SpreadElement'
|
||||
| 'RestElement'
|
||||
| 'SequenceExpression'
|
||||
| 'DebuggerStatement'
|
||||
| 'LabeledStatement'
|
||||
| 'DoWhileStatement'
|
||||
| 'WithStatement'
|
||||
| 'EmptyStatement'
|
||||
| 'Literal'
|
||||
| 'ArrayPattern'
|
||||
| 'ObjectPattern'
|
||||
| 'AssignmentPattern'
|
||||
| 'MetaProperty'
|
||||
| 'Super'
|
||||
| 'Import'
|
||||
| 'RegExpLiteral'
|
||||
| 'BooleanLiteral'
|
||||
| 'NullLiteral'
|
||||
| 'TypeAnnotation'
|
||||
// | 'NumberLiteral'
|
||||
// | 'StringLiteral'
|
||||
// | 'IfStatement'
|
||||
// | 'WhileStatement'
|
||||
// | 'FunctionDeclaration'
|
||||
// | 'AssignmentExpression'
|
||||
// | 'UnaryExpression'
|
||||
// | 'Property'
|
||||
// | 'LogicalExpression'
|
||||
// | 'ConditionalExpression'
|
||||
// | 'ForStatement'
|
||||
// | 'ForInStatement'
|
||||
// | 'ForOfStatement'
|
||||
// | 'BreakStatement'
|
||||
// | 'ContinueStatement'
|
||||
// | 'SwitchStatement'
|
||||
// | 'SwitchCase'
|
||||
// | 'ThrowStatement'
|
||||
// | 'TryStatement'
|
||||
// | 'CatchClause'
|
||||
// | 'ClassDeclaration'
|
||||
// | 'ClassBody'
|
||||
// | 'MethodDefinition'
|
||||
// | 'NewExpression'
|
||||
// | 'ThisExpression'
|
||||
// | 'UpdateExpression'
|
||||
// | 'YieldExpression'
|
||||
// | 'AwaitExpression'
|
||||
// | 'ImportDeclaration'
|
||||
// | 'ImportSpecifier'
|
||||
// | 'ImportDefaultSpecifier'
|
||||
// | 'ImportNamespaceSpecifier'
|
||||
// | 'ExportNamedDeclaration'
|
||||
// | 'ExportDefaultDeclaration'
|
||||
// | 'ExportAllDeclaration'
|
||||
// | 'ExportSpecifier'
|
||||
// | 'TaggedTemplateExpression'
|
||||
// | 'TemplateLiteral'
|
||||
// | 'TemplateElement'
|
||||
// | 'SpreadElement'
|
||||
// | 'RestElement'
|
||||
// | 'SequenceExpression'
|
||||
// | 'DebuggerStatement'
|
||||
// | 'LabeledStatement'
|
||||
// | 'DoWhileStatement'
|
||||
// | 'WithStatement'
|
||||
// | 'EmptyStatement'
|
||||
// | 'ArrayPattern'
|
||||
// | 'ObjectPattern'
|
||||
// | 'AssignmentPattern'
|
||||
// | 'MetaProperty'
|
||||
// | 'Super'
|
||||
// | 'Import'
|
||||
// | 'RegExpLiteral'
|
||||
// | 'BooleanLiteral'
|
||||
// | 'NullLiteral'
|
||||
// | 'TypeAnnotation'
|
||||
|
||||
export interface Program {
|
||||
type: syntaxType
|
||||
@ -1345,27 +1345,37 @@ function debuggerr(tokens: Token[], indexes: number[], msg = ''): string {
|
||||
return debugResult
|
||||
}
|
||||
|
||||
export function getNodeFromPath(
|
||||
export function getNodeFromPath<T>(
|
||||
node: Program,
|
||||
path: (string | number)[],
|
||||
stopAt: string = '',
|
||||
returnEarly = false
|
||||
) {
|
||||
): {
|
||||
node: T
|
||||
path: PathToNode
|
||||
} {
|
||||
let currentNode = node as any
|
||||
let stopAtNode = null
|
||||
let successfulPaths: (string | number)[] = []
|
||||
let successfulPaths: PathToNode = []
|
||||
let pathsExplored: PathToNode = []
|
||||
for (const pathItem of path) {
|
||||
try {
|
||||
if (typeof currentNode[pathItem] !== 'object')
|
||||
throw new Error('not an object')
|
||||
currentNode = currentNode[pathItem]
|
||||
successfulPaths.push(pathItem)
|
||||
if (!stopAtNode) {
|
||||
pathsExplored.push(pathItem)
|
||||
}
|
||||
if (currentNode.type === stopAt) {
|
||||
// it will match the deepest node of the type
|
||||
// instead of returning at the first match
|
||||
stopAtNode = currentNode
|
||||
if (returnEarly) {
|
||||
return stopAtNode
|
||||
return {
|
||||
node: stopAtNode,
|
||||
path: pathsExplored,
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -1378,7 +1388,10 @@ export function getNodeFromPath(
|
||||
)
|
||||
}
|
||||
}
|
||||
return stopAtNode || currentNode
|
||||
return {
|
||||
node: stopAtNode || currentNode,
|
||||
path: pathsExplored,
|
||||
}
|
||||
}
|
||||
|
||||
type Path = (string | number)[]
|
||||
|
@ -21,7 +21,7 @@ describe('testing getNodePathFromSourceRange', () => {
|
||||
|
||||
const ast = abstractSyntaxTree(lexer(code))
|
||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const node = getNodeFromPath(ast, nodePath)
|
||||
const { node } = getNodeFromPath<any>(ast, nodePath)
|
||||
|
||||
expect([node.start, node.end]).toEqual(sourceRange)
|
||||
expect(node.type).toBe('CallExpression')
|
||||
|
@ -202,11 +202,11 @@ export function addLine(
|
||||
): { modifiedAst: Program; pathToNode: (string | number)[] } {
|
||||
const _node = { ...node }
|
||||
const dumbyStartend = { start: 0, end: 0 }
|
||||
const sketchExpression = getNodeFromPath(
|
||||
const { node: sketchExpression } = getNodeFromPath<SketchExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'SketchExpression'
|
||||
) as SketchExpression
|
||||
)
|
||||
const line: ExpressionStatement = {
|
||||
type: 'ExpressionStatement',
|
||||
...dumbyStartend,
|
||||
@ -251,7 +251,10 @@ export function changeArguments(
|
||||
const _node = { ...node }
|
||||
const dumbyStartend = { start: 0, end: 0 }
|
||||
// const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
||||
const callExpression = getNodeFromPath(_node, pathToNode) as CallExpression
|
||||
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode
|
||||
)
|
||||
const newXArg: CallExpression['arguments'][number] =
|
||||
callExpression.arguments[0].type === 'Literal'
|
||||
? {
|
||||
@ -285,24 +288,29 @@ export function extrudeSketch(
|
||||
node: Program,
|
||||
pathToNode: (string | number)[],
|
||||
shouldPipe = true
|
||||
): { modifiedAst: Program; pathToNode: (string | number)[] } {
|
||||
): {
|
||||
modifiedAst: Program
|
||||
pathToNode: PathToNode
|
||||
pathToExtrudeArg: PathToNode
|
||||
} {
|
||||
const _node = { ...node }
|
||||
const dumbyStartend = { start: 0, end: 0 }
|
||||
const sketchExpression = getNodeFromPath(
|
||||
const { node: sketchExpression } = getNodeFromPath<SketchExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'SketchExpression'
|
||||
) as SketchExpression
|
||||
)
|
||||
|
||||
// determine if sketchExpression is in a pipeExpression or not
|
||||
const pipeExpression = getNodeFromPath(_node, pathToNode, 'PipeExpression')
|
||||
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
||||
|
||||
const variableDeclorator = getNodeFromPath(
|
||||
const { node: pipeExpression } = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
) as VariableDeclarator
|
||||
'PipeExpression'
|
||||
)
|
||||
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
||||
|
||||
const { node: variableDeclorator, path: pathToDecleration } =
|
||||
getNodeFromPath<VariableDeclarator>(_node, pathToNode, 'VariableDeclarator')
|
||||
|
||||
const extrudeCall: CallExpression = {
|
||||
type: 'CallExpression',
|
||||
@ -346,10 +354,19 @@ export function extrudeSketch(
|
||||
}
|
||||
|
||||
variableDeclorator.init = pipeChain
|
||||
const pathToExtrudeArg = [
|
||||
...pathToDecleration,
|
||||
'init',
|
||||
'body',
|
||||
pipeChain.body.length - 1,
|
||||
'arguments',
|
||||
0,
|
||||
]
|
||||
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
pathToExtrudeArg,
|
||||
}
|
||||
}
|
||||
const name = findUniqueName(node, 'part')
|
||||
@ -372,9 +389,19 @@ export function extrudeSketch(
|
||||
}
|
||||
const showCallIndex = getShowIndex(_node)
|
||||
_node.body.splice(showCallIndex, 0, VariableDeclaration)
|
||||
const pathToExtrudeArg = [
|
||||
'body',
|
||||
showCallIndex,
|
||||
'declarations',
|
||||
0,
|
||||
'init',
|
||||
'arguments',
|
||||
0,
|
||||
]
|
||||
return {
|
||||
modifiedAst: addToShow(_node, name),
|
||||
pathToNode: [...pathToNode.slice(0, -1), showCallIndex],
|
||||
pathToExtrudeArg,
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,22 +412,27 @@ export function sketchOnExtrudedFace(
|
||||
const _node = { ...node }
|
||||
const dumbyStartend = { start: 0, end: 0 }
|
||||
const newSketchName = findUniqueName(node, 'part')
|
||||
const oldSketchName = getNodeFromPath(
|
||||
const oldSketchName = getNodeFromPath<VariableDeclarator>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'VariableDeclarator',
|
||||
true
|
||||
).id.name
|
||||
const expression = getNodeFromPath(_node, pathToNode, 'CallExpression') as
|
||||
| VariableDeclarator
|
||||
| CallExpression
|
||||
).node.id.name
|
||||
const { node: expression } = getNodeFromPath<
|
||||
VariableDeclarator | CallExpression
|
||||
>(_node, pathToNode, 'CallExpression')
|
||||
|
||||
const pathName =
|
||||
expression.type === 'VariableDeclarator'
|
||||
? expression.id.name
|
||||
: findUniqueName(node, 'path', 2)
|
||||
|
||||
if (expression.type === 'CallExpression') {
|
||||
const block = getNodeFromPath(_node, pathToNode, 'BlockStatement')
|
||||
const { node: block } = getNodeFromPath<BlockStatement>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'BlockStatement'
|
||||
)
|
||||
const expressionIndex = getLastIndex(pathToNode)
|
||||
if (expression.callee.name !== 'lineTo')
|
||||
throw new Error('expected a lineTo call')
|
||||
|
@ -1,6 +1,10 @@
|
||||
import create from 'zustand'
|
||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
||||
import {
|
||||
Program,
|
||||
abstractSyntaxTree,
|
||||
getNodeFromPath,
|
||||
} from './lang/abstractSyntaxTree'
|
||||
import { ProgramMemory, Position, PathToNode, Rotation } from './lang/executor'
|
||||
import { recast } from './lang/recast'
|
||||
import { lexer } from './lang/tokeniser'
|
||||
@ -48,7 +52,7 @@ interface StoreState {
|
||||
setEditorView: (editorView: EditorView) => void
|
||||
highlightRange: [number, number]
|
||||
setHighlightRange: (range: Range) => void
|
||||
setCursor: (cursor: number) => void
|
||||
setCursor: (start: number, end?: number) => void
|
||||
selectionRange: [number, number]
|
||||
setSelectionRange: (range: Range) => void
|
||||
guiMode: GuiModes
|
||||
@ -60,7 +64,7 @@ interface StoreState {
|
||||
resetLogs: () => void
|
||||
ast: Program | null
|
||||
setAst: (ast: Program | null) => void
|
||||
updateAst: (ast: Program) => void
|
||||
updateAst: (ast: Program, focusPath?: PathToNode) => void
|
||||
code: string
|
||||
setCode: (code: string) => void
|
||||
formatCode: () => void
|
||||
@ -86,11 +90,11 @@ export const useStore = create<StoreState>()((set, get) => ({
|
||||
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) })
|
||||
}
|
||||
},
|
||||
setCursor: (cursor: number) => {
|
||||
setCursor: (start: number, end: number = start) => {
|
||||
const editorView = get().editorView
|
||||
if (!editorView) return
|
||||
editorView.dispatch({
|
||||
selection: { anchor: cursor, head: cursor },
|
||||
selection: { anchor: start, head: end },
|
||||
})
|
||||
},
|
||||
selectionRange: [0, 0],
|
||||
@ -118,9 +122,19 @@ export const useStore = create<StoreState>()((set, get) => ({
|
||||
setAst: (ast) => {
|
||||
set({ ast })
|
||||
},
|
||||
updateAst: (ast) => {
|
||||
updateAst: (ast, focusPath) => {
|
||||
const newCode = recast(ast)
|
||||
set({ ast, code: newCode })
|
||||
const astWithUpdatedSource = abstractSyntaxTree(lexer(newCode))
|
||||
|
||||
set({ ast: astWithUpdatedSource, code: newCode })
|
||||
if (focusPath) {
|
||||
const { node } = getNodeFromPath<any>(astWithUpdatedSource, focusPath)
|
||||
const { start, end } = node
|
||||
if (!start || !end) return
|
||||
setTimeout(() => {
|
||||
get().setCursor(start, end)
|
||||
})
|
||||
}
|
||||
},
|
||||
code: '',
|
||||
setCode: (code) => {
|
||||
|
Reference in New Issue
Block a user