From 071a1720d9d4c1ef1c867852ecbe8f5db7b69ed7 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 2 Jul 2023 23:11:26 +0200 Subject: [PATCH 1/5] Make token removal range less aggressive --- .../TokenInsertionRemovalBehavior.ts | 83 +++++++------------ .../languages/rust/chuckFunkNameFine.yml | 2 +- .../recorded/languages/rust/chuckName2.yml | 2 +- .../fixtures/recorded/positions/chuckAir.yml | 2 +- .../recorded/scopes/token/chuckToken.yml | 22 +++++ .../recorded/scopes/token/chuckToken10.yml | 22 +++++ .../recorded/scopes/token/chuckToken11.yml | 22 +++++ .../recorded/scopes/token/chuckToken12.yml | 22 +++++ .../recorded/scopes/token/chuckToken13.yml | 22 +++++ .../recorded/scopes/token/chuckToken14.yml | 22 +++++ .../recorded/scopes/token/chuckToken2.yml | 22 +++++ .../recorded/scopes/token/chuckToken3.yml | 22 +++++ .../recorded/scopes/token/chuckToken4.yml | 22 +++++ .../recorded/scopes/token/chuckToken5.yml | 22 +++++ .../recorded/scopes/token/chuckToken6.yml | 22 +++++ .../recorded/scopes/token/chuckToken7.yml | 22 +++++ .../recorded/scopes/token/chuckToken8.yml | 22 +++++ .../recorded/scopes/token/chuckToken9.yml | 22 +++++ 18 files changed, 339 insertions(+), 58 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken10.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken11.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken12.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken13.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken14.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken6.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken7.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken8.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken9.yml diff --git a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts index 5b10c9cfdf..e20ac1a194 100644 --- a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts +++ b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts @@ -1,9 +1,11 @@ -import { Range, TextDocument, TextEditor } from "@cursorless/common"; -import { tokenize } from "../../../tokenizer"; +import { Range, TextEditor } from "@cursorless/common"; import type { Target } from "../../../typings/target.types"; import { expandToFullLine } from "../../../util/rangeUtils"; import { PlainTarget } from "../../targets"; +const leftDelimiters = ['"', "'", "(", "[", "{", "<"]; +const rightDelimiters = ['"', "'", ")", "]", "}", ">", ",", ";", ":"]; + export function getTokenLeadingDelimiterTarget( target: Target, ): Target | undefined { @@ -83,25 +85,21 @@ export function getTokenRemovalRange(target: Target): Range { } if (!trailingWhitespaceRange.isEmpty) { - const candidateRemovalRange = contentRange.union(trailingWhitespaceRange); - - if (!mergesTokens(editor, contentRange, candidateRemovalRange)) { - // If there is trailing whitespace and it doesn't result in tokens getting - // merged, then we remove it - return candidateRemovalRange; + if ( + !leadingWhitespaceRange.isEmpty || + contentRange.start.character === 0 || + leftDelimiters.includes(getLeadingCharacter(editor, contentRange)) + ) { + return contentRange.union(trailingWhitespaceRange); } } - if ( - !leadingWhitespaceRange.isEmpty && - leadingWhitespaceRange.start.character !== 0 - ) { - const candidateRemovalRange = leadingWhitespaceRange.union(contentRange); - - if (!mergesTokens(editor, contentRange, candidateRemovalRange)) { - // If there is leading whitespace that is not indentation and it doesn't - // result in tokens getting merged, then we remove it - return candidateRemovalRange; + if (!leadingWhitespaceRange.isEmpty) { + if ( + contentRange.end.isEqual(fullLineRange.end) || + rightDelimiters.includes(getTrailingCharacter(editor, contentRange)) + ) { + return contentRange.union(leadingWhitespaceRange); } } @@ -109,43 +107,18 @@ export function getTokenRemovalRange(target: Target): Range { return contentRange; } -/** Returns true if removal range causes tokens to merge */ -function mergesTokens( - editor: TextEditor, - contentRange: Range, - removalRange: Range, -) { - const { document } = editor; - const fullRange = expandToFullLine(editor, contentRange); - const fullText = document.getText(fullRange); - const fullTextOffset = document.offsetAt(fullRange.start); - - const numTokensContentRangeRemoved = calculateNumberOfTokensAfterRemoval( - document, - fullText, - fullTextOffset, - contentRange, - ); - - const numTokensRemovalRangeRemoved = calculateNumberOfTokensAfterRemoval( - document, - fullText, - fullTextOffset, - removalRange, - ); - - return numTokensContentRangeRemoved !== numTokensRemovalRangeRemoved; +function getLeadingCharacter(editor: TextEditor, contentRange: Range): string { + const { start } = contentRange; + const line = editor.document.lineAt(start); + return start.isAfter(line.range.start) + ? editor.document.getText(new Range(start.translate(undefined, -1), start)) + : ""; } -function calculateNumberOfTokensAfterRemoval( - document: TextDocument, - fullText: string, - fullTextOffset: number, - removalRange: Range, -): number { - const startIndex = document.offsetAt(removalRange.start) - fullTextOffset; - const endIndex = document.offsetAt(removalRange.end) - fullTextOffset; - const modifiedText = fullText.slice(0, startIndex) + fullText.slice(endIndex); - const tokens = tokenize(modifiedText, document.languageId, (m) => m); - return tokens.length; +function getTrailingCharacter(editor: TextEditor, contentRange: Range): string { + const { end } = contentRange; + const line = editor.document.lineAt(end); + return end.isBefore(line.range.end) + ? editor.document.getText(new Range(end.translate(undefined, 1), end)) + : ""; } diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckFunkNameFine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckFunkNameFine.yml index 132e5347eb..24b84fdfa3 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckFunkNameFine.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckFunkNameFine.yml @@ -21,7 +21,7 @@ initialState: end: {line: 0, character: 2} finalState: documentContents: |- - fn() { + fn () { println!("Hello, world!"); } selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckName2.yml index 98af96ec89..a8664e71eb 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/rust/chuckName2.yml @@ -20,7 +20,7 @@ initialState: marks: {} finalState: documentContents: | - fn() -> Result<(), Error> { + fn () -> Result<(), Error> { } selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAir.yml index f088b03804..51ea8f9b0e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAir.yml @@ -24,7 +24,7 @@ finalState: "hello " " hello" " hello " - return.b + return .b selections: - anchor: {line: 4, character: 0} active: {line: 4, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken.yml new file mode 100644 index 0000000000..fb085b07df --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: foo = bar.baz + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 10} + marks: {} +finalState: + documentContents: foo = baz + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken10.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken10.yml new file mode 100644 index 0000000000..64424a2d13 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken10.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: "\"a b c\"" + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} + marks: {} +finalState: + documentContents: "\"a b\"" + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken11.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken11.yml new file mode 100644 index 0000000000..ef967915d8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken11.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: a.b c + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + marks: {} +finalState: + documentContents: a. c + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken12.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken12.yml new file mode 100644 index 0000000000..11a0bc31f3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken12.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: a! -b + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + marks: {} +finalState: + documentContents: a -b + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken13.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken13.yml new file mode 100644 index 0000000000..c3ebdc352e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken13.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: a b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken14.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken14.yml new file mode 100644 index 0000000000..85fe5c4d45 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken14.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: a b + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + marks: {} +finalState: + documentContents: a + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken2.yml new file mode 100644 index 0000000000..98c2002cd7 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken2.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: "\"foo-bar bongo-bazman\"" + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 21} + marks: {} +finalState: + documentContents: "\"foo-bar\"" + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken3.yml new file mode 100644 index 0000000000..4ce757f1b3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken3.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: hello there, + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + marks: {} +finalState: + documentContents: hello, + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken4.yml new file mode 100644 index 0000000000..5cef2e2af3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken4.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: "\"foo bar\" baz-bongo" + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 14} + marks: {} +finalState: + documentContents: "\"foo bar\" bongo" + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken5.yml new file mode 100644 index 0000000000..d93f9087b2 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken5.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: foo = bar + baz; + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 15} + marks: {} +finalState: + documentContents: foo = bar; + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken6.yml new file mode 100644 index 0000000000..ed4a92436e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken6.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: " hello " + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken7.yml new file mode 100644 index 0000000000..b60af0901c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken7.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: "\" a b c \"" + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + marks: {} +finalState: + documentContents: "\" b c \"" + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken8.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken8.yml new file mode 100644 index 0000000000..357bf0ad9e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken8.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: "\" a b c \"" + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + marks: {} +finalState: + documentContents: "\" a b \"" + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken9.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken9.yml new file mode 100644 index 0000000000..8fd7378fc6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckToken9.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck token + action: {name: remove} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: "\"a b c\"" + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: "\"b c\"" + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} From e46ff0b2279754d313d2957feeb2d8f2d64d0b79 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 2 Jul 2023 23:16:25 +0200 Subject: [PATCH 2/5] update comment --- .../TokenInsertionRemovalBehavior.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts index e20ac1a194..23f267d211 100644 --- a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts +++ b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts @@ -55,10 +55,9 @@ export function getTokenTrailingDelimiterTarget( } /** - * Constructs a removal range for the given target that will clean up a json - * whitespace on one side unless it will cause two tokens to be merged. This - * removal range is designed to be used with things that should clean themselves - * up as if they're a range of tokens. + * Constructs a removal range for the given target that will clean up a + * whitespace on one side. This removal range is designed to be used with things + * that should clean themselves up as if they're a range of tokens. * @param target The target to get the token removal range for * @returns The removal range for the given target */ From 790a476400de4276e496b5b21f7e48ea666b4a10 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 2 Jul 2023 23:20:59 +0200 Subject: [PATCH 3/5] cleanup --- .../TokenInsertionRemovalBehavior.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts index 23f267d211..4a793408fb 100644 --- a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts +++ b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts @@ -3,8 +3,8 @@ import type { Target } from "../../../typings/target.types"; import { expandToFullLine } from "../../../util/rangeUtils"; import { PlainTarget } from "../../targets"; -const leftDelimiters = ['"', "'", "(", "[", "{", "<"]; -const rightDelimiters = ['"', "'", ")", "]", "}", ">", ",", ";", ":"]; +const leadingDelimiters = ['"', "'", "(", "[", "{", "<"]; +const trailingDelimiters = ['"', "'", ")", "]", "}", ">", ",", ";", ":"]; export function getTokenLeadingDelimiterTarget( target: Target, @@ -83,20 +83,24 @@ export function getTokenRemovalRange(target: Target): Range { return fullLineRange; } + // Use trailing range if: There is a leading range OR there is no leading + // content OR there is an approved leading delimiter character if (!trailingWhitespaceRange.isEmpty) { if ( !leadingWhitespaceRange.isEmpty || - contentRange.start.character === 0 || - leftDelimiters.includes(getLeadingCharacter(editor, contentRange)) + contentRange.start.isEqual(fullLineRange.start) || + leadingDelimiters.includes(getLeadingCharacter(editor, contentRange)) ) { return contentRange.union(trailingWhitespaceRange); } } + // Use leading range if: There is no trailing content OR there is an approved + // trailing delimiter character if (!leadingWhitespaceRange.isEmpty) { if ( contentRange.end.isEqual(fullLineRange.end) || - rightDelimiters.includes(getTrailingCharacter(editor, contentRange)) + trailingDelimiters.includes(getTrailingCharacter(editor, contentRange)) ) { return contentRange.union(leadingWhitespaceRange); } From 038f8c14b6e90333ed8e3f9ccf8c47611a456025 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:50:08 +0100 Subject: [PATCH 4/5] Improve doc string --- .../TokenInsertionRemovalBehavior.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts index 4a793408fb..004f8fe1da 100644 --- a/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts +++ b/packages/cursorless-engine/src/processTargets/targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior.ts @@ -55,9 +55,27 @@ export function getTokenTrailingDelimiterTarget( } /** - * Constructs a removal range for the given target that will clean up a - * whitespace on one side. This removal range is designed to be used with things - * that should clean themselves up as if they're a range of tokens. + * Constructs a removal range for the given target that may remove whitespace on + * one side. This removal range is designed to be used with things that should + * clean themselves up as if they're a range of tokens. + * + * We determine whether to include adjacent whitespace based on the following + * rules: + * + * - If we would just be leaving a line with whitespace on it, we delete the + * whitespace + * - Otherwise, if there is trailing whitespace, we include it if any of the + * following is true: + * - there is leading whitespace, OR + * - we are at start of line, OR + * - there is an approved leading delimiter character (eg `(`, `[`, etc). + * - Otherwise, if there is leading whitespace, we include it if any of the + * following is true: + * - we are at end of line, OR + * - there is an approved trailing delimiter character (eg `)`, `]`, `:`, `;`, + * etc). + * - Otherwise, we don't include any adjacent whitespace + * * @param target The target to get the token removal range for * @returns The removal range for the given target */ From 08d0abd44f7c523bbc3da64c9dfedeaa34855cbb Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 6 Jul 2023 19:19:53 +0100 Subject: [PATCH 5/5] Add testcase --- .../recorded/scopes/token/chuckBat.yml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckBat.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckBat.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckBat.yml new file mode 100644 index 0000000000..80eac0bffb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scopes/token/chuckBat.yml @@ -0,0 +1,23 @@ +languageId: plaintext +command: + version: 5 + spokenForm: chuck bat + action: {name: remove} + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: b} + usePrePhraseSnapshot: true +initialState: + documentContents: "aaa bbb: ccc" + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + marks: + default.b: + start: {line: 0, character: 4} + end: {line: 0, character: 7} +finalState: + documentContents: "aaa: ccc" + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8}