Users should be able to select points (not just lines) (#97)

* update selection datastructure to accomodate more selection metadata

* Users should be able to select points (not just lines)
This commit is contained in:
Kurt Hutten
2023-04-03 16:05:25 +10:00
committed by GitHub
parent 7013eb861d
commit a8b68bab6a
25 changed files with 292 additions and 146 deletions

View File

@ -45,6 +45,7 @@ function App() {
errorState, errorState,
setProgramMemory, setProgramMemory,
resetLogs, resetLogs,
selectionRangeTypeMap,
} = useStore((s) => ({ } = useStore((s) => ({
editorView: s.editorView, editorView: s.editorView,
setEditorView: s.setEditorView, setEditorView: s.setEditorView,
@ -61,6 +62,7 @@ function App() {
errorState: s.errorState, errorState: s.errorState,
setProgramMemory: s.setProgramMemory, setProgramMemory: s.setProgramMemory,
resetLogs: s.resetLogs, resetLogs: s.resetLogs,
selectionRangeTypeMap: s.selectionRangeTypeMap,
})) }))
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => { // const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
const onChange = (value: string, viewUpdate: ViewUpdate) => { const onChange = (value: string, viewUpdate: ViewUpdate) => {
@ -76,13 +78,30 @@ function App() {
const ranges = viewUpdate.state.selection.ranges const ranges = viewUpdate.state.selection.ranges
const isChange = const isChange =
ranges.length !== selectionRange.length || ranges.length !== selectionRange.codeBasedSelections.length ||
ranges.some(({ from, to }, i) => { ranges.some(({ from, to }, i) => {
return from !== selectionRange[i][0] || to !== selectionRange[i][1] return (
from !== selectionRange.codeBasedSelections[i].range[0] ||
to !== selectionRange.codeBasedSelections[i].range[1]
)
}) })
if (!isChange) return if (!isChange) return
setSelectionRanges(ranges.map(({ from, to }) => [from, to])) setSelectionRanges({
otherSelections: [],
codeBasedSelections: ranges.map(({ from, to }, i) => {
if (selectionRangeTypeMap[to]) {
return {
type: selectionRangeTypeMap[to],
range: [from, to],
}
}
return {
type: 'default',
range: [from, to],
}
}),
})
} }
const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([]) const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([])
useEffect(() => { useEffect(() => {
@ -163,7 +182,7 @@ function App() {
<div className="h-screen"> <div className="h-screen">
<ModalContainer /> <ModalContainer />
<Allotment snap={true}> <Allotment snap={true}>
<Allotment vertical defaultSizes={[4, 1, 1]} minSize={20}> <Allotment vertical defaultSizes={[400, 1, 1]} minSize={20}>
<div className="h-full flex flex-col items-start"> <div className="h-full flex flex-col items-start">
<PanelHeader title="Editor" /> <PanelHeader title="Editor" />
{/* <button {/* <button
@ -190,7 +209,7 @@ function App() {
<MemoryPanel /> <MemoryPanel />
<Logs /> <Logs />
</Allotment> </Allotment>
<Allotment vertical defaultSizes={[4, 1]} minSize={20}> <Allotment vertical defaultSizes={[400, 1]} minSize={20}>
<div className="h-full"> <div className="h-full">
<PanelHeader title="Drafting Board" /> <PanelHeader title="Drafting Board" />
<Toolbar /> <Toolbar />

View File

@ -48,7 +48,7 @@ export const Toolbar = () => {
if (!ast) return if (!ast) return
const pathToNode = getNodePathFromSourceRange( const pathToNode = getNodePathFromSourceRange(
ast, ast,
selectionRanges[0] selectionRanges.codeBasedSelections[0].range
) )
const { modifiedAst } = sketchOnExtrudedFace( const { modifiedAst } = sketchOnExtrudedFace(
ast, ast,
@ -85,7 +85,7 @@ export const Toolbar = () => {
if (!ast) return if (!ast) return
const pathToNode = getNodePathFromSourceRange( const pathToNode = getNodePathFromSourceRange(
ast, ast,
selectionRanges[0] selectionRanges.codeBasedSelections[0].range
) )
const { modifiedAst, pathToExtrudeArg } = extrudeSketch( const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
ast, ast,
@ -102,7 +102,7 @@ export const Toolbar = () => {
if (!ast) return if (!ast) return
const pathToNode = getNodePathFromSourceRange( const pathToNode = getNodePathFromSourceRange(
ast, ast,
selectionRanges[0] selectionRanges.codeBasedSelections[0].range
) )
const { modifiedAst, pathToExtrudeArg } = extrudeSketch( const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
ast, ast,

View File

@ -98,7 +98,7 @@ export function useCalc({
const { ast, programMemory, selectionRange } = useStore((s) => ({ const { ast, programMemory, selectionRange } = useStore((s) => ({
ast: s.ast, ast: s.ast,
programMemory: s.programMemory, programMemory: s.programMemory,
selectionRange: s.selectionRanges[0], selectionRange: s.selectionRanges.codeBasedSelections[0].range,
})) }))
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const [availableVarInfo, setAvailableVarInfo] = useState< const [availableVarInfo, setAvailableVarInfo] = useState<

View File

@ -28,7 +28,7 @@ import { useSetCursor } from '../hooks/useSetCursor'
import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos' import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos'
import { createCallExpression, createPipeSubstitution } from '../lang/modifyAst' import { createCallExpression, createPipeSubstitution } from '../lang/modifyAst'
function MovingSphere({ function LineEnd({
geo, geo,
sourceRange, sourceRange,
editorCursor, editorCursor,
@ -51,6 +51,8 @@ function MovingSphere({
const [isMouseDown, setIsMouseDown] = useState(false) const [isMouseDown, setIsMouseDown] = useState(false)
const baseColor = useConstraintColors(sourceRange) const baseColor = useConstraintColors(sourceRange)
const setCursor = useSetCursor(sourceRange, 'line-end')
const { setHighlightRange, guiMode, ast, updateAst, programMemory } = const { setHighlightRange, guiMode, ast, updateAst, programMemory } =
useStore((s) => ({ useStore((s) => ({
setHighlightRange: s.setHighlightRange, setHighlightRange: s.setHighlightRange,
@ -84,7 +86,6 @@ function MovingSphere({
useEffect(() => { useEffect(() => {
const handleMouseUp = () => { const handleMouseUp = () => {
if (isMouseDown && ast) { if (isMouseDown && ast) {
const thePath = getNodePathFromSourceRange(ast, sourceRange)
const current2d = point2DRef.current.clone() const current2d = point2DRef.current.clone()
const inverseQuaternion = new Quaternion() const inverseQuaternion = new Quaternion()
if ( if (
@ -107,8 +108,8 @@ function MovingSphere({
guiMode, guiMode,
from from
) )
if (!(current2d.x === 0 && current2d.y === 0 && current2d.z === 0))
updateAst(modifiedAst) updateAst(modifiedAst)
ref.current.position.set(...position) ref.current.position.set(...position)
} }
setIsMouseDown(false) setIsMouseDown(false)
@ -142,7 +143,10 @@ function MovingSphere({
setHover(false) setHover(false)
setHighlightRange([0, 0]) setHighlightRange([0, 0])
}} }}
onPointerDown={() => inEditMode && setIsMouseDown(true)} onPointerDown={() => {
inEditMode && setIsMouseDown(true)
setCursor()
}}
> >
<primitive object={geo} scale={hovered ? 2 : 1} /> <primitive object={geo} scale={hovered ? 2 : 1} />
<meshStandardMaterial <meshStandardMaterial
@ -313,8 +317,8 @@ function WallRender({
const [editorCursor, setEditorCursor] = useState(false) const [editorCursor, setEditorCursor] = useState(false)
useEffect(() => { useEffect(() => {
const shouldHighlight = selectionRanges.some((range) => const shouldHighlight = selectionRanges.codeBasedSelections.some(
isOverlap(geoInfo.__geoMeta.sourceRange, range) ({ range }) => isOverlap(geoInfo.__geoMeta.sourceRange, range)
) )
setEditorCursor(shouldHighlight) setEditorCursor(shouldHighlight)
}, [selectionRanges, geoInfo]) }, [selectionRanges, geoInfo])
@ -369,11 +373,17 @@ function PathRender({
guiMode: s.guiMode, guiMode: s.guiMode,
})) }))
const [editorCursor, setEditorCursor] = useState(false) const [editorCursor, setEditorCursor] = useState(false)
const [editorLineCursor, setEditorLineCursor] = useState(false)
useEffect(() => { useEffect(() => {
const shouldHighlight = selectionRanges.some((range) => const shouldHighlight = selectionRanges.codeBasedSelections.some(
isOverlap(geoInfo.__geoMeta.sourceRange, range) ({ range }) => isOverlap(geoInfo.__geoMeta.sourceRange, range)
)
const shouldHighlightLine = selectionRanges.codeBasedSelections.some(
({ range, type }) =>
isOverlap(geoInfo.__geoMeta.sourceRange, range) && type === 'default'
) )
setEditorCursor(shouldHighlight) setEditorCursor(shouldHighlight)
setEditorLineCursor(shouldHighlightLine)
}, [selectionRanges, geoInfo]) }, [selectionRanges, geoInfo])
return ( return (
<> <>
@ -384,14 +394,14 @@ function PathRender({
key={i} key={i}
geo={meta.geo} geo={meta.geo}
sourceRange={geoInfo.__geoMeta.sourceRange} sourceRange={geoInfo.__geoMeta.sourceRange}
forceHighlight={forceHighlight || editorCursor} forceHighlight={editorLineCursor}
rotation={rotation} rotation={rotation}
position={position} position={position}
/> />
) )
if (meta.type === 'lineEnd') if (meta.type === 'lineEnd')
return ( return (
<MovingSphere <LineEnd
key={i} key={i}
geo={meta.geo} geo={meta.geo}
from={geoInfo.from} from={geoInfo.from}
@ -407,7 +417,7 @@ function PathRender({
key={i} key={i}
geo={meta.geo} geo={meta.geo}
sourceRange={geoInfo.__geoMeta.sourceRange} sourceRange={geoInfo.__geoMeta.sourceRange}
forceHighlight={forceHighlight || editorCursor} forceHighlight={forceHighlight || editorLineCursor}
rotation={rotation} rotation={rotation}
position={position} position={position}
onClick={() => { onClick={() => {
@ -533,7 +543,12 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
)[] = [] )[] = []
artifacts?.forEach((artifact) => { artifacts?.forEach((artifact) => {
artifact.value.forEach((geo) => { artifact.value.forEach((geo) => {
if (isOverlap(geo.__geoMeta.sourceRange, selectionRanges[0])) { if (
isOverlap(
geo.__geoMeta.sourceRange,
selectionRanges.codeBasedSelections[0].range
)
) {
artifactsWithinCursorRange.push({ artifactsWithinCursorRange.push({
parentType: artifact.type, parentType: artifact.type,
isParent: false, isParent: false,
@ -545,7 +560,12 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
} }
}) })
artifact.__meta.forEach((meta) => { artifact.__meta.forEach((meta) => {
if (isOverlap(meta.sourceRange, selectionRanges[0])) { if (
isOverlap(
meta.sourceRange,
selectionRanges.codeBasedSelections[0].range
)
) {
artifactsWithinCursorRange.push({ artifactsWithinCursorRange.push({
parentType: artifact.type, parentType: artifact.type,
isParent: true, isParent: true,

View File

@ -21,9 +21,12 @@ export const ConvertToVariable = () => {
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const { isSafe, value } = isNodeSafeToReplace(ast, selectionRanges[0]) const { isSafe, value } = isNodeSafeToReplace(
ast,
selectionRanges.codeBasedSelections[0].range
)
const canReplace = isSafe && value.type !== 'Identifier' const canReplace = isSafe && value.type !== 'Identifier'
const isOnlyOneSelection = selectionRanges.length === 1 const isOnlyOneSelection = selectionRanges.codeBasedSelections.length === 1
const _enableHorz = canReplace && isOnlyOneSelection const _enableHorz = canReplace && isOnlyOneSelection
setEnableAngLen(_enableHorz) setEnableAngLen(_enableHorz)
@ -41,7 +44,7 @@ export const ConvertToVariable = () => {
const { modifiedAst: _modifiedAst } = moveValueIntoNewVariable( const { modifiedAst: _modifiedAst } = moveValueIntoNewVariable(
ast, ast,
programMemory, programMemory,
selectionRanges[0], selectionRanges.codeBasedSelections[0].range,
variableName variableName
) )

View File

@ -26,8 +26,8 @@ export const EqualAngle = () => {
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
@ -52,7 +52,10 @@ export const EqualAngle = () => {
) )
const theTransforms = getTransformInfos( const theTransforms = getTransformInfos(
selectionRanges.slice(1), {
...selectionRanges,
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
},
ast, ast,
'equalAngle' 'equalAngle'
) )

View File

@ -26,8 +26,8 @@ export const EqualLength = () => {
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
@ -52,7 +52,10 @@ export const EqualLength = () => {
) )
const theTransforms = getTransformInfos( const theTransforms = getTransformInfos(
selectionRanges.slice(1), {
...selectionRanges,
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
},
ast, ast,
'equalLength' 'equalLength'
) )

View File

@ -29,8 +29,8 @@ export const HorzVert = ({
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node

View File

@ -39,8 +39,8 @@ export const Intersect = () => {
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
@ -68,7 +68,10 @@ export const Intersect = () => {
) )
const theTransforms = getTransformInfos( const theTransforms = getTransformInfos(
selectionRanges.slice(1), {
...selectionRanges,
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
},
ast, ast,
'intersect' 'intersect'
) )
@ -150,7 +153,7 @@ export const Intersect = () => {
}`} }`}
disabled={!enable} disabled={!enable}
> >
Intersect perpendicularDistance
</button> </button>
) )
} }

View File

@ -25,8 +25,8 @@ export const RemoveConstrainingValues = () => {
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
@ -37,15 +37,19 @@ export const RemoveConstrainingValues = () => {
toolTips.includes(node.callee.name as any) toolTips.includes(node.callee.name as any)
) )
const theTransforms = getRemoveConstraintsTransforms( try {
selectionRanges, const theTransforms = getRemoveConstraintsTransforms(
ast, selectionRanges,
'removeConstrainingValues' ast,
) 'removeConstrainingValues'
setTransformInfos(theTransforms) )
setTransformInfos(theTransforms)
const _enableHorz = isAllTooltips && theTransforms.every(Boolean) const _enableHorz = isAllTooltips && theTransforms.every(Boolean)
setEnableHorz(_enableHorz) setEnableHorz(_enableHorz)
} catch (e) {
console.error(e)
}
}, [guiMode, selectionRanges]) }, [guiMode, selectionRanges])
if (guiMode.mode !== 'sketch') return null if (guiMode.mode !== 'sketch') return null

View File

@ -35,8 +35,8 @@ export const SetAngleLength = ({
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => (pathToNode) =>
@ -60,7 +60,7 @@ export const SetAngleLength = ({
<button <button
onClick={async () => { onClick={async () => {
if (!(transformInfos && ast)) return if (!(transformInfos && ast)) return
const { modifiedAst, valueUsedInTransform } = transformAstSketchLines({ const { valueUsedInTransform } = transformAstSketchLines({
ast: JSON.parse(JSON.stringify(ast)), ast: JSON.parse(JSON.stringify(ast)),
selectionRanges, selectionRanges,
transformInfos, transformInfos,

View File

@ -43,8 +43,8 @@ export const SetHorzDistance = ({
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => { useEffect(() => {
if (!ast) return if (!ast) return
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
@ -72,7 +72,10 @@ export const SetHorzDistance = ({
) )
const theTransforms = getTransformInfos( const theTransforms = getTransformInfos(
selectionRanges.slice(1), {
...selectionRanges,
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
},
ast, ast,
horOrVert horOrVert
) )
@ -127,7 +130,6 @@ export const SetHorzDistance = ({
sign, sign,
variableName variableName
) )
console.log(finalValue)
// transform again but forcing certain values // transform again but forcing certain values
const { modifiedAst: _modifiedAst } = const { modifiedAst: _modifiedAst } =
transformSecondarySketchLinesTagFirst({ transformSecondarySketchLinesTagFirst({

View File

@ -1,15 +1,21 @@
import { useStore, Range } from '../useStore' import { useStore, Selection, Selections } from '../useStore'
export function useSetCursor(sourceRange: Range) { export function useSetCursor(
sourceRange: Selection['range'],
type: Selection['type'] = 'default'
) {
const { setCursor, selectionRanges, isShiftDown } = useStore((s) => ({ const { setCursor, selectionRanges, isShiftDown } = useStore((s) => ({
setCursor: s.setCursor, setCursor: s.setCursor,
selectionRanges: s.selectionRanges, selectionRanges: s.selectionRanges,
isShiftDown: s.isShiftDown, isShiftDown: s.isShiftDown,
})) }))
return () => { return () => {
const ranges = isShiftDown const selections: Selections = {
? [...selectionRanges, sourceRange] ...selectionRanges,
: [sourceRange] codeBasedSelections: isShiftDown
setCursor(ranges) ? [...selectionRanges.codeBasedSelections, { range: sourceRange, type }]
: [{ range: sourceRange, type }],
}
setCursor(selections)
} }
} }

View File

@ -67,14 +67,17 @@ export function lineGeo({
sign, sign,
} = trigCalcs({ from, to }) } = trigCalcs({ from, to })
const lineEndLength = 0.25
// create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ry, rz] // create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ry, rz]
const lineBody = new BoxGeometry(Hypotenuse3d, 0.1, 0.1) const lineBody = new BoxGeometry(Hypotenuse3d - lineEndLength, 0.1, 0.1)
const __sign = to[0] === from[0] ? -1 : 1
lineBody.translate((__sign * lineEndLength) / 2, 0, 0)
lineBody.rotateY(ry) lineBody.rotateY(ry)
lineBody.rotateZ(rz) lineBody.rotateZ(rz)
lineBody.translate(centre[0], centre[1], centre[2]) lineBody.translate(centre[0], centre[1], centre[2])
// create line end points with CylinderGeometry at `to` // create line end points with CylinderGeometry at `to`
const lineEnd1 = new CylinderGeometry(0.05, 0.22, 0.25, 4) const lineEnd1 = new CylinderGeometry(0.05, 0.22, lineEndLength + 0.05, 4)
lineEnd1.translate(0, -0.1, 0) lineEnd1.translate(0, -0.1, 0)
lineEnd1.rotateY(Math.PI / 4) lineEnd1.rotateY(Math.PI / 4)
lineEnd1.rotateZ(rz) lineEnd1.rotateZ(rz)

View File

@ -15,7 +15,7 @@ import { internalFns } from './std/std'
import { BufferGeometry } from 'three' import { BufferGeometry } from 'three'
export type SourceRange = [number, number] export type SourceRange = [number, number]
export type PathToNode = [string | number, string][] export type PathToNode = [string | number, string][] // [pathKey, nodeType][]
export type Metadata = { export type Metadata = {
sourceRange: SourceRange sourceRange: SourceRange
pathToNode: PathToNode pathToNode: PathToNode

View File

@ -1,4 +1,4 @@
import { Range, TooTip } from '../useStore' import { Selection, TooTip } from '../useStore'
import { import {
Program, Program,
CallExpression, CallExpression,
@ -534,7 +534,7 @@ export function createBinaryExpressionWithUnary([left, right]: [
export function giveSketchFnCallTag( export function giveSketchFnCallTag(
ast: Program, ast: Program,
range: Range, range: Selection['range'],
tag?: string tag?: string
): { modifiedAst: Program; tag: string; isTagExisting: boolean } { ): { modifiedAst: Program; tag: string; isTagExisting: boolean } {
const { node: primaryCallExp } = getNodeFromPath<CallExpression>( const { node: primaryCallExp } = getNodeFromPath<CallExpression>(
@ -563,7 +563,7 @@ export function giveSketchFnCallTag(
export function moveValueIntoNewVariable( export function moveValueIntoNewVariable(
ast: Program, ast: Program,
programMemory: ProgramMemory, programMemory: ProgramMemory,
sourceRange: Range, sourceRange: Selection['range'],
variableName: string variableName: string
): { ): {
modifiedAst: Program modifiedAst: Program

View File

@ -1,5 +1,5 @@
import { PathToNode, ProgramMemory } from './executor' import { PathToNode, ProgramMemory } from './executor'
import { Range } from '../useStore' import { Selection } from '../useStore'
import { import {
BinaryExpression, BinaryExpression,
Program, Program,
@ -96,7 +96,7 @@ export function getNodeFromPathCurry(
function moreNodePathFromSourceRange( function moreNodePathFromSourceRange(
node: Value | ExpressionStatement | VariableDeclaration | ReturnStatement, node: Value | ExpressionStatement | VariableDeclaration | ReturnStatement,
sourceRange: Range, sourceRange: Selection['range'],
previousPath: PathToNode = [['body', '']] previousPath: PathToNode = [['body', '']]
): PathToNode { ): PathToNode {
const [start, end] = sourceRange const [start, end] = sourceRange
@ -239,7 +239,7 @@ function moreNodePathFromSourceRange(
export function getNodePathFromSourceRange( export function getNodePathFromSourceRange(
node: Program, node: Program,
sourceRange: Range, sourceRange: Selection['range'],
previousPath: PathToNode = [['body', '']] previousPath: PathToNode = [['body', '']]
): PathToNode { ): PathToNode {
const [start, end] = sourceRange const [start, end] = sourceRange
@ -269,7 +269,7 @@ export interface PrevVariable<T> {
export function findAllPreviousVariables( export function findAllPreviousVariables(
ast: Program, ast: Program,
programMemory: ProgramMemory, programMemory: ProgramMemory,
sourceRange: Range, sourceRange: Selection['range'],
type: 'number' | 'string' = 'number' type: 'number' | 'string' = 'number'
): { ): {
variables: PrevVariable<typeof type extends 'number' ? number : string>[] variables: PrevVariable<typeof type extends 'number' ? number : string>[]

View File

@ -64,7 +64,7 @@ export function getCoordsFromPaths(skGroup: SketchGroup, index = 0): Coords2d {
export function createFirstArg( export function createFirstArg(
sketchFn: TooTip, sketchFn: TooTip,
val: Value | [Value, Value], val: Value | [Value, Value] | [Value, Value, Value],
tag?: Value tag?: Value
): Value { ): Value {
if (!tag) { if (!tag) {
@ -84,6 +84,13 @@ export function createFirstArg(
return createObjectExpression({ angle: val[0], length: val[1], tag }) return createObjectExpression({ angle: val[0], length: val[1], tag })
if (['angledLineToX', 'angledLineToY'].includes(sketchFn)) if (['angledLineToX', 'angledLineToY'].includes(sketchFn))
return createObjectExpression({ angle: val[0], to: val[1], tag }) return createObjectExpression({ angle: val[0], to: val[1], tag })
if (['angledLineThatIntersects'].includes(sketchFn) && val[2])
return createObjectExpression({
angle: val[0],
offset: val[1],
intersectTag: val[2],
tag,
})
} else { } else {
if (['xLine', 'yLine'].includes(sketchFn)) if (['xLine', 'yLine'].includes(sketchFn))
return createObjectExpression({ length: val, tag }) return createObjectExpression({ length: val, tag })
@ -1678,7 +1685,7 @@ function getFirstArgValuesForXYLineFns(callExpression: CallExpression): {
const getAngledLineThatIntersects = ( const getAngledLineThatIntersects = (
callExp: CallExpression callExp: CallExpression
): { ): {
val: [Value, Value] val: [Value, Value, Value]
tag?: Value tag?: Value
} => { } => {
const firstArg = callExp.arguments[0] const firstArg = callExp.arguments[0]
@ -1688,15 +1695,18 @@ const getAngledLineThatIntersects = (
const offset = firstArg.properties.find( const offset = firstArg.properties.find(
(p) => p.key.name === 'offset' (p) => p.key.name === 'offset'
)?.value )?.value
if (angle && offset) { const intersectTag = firstArg.properties.find(
return { val: [angle, offset], tag } (p) => p.key.name === 'intersectTag'
)?.value
if (angle && offset && intersectTag) {
return { val: [angle, offset, intersectTag], tag }
} }
} }
throw new Error('expected ArrayExpression or ObjectExpression') throw new Error('expected ArrayExpression or ObjectExpression')
} }
export function getFirstArg(callExp: CallExpression): { export function getFirstArg(callExp: CallExpression): {
val: Value | [Value, Value] val: Value | [Value, Value] | [Value, Value, Value]
tag?: Value tag?: Value
} { } {
const name = callExp?.callee?.name const name = callExp?.callee?.name

View File

@ -9,6 +9,7 @@ import {
import { recast } from '../recast' import { recast } from '../recast'
import { initPromise } from '../rust' import { initPromise } from '../rust'
import { getSketchSegmentFromSourceRange } from './sketchConstraints' import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { Selection } from '../../useStore'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -26,23 +27,30 @@ function testingSwapSketchFnCall({
originalRange: [number, number] originalRange: [number, number]
} { } {
const startIndex = inputCode.indexOf(callToSwap) const startIndex = inputCode.indexOf(callToSwap)
const range: [number, number] = [startIndex, startIndex + callToSwap.length] const range: Selection = {
type: 'default',
range: [startIndex, startIndex + callToSwap.length],
}
const tokens = lexer(inputCode) const tokens = lexer(inputCode)
const ast = abstractSyntaxTree(tokens) const ast = abstractSyntaxTree(tokens)
const programMemory = executor(ast) const programMemory = executor(ast)
const transformInfos = getTransformInfos([range], ast, constraintType) const selections = {
codeBasedSelections: [range],
otherSelections: [],
}
const transformInfos = getTransformInfos(selections, ast, constraintType)
if (!transformInfos) throw new Error('nope') if (!transformInfos) throw new Error('nope')
const { modifiedAst } = transformAstSketchLines({ const { modifiedAst } = transformAstSketchLines({
ast, ast,
programMemory, programMemory,
selectionRanges: [range], selectionRanges: selections,
transformInfos, transformInfos,
referenceSegName: '', referenceSegName: '',
}) })
return { return {
newCode: recast(modifiedAst), newCode: recast(modifiedAst),
originalRange: range, originalRange: range.range,
} }
} }

View File

@ -1,16 +1,16 @@
import { getAngle } from '../../lib/utils' import { getAngle } from '../../lib/utils'
import { Range, TooTip, toolTips } from '../../useStore' import { Selection, TooTip, toolTips } from '../../useStore'
import { import {
Program, Program,
VariableDeclarator, VariableDeclarator,
CallExpression, CallExpression,
} from '../abstractSyntaxTree' } from '../abstractSyntaxTree'
import { SketchGroup } from '../executor' import { SketchGroup, SourceRange } from '../executor'
import { InternalFn } from './stdTypes' import { InternalFn } from './stdTypes'
export function getSketchSegmentFromSourceRange( export function getSketchSegmentFromSourceRange(
sketchGroup: SketchGroup, sketchGroup: SketchGroup,
[rangeStart, rangeEnd]: Range [rangeStart, rangeEnd]: SourceRange
): SketchGroup['value'][number] { ): SketchGroup['value'][number] {
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
if ( if (

View File

@ -9,7 +9,7 @@ import {
getConstraintLevelFromSourceRange, getConstraintLevelFromSourceRange,
} from './sketchcombos' } from './sketchcombos'
import { initPromise } from '../rust' import { initPromise } from '../rust'
import { TooTip } from '../../useStore' import { Selections, TooTip } from '../../useStore'
import { executor } from '../../lang/executor' import { executor } from '../../lang/executor'
import { recast } from '../../lang/recast' import { recast } from '../../lang/recast'
@ -80,6 +80,15 @@ function getConstraintTypeFromSourceHelper2(
return getConstraintType(arg, fnName) return getConstraintType(arg, fnName)
} }
function makeSelections(
codeBaseSelections: Selections['codeBasedSelections']
): Selections {
return {
codeBasedSelections: codeBaseSelections,
otherSelections: [],
}
}
describe('testing transformAstForSketchLines for equal length constraint', () => { describe('testing transformAstForSketchLines for equal length constraint', () => {
const inputScript = `const myVar = 3 const inputScript = `const myVar = 3
const myVar2 = 5 const myVar2 = 5
@ -189,25 +198,28 @@ const part001 = startSketchAt([0, 0])
show(part001)` show(part001)`
it('It should transform the ast', () => { it('It should transform the ast', () => {
const ast = abstractSyntaxTree(lexer(inputScript)) const ast = abstractSyntaxTree(lexer(inputScript))
const selectionRanges = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => ln.includes('//')) .filter((ln) => ln.includes('//'))
.map((ln) => { .map((ln) => {
const comment = ln.split('//')[1] const comment = ln.split('//')[1]
const start = inputScript.indexOf('//' + comment) - 7 const start = inputScript.indexOf('//' + comment) - 7
return [start, start] return {
}) as [number, number][] type: 'default',
range: [start, start],
}
})
const programMemory = executor(ast) const programMemory = executor(ast)
const transformInfos = getTransformInfos( const transformInfos = getTransformInfos(
selectionRanges.slice(1), makeSelections(selectionRanges.slice(1)),
ast, ast,
'equalLength' 'equalLength'
) )
const newAst = transformSecondarySketchLinesTagFirst({ const newAst = transformSecondarySketchLinesTagFirst({
ast, ast,
selectionRanges, selectionRanges: makeSelections(selectionRanges),
transformInfos, transformInfos,
programMemory, programMemory,
})?.modifiedAst })?.modifiedAst
@ -271,21 +283,28 @@ const part001 = startSketchAt([0, 0])
|> angledLineToY([301, myVar], %) // select for vertical constraint 10 |> angledLineToY([301, myVar], %) // select for vertical constraint 10
show(part001)` show(part001)`
const ast = abstractSyntaxTree(lexer(inputScript)) const ast = abstractSyntaxTree(lexer(inputScript))
const selectionRanges = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => ln.includes('// select for horizontal constraint')) .filter((ln) => ln.includes('// select for horizontal constraint'))
.map((ln) => { .map((ln) => {
const comment = ln.split('//')[1] const comment = ln.split('//')[1]
const start = inputScript.indexOf('//' + comment) - 7 const start = inputScript.indexOf('//' + comment) - 7
return [start, start] return {
}) as [number, number][] type: 'default',
range: [start, start],
}
})
const programMemory = executor(ast) const programMemory = executor(ast)
const transformInfos = getTransformInfos(selectionRanges, ast, 'horizontal') const transformInfos = getTransformInfos(
makeSelections(selectionRanges),
ast,
'horizontal'
)
const newAst = transformAstSketchLines({ const newAst = transformAstSketchLines({
ast, ast,
selectionRanges, selectionRanges: makeSelections(selectionRanges),
transformInfos, transformInfos,
programMemory, programMemory,
referenceSegName: '', referenceSegName: '',
@ -321,21 +340,28 @@ const part001 = startSketchAt([0, 0])
|> yLineTo(myVar, %) // select for vertical constraint 10 |> yLineTo(myVar, %) // select for vertical constraint 10
show(part001)` show(part001)`
const ast = abstractSyntaxTree(lexer(inputScript)) const ast = abstractSyntaxTree(lexer(inputScript))
const selectionRanges = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => ln.includes('// select for vertical constraint')) .filter((ln) => ln.includes('// select for vertical constraint'))
.map((ln) => { .map((ln) => {
const comment = ln.split('//')[1] const comment = ln.split('//')[1]
const start = inputScript.indexOf('//' + comment) - 7 const start = inputScript.indexOf('//' + comment) - 7
return [start, start] return {
}) as [number, number][] type: 'default',
range: [start, start],
}
})
const programMemory = executor(ast) const programMemory = executor(ast)
const transformInfos = getTransformInfos(selectionRanges, ast, 'vertical') const transformInfos = getTransformInfos(
makeSelections(selectionRanges),
ast,
'vertical'
)
const newAst = transformAstSketchLines({ const newAst = transformAstSketchLines({
ast, ast,
selectionRanges, selectionRanges: makeSelections(selectionRanges),
transformInfos, transformInfos,
programMemory, programMemory,
referenceSegName: '', referenceSegName: '',
@ -404,7 +430,7 @@ function helperThing(
constraint: ConstraintType constraint: ConstraintType
): string { ): string {
const ast = abstractSyntaxTree(lexer(inputScript)) const ast = abstractSyntaxTree(lexer(inputScript))
const selectionRanges = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => .filter((ln) =>
linesOfInterest.some((lineOfInterest) => ln.includes(lineOfInterest)) linesOfInterest.some((lineOfInterest) => ln.includes(lineOfInterest))
@ -412,19 +438,22 @@ function helperThing(
.map((ln) => { .map((ln) => {
const comment = ln.split('//')[1] const comment = ln.split('//')[1]
const start = inputScript.indexOf('//' + comment) - 7 const start = inputScript.indexOf('//' + comment) - 7
return [start, start] return {
}) as [number, number][] type: 'default',
range: [start, start],
}
})
const programMemory = executor(ast) const programMemory = executor(ast)
const transformInfos = getTransformInfos( const transformInfos = getTransformInfos(
selectionRanges.slice(1), makeSelections(selectionRanges.slice(1)),
ast, ast,
constraint constraint
) )
const newAst = transformSecondarySketchLinesTagFirst({ const newAst = transformSecondarySketchLinesTagFirst({
ast, ast,
selectionRanges, selectionRanges: makeSelections(selectionRanges),
transformInfos, transformInfos,
programMemory, programMemory,
})?.modifiedAst })?.modifiedAst

View File

@ -1,5 +1,5 @@
import { TransformCallback } from './stdTypes' import { TransformCallback } from './stdTypes'
import { Ranges, toolTips, TooTip, Range } from '../../useStore' import { Selections, toolTips, TooTip, Selection } from '../../useStore'
import { import {
BinaryPart, BinaryPart,
CallExpression, CallExpression,
@ -127,7 +127,6 @@ const xyLineSetLength =
: referenceSeg : referenceSeg
? segRef ? segRef
: args[0] : args[0]
// console.log({ lineVal, segRef, forceValueUsedInTransform, args })
return createCallWrapper(xOrY, lineVal, tag, getArgLiteralVal(args[0])) return createCallWrapper(xOrY, lineVal, tag, getArgLiteralVal(args[0]))
} }
@ -279,7 +278,6 @@ const setHorzVertDistanceForAngleLineCreateNode =
(forceValueUsedInTransform as BinaryPart) || (forceValueUsedInTransform as BinaryPart) ||
createLiteral(valueUsedInTransform), createLiteral(valueUsedInTransform),
]) ])
console.log('here or no?', binExp)
return createCallWrapper( return createCallWrapper(
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY', xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
[varValA, binExp], [varValA, binExp],
@ -1099,7 +1097,7 @@ export function getTransformInfo(
} }
export function getConstraintType( export function getConstraintType(
val: Value | [Value, Value], val: Value | [Value, Value] | [Value, Value, Value],
fnName: TooTip fnName: TooTip
): LineInputsType | null { ): LineInputsType | null {
// this function assumes that for two val sketch functions that one arg is locked down not both // this function assumes that for two val sketch functions that one arg is locked down not both
@ -1133,12 +1131,12 @@ export function getConstraintType(
} }
export function getTransformInfos( export function getTransformInfos(
selectionRanges: Ranges, selectionRanges: Selections,
ast: Program, ast: Program,
constraintType: ConstraintType constraintType: ConstraintType
): TransformInfo[] { ): TransformInfo[] {
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => (pathToNode) =>
@ -1160,13 +1158,13 @@ export function getTransformInfos(
} }
export function getRemoveConstraintsTransforms( export function getRemoveConstraintsTransforms(
selectionRanges: Ranges, selectionRanges: Selections,
ast: Program, ast: Program,
constraintType: ConstraintType constraintType: ConstraintType
): TransformInfo[] { ): TransformInfo[] {
// return () // return ()
const paths = selectionRanges.map((selectionRange) => const paths = selectionRanges.codeBasedSelections.map((selectionRange) =>
getNodePathFromSourceRange(ast, selectionRange) getNodePathFromSourceRange(ast, selectionRange.range)
) )
const nodes = paths.map( const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node (pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
@ -1190,7 +1188,7 @@ export function transformSecondarySketchLinesTagFirst({
forceValueUsedInTransform, forceValueUsedInTransform,
}: { }: {
ast: Program ast: Program
selectionRanges: Ranges selectionRanges: Selections
transformInfos: TransformInfo[] transformInfos: TransformInfo[]
programMemory: ProgramMemory programMemory: ProgramMemory
forceSegName?: string forceSegName?: string
@ -1204,7 +1202,7 @@ export function transformSecondarySketchLinesTagFirst({
} }
} { } {
// let node = JSON.parse(JSON.stringify(ast)) // let node = JSON.parse(JSON.stringify(ast))
const primarySelection = selectionRanges[0] const primarySelection = selectionRanges.codeBasedSelections[0].range
const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag( const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(
ast, ast,
@ -1215,7 +1213,10 @@ export function transformSecondarySketchLinesTagFirst({
return { return {
...transformAstSketchLines({ ...transformAstSketchLines({
ast: modifiedAst, ast: modifiedAst,
selectionRanges: selectionRanges.slice(1), selectionRanges: {
...selectionRanges,
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
},
referencedSegmentRange: primarySelection, referencedSegmentRange: primarySelection,
transformInfos, transformInfos,
programMemory, programMemory,
@ -1239,18 +1240,18 @@ export function transformAstSketchLines({
referencedSegmentRange, referencedSegmentRange,
}: { }: {
ast: Program ast: Program
selectionRanges: Ranges selectionRanges: Selections
transformInfos: TransformInfo[] transformInfos: TransformInfo[]
programMemory: ProgramMemory programMemory: ProgramMemory
referenceSegName: string referenceSegName: string
forceValueUsedInTransform?: Value forceValueUsedInTransform?: Value
referencedSegmentRange?: Range referencedSegmentRange?: Selection['range']
}): { modifiedAst: Program; valueUsedInTransform?: number } { }): { modifiedAst: Program; valueUsedInTransform?: number } {
// deep clone since we are mutating in a loop, of which any could fail // deep clone since we are mutating in a loop, of which any could fail
let node = JSON.parse(JSON.stringify(ast)) let node = JSON.parse(JSON.stringify(ast))
let _valueUsedInTransform // TODO should this be an array? let _valueUsedInTransform // TODO should this be an array?
selectionRanges.forEach((range, index) => { selectionRanges.codeBasedSelections.forEach(({ range }, index) => {
const callBack = transformInfos?.[index].createNode const callBack = transformInfos?.[index].createNode
const transformTo = transformInfos?.[index].tooltip const transformTo = transformInfos?.[index].tooltip
if (!callBack || !transformTo) throw new Error('no callback helper') if (!callBack || !transformTo) throw new Error('no callback helper')
@ -1343,7 +1344,7 @@ function getArgLiteralVal(arg: Value): number {
} }
export function getConstraintLevelFromSourceRange( export function getConstraintLevelFromSourceRange(
cursorRange: Range, cursorRange: Selection['range'],
ast: Program ast: Program
): 'free' | 'partial' | 'full' { ): 'free' | 'partial' | 'full' {
const { node: sketchFnExp } = getNodeFromPath<CallExpression>( const { node: sketchFnExp } = getNodeFromPath<CallExpression>(

View File

@ -1,5 +1,5 @@
import { isOverlap, roundOff } from './utils' import { isOverlap, roundOff } from './utils'
import { Range } from '../useStore' import { SourceRange } from '../lang/executor'
describe('testing isOverlapping', () => { describe('testing isOverlapping', () => {
testBothOrders([0, 3], [3, 10]) testBothOrders([0, 3], [3, 10])
@ -11,7 +11,7 @@ describe('testing isOverlapping', () => {
testBothOrders([0, 5], [-2, -1], false) testBothOrders([0, 5], [-2, -1], false)
}) })
function testBothOrders(a: Range, b: Range, result = true) { function testBothOrders(a: SourceRange, b: SourceRange, result = true) {
it(`test is overlapping ${a} ${b}`, () => { it(`test is overlapping ${a} ${b}`, () => {
expect(isOverlap(a, b)).toBe(result) expect(isOverlap(a, b)).toBe(result)
expect(isOverlap(b, a)).toBe(result) expect(isOverlap(b, a)).toBe(result)

View File

@ -1,6 +1,6 @@
import { Range } from '../useStore' import { SourceRange } from '../lang/executor'
export function isOverlap(a: Range, b: Range) { export function isOverlap(a: SourceRange, b: SourceRange) {
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a] const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]
const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]] const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]]
return lastOfFirst >= firstOfSecond return lastOfFirst >= firstOfSecond

View File

@ -3,13 +3,25 @@ import { persist } from 'zustand/middleware'
import { addLineHighlight, EditorView } from './editor/highlightextension' import { addLineHighlight, EditorView } from './editor/highlightextension'
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree' import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
import { getNodeFromPath } from './lang/queryAst' import { getNodeFromPath } from './lang/queryAst'
import { ProgramMemory, Position, PathToNode, Rotation } from './lang/executor' import {
ProgramMemory,
Position,
PathToNode,
Rotation,
SourceRange,
} from './lang/executor'
import { recast } from './lang/recast' import { recast } from './lang/recast'
import { asyncLexer } from './lang/tokeniser' import { asyncLexer } from './lang/tokeniser'
import { EditorSelection } from '@codemirror/state' import { EditorSelection } from '@codemirror/state'
export type Range = [number, number] export type Selection = {
export type Ranges = Range[] type: 'default' | 'line-end' | 'line-mid'
range: SourceRange
}
export type Selections = {
otherSelections: ('y-axis' | 'x-axis' | 'z-axis')[]
codeBasedSelections: Selection[]
}
export type TooTip = export type TooTip =
| 'lineTo' | 'lineTo'
| 'line' | 'line'
@ -80,10 +92,11 @@ interface StoreState {
editorView: EditorView | null editorView: EditorView | null
setEditorView: (editorView: EditorView) => void setEditorView: (editorView: EditorView) => void
highlightRange: [number, number] highlightRange: [number, number]
setHighlightRange: (range: Range) => void setHighlightRange: (range: Selection['range']) => void
setCursor: (selections: Ranges) => void setCursor: (selections: Selections) => void
selectionRanges: Ranges selectionRanges: Selections
setSelectionRanges: (range: Ranges) => void selectionRangeTypeMap: { [key: number]: Selection['type'] }
setSelectionRanges: (range: Selections) => void
guiMode: GuiModes guiMode: GuiModes
lastGuiMode: GuiModes lastGuiMode: GuiModes
setGuiMode: (guiMode: GuiModes) => void setGuiMode: (guiMode: GuiModes) => void
@ -118,27 +131,39 @@ export const useStore = create<StoreState>()(
set({ editorView }) set({ editorView })
}, },
highlightRange: [0, 0], highlightRange: [0, 0],
setHighlightRange: (highlightRange) => { setHighlightRange: (selection) => {
set({ highlightRange }) set({ highlightRange: selection })
const editorView = get().editorView const editorView = get().editorView
if (editorView) { if (editorView) {
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) }) editorView.dispatch({ effects: addLineHighlight.of(selection) })
} }
}, },
setCursor: (ranges: Ranges) => { setCursor: (selections) => {
const { editorView } = get() const { editorView } = get()
if (!editorView) return if (!editorView) return
editorView.dispatch({ const ranges: ReturnType<typeof EditorSelection.cursor>[] = []
selection: EditorSelection.create( const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {}
[...ranges.map(([start, end]) => EditorSelection.cursor(end))], set({ selectionRangeTypeMap })
ranges.length - 1 selections.codeBasedSelections.forEach(({ range, type }) => {
), ranges.push(EditorSelection.cursor(range[1]))
selectionRangeTypeMap[range[1]] = type
})
setTimeout(() => {
editorView.dispatch({
selection: EditorSelection.create(
ranges,
selections.codeBasedSelections.length - 1
),
})
}) })
}, },
selectionRanges: [[0, 0]], selectionRangeTypeMap: {},
setSelectionRanges: (selectionRanges) => { selectionRanges: {
set({ selectionRanges }) otherSelections: [],
codeBasedSelections: [],
}, },
setSelectionRanges: (selectionRanges) =>
set({ selectionRanges, selectionRangeTypeMap: {} }),
guiMode: { mode: 'default' }, guiMode: { mode: 'default' },
lastGuiMode: { mode: 'default' }, lastGuiMode: { mode: 'default' },
setGuiMode: (guiMode) => { setGuiMode: (guiMode) => {
@ -162,7 +187,6 @@ export const useStore = create<StoreState>()(
}, },
updateAst: async (ast, focusPath) => { updateAst: async (ast, focusPath) => {
const newCode = recast(ast) const newCode = recast(ast)
console.log('running update Ast', ast)
const astWithUpdatedSource = abstractSyntaxTree( const astWithUpdatedSource = abstractSyntaxTree(
await asyncLexer(newCode) await asyncLexer(newCode)
) )
@ -173,7 +197,15 @@ export const useStore = create<StoreState>()(
const { start, end } = node const { start, end } = node
if (!start || !end) return if (!start || !end) return
setTimeout(() => { setTimeout(() => {
get().setCursor([[start, end]]) get().setCursor({
codeBasedSelections: [
{
type: 'default',
range: [start, end],
},
],
otherSelections: [],
})
}) })
} }
}, },