Files
modeling-app/src/lang/util.ts

171 lines
4.4 KiB
TypeScript

import { Selections } from 'lib/selections'
import {
PathToNode,
CallExpression,
Literal,
ArrayExpression,
BinaryExpression,
ArtifactGraph,
CallExpressionKw,
Expr,
LiteralValue,
NumericSuffix,
} from './wasm'
import { filterArtifacts } from 'lang/std/artifactGraph'
import { isArray, isOverlap } from 'lib/utils'
/**
* Updates pathToNode body indices to account for the insertion of an expression
* PathToNode expression is after the insertion index, that the body index is incremented
* Negative insertion index means no insertion
*/
export function updatePathToNodePostExprInjection(
pathToNode: PathToNode,
exprInsertIndex: number
): PathToNode {
if (exprInsertIndex < 0) return pathToNode
const bodyIndex = Number(pathToNode[1][0])
if (bodyIndex < exprInsertIndex) return pathToNode
const clone = structuredClone(pathToNode)
clone[1][0] = bodyIndex + 1
return clone
}
export function updateSketchDetailsNodePaths({
sketchEntryNodePath,
sketchNodePaths,
planeNodePath,
exprInsertIndex,
}: {
sketchEntryNodePath: PathToNode
sketchNodePaths: Array<PathToNode>
planeNodePath: PathToNode
exprInsertIndex: number
}) {
return {
updatedSketchEntryNodePath: updatePathToNodePostExprInjection(
sketchEntryNodePath,
exprInsertIndex
),
updatedSketchNodePaths: sketchNodePaths.map((path) =>
updatePathToNodePostExprInjection(path, exprInsertIndex)
),
updatedPlaneNodePath: updatePathToNodePostExprInjection(
planeNodePath,
exprInsertIndex
),
}
}
export function isCursorInSketchCommandRange(
artifactGraph: ArtifactGraph,
selectionRanges: Selections
): string | false {
const overlappingEntries = filterArtifacts(
{
types: ['segment', 'path', 'plane'],
predicate: (artifact) => {
return selectionRanges.graphSelections.some(
(selection) =>
isArray(selection?.codeRef?.range) &&
isArray(artifact?.codeRef?.range) &&
isOverlap(selection?.codeRef?.range, artifact.codeRef.range)
)
},
},
artifactGraph
)
const firstEntry = [...overlappingEntries.values()]?.[0]
const parentId = firstEntry?.type === 'segment' ? firstEntry.pathId : false
return parentId
? parentId
: [...overlappingEntries].find(
([, artifact]) => artifact.type === 'path'
)?.[0] || false
}
export function isCallExpression(e: any): e is CallExpression {
return e && e.type === 'CallExpression'
}
export function isArrayExpression(e: any): e is ArrayExpression {
return e && e.type === 'ArrayExpression'
}
export function isLiteral(e: any): e is Literal {
return e && e.type === 'Literal'
}
export function isBinaryExpression(e: any): e is BinaryExpression {
return e && e.type === 'BinaryExpression'
}
/**
Search the keyword arguments from a call for an argument with this label.
*/
export function findKwArg(
label: string,
call: CallExpressionKw
): Expr | undefined {
return call?.arguments?.find((arg) => {
return arg.label.name === label
})?.arg
}
/**
Search the keyword arguments from a call for an argument with this label,
returns the index of the argument as well.
*/
export function findKwArgWithIndex(
label: string,
call: CallExpressionKw
): { expr: Expr; argIndex: number } | undefined {
const index = call.arguments.findIndex((arg) => {
return arg.label.name === label
})
return index >= 0
? { expr: call.arguments[index].arg, argIndex: index }
: undefined
}
/**
Search the keyword arguments from a call for an argument with one of these labels.
*/
export function findKwArgAny(
labels: string[],
call: CallExpressionKw
): Expr | undefined {
return call.arguments.find((arg) => {
return labels.includes(arg.label.name)
})?.arg
}
/**
Search the keyword arguments from a call for an argument with one of these labels.
*/
export function findKwArgAnyIndex(
labels: string[],
call: CallExpressionKw
): number | undefined {
return call.arguments.findIndex((arg) => {
return labels.includes(arg.label.name)
})
}
export function isAbsolute(call: CallExpressionKw): boolean {
return findKwArgAny(['endAbsolute'], call) !== undefined
}
export function isLiteralValueNumber(
e: LiteralValue
): e is { value: number; suffix: NumericSuffix } {
return (
typeof e === 'object' &&
'value' in e &&
typeof e.value === 'number' &&
'suffix' in e &&
typeof e.suffix === 'string'
)
}