fix out of range error (#4931)

* fix out of range error

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* remove console logs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add a regression test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2025-01-06 17:18:58 -08:00
committed by GitHub
parent d9c2dd376e
commit 7e54f08778
4 changed files with 112 additions and 18 deletions

View File

@ -1323,3 +1323,85 @@ test.describe(`Sketching with offset planes`, () => {
})
})
})
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4891
test.describe(`Click based selection don't brick the app when clicked out of range after format using cache`, () => {
test(`Can select a line that reformmed after entering sketch mode`, async ({
context,
page,
scene,
toolbar,
editor,
homePage,
}) => {
// We seed the scene with a single offset plane
await context.addInitScript(() => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([3.14, 3.14], %)
|> arcTo({
end = [4, 2],
interior = [1, 2]
}, %)
`
)
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await test.step(`format the code`, async () => {
// doesn't contain condensed version
await editor.expectEditor.not.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
// click the code to enter sketch mode
await page.getByText(`arcTo`).click()
// Format the code.
await page.locator('#code-pane button:first-child').click()
await page.locator('button:has-text("Format code")').click()
})
await test.step(`Ensure the code reformatted`, async () => {
await editor.expectEditor.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
})
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
await test.step('Ensure we can hover the arc', async () => {
await arcHover()
// Check that the code is highlighted
await editor.expectState({
activeLines: ["sketch001=startSketchOn('XZ')"],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
})
})
await test.step('reset the selection', async () => {
// Move the mouse out of the way
await page.mouse.move(655, 337)
await editor.expectState({
activeLines: ["sketch001=startSketchOn('XZ')"],
diagnostics: [],
highlightedCode: '',
})
})
await test.step('Ensure we can click the arc', async () => {
await arcClick()
// Check that the code is highlighted
await editor.expectState({
activeLines: [],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
})
})
})
})

View File

@ -97,6 +97,7 @@ export const KclEditorPane = () => {
if (!editorIsMounted || !lastSelectionEvent || !editorManager.editorView) {
return
}
editorManager.editorView.dispatch({
selection: lastSelectionEvent.codeMirrorSelection,
annotations: [modelingMachineEvent, Transaction.addToHistory.of(false)],

View File

@ -129,6 +129,21 @@ export default class EditorManager {
this._isShiftDown = isShiftDown
}
private selectionsWithSafeEnds(
selection: Array<Selection['codeRef']['range']>
): Array<[number, number]> {
if (!this._editorView) {
return selection.map((s): [number, number] => {
return [s[0], s[1]]
})
}
return selection.map((s): [number, number] => {
const safeEnd = Math.min(s[1], this._editorView?.state.doc.length || s[1])
return [s[0], safeEnd]
})
}
set selectionRanges(selectionRanges: Selections) {
this._selectionRanges = selectionRanges
}
@ -154,14 +169,9 @@ export default class EditorManager {
}
setHighlightRange(range: Array<Selection['codeRef']['range']>): void {
this._highlightRange = range.map((s): [number, number] => {
return [s[0], s[1]]
})
const selectionsWithSafeEnds = this.selectionsWithSafeEnds(range)
const selectionsWithSafeEnds = range.map((s): [number, number] => {
const safeEnd = Math.min(s[1], this._editorView?.state.doc.length || s[1])
return [s[0], safeEnd]
})
this._highlightRange = selectionsWithSafeEnds
if (this._editorView) {
this._editorView.dispatch({
@ -302,20 +312,20 @@ export default class EditorManager {
}
let codeBasedSelections = []
for (const selection of selections.graphSelections) {
codeBasedSelections.push(
EditorSelection.range(
selection.codeRef.range[0],
selection.codeRef.range[1]
const safeEnd = Math.min(
selection.codeRef.range[1],
this._editorView?.state.doc.length || selection.codeRef.range[1]
)
codeBasedSelections.push(
EditorSelection.range(selection.codeRef.range[0], safeEnd)
)
}
codeBasedSelections.push(
EditorSelection.cursor(
selections.graphSelections[selections.graphSelections.length - 1]
.codeRef.range[1]
)
)
const end =
selections.graphSelections[selections.graphSelections.length - 1].codeRef
.range[1]
const safeEnd = Math.min(end, this._editorView?.state.doc.length || end)
codeBasedSelections.push(EditorSelection.cursor(safeEnd))
if (!this._editorView) {
return

View File

@ -323,7 +323,8 @@ export function handleSelectionBatch({
resetAndSetEngineEntitySelectionCmds(selectionToEngine)
selections.graphSelections.forEach(({ codeRef }) => {
if (codeRef.range?.[1]) {
ranges.push(EditorSelection.cursor(codeRef.range[1]))
const safeEnd = Math.min(codeRef.range[1], codeManager.code.length)
ranges.push(EditorSelection.cursor(safeEnd))
}
})
if (ranges.length)