refactor selections (#876)
* migrate selection types * extract selection event into selections.ts * move code-mirror selection functions into selections.ts * move more selection logit out of code mirror and engine connection * add selection functions pure * tidy up naming * write a novel about how selections work * final comments
This commit is contained in:
@ -36,11 +36,8 @@ import { applyConstraintAngleBetween } from './Toolbar/SetAngleBetween'
|
||||
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import { pathMapToSelections } from 'lang/util'
|
||||
import {
|
||||
dispatchCodeMirrorCursor,
|
||||
setCodeMirrorCursor,
|
||||
useStore,
|
||||
} from 'useStore'
|
||||
import { useStore } from 'useStore'
|
||||
import { handleSelectionBatch, handleSelectionWithShift } from 'lib/selections'
|
||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
@ -270,25 +267,37 @@ export const ModelingMachineProvider = ({
|
||||
// I've found this the best way to deal with the editor without causing an infinite loop
|
||||
// and really we want the editor to be in charge of cursor positions and for `selectionRanges` mirror it
|
||||
// because we want to respect the user manually placing the cursor too.
|
||||
const selectionRangeTypeMap = setCodeMirrorCursor({
|
||||
codeSelection: setSelections.selection,
|
||||
currestSelections: selectionRanges,
|
||||
editorView,
|
||||
isShiftDown,
|
||||
})
|
||||
return {
|
||||
selectionRangeTypeMap,
|
||||
|
||||
// for more details on how selections see `src/lib/selections.ts`.
|
||||
const { codeMirrorSelection, selectionRangeTypeMap } =
|
||||
handleSelectionWithShift({
|
||||
codeSelection: setSelections.selection,
|
||||
currestSelections: selectionRanges,
|
||||
isShiftDown,
|
||||
})
|
||||
if (codeMirrorSelection) {
|
||||
setTimeout(() => {
|
||||
editorView.dispatch({
|
||||
selection: codeMirrorSelection,
|
||||
})
|
||||
})
|
||||
}
|
||||
return { selectionRangeTypeMap }
|
||||
}
|
||||
// This DOES NOT set the `selectionRanges` in xstate context
|
||||
// same as comment above
|
||||
const { selectionRangeTypeMap } = dispatchCodeMirrorCursor({
|
||||
selections: setSelections.selection,
|
||||
editorView,
|
||||
})
|
||||
return {
|
||||
selectionRangeTypeMap,
|
||||
const { codeMirrorSelection, selectionRangeTypeMap } =
|
||||
handleSelectionBatch({
|
||||
selections: setSelections.selection,
|
||||
})
|
||||
if (codeMirrorSelection) {
|
||||
setTimeout(() => {
|
||||
editorView.dispatch({
|
||||
selection: codeMirrorSelection,
|
||||
})
|
||||
})
|
||||
}
|
||||
return { selectionRangeTypeMap }
|
||||
}),
|
||||
},
|
||||
guards: {
|
||||
|
||||
@ -13,7 +13,8 @@ import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { useMemo } from 'react'
|
||||
import { linter, lintGutter } from '@codemirror/lint'
|
||||
import { Selections, useStore } from 'useStore'
|
||||
import { useStore } from 'useStore'
|
||||
import { processCodeMirrorRanges } from 'lib/selections'
|
||||
import { LanguageServerClient } from 'editor/lsp'
|
||||
import kclLanguage from 'editor/lsp/language'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
@ -132,74 +133,17 @@ export const TextEditor = ({
|
||||
if (!editorView) {
|
||||
setEditorView(viewUpdate.view)
|
||||
}
|
||||
const ranges = viewUpdate.state.selection.ranges
|
||||
|
||||
const isChange =
|
||||
ranges.length !== selectionRanges.codeBasedSelections.length ||
|
||||
ranges.some(({ from, to }, i) => {
|
||||
return (
|
||||
from !== selectionRanges.codeBasedSelections[i].range[0] ||
|
||||
to !== selectionRanges.codeBasedSelections[i].range[1]
|
||||
)
|
||||
})
|
||||
|
||||
if (!isChange) return
|
||||
const codeBasedSelections: Selections['codeBasedSelections'] = ranges.map(
|
||||
({ from, to }) => {
|
||||
if (selectionRangeTypeMap[to]) {
|
||||
return {
|
||||
type: selectionRangeTypeMap[to],
|
||||
range: [from, to],
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'default',
|
||||
range: [from, to],
|
||||
}
|
||||
}
|
||||
)
|
||||
const idBasedSelections = codeBasedSelections
|
||||
.map(({ type, range }) => {
|
||||
// TODO #868: loops over all artifacts will become inefficient at a large scale
|
||||
const entriesWithOverlap = Object.entries(
|
||||
engineCommandManager.artifactMap || {}
|
||||
).filter(([_, artifact]) => {
|
||||
return artifact.range && isOverlap(artifact.range, range)
|
||||
? artifact
|
||||
: false
|
||||
})
|
||||
if (entriesWithOverlap.length) {
|
||||
const [id, artifact] = entriesWithOverlap?.[0]
|
||||
return {
|
||||
type,
|
||||
id:
|
||||
type === 'line-end' &&
|
||||
artifact.type === 'result' &&
|
||||
artifact.headVertexId
|
||||
? artifact.headVertexId
|
||||
: id,
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as any
|
||||
|
||||
engineCommandManager.cusorsSelected({
|
||||
otherSelections: [],
|
||||
idBasedSelections,
|
||||
const eventInfo = processCodeMirrorRanges({
|
||||
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
||||
selectionRanges,
|
||||
selectionRangeTypeMap,
|
||||
})
|
||||
if (!eventInfo) return
|
||||
|
||||
selectionRanges &&
|
||||
send({
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'mirrorCodeMirrorSelections',
|
||||
selection: {
|
||||
...selectionRanges,
|
||||
codeBasedSelections,
|
||||
},
|
||||
},
|
||||
})
|
||||
send(eventInfo.modelingEvent)
|
||||
eventInfo.engineEvents.forEach((event) =>
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
)
|
||||
}
|
||||
|
||||
const editorExtensions = useMemo(() => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, ProgramMemory, Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
@ -10,7 +11,6 @@ import {
|
||||
transformAstSketchLines,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { Selections } from 'useStore'
|
||||
|
||||
export function horzVertInfo(
|
||||
selectionRanges: Selections,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
@ -14,7 +14,7 @@ import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { Selections } from 'useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Selections, toolTips } from '../../useStore'
|
||||
import { toolTips } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
|
||||
Reference in New Issue
Block a user