From 09c28c81cd4efd80e51fc2d607838a9c9d582537 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:04:29 +0000 Subject: [PATCH] Add "name" / "value" support for Python `with` (#2162) ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- .../common/src/scopeSupportFacets/python.ts | 4 + .../scopeSupportFacetInfos.ts | 20 ++++ .../scopeSupportFacets.types.ts | 4 + .../python/name.resource.iteration.scope | 19 ++++ .../scopes/python/name.resource.scope | 23 +++++ .../scopes/python/name.resource2.scope | 72 ++++++++++++++ .../scopes/python/name.resource3.scope | 75 +++++++++++++++ .../python/value.resource.iteration.scope | 19 ++++ .../python/value.resource.iteration2.scope | 36 +++++++ .../scopes/python/value.resource.scope | 23 +++++ .../scopes/python/value.resource2.scope | 69 +++++++++++++ .../scopes/python/value.resource3.scope | 23 +++++ .../scopes/python/value.resource4.scope | 72 ++++++++++++++ .../scopes/python/value.resource5.scope | 75 +++++++++++++++ queries/python.scm | 96 ++++++++++++++++++- 15 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.iteration.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource2.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource3.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration2.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource2.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource3.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource4.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource5.scope diff --git a/packages/common/src/scopeSupportFacets/python.ts b/packages/common/src/scopeSupportFacets/python.ts index 4c6d25572e..1e1b4fd791 100644 --- a/packages/common/src/scopeSupportFacets/python.ts +++ b/packages/common/src/scopeSupportFacets/python.ts @@ -9,7 +9,11 @@ const { supported, supportedLegacy, notApplicable } = ScopeSupportFacetLevel; export const pythonScopeSupport: LanguageScopeSupportFacetMap = { "name.foreach": supported, + "name.resource": supported, + "name.resource.iteration": supported, "value.foreach": supported, + "value.resource": supported, + "value.resource.iteration": supported, "argument.actual": supportedLegacy, "argument.actual.iteration": supportedLegacy, diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index ccb8dc057f..d47086df2c 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -239,6 +239,16 @@ export const scopeSupportFacetInfos: Record< description: "Name (LHS) of a field in a class / interface", scopeType: "name", }, + "name.resource": { + description: "Name in a 'with' / 'use' / 'using' statement", + scopeType: "name", + }, + "name.resource.iteration": { + description: + "Iteration scope for names in a 'with' / 'use' / 'using' statement", + scopeType: "name", + isIteration: true, + }, "key.attribute": { description: "Key (LHS) of an attribute eg in an xml element", @@ -289,6 +299,16 @@ export const scopeSupportFacetInfos: Record< description: "Value (RHS) of a field in a class / interface", scopeType: "value", }, + "value.resource": { + description: "Value of a 'with' / 'use' / 'using' statement", + scopeType: "value", + }, + "value.resource.iteration": { + description: + "Iteration scope for values in a 'with' / 'use' / 'using' statement", + scopeType: "value", + isIteration: true, + }, "type.assignment": { description: "Type of variable in an assignment", diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index 2e052384b1..3adfe5c1ca 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -63,6 +63,8 @@ const scopeSupportFacets = [ "name.function", "name.class", "name.field", + "name.resource", + "name.resource.iteration", "key.attribute", "key.mapPair", @@ -76,6 +78,8 @@ const scopeSupportFacets = [ "value.return", "value.return.lambda", "value.field", + "value.resource", + "value.resource.iteration", "type.assignment", "type.formalParameter", diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.iteration.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.iteration.scope new file mode 100644 index 0000000000..f8eb5aaed8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.iteration.scope @@ -0,0 +1,19 @@ +with aaa, bbb as ccc: + pass +--- + +[#1 Range] = 0:5-0:20 +0| with aaa, bbb as ccc: + >---------------< + +[#1 Domain] = 0:0-1:8 +0| with aaa, bbb as ccc: + >--------------------- +1| pass + --------< + + +[#2 Range] = +[#2 Domain] = 1:4-1:8 +1| pass + >----< diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.scope new file mode 100644 index 0000000000..17d8834137 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource.scope @@ -0,0 +1,23 @@ +with aaa as bbb: + pass +--- + +[Content] = 0:12-0:15 +0| with aaa as bbb: + >---< + +[Removal] = 0:8-0:15 +0| with aaa as bbb: + >-------< + +[Leading delimiter] = 0:8-0:12 +0| with aaa as bbb: + >----< + +[Domain] = 0:0-1:8 +0| with aaa as bbb: + >---------------- +1| pass + --------< + +[Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource2.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource2.scope new file mode 100644 index 0000000000..814fedcfa4 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource2.scope @@ -0,0 +1,72 @@ +with aaa, bbb as ccc: + pass +--- + +[#1.1 Content] = 0:5-0:8 +0| with aaa, bbb as ccc: + >---< + +[#1.1 Removal] = 0:5-0:10 +0| with aaa, bbb as ccc: + >-----< + +[#1.1 Trailing delimiter] = 0:8-0:10 +0| with aaa, bbb as ccc: + >--< + +[#1.1 Insertion delimiter] = " " + +[#1.2 Content] = 0:17-0:20 +0| with aaa, bbb as ccc: + >---< + +[#1.2 Removal] = 0:13-0:20 +0| with aaa, bbb as ccc: + >-------< + +[#1.2 Leading delimiter] = 0:13-0:17 +0| with aaa, bbb as ccc: + >----< + +[#1.2 Insertion delimiter] = " " + +[#1 Domain] = 0:0-1:8 +0| with aaa, bbb as ccc: + >--------------------- +1| pass + --------< + + +[#2 Content] = +[#2 Domain] = 0:5-0:8 +0| with aaa, bbb as ccc: + >---< + +[#2 Removal] = 0:5-0:10 +0| with aaa, bbb as ccc: + >-----< + +[#2 Trailing delimiter] = 0:8-0:10 +0| with aaa, bbb as ccc: + >--< + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 0:17-0:20 +0| with aaa, bbb as ccc: + >---< + +[#3 Removal] = 0:13-0:20 +0| with aaa, bbb as ccc: + >-------< + +[#3 Leading delimiter] = 0:13-0:17 +0| with aaa, bbb as ccc: + >----< + +[#3 Domain] = 0:10-0:20 +0| with aaa, bbb as ccc: + >----------< + +[#3 Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource3.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource3.scope new file mode 100644 index 0000000000..6b09252dac --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/name.resource3.scope @@ -0,0 +1,75 @@ +with aaa as bbb, ccc as ddd: + pass +--- + +[#1.1 Content] = 0:12-0:15 +0| with aaa as bbb, ccc as ddd: + >---< + +[#1.1 Removal] = 0:8-0:15 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#1.1 Leading delimiter] = 0:8-0:12 +0| with aaa as bbb, ccc as ddd: + >----< + +[#1.1 Insertion delimiter] = " " + +[#1.2 Content] = 0:24-0:27 +0| with aaa as bbb, ccc as ddd: + >---< + +[#1.2 Removal] = 0:20-0:27 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#1.2 Leading delimiter] = 0:20-0:24 +0| with aaa as bbb, ccc as ddd: + >----< + +[#1.2 Insertion delimiter] = " " + +[#1 Domain] = 0:0-1:8 +0| with aaa as bbb, ccc as ddd: + >---------------------------- +1| pass + --------< + + +[#2 Content] = 0:12-0:15 +0| with aaa as bbb, ccc as ddd: + >---< + +[#2 Removal] = 0:8-0:15 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#2 Leading delimiter] = 0:8-0:12 +0| with aaa as bbb, ccc as ddd: + >----< + +[#2 Domain] = 0:5-0:15 +0| with aaa as bbb, ccc as ddd: + >----------< + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 0:24-0:27 +0| with aaa as bbb, ccc as ddd: + >---< + +[#3 Removal] = 0:20-0:27 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#3 Leading delimiter] = 0:20-0:24 +0| with aaa as bbb, ccc as ddd: + >----< + +[#3 Domain] = 0:17-0:27 +0| with aaa as bbb, ccc as ddd: + >----------< + +[#3 Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration.scope new file mode 100644 index 0000000000..f8eb5aaed8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration.scope @@ -0,0 +1,19 @@ +with aaa, bbb as ccc: + pass +--- + +[#1 Range] = 0:5-0:20 +0| with aaa, bbb as ccc: + >---------------< + +[#1 Domain] = 0:0-1:8 +0| with aaa, bbb as ccc: + >--------------------- +1| pass + --------< + + +[#2 Range] = +[#2 Domain] = 1:4-1:8 +1| pass + >----< diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration2.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration2.scope new file mode 100644 index 0000000000..3231341249 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.iteration2.scope @@ -0,0 +1,36 @@ +with aaa: + pass + +bbb = ccc +ddd = eee +--- + +[#1 Range] = 0:5-0:8 +0| with aaa: + >---< + +[#1 Domain] = 0:0-1:8 +0| with aaa: + >--------- +1| pass + --------< + + +[#2 Range] = +[#2 Domain] = 0:0-4:9 +0| with aaa: + >--------- +1| pass + -------- +2| + +3| bbb = ccc + --------- +4| ddd = eee + ---------< + + +[#3 Range] = +[#3 Domain] = 1:4-1:8 +1| pass + >----< diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.scope new file mode 100644 index 0000000000..a5dd036e23 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource.scope @@ -0,0 +1,23 @@ +with aaa: + pass +--- + +[Content] = 0:5-0:8 +0| with aaa: + >---< + +[Removal] = 0:4-0:8 +0| with aaa: + >----< + +[Leading delimiter] = 0:4-0:5 +0| with aaa: + >-< + +[Domain] = 0:0-1:8 +0| with aaa: + >--------- +1| pass + --------< + +[Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource2.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource2.scope new file mode 100644 index 0000000000..7a4e1450a3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource2.scope @@ -0,0 +1,69 @@ +with aaa, bbb: + pass +--- + +[#1.1 Content] = 0:5-0:8 +0| with aaa, bbb: + >---< + +[#1.1 Removal] = 0:5-0:10 +0| with aaa, bbb: + >-----< + +[#1.1 Trailing delimiter] = 0:8-0:10 +0| with aaa, bbb: + >--< + +[#1.1 Insertion delimiter] = " " + +[#1.2 Content] = 0:10-0:13 +0| with aaa, bbb: + >---< + +[#1.2 Removal] = 0:8-0:13 +0| with aaa, bbb: + >-----< + +[#1.2 Leading delimiter] = 0:8-0:10 +0| with aaa, bbb: + >--< + +[#1.2 Insertion delimiter] = " " + +[#1 Domain] = 0:0-1:8 +0| with aaa, bbb: + >-------------- +1| pass + --------< + + +[#2 Content] = +[#2 Domain] = 0:5-0:8 +0| with aaa, bbb: + >---< + +[#2 Removal] = 0:5-0:10 +0| with aaa, bbb: + >-----< + +[#2 Trailing delimiter] = 0:8-0:10 +0| with aaa, bbb: + >--< + +[#2 Insertion delimiter] = " " + + +[#3 Content] = +[#3 Domain] = 0:10-0:13 +0| with aaa, bbb: + >---< + +[#3 Removal] = 0:8-0:13 +0| with aaa, bbb: + >-----< + +[#3 Leading delimiter] = 0:8-0:10 +0| with aaa, bbb: + >--< + +[#3 Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource3.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource3.scope new file mode 100644 index 0000000000..78a3f0d13d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource3.scope @@ -0,0 +1,23 @@ +with aaa as bbb: + pass +--- + +[Content] = 0:5-0:8 +0| with aaa as bbb: + >---< + +[Removal] = 0:5-0:12 +0| with aaa as bbb: + >-------< + +[Trailing delimiter] = 0:8-0:12 +0| with aaa as bbb: + >----< + +[Domain] = 0:0-1:8 +0| with aaa as bbb: + >---------------- +1| pass + --------< + +[Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource4.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource4.scope new file mode 100644 index 0000000000..23a36a6039 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource4.scope @@ -0,0 +1,72 @@ +with aaa, bbb as ccc: + pass +--- + +[#1.1 Content] = 0:5-0:8 +0| with aaa, bbb as ccc: + >---< + +[#1.1 Removal] = 0:5-0:10 +0| with aaa, bbb as ccc: + >-----< + +[#1.1 Trailing delimiter] = 0:8-0:10 +0| with aaa, bbb as ccc: + >--< + +[#1.1 Insertion delimiter] = " " + +[#1.2 Content] = 0:10-0:13 +0| with aaa, bbb as ccc: + >---< + +[#1.2 Removal] = 0:10-0:17 +0| with aaa, bbb as ccc: + >-------< + +[#1.2 Trailing delimiter] = 0:13-0:17 +0| with aaa, bbb as ccc: + >----< + +[#1.2 Insertion delimiter] = " " + +[#1 Domain] = 0:0-1:8 +0| with aaa, bbb as ccc: + >--------------------- +1| pass + --------< + + +[#2 Content] = +[#2 Domain] = 0:5-0:8 +0| with aaa, bbb as ccc: + >---< + +[#2 Removal] = 0:5-0:10 +0| with aaa, bbb as ccc: + >-----< + +[#2 Trailing delimiter] = 0:8-0:10 +0| with aaa, bbb as ccc: + >--< + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 0:10-0:13 +0| with aaa, bbb as ccc: + >---< + +[#3 Removal] = 0:10-0:17 +0| with aaa, bbb as ccc: + >-------< + +[#3 Trailing delimiter] = 0:13-0:17 +0| with aaa, bbb as ccc: + >----< + +[#3 Domain] = 0:10-0:20 +0| with aaa, bbb as ccc: + >----------< + +[#3 Insertion delimiter] = " " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource5.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource5.scope new file mode 100644 index 0000000000..89bd299ecb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/python/value.resource5.scope @@ -0,0 +1,75 @@ +with aaa as bbb, ccc as ddd: + pass +--- + +[#1.1 Content] = 0:5-0:8 +0| with aaa as bbb, ccc as ddd: + >---< + +[#1.1 Removal] = 0:5-0:12 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#1.1 Trailing delimiter] = 0:8-0:12 +0| with aaa as bbb, ccc as ddd: + >----< + +[#1.1 Insertion delimiter] = " " + +[#1.2 Content] = 0:17-0:20 +0| with aaa as bbb, ccc as ddd: + >---< + +[#1.2 Removal] = 0:17-0:24 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#1.2 Trailing delimiter] = 0:20-0:24 +0| with aaa as bbb, ccc as ddd: + >----< + +[#1.2 Insertion delimiter] = " " + +[#1 Domain] = 0:0-1:8 +0| with aaa as bbb, ccc as ddd: + >---------------------------- +1| pass + --------< + + +[#2 Content] = 0:5-0:8 +0| with aaa as bbb, ccc as ddd: + >---< + +[#2 Removal] = 0:5-0:12 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#2 Trailing delimiter] = 0:8-0:12 +0| with aaa as bbb, ccc as ddd: + >----< + +[#2 Domain] = 0:5-0:15 +0| with aaa as bbb, ccc as ddd: + >----------< + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 0:17-0:20 +0| with aaa as bbb, ccc as ddd: + >---< + +[#3 Removal] = 0:17-0:24 +0| with aaa as bbb, ccc as ddd: + >-------< + +[#3 Trailing delimiter] = 0:20-0:24 +0| with aaa as bbb, ccc as ddd: + >----< + +[#3 Domain] = 0:17-0:27 +0| with aaa as bbb, ccc as ddd: + >----------< + +[#3 Insertion delimiter] = " " diff --git a/queries/python.scm b/queries/python.scm index e08883941d..1f465abea9 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -142,6 +142,91 @@ (_) @value ) @_.domain +;;!! with aaa: +;;! ^^^ +;;! -------- +( + (with_statement + (with_clause + (with_item)? @_.leading.endOf + . + (with_item + value: (_) @value @name + ) + . + (with_item)? @_.trailing.startOf + ) + ) @_.domain + (#not-type? @value "as_pattern") + (#allow-multiple! @value) + (#allow-multiple! @name) +) + +;;!! with aaa: +;;! ^^^ +;;! -------- +( + (with_statement + (with_clause + (with_item)? @_.leading.endOf + . + (with_item + value: (_) @value @name + ) + . + (with_item)? @_.trailing.startOf + ) @_with_clause + ) + (#not-type? @value "as_pattern") + (#has-multiple-children-of-type? @_with_clause "with_item") + (#allow-multiple! @value) + (#allow-multiple! @name) +) + +;;!! with aaa as bbb: +;;! ^^^ <~~ value +;;! ^^^ <~~ name +;;! ---------------- +( + (with_statement + (with_clause + (with_item + value: (as_pattern + (_) @value @name.leading.endOf + alias: (_) @name @value.trailing.startOf + ) + ) + ) + ) @_.domain + (#allow-multiple! @value) + (#allow-multiple! @name) +) + +;;!! with aaa as ccc, bbb: +;;! ^^^ ^^^ +;;! ---------- --- +( + (with_statement + (with_clause + (with_item + value: (as_pattern + (_) @value @name.leading.endOf + alias: (_) @name @value.trailing.startOf + ) + ) @_.domain + ) @_with_clause + ) + (#has-multiple-children-of-type? @_with_clause "with_item") + (#allow-multiple! @value) + (#allow-multiple! @name) +) + +( + (with_statement + (with_clause) @value.iteration @name.iteration + ) @value.iteration.domain @name.iteration.domain +) + ;;!! lambda str: len(str) > 0 ;;! ^^^^^^^^^^^^ ;;! ------------------------ @@ -209,7 +294,16 @@ ) @class @className.domain (module) @className.iteration @class.iteration -(module) @statement.iteration @value.iteration @name.iteration +(module) @statement.iteration + +;; This is a hack to handle the case where the entire document is a `with` statement +( + (module + (_) @_statement + ) @value.iteration @name.iteration + (#not-type? @_statement "with_statement") +) + (module) @namedFunction.iteration @functionName.iteration (class_definition) @namedFunction.iteration @functionName.iteration