diff --git a/data/playground/go/branch.go b/data/playground/go/branch.go new file mode 100644 index 0000000000..c1fb90e5be --- /dev/null +++ b/data/playground/go/branch.go @@ -0,0 +1,57 @@ +package p + +func switches(x any) { + switch x { + case 1: + // bar + case 2: + x = nil + case "s": + case 4, "t": + x = 7 + // qux + case 5: + // foo + fallthrough + default: + panic("x") + } + switch x := x.(type) { + case int: + x++ + case string, struct{}: + println(x) + default: + panic(x) + } + switch { + case x == 1: + panic("one") + case false: + // unreachable + } +} + +func ifElseChains(x int) { + if y := 0; x == 1 { + // foo + } else if z:=0; x == 2 { + x-- + x-- + x-- + } else if z:=0; x == 2 { + x-- + x-- + x-- + } else if x == 3 { + x++ + } else if x == 3 { + x++ + } else { + x *= 2 + } + + if x == 4{ + x++ + } +} diff --git a/packages/common/src/types/Position.ts b/packages/common/src/types/Position.ts index e4b423218b..5751530929 100644 --- a/packages/common/src/types/Position.ts +++ b/packages/common/src/types/Position.ts @@ -1,4 +1,4 @@ -import { Range } from ".."; +import { Range, TextDocument } from ".."; export class Position { /** @@ -151,3 +151,19 @@ export class Position { return this.concise(); } } + +/** + * adjustPosition returns a new position that is offset by the given amount. + * It corrects line and character positions to remain valid in doc. + * @param doc The document + * @param pos The position to adjust + * @param by The amount to adjust by + * @returns The adjusted position + */ +export function adjustPosition( + doc: TextDocument, + pos: Position, + by: number, +): Position { + return doc.positionAt(doc.offsetAt(pos) + by); +} diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 4d02f96acf..a5077ffbde 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -1,4 +1,4 @@ -import { Range } from "@cursorless/common"; +import { Range, adjustPosition } from "@cursorless/common"; import { z } from "zod"; import { makeRangeFromPositions } from "../../util/nodeSelectors"; import { MutableQueryCapture } from "./QueryCapture"; @@ -139,6 +139,26 @@ class ShrinkToMatch extends QueryPredicateOperator { } } +/** + * A predicate operator that modifies the range of the match by trimming trailing whitespace, + * similar to the javascript trimEnd function. + */ +class TrimEnd extends QueryPredicateOperator { + name = "trim-end!" as const; + schema = z.tuple([q.node]); + + run(nodeInfo: MutableQueryCapture) { + const { document, range } = nodeInfo; + const text = document.getText(range); + const whitespaceLength = text.length - text.trimEnd().length; + nodeInfo.range = new Range( + range.start, + adjustPosition(document, range.end, -whitespaceLength), + ); + return true; + } +} + /** * Indicates that it is ok for multiple captures to have the same domain but * different targets. For example, if we have the query `(#allow-multiple! @@ -197,6 +217,7 @@ class InsertionDelimiter extends QueryPredicateOperator { export const queryPredicateOperators = [ new Log(), new NotType(), + new TrimEnd(), new NotParentType(), new IsNthChild(), new ChildRange(), diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch.yml new file mode 100644 index 0000000000..cf5fba1475 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: change branch + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} " + selections: + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch2.yml new file mode 100644 index 0000000000..a15eafb967 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch2.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: change branch + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: " else {\n\tx--\n}" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch3.yml new file mode 100644 index 0000000000..ea3cda0318 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranch3.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: change branch + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 7} + active: {line: 2, character: 7} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchAir.yml new file mode 100644 index 0000000000..fabb4747d6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchAir.yml @@ -0,0 +1,36 @@ +languageId: go +command: + version: 6 + spokenForm: change branch air + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} + marks: + default.a: + start: {line: 2, character: 0} + end: {line: 2, character: 4} +finalState: + documentContents: |- + switch x { + case 1: + + } + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchCap.yml new file mode 100644 index 0000000000..4e5a03bf54 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchCap.yml @@ -0,0 +1,35 @@ +languageId: go +command: + version: 6 + spokenForm: change branch cap + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: c} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + case 2: + } + selections: + - anchor: {line: 2, character: 7} + active: {line: 2, character: 7} + marks: + default.c: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x { + + case 2: + } + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchCap2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchCap2.yml new file mode 100644 index 0000000000..a28dcadf4d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchCap2.yml @@ -0,0 +1,37 @@ +languageId: go +command: + version: 6 + spokenForm: change branch cap + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: c} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.c: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x := x.(type) { + + default: + panic(x) + } + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchDrum.yml new file mode 100644 index 0000000000..34e90746b6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeBranchDrum.yml @@ -0,0 +1,36 @@ +languageId: go +command: + version: 6 + spokenForm: change branch drum + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: d} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.d: + start: {line: 2, character: 0} + end: {line: 2, character: 7} +finalState: + documentContents: |- + switch x := x.(type) { + case int: + + } + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranch.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranch.yml new file mode 100644 index 0000000000..1b2006169a --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranch.yml @@ -0,0 +1,27 @@ +languageId: go +command: + version: 6 + spokenForm: change every branch + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: " " + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranch2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranch2.yml new file mode 100644 index 0000000000..26a2679316 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranch2.yml @@ -0,0 +1,27 @@ +languageId: go +command: + version: 6 + spokenForm: change every branch + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + marks: {} +finalState: + documentContents: " " + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranchEach.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranchEach.yml new file mode 100644 index 0000000000..65a4b3d688 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranchEach.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: change every branch each + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + modifiers: + - type: everyScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.e: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x := x.(type) { + + + } + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranchSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranchSun.yml new file mode 100644 index 0000000000..8eb778e9c1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/changeEveryBranchSun.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: change every branch sun + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: s} + modifiers: + - type: everyScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + default: + panic(x) + } + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} + marks: + default.s: + start: {line: 0, character: 0} + end: {line: 0, character: 6} +finalState: + documentContents: |- + switch x { + + + } + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch.yml new file mode 100644 index 0000000000..4fdf970565 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: chuck branch + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch2.yml new file mode 100644 index 0000000000..ddf79a28ba --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch2.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: chuck branch + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 7} + active: {line: 2, character: 7} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch3.yml new file mode 100644 index 0000000000..36fcaa68a6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/chuckBranch3.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: chuck branch + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 4, character: 7} + active: {line: 4, character: 7} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n}" + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch.yml new file mode 100644 index 0000000000..ef81c6d112 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch + action: + name: insertCopyAfter + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 6} + active: {line: 2, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch2.yml new file mode 100644 index 0000000000..1e0676338e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch2.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch + action: + name: insertCopyAfter + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 4, character: 4} + active: {line: 4, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch3.yml new file mode 100644 index 0000000000..a6c2babb38 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranch3.yml @@ -0,0 +1,23 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch + action: + name: insertCopyAfter + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n}" + selections: + - anchor: {line: 4, character: 4} + active: {line: 4, character: 4} + marks: {} +finalState: + documentContents: "if x {\n\tx++\n} else if y {\n\ty++\n} else {\n\tx--\n} else {\n\tx--\n}" + selections: + - anchor: {line: 6, character: 4} + active: {line: 6, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchCap.yml new file mode 100644 index 0000000000..98f6a9a136 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchCap.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch cap + action: + name: insertCopyAfter + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: c} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} + marks: + default.c: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x { + case 1: + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 5, character: 1} + active: {line: 5, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchDrum.yml new file mode 100644 index 0000000000..6582fb2687 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchDrum.yml @@ -0,0 +1,39 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch drum + action: + name: insertCopyAfter + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: d} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.d: + start: {line: 2, character: 0} + end: {line: 2, character: 7} +finalState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + default: + panic(x) + } + selections: + - anchor: {line: 4, character: 8} + active: {line: 4, character: 8} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchEach.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchEach.yml new file mode 100644 index 0000000000..5090a93547 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchEach.yml @@ -0,0 +1,39 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch each + action: + name: insertCopyAfter + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} + marks: + default.e: + start: {line: 2, character: 0} + end: {line: 2, character: 4} +finalState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + case 2: + panic(x) + } + selections: + - anchor: {line: 6, character: 1} + active: {line: 6, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchEach2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchEach2.yml new file mode 100644 index 0000000000..d6c7f60e5e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/cloneBranchEach2.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: clone branch each + action: + name: insertCopyAfter + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.e: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x := x.(type) { + case int: + case int: + default: + panic(x) + } + selections: + - anchor: {line: 3, character: 8} + active: {line: 3, character: 8} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/drinkBranchCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/drinkBranchCap.yml new file mode 100644 index 0000000000..9d91dc8071 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/drinkBranchCap.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: drink branch cap + action: + name: editNewLineBefore + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: c} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} + marks: + default.c: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x { + + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/drinkBranchEach.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/drinkBranchEach.yml new file mode 100644 index 0000000000..c736445112 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/drinkBranchEach.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: drink branch each + action: + name: editNewLineBefore + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.e: + start: {line: 1, character: 0} + end: {line: 1, character: 4} +finalState: + documentContents: |- + switch x := x.(type) { + + case int: + default: + panic(x) + } + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/pourBranchDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/pourBranchDrum.yml new file mode 100644 index 0000000000..e6f99e0f42 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/pourBranchDrum.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: pour branch drum + action: + name: editNewLineAfter + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: d} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + } + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: + default.d: + start: {line: 2, character: 0} + end: {line: 2, character: 7} +finalState: + documentContents: |- + switch x := x.(type) { + case int: + default: + panic(x) + + } + selections: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/pourBranchEach.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/pourBranchEach.yml new file mode 100644 index 0000000000..ceea055e5f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/go/pourBranchEach.yml @@ -0,0 +1,38 @@ +languageId: go +command: + version: 6 + spokenForm: pour branch each + action: + name: editNewLineAfter + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + modifiers: + - type: containingScope + scopeType: {type: branch} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + } + selections: + - anchor: {line: 4, character: 1} + active: {line: 4, character: 1} + marks: + default.e: + start: {line: 2, character: 0} + end: {line: 2, character: 4} +finalState: + documentContents: |- + switch x { + case 1: + case 2: + panic(x) + + } + selections: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} diff --git a/queries/go.scm b/queries/go.scm index 1f71bde6f5..09bd1707db 100644 --- a/queries/go.scm +++ b/queries/go.scm @@ -222,3 +222,55 @@ . ) ) @anonymousFunction @namedFunction + +;; switch-based branch + +( + [ + (default_case) + (expression_case) + (type_case) + ] @branch + (#trim-end! @branch) + (#insertion-delimiter! @branch "\n") +) + +[ + (type_switch_statement) + (expression_switch_statement) +] @branch.iteration + +;; if-else-based branch + +;; first if in an if-else chain +( + (if_statement + consequence: (block) @branch.end.endOf + ) @_if @branch.start.startOf + (#not-parent-type? @_if if_statement) + (#insertion-delimiter! @branch.start.startOf " ") +) + +;; internal if in an if-else chain +(if_statement + "else" @branch.start + alternative: (if_statement + consequence: (block) @branch.end + ) + (#insertion-delimiter! @branch.start " ") +) + +;; final else branch in an if-else chain +( + (if_statement + "else" @branch.start.startOf + alternative: (block) + ) @branch.end.endOf + (#insertion-delimiter! @branch.start.startOf " ") +) + +;; iteration scope is always the outermost if statement +( + (if_statement) @_if @branch.iteration + (#not-parent-type? @_if if_statement) +)