Symbols overlay (#2033)
* start of overlay work
* add new icons
* add constraint symbols
* add three dots
* add primary colours
* refactor how we get constraint info for overlays
* refactor how we get constraint info for overlays
* get symbols working for tangential arc too
* extra data on constraint info
* add initial delete
* fix types and circular dep issue after rebase
* fix quirk with horz vert line overlays
* fix setup and tear down of overlays
* remove overlays that are too small
* throttle overlay updates and prove tests selecting html instead of hardcoded px coords
* initial show overaly on segment hover
* remove overlays when tool is equipped
* dounce overlay updates
* tsc
* make higlighting robust to small changes in source ranges
* replace with variable for unconstrained values, and improve styles for popover
* background tweak
* make overlays unconstrain inputs
* fix small regression
* write query for finding related tag references
* make delete segment safe
* typo
* un used imports
* test deleteSegmentFromPipeExpression
* add getConstraintInfo test
* test removeSingleConstraintInfo
* more tests
* tsc
* add tests for overlay buttons
* rename tests
* fmt
* better naming structure
* more reliablity
* more test tweaks
* fix selection test
* add delete segments with overlays tests
* dependant tag tests for segment delet
* typo
* test clean up
* fix some perf issus
* clean up
* clean up
* make things a little more dry
* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)
* trigger ci
* Make constraint hover popovers readable on light mode
* Touch up the new variable dialog
* Little touch-up to three-dot menu style
* fix highlight issue
* fmt
* use optional chain
* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)"
This reverts commit be3d61e4a3
.
* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)
* disable var panel in sketch mode
* fix overlay tests after mergi in main
* test tweak
* try fix ubuntu
* fmt
* more test tweaks
* tweak
* tweaks
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
This commit is contained in:
@ -26,10 +26,17 @@ import {
|
||||
getConstraintType,
|
||||
} from './std/sketchcombos'
|
||||
|
||||
/**
|
||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||
* This function navigates through the AST (Abstract Syntax Tree) based on the provided path, attempting to locate
|
||||
* and return the node at the end of this path.
|
||||
* By default it will return the node of the deepest "stopAt" type encountered, or the node at the end of the path if no "stopAt" type is provided.
|
||||
* If the "returnEarly" flag is set to true, the function will return as soon as a node of the specified type is found.
|
||||
*/
|
||||
export function getNodeFromPath<T>(
|
||||
node: Program,
|
||||
path: PathToNode,
|
||||
stopAt: string | string[] = '',
|
||||
stopAt?: SyntaxType | SyntaxType[],
|
||||
returnEarly = false
|
||||
): {
|
||||
node: T
|
||||
@ -50,9 +57,10 @@ export function getNodeFromPath<T>(
|
||||
pathsExplored.push(pathItem)
|
||||
}
|
||||
if (
|
||||
Array.isArray(stopAt)
|
||||
typeof stopAt !== 'undefined' &&
|
||||
(Array.isArray(stopAt)
|
||||
? stopAt.includes(currentNode.type)
|
||||
: currentNode.type === stopAt
|
||||
: currentNode.type === stopAt)
|
||||
) {
|
||||
// it will match the deepest node of the type
|
||||
// instead of returning at the first match
|
||||
@ -82,17 +90,20 @@ export function getNodeFromPath<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions the same as getNodeFromPath, but returns a curried function that can be called with the stopAt and returnEarly arguments.
|
||||
*/
|
||||
export function getNodeFromPathCurry(
|
||||
node: Program,
|
||||
path: PathToNode
|
||||
): <T>(
|
||||
stopAt: string,
|
||||
stopAt?: SyntaxType | SyntaxType[],
|
||||
returnEarly?: boolean
|
||||
) => {
|
||||
node: T
|
||||
path: PathToNode
|
||||
} {
|
||||
return <T>(stopAt: string = '', returnEarly = false) => {
|
||||
return <T>(stopAt?: SyntaxType | SyntaxType[], returnEarly = false) => {
|
||||
const { node: _node, shallowPath } = getNodeFromPath<T>(
|
||||
node,
|
||||
path,
|
||||
@ -353,29 +364,31 @@ export interface PrevVariable<T> {
|
||||
value: T
|
||||
}
|
||||
|
||||
export function findAllPreviousVariables(
|
||||
export function findAllPreviousVariablesPath(
|
||||
ast: Program,
|
||||
programMemory: ProgramMemory,
|
||||
sourceRange: Selection['range'],
|
||||
path: PathToNode,
|
||||
type: 'number' | 'string' = 'number'
|
||||
): {
|
||||
variables: PrevVariable<typeof type extends 'number' ? number : string>[]
|
||||
bodyPath: PathToNode
|
||||
insertIndex: number
|
||||
} {
|
||||
const path = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const { shallowPath: pathToDec } = getNodeFromPath(
|
||||
const { shallowPath: pathToDec, node } = getNodeFromPath(
|
||||
ast,
|
||||
path,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
|
||||
const startRange = (node as any).start
|
||||
|
||||
const { index: insertIndex, path: bodyPath } = splitPathAtLastIndex(pathToDec)
|
||||
|
||||
const { node: bodyItems } = getNodeFromPath<Program['body']>(ast, bodyPath)
|
||||
|
||||
const variables: PrevVariable<any>[] = []
|
||||
bodyItems?.forEach?.((item) => {
|
||||
if (item.type !== 'VariableDeclaration' || item.end > sourceRange[0]) return
|
||||
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
||||
const varName = item.declarations[0].id.name
|
||||
const varValue = programMemory?.root[varName]
|
||||
if (typeof varValue?.value !== type) return
|
||||
@ -392,25 +405,42 @@ export function findAllPreviousVariables(
|
||||
}
|
||||
}
|
||||
|
||||
type ReplacerFn = (_ast: Program, varName: string) => { modifiedAst: Program }
|
||||
|
||||
export function isNodeSafeToReplace(
|
||||
export function findAllPreviousVariables(
|
||||
ast: Program,
|
||||
sourceRange: [number, number]
|
||||
programMemory: ProgramMemory,
|
||||
sourceRange: Selection['range'],
|
||||
type: 'number' | 'string' = 'number'
|
||||
): {
|
||||
variables: PrevVariable<typeof type extends 'number' ? number : string>[]
|
||||
bodyPath: PathToNode
|
||||
insertIndex: number
|
||||
} {
|
||||
const path = getNodePathFromSourceRange(ast, sourceRange)
|
||||
return findAllPreviousVariablesPath(ast, programMemory, path, type)
|
||||
}
|
||||
|
||||
type ReplacerFn = (
|
||||
_ast: Program,
|
||||
varName: string
|
||||
) => { modifiedAst: Program; pathToReplaced: PathToNode }
|
||||
|
||||
export function isNodeSafeToReplacePath(
|
||||
ast: Program,
|
||||
path: PathToNode
|
||||
): {
|
||||
isSafe: boolean
|
||||
value: Value
|
||||
replacer: ReplacerFn
|
||||
} {
|
||||
let path = getNodePathFromSourceRange(ast, sourceRange)
|
||||
if (path[path.length - 1][0] === 'callee') {
|
||||
path = path.slice(0, -1)
|
||||
}
|
||||
const acceptedNodeTypes = [
|
||||
const acceptedNodeTypes: SyntaxType[] = [
|
||||
'BinaryExpression',
|
||||
'Identifier',
|
||||
'CallExpression',
|
||||
'Literal',
|
||||
'UnaryExpression',
|
||||
]
|
||||
const { node: value, deepPath: outPath } = getNodeFromPath(
|
||||
ast,
|
||||
@ -431,10 +461,12 @@ export function isNodeSafeToReplace(
|
||||
const replaceNodeWithIdentifier: ReplacerFn = (_ast, varName) => {
|
||||
const identifier = createIdentifier(varName)
|
||||
const last = finPath[finPath.length - 1]
|
||||
const pathToReplaced = JSON.parse(JSON.stringify(finPath))
|
||||
pathToReplaced[1][0] = pathToReplaced[1][0] + 1
|
||||
const startPath = finPath.slice(0, -1)
|
||||
const nodeToReplace = getNodeFromPath(_ast, startPath).node as any
|
||||
nodeToReplace[last[0]] = identifier
|
||||
return { modifiedAst: _ast }
|
||||
return { modifiedAst: _ast, pathToReplaced }
|
||||
}
|
||||
|
||||
const hasPipeSub = isTypeInValue(finVal as Value, 'PipeSubstitution')
|
||||
@ -450,6 +482,18 @@ export function isNodeSafeToReplace(
|
||||
}
|
||||
}
|
||||
|
||||
export function isNodeSafeToReplace(
|
||||
ast: Program,
|
||||
sourceRange: [number, number]
|
||||
): {
|
||||
isSafe: boolean
|
||||
value: Value
|
||||
replacer: ReplacerFn
|
||||
} {
|
||||
let path = getNodePathFromSourceRange(ast, sourceRange)
|
||||
return isNodeSafeToReplacePath(ast, path)
|
||||
}
|
||||
|
||||
export function isTypeInValue(node: Value, syntaxType: SyntaxType): boolean {
|
||||
if (node.type === syntaxType) return true
|
||||
if (node.type === 'BinaryExpression') return isTypeInBinExp(node, syntaxType)
|
||||
@ -632,3 +676,47 @@ export function isSingleCursorInPipe(
|
||||
if (nodeTypes.includes('PipeExpression')) return true
|
||||
return false
|
||||
}
|
||||
|
||||
export function findUsesOfTagInPipe(
|
||||
ast: Program,
|
||||
pathToNode: PathToNode
|
||||
): SourceRange[] {
|
||||
const stdlibFunctionsThatTakeTagInputs = [
|
||||
'segAng',
|
||||
'segEndX',
|
||||
'segEndY',
|
||||
'segLen',
|
||||
]
|
||||
const node = getNodeFromPath<CallExpression>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
).node
|
||||
if (node.type !== 'CallExpression') return []
|
||||
const tagIndex = node.callee.name === 'close' ? 1 : 2
|
||||
const thirdParam = node.arguments[tagIndex]
|
||||
if (thirdParam?.type !== 'Literal') return []
|
||||
const tag = String(thirdParam.value)
|
||||
|
||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'VariableDeclaration'
|
||||
).node
|
||||
const dependentRanges: SourceRange[] = []
|
||||
|
||||
traverse(varDec, {
|
||||
enter: (node) => {
|
||||
if (
|
||||
node.type !== 'CallExpression' ||
|
||||
!stdlibFunctionsThatTakeTagInputs.includes(node.callee.name)
|
||||
)
|
||||
return
|
||||
const tagArg = node.arguments[0]
|
||||
if (tagArg.type !== 'Literal') return
|
||||
if (String(tagArg.value) === tag)
|
||||
dependentRanges.push([node.start, node.end])
|
||||
},
|
||||
})
|
||||
return dependentRanges
|
||||
}
|
||||
|
Reference in New Issue
Block a user