diff --git a/packages/cursorless-engine/src/languages/python.ts b/packages/cursorless-engine/src/languages/python.ts index 32ef092e49..309d5b79fa 100644 --- a/packages/cursorless-engine/src/languages/python.ts +++ b/packages/cursorless-engine/src/languages/python.ts @@ -1,24 +1,17 @@ -import { Selection } from "@cursorless/common"; +import { Selection, SimpleScopeTypeType } from "@cursorless/common"; import type { SyntaxNode } from "web-tree-sitter"; -import { SimpleScopeTypeType } from "@cursorless/common"; import { NodeFinder, NodeMatcherAlternative } from "../typings/Types"; import { argumentNodeFinder, patternFinder } from "../util/nodeFinders"; import { argumentMatcher, cascadingMatcher, - conditionMatcher, createPatternMatchers, - leadingMatcher, matcher, - patternMatcher, - trailingMatcher, } from "../util/nodeMatchers"; import { argumentSelectionExtractor, childRangeSelector, } from "../util/nodeSelectors"; -import { branchMatcher } from "./branchMatcher"; -import { ternaryBranchMatcher } from "./ternaryBranchMatcher"; export const getTypeNode = (node: SyntaxNode) => node.children.find((child) => child.type === "type") ?? null; @@ -55,38 +48,10 @@ const nodeMatchers: Partial< argumentSelectionExtractor(), ), ), - collectionKey: trailingMatcher(["pair[key]"], [":"]), - ifStatement: "if_statement", - anonymousFunction: "lambda?.lambda", - functionCall: "call", - functionCallee: "call[function]", - condition: cascadingMatcher( - conditionMatcher("*[condition]"), - - // Comprehensions and match statements - leadingMatcher(["*.if_clause![0]"], ["if"]), - - // Ternaries - patternMatcher("conditional_expression[1]"), - ), argumentOrParameter: cascadingMatcher( argumentMatcher("parameters", "argument_list"), matcher(patternFinder("call.generator_expression!"), childRangeSelector()), ), - branch: cascadingMatcher( - patternMatcher("case_clause"), - branchMatcher("if_statement", ["else_clause", "elif_clause"]), - branchMatcher("while_statement", ["else_clause"]), - branchMatcher("for_statement", ["else_clause"]), - branchMatcher("try_statement", [ - "except_clause", - "finally_clause", - "else_clause", - "except_group_clause", - ]), - ternaryBranchMatcher("conditional_expression", [0, 2]), - ), - ["private.switchStatementSubject"]: "match_statement[subject]", }; export default createPatternMatchers(nodeMatchers); diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml index 8b7ec1fde0..d4da457933 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml @@ -18,4 +18,10 @@ initialState: - anchor: {line: 1, character: 4} active: {line: 1, character: 4} marks: {} -thrownError: {name: NoContainingScopeError} +finalState: + documentContents: |- + for in bbb: + pass + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName3.yml new file mode 100644 index 0000000000..a93bdc3e89 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName3.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + for name in value: + pass + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + for in value: + pass + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml index d2888bfc7f..f1213907d6 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml @@ -18,4 +18,10 @@ initialState: - anchor: {line: 1, character: 4} active: {line: 1, character: 4} marks: {} -thrownError: {name: NoContainingScopeError} +finalState: + documentContents: |- + for aaa in : + pass + selections: + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue5.yml new file mode 100644 index 0000000000..7428b89163 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue5.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + for name in value: + pass + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + for name in : + pass + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/clearCondition6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/clearCondition6.yml index cc3dcd6289..9b8a88e416 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/clearCondition6.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/clearCondition6.yml @@ -21,8 +21,8 @@ initialState: finalState: documentContents: |- match 0: - case a if : + case : pass selections: - - anchor: {line: 1, character: 14} - active: {line: 1, character: 14} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/ditchCondition2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/ditchCondition2.yml index 6865adae6a..ec34cfa59e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/ditchCondition2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/ditchCondition2.yml @@ -21,7 +21,7 @@ initialState: finalState: documentContents: |- match 0: - case a : + case: pass selections: - anchor: {line: 2, character: 8} diff --git a/queries/python.scm b/queries/python.scm index 9703847594..5b2d3dfec7 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -230,3 +230,169 @@ "(" @value.iteration.start.endOf @name.iteration.start.endOf @type.iteration.start.endOf ")" @value.iteration.end.startOf @name.iteration.end.startOf @type.iteration.end.startOf ) + +;;!! if true: pass +;;! ^^^^^^^^^^^^^ +(if_statement) @ifStatement + +;;!! foo() +;;! ^^^^^ +(call) @functionCall + +;;!! foo() +;;! ^^^^^ +(call + function: (_) @functionCallee +) @_.domain + +;;!! lambda _: pass +;;! ^^^^^^^^^^^^^^ +(lambda) @anonymousFunction + +;;!! match value: +;;! ^^^^^ +(match_statement + subject: (_) @private.switchStatementSubject +) @_.domain + +;;!! { "value": 0 } +;;! ^^^^^^^ +;;! xxxxxxxxx +(pair + key: (_) @collectionKey @collectionKey.trailing.start.endOf + value: (_) @collectionKey.trailing.end.startOf +) @_.domain + +;;!! if True: +;;! ^^^^ +;;!! elif True: +;;! ^^^^ +;;!! while True: +;;! ^^^^ +(_ + condition: (_) @condition +) @_.domain + +;;!! match value: +;;! ^^^^^ +(case_clause + pattern: (_) @condition.start + guard: (_)? @condition.end +) @_.domain + +;;!! case 0: pass +;;! ^^^^^^^^^^^^ +(case_clause) @branch + +(match_statement) @branch.iteration @condition.iteration + +;;!! 1 if True else 0 +;;! ^^^^ +;;! ---------------- +( + (conditional_expression + "if" + . + (_) @condition + ) @_.domain +) + +;;!! 1 if True else 0 +;;! ^ +( + (conditional_expression + (_) @branch + . + "if" + ) +) + +;;!! 1 if True else 0 +;;! ^ +( + (conditional_expression + "else" + . + (_) @branch + ) +) + +(conditional_expression) @branch.iteration + +;;!! [aaa for aaa in bbb if ccc] +;;!! (aaa for aaa in bbb if ccc) +;;!! {aaa for aaa in bbb if ccc} +;;! ^^^ +;;! xxxxxx +;;! --------------------------- +;;!! {aaa: aaa for aaa in bbb if ccc} +;;! ^^^ +;;! xxxxxx +;;! -------------------------------- +(_ + (if_clause + "if" + (_) @condition + ) @_.removal + (#not-parent-type? @_.removal case_clause) +) @_.domain + +;;!! for name in value: +;;! ^^^^ ^^^^^ +;;! ------------------ +(for_statement + left: (_) @name + right: (_) @value +) @_.domain + +;;!! if True: pass +;;! ^^^^^^^^^^^^^ +(if_statement + "if" @branch.start + consequence: (_) @branch.end +) + +;;!! elif True: pass +;;! ^^^^^^^^^^^^^^^ +(elif_clause) @branch + +;;!! else: pass +;;! ^^^^^^^^^^ +(else_clause) @branch + +(if_statement) @branch.iteration + +;;!! try: pass +;;! ^^^^^^^^^ +(try_statement + "try" @branch.start + body: (_) @branch.end +) + +;;!! except: pass +;;! ^^^^^^^^^^^^ +(except_clause) @branch + +;;!! finally: pass +;;! ^^^^^^^^^^^^^ +(finally_clause) @branch + +(try_statement) @branch.iteration + +;;!! while True: pass +;;! ^^^^^^^^^^^^^^^^ +(while_statement + "while" @branch.start + body: (_) @branch.end +) + +(while_statement) @branch.iteration + +;;!! for aaa in bbb: pass +;;! ^^^^^^^^^^^^^^^^^^^^ +(for_statement + "for" @branch.start + body: (_) @branch.end +) + +(for_statement) @branch.iteration