diff --git a/src/actions/SetBreakpoint.ts b/src/actions/SetBreakpoint.ts index 82db116266..0a224d1363 100644 --- a/src/actions/SetBreakpoint.ts +++ b/src/actions/SetBreakpoint.ts @@ -14,6 +14,7 @@ import { Breakpoint, } from "vscode"; import displayPendingEditDecorations from "../util/editDisplayUtils"; +import { isLineSelectionType } from "../util/selectionType"; function getBreakpoints(uri: Uri, range: Range) { return debug.breakpoints.filter( @@ -46,16 +47,20 @@ export default class SetBreakpoint implements Action { const toRemove: Breakpoint[] = []; targets.forEach((target) => { + let range: Range = target.selection.selection; + // The action preference give us line content but line breakpoints are registered on character 0 + if (isLineSelectionType(target.selectionType)) { + range = range.with(range.start.with(undefined, 0), undefined); + } const uri = target.selection.editor.document.uri; - const existing = getBreakpoints(uri, target.selection.selection); + const existing = getBreakpoints(uri, range); if (existing.length > 0) { toRemove.push(...existing); } else { - toAdd.push( - new SourceBreakpoint( - new Location(uri, target.selection.selection.start) - ) - ); + if (isLineSelectionType(target.selectionType)) { + range = range.with(undefined, range.end.with(undefined, 0)); + } + toAdd.push(new SourceBreakpoint(new Location(uri, range))); } }); diff --git a/src/test/suite/breakpoints.test.ts b/src/test/suite/breakpoints.test.ts new file mode 100644 index 0000000000..490c5e4e0e --- /dev/null +++ b/src/test/suite/breakpoints.test.ts @@ -0,0 +1,160 @@ +import * as assert from "assert"; +import * as vscode from "vscode"; +import * as sinon from "sinon"; +import { getCursorlessApi } from "../../util/getExtensionApi"; +import { openNewEditor } from "../openNewEditor"; + +suite("breakpoints", async function () { + this.timeout("100s"); + this.retries(3); + + setup(() => { + removeBreakpoints(); + }); + + teardown(() => { + sinon.restore(); + }); + + suiteTeardown(() => { + removeBreakpoints(); + }); + + test("breakpoint harp add", breakpointHarpAdd); + test("breakpoint token harp add", breakpointTokenHarpAdd); + test("breakpoint harp remove", breakpointHarpRemove); + test("breakpoint token harp remove", breakpointTokenHarpRemove); +}); + +async function breakpointHarpAdd() { + const graph = (await getCursorlessApi()).graph!; + await openNewEditor(" hello"); + await graph.hatTokenMap.addDecorations(); + + await vscode.commands.executeCommand( + "cursorless.command", + "breakpoint harp", + "setBreakpoint", + [ + { + type: "primitive", + mark: { + type: "decoratedSymbol", + symbolColor: "default", + character: "h", + }, + }, + ] + ); + + const breakpoints = vscode.debug.breakpoints; + assert.deepStrictEqual(breakpoints.length, 1); + assert.ok(breakpoints[0] instanceof vscode.SourceBreakpoint); + const breakpoint = breakpoints[0]; + assert.ok(breakpoint.location.range.isEqual(new vscode.Range(0, 0, 0, 0))); +} + +async function breakpointTokenHarpAdd() { + const graph = (await getCursorlessApi()).graph!; + await openNewEditor(" hello"); + await graph.hatTokenMap.addDecorations(); + + await vscode.commands.executeCommand( + "cursorless.command", + "breakpoint token harp", + "setBreakpoint", + [ + { + type: "primitive", + selectionType: "token", + mark: { + type: "decoratedSymbol", + symbolColor: "default", + character: "h", + }, + }, + ] + ); + + const breakpoints = vscode.debug.breakpoints; + assert.deepStrictEqual(breakpoints.length, 1); + assert.ok(breakpoints[0] instanceof vscode.SourceBreakpoint); + const breakpoint = breakpoints[0]; + assert.ok(breakpoint.location.range.isEqual(new vscode.Range(0, 2, 0, 7))); +} + +async function breakpointHarpRemove() { + const graph = (await getCursorlessApi()).graph!; + const editor = await openNewEditor(" hello"); + await graph.hatTokenMap.addDecorations(); + + vscode.debug.addBreakpoints([ + new vscode.SourceBreakpoint( + new vscode.Location(editor.document.uri, new vscode.Range(0, 0, 0, 0)) + ), + ]); + + assert.deepStrictEqual(vscode.debug.breakpoints.length, 1); + + await vscode.commands.executeCommand( + "cursorless.command", + "breakpoint harp", + "setBreakpoint", + [ + { + type: "primitive", + mark: { + type: "decoratedSymbol", + symbolColor: "default", + character: "h", + }, + }, + ] + ); + + assert.deepStrictEqual(vscode.debug.breakpoints.length, 0); +} + +async function breakpointTokenHarpRemove() { + const graph = (await getCursorlessApi()).graph!; + const editor = await openNewEditor(" hello"); + await graph.hatTokenMap.addDecorations(); + + vscode.debug.addBreakpoints([ + new vscode.SourceBreakpoint( + new vscode.Location(editor.document.uri, new vscode.Range(0, 0, 0, 0)) + ), + new vscode.SourceBreakpoint( + new vscode.Location(editor.document.uri, new vscode.Range(0, 2, 0, 7)) + ), + ]); + + assert.deepStrictEqual(vscode.debug.breakpoints.length, 2); + + await vscode.commands.executeCommand( + "cursorless.command", + "breakpoint token harp", + "setBreakpoint", + [ + { + type: "primitive", + selectionType: "token", + mark: { + type: "decoratedSymbol", + symbolColor: "default", + character: "h", + }, + }, + ] + ); + + const breakpoints = vscode.debug.breakpoints; + assert.deepStrictEqual(breakpoints.length, 1); + assert.ok(breakpoints[0] instanceof vscode.SourceBreakpoint); + const breakpoint = breakpoints[0]; + assert.ok(breakpoint.location.range.isEqual(new vscode.Range(0, 0, 0, 0))); +} + +function removeBreakpoints() { + vscode.debug.removeBreakpoints(vscode.debug.breakpoints); +}