From a669ea2e086310d8275a7f6207013f8a398a3db0 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 14:14:31 +0100 Subject: [PATCH 01/39] Create interior scope --- cursorless-talon/src/spoken_forms.py | 1 + data/fixtures/scopes/html/element.scope | 12 +- .../scopes/html/interior.element.scope | 37 +++++ .../scopes/javascript.jsx/element.scope | 12 +- .../javascript.jsx/interior.element.scope | 37 +++++ .../scopes/markdown/interior.cell.scope | 156 ++++++++++++++++++ .../scopes/markdown/notebookCell.scope | 23 --- .../scopes/python/anonymousFunction.scope | 4 - .../scopes/python/anonymousFunction2.scope | 4 - data/fixtures/scopes/python/class.scope | 13 ++ .../scopes/python/interior.class.scope | 23 +++ .../scopes/python/interior.function.scope | 35 ++++ .../scopes/python/interior.function2.scope | 37 +++++ .../scopes/python/interior.lambda.scope | 20 +++ .../scopes/python/interior.lambda2.scope | 20 +++ .../scopes/python/namedFunction.scope | 4 - .../scopes/python/namedFunction2.scope | 4 - data/fixtures/scopes/talon/command.scope | 4 - .../scopes/talon/interior.command.scope | 23 +++ .../common/src/scopeSupportFacets/html.ts | 1 + .../src/scopeSupportFacets/javascript.ts | 2 + .../common/src/scopeSupportFacets/markdown.ts | 1 + .../common/src/scopeSupportFacets/python.ts | 6 + .../scopeSupportFacetInfos.ts | 25 +++ .../scopeSupportFacets.types.ts | 7 + .../common/src/scopeSupportFacets/talon.ts | 1 + .../command/PartialTargetDescriptor.types.ts | 3 + .../validateQueryCaptures.test.ts | 6 +- .../TreeSitterQuery/validateQueryCaptures.ts | 1 - .../processTargets/modifiers/InteriorStage.ts | 35 ++-- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 10 +- .../InteriorScopeHandler.ts | 77 +++++++++ .../SurroundingPairInteriorScopeHandler.ts | 2 +- .../src/scopeProviders/ScopeInfoProvider.ts | 1 + .../spokenForms/defaultSpokenFormMapCore.ts | 1 + queries/go.scm | 18 +- queries/html.scm | 6 +- queries/javascript.jsx.scm | 9 +- queries/latex.scm | 4 +- queries/lua.scm | 41 +++-- queries/markdown.scm | 12 +- queries/python.scm | 20 +-- queries/talon.scm | 4 +- queries/xml.scm | 6 +- 44 files changed, 628 insertions(+), 140 deletions(-) create mode 100644 data/fixtures/scopes/html/interior.element.scope create mode 100644 data/fixtures/scopes/javascript.jsx/interior.element.scope create mode 100644 data/fixtures/scopes/markdown/interior.cell.scope create mode 100644 data/fixtures/scopes/python/class.scope create mode 100644 data/fixtures/scopes/python/interior.class.scope create mode 100644 data/fixtures/scopes/python/interior.function.scope create mode 100644 data/fixtures/scopes/python/interior.function2.scope create mode 100644 data/fixtures/scopes/python/interior.lambda.scope create mode 100644 data/fixtures/scopes/python/interior.lambda2.scope create mode 100644 data/fixtures/scopes/talon/interior.command.scope create mode 100644 packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index 5d66e1ffc1..044978b95e 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -161,6 +161,7 @@ def handle_new_values(csv_name: str, values: list[SpokenFormEntry]): "textFragment", "disqualifyDelimiter", "pairDelimiter", + "interior", ], default_list_name="scope_type", ), diff --git a/data/fixtures/scopes/html/element.scope b/data/fixtures/scopes/html/element.scope index 23ebb42694..7fce7e4f9f 100644 --- a/data/fixtures/scopes/html/element.scope +++ b/data/fixtures/scopes/html/element.scope @@ -1,14 +1,10 @@ -
+
text
--- [Content] = [Removal] = -[Domain] = 0:0-0:21 - >---------------------< -0|
- -[Interior] = 0:15-0:15 - >< -0|
+[Domain] = 0:0-0:25 + >-------------------------< +0|
text
[Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/html/interior.element.scope b/data/fixtures/scopes/html/interior.element.scope new file mode 100644 index 0000000000..0a0fca3688 --- /dev/null +++ b/data/fixtures/scopes/html/interior.element.scope @@ -0,0 +1,37 @@ +
text
+--- + +[#1 Content] = +[#1 Removal] = 0:1-0:4 + >---< +0|
text
+ +[#1 Domain] = 0:0-0:5 + >-----< +0|
text
+ +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:5-0:9 + >----< +0|
text
+ +[#2 Domain] = 0:0-0:15 + >---------------< +0|
text
+ +[#2 Insertion delimiter] = " " + + +[#3 Content] = +[#3 Removal] = 0:11-0:14 + >---< +0|
text
+ +[#3 Domain] = 0:9-0:15 + >------< +0|
text
+ +[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/javascript.jsx/element.scope b/data/fixtures/scopes/javascript.jsx/element.scope index 23ebb42694..7fce7e4f9f 100644 --- a/data/fixtures/scopes/javascript.jsx/element.scope +++ b/data/fixtures/scopes/javascript.jsx/element.scope @@ -1,14 +1,10 @@ -
+
text
--- [Content] = [Removal] = -[Domain] = 0:0-0:21 - >---------------------< -0|
- -[Interior] = 0:15-0:15 - >< -0|
+[Domain] = 0:0-0:25 + >-------------------------< +0|
text
[Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/javascript.jsx/interior.element.scope b/data/fixtures/scopes/javascript.jsx/interior.element.scope new file mode 100644 index 0000000000..0a0fca3688 --- /dev/null +++ b/data/fixtures/scopes/javascript.jsx/interior.element.scope @@ -0,0 +1,37 @@ +
text
+--- + +[#1 Content] = +[#1 Removal] = 0:1-0:4 + >---< +0|
text
+ +[#1 Domain] = 0:0-0:5 + >-----< +0|
text
+ +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:5-0:9 + >----< +0|
text
+ +[#2 Domain] = 0:0-0:15 + >---------------< +0|
text
+ +[#2 Insertion delimiter] = " " + + +[#3 Content] = +[#3 Removal] = 0:11-0:14 + >---< +0|
text
+ +[#3 Domain] = 0:9-0:15 + >------< +0|
text
+ +[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/markdown/interior.cell.scope b/data/fixtures/scopes/markdown/interior.cell.scope new file mode 100644 index 0000000000..cab45f9f98 --- /dev/null +++ b/data/fixtures/scopes/markdown/interior.cell.scope @@ -0,0 +1,156 @@ +```python +def foo(): + pass +``` + +``` +hello +``` + +--- + +[#1 Content] = +[#1 Removal] = 0:1-0:1 + >< +0| ```python + +[#1 Domain] = 0:0-0:2 + >--< +0| ```python + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:9-3:0 + > +0| ```python +1| def foo(): +2| pass +3| ``` + < + +[#2 Domain] = 0:0-4:0 + >--------- +0| ```python +1| def foo(): +2| pass +3| ``` +4| + < + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 0:3-2:8 + >------ +0| ```python +1| def foo(): +2| pass + --------< + +[#3 Removal] = 0:3-3:0 + >------ +0| ```python +1| def foo(): +2| pass +3| ``` + < + +[#3 Domain] = 0:2-3:1 + >------- +0| ```python +1| def foo(): +2| pass +3| ``` + -< + +[#3 Insertion delimiter] = " " + + +[#4 Content] = +[#4 Removal] = 1:8-1:8 + >< +1| def foo(): + +[#4 Domain] = 1:7-1:9 + >--< +1| def foo(): + +[#4 Insertion delimiter] = " " + + +[#5 Content] = +[#5 Removal] = 3:2-3:2 + >< +3| ``` + +[#5 Domain] = 3:1-3:3 + >--< +3| ``` + +[#5 Insertion delimiter] = " " + + +[#6 Content] = +[#6 Removal] = 5:1-5:1 + >< +5| ``` + +[#6 Domain] = 5:0-5:2 + >--< +5| ``` + +[#6 Insertion delimiter] = " " + + +[#7 Content] = +[#7 Removal] = 5:3-7:0 + > +5| ``` +6| hello +7| ``` + < + +[#7 Domain] = 5:0-8:0 + >--- +5| ``` +6| hello +7| ``` +8| + < + +[#7 Insertion delimiter] = " " + + +[#8 Content] = 6:0-6:5 + >-----< +6| hello + +[#8 Removal] = 5:3-7:0 + > +5| ``` +6| hello +7| ``` + < + +[#8 Domain] = 5:2-7:1 + >- +5| ``` +6| hello +7| ``` + -< + +[#8 Insertion delimiter] = " " + + +[#9 Content] = +[#9 Removal] = 7:2-7:2 + >< +7| ``` + +[#9 Domain] = 7:1-7:3 + >--< +7| ``` + +[#9 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/markdown/notebookCell.scope b/data/fixtures/scopes/markdown/notebookCell.scope index bc17667883..3d07156fb1 100644 --- a/data/fixtures/scopes/markdown/notebookCell.scope +++ b/data/fixtures/scopes/markdown/notebookCell.scope @@ -19,19 +19,6 @@ hello 3| ``` ---< -[#1 Interior: Content] = 1:0-2:8 - >---------- -1| def foo(): -2| pass - --------< -[#1 Interior: Removal] = 0:9-3:0 - > -0| ```python -1| def foo(): -2| pass -3| ``` - < - [#1 Insertion delimiter] = "\n\n" @@ -44,14 +31,4 @@ hello 7| ``` ---< -[#2 Interior: Content] = 6:0-6:5 - >-----< -6| hello -[#2 Interior: Removal] = 5:3-7:0 - > -5| ``` -6| hello -7| ``` - < - [#2 Insertion delimiter] = "\n\n" diff --git a/data/fixtures/scopes/python/anonymousFunction.scope b/data/fixtures/scopes/python/anonymousFunction.scope index bfb98d9a04..4e5afbda7e 100644 --- a/data/fixtures/scopes/python/anonymousFunction.scope +++ b/data/fixtures/scopes/python/anonymousFunction.scope @@ -7,8 +7,4 @@ lambda: pass >------------< 0| lambda: pass -[Interior] = 0:8-0:12 - >----< -0| lambda: pass - [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/python/anonymousFunction2.scope b/data/fixtures/scopes/python/anonymousFunction2.scope index 0f251e1fb7..78b05442a5 100644 --- a/data/fixtures/scopes/python/anonymousFunction2.scope +++ b/data/fixtures/scopes/python/anonymousFunction2.scope @@ -7,8 +7,4 @@ lambda x: x >-----------< 0| lambda x: x -[Interior] = 0:10-0:11 - >-< -0| lambda x: x - [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/python/class.scope b/data/fixtures/scopes/python/class.scope new file mode 100644 index 0000000000..4463b9d47c --- /dev/null +++ b/data/fixtures/scopes/python/class.scope @@ -0,0 +1,13 @@ +class MyClass: + pass +--- + +[Content] = +[Removal] = +[Domain] = 0:0-1:8 + >-------------- +0| class MyClass: +1| pass + --------< + +[Insertion delimiter] = "\n\n" diff --git a/data/fixtures/scopes/python/interior.class.scope b/data/fixtures/scopes/python/interior.class.scope new file mode 100644 index 0000000000..1ad5b8a943 --- /dev/null +++ b/data/fixtures/scopes/python/interior.class.scope @@ -0,0 +1,23 @@ +class MyClass: + pass +--- + +[Content] = 1:4-1:8 + >----< +1| pass + +[Removal] = 1:0-1:8 + >--------< +1| pass + +[Leading delimiter] = 1:0-1:4 + >----< +1| pass + +[Domain] = 0:0-1:8 + >-------------- +0| class MyClass: +1| pass + --------< + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/interior.function.scope b/data/fixtures/scopes/python/interior.function.scope new file mode 100644 index 0000000000..b5e6a0404a --- /dev/null +++ b/data/fixtures/scopes/python/interior.function.scope @@ -0,0 +1,35 @@ +def foo(): + pass +--- + +[#1 Content] = 1:4-1:8 + >----< +1| pass + +[#1 Removal] = 1:0-1:8 + >--------< +1| pass + +[#1 Leading delimiter] = 1:0-1:4 + >----< +1| pass + +[#1 Domain] = 0:0-1:8 + >---------- +0| def foo(): +1| pass + --------< + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:8-0:8 + >< +0| def foo(): + +[#2 Domain] = 0:7-0:9 + >--< +0| def foo(): + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/interior.function2.scope b/data/fixtures/scopes/python/interior.function2.scope new file mode 100644 index 0000000000..a96e9e5bd2 --- /dev/null +++ b/data/fixtures/scopes/python/interior.function2.scope @@ -0,0 +1,37 @@ +@foo +def bar(value: string) -> str: + return value +--- + +[#1 Content] = 2:4-2:16 + >------------< +2| return value + +[#1 Removal] = 2:0-2:16 + >----------------< +2| return value + +[#1 Leading delimiter] = 2:0-2:4 + >----< +2| return value + +[#1 Domain] = 0:0-2:16 + >---- +0| @foo +1| def bar(value: string) -> str: +2| return value + ----------------< + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 1:8-1:21 + >-------------< +1| def bar(value: string) -> str: + +[#2 Domain] = 1:7-1:22 + >---------------< +1| def bar(value: string) -> str: + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/interior.lambda.scope b/data/fixtures/scopes/python/interior.lambda.scope new file mode 100644 index 0000000000..af3d0efee5 --- /dev/null +++ b/data/fixtures/scopes/python/interior.lambda.scope @@ -0,0 +1,20 @@ +lambda: pass +--- + +[Content] = 0:8-0:12 + >----< +0| lambda: pass + +[Removal] = 0:7-0:12 + >-----< +0| lambda: pass + +[Leading delimiter] = 0:7-0:8 + >-< +0| lambda: pass + +[Domain] = 0:0-0:12 + >------------< +0| lambda: pass + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/interior.lambda2.scope b/data/fixtures/scopes/python/interior.lambda2.scope new file mode 100644 index 0000000000..76ce203320 --- /dev/null +++ b/data/fixtures/scopes/python/interior.lambda2.scope @@ -0,0 +1,20 @@ +lambda x: x +--- + +[Content] = 0:10-0:11 + >-< +0| lambda x: x + +[Removal] = 0:9-0:11 + >--< +0| lambda x: x + +[Leading delimiter] = 0:9-0:10 + >-< +0| lambda x: x + +[Domain] = 0:0-0:11 + >-----------< +0| lambda x: x + +[Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/namedFunction.scope b/data/fixtures/scopes/python/namedFunction.scope index abcd7da01a..ca7c1ce6ef 100644 --- a/data/fixtures/scopes/python/namedFunction.scope +++ b/data/fixtures/scopes/python/namedFunction.scope @@ -10,8 +10,4 @@ def foo(): 1| pass --------< -[Interior] = 1:4-1:8 - >----< -1| pass - [Insertion delimiter] = "\n\n" diff --git a/data/fixtures/scopes/python/namedFunction2.scope b/data/fixtures/scopes/python/namedFunction2.scope index 18ce418ab6..bb85d94947 100644 --- a/data/fixtures/scopes/python/namedFunction2.scope +++ b/data/fixtures/scopes/python/namedFunction2.scope @@ -12,8 +12,4 @@ def bar(value: string) -> str: 2| return value ----------------< -[Interior] = 2:4-2:16 - >------------< -2| return value - [Insertion delimiter] = "\n\n" diff --git a/data/fixtures/scopes/talon/command.scope b/data/fixtures/scopes/talon/command.scope index a9db1a7118..7f114b88bb 100644 --- a/data/fixtures/scopes/talon/command.scope +++ b/data/fixtures/scopes/talon/command.scope @@ -10,8 +10,4 @@ press {user.key}: 1| key(key) ------------< -[Interior] = 1:4-1:12 - >--------< -1| key(key) - [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/talon/interior.command.scope b/data/fixtures/scopes/talon/interior.command.scope new file mode 100644 index 0000000000..9f5429cc96 --- /dev/null +++ b/data/fixtures/scopes/talon/interior.command.scope @@ -0,0 +1,23 @@ +do something: + a = 2 +--- + +[Content] = 1:4-1:9 + >-----< +1| a = 2 + +[Removal] = 1:0-1:9 + >---------< +1| a = 2 + +[Leading delimiter] = 1:0-1:4 + >----< +1| a = 2 + +[Domain] = 0:0-1:9 + >------------- +0| do something: +1| a = 2 + ---------< + +[Insertion delimiter] = " " diff --git a/packages/common/src/scopeSupportFacets/html.ts b/packages/common/src/scopeSupportFacets/html.ts index 9380d91003..68ae5e7779 100644 --- a/packages/common/src/scopeSupportFacets/html.ts +++ b/packages/common/src/scopeSupportFacets/html.ts @@ -12,6 +12,7 @@ export const htmlScopeSupport: LanguageScopeSupportFacetMap = { "key.attribute": supported, "value.attribute": supported, "comment.block": supported, + "interior.element": supported, "argument.actual": notApplicable, "argument.actual.iteration": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/javascript.ts b/packages/common/src/scopeSupportFacets/javascript.ts index 0b44f2c6c3..c579ae4b58 100644 --- a/packages/common/src/scopeSupportFacets/javascript.ts +++ b/packages/common/src/scopeSupportFacets/javascript.ts @@ -114,6 +114,8 @@ export const javascriptCoreScopeSupport: LanguageScopeSupportFacetMap = { "value.field": supported, "collectionItem.unenclosed": supported, + + "interior.element": supported, }; export const javascriptJsxScopeSupport: LanguageScopeSupportFacetMap = { diff --git a/packages/common/src/scopeSupportFacets/markdown.ts b/packages/common/src/scopeSupportFacets/markdown.ts index 7831a7c036..2e39e77264 100644 --- a/packages/common/src/scopeSupportFacets/markdown.ts +++ b/packages/common/src/scopeSupportFacets/markdown.ts @@ -9,4 +9,5 @@ export const markdownScopeSupport: LanguageScopeSupportFacetMap = { "comment.block": supported, section: supported, notebookCell: supported, + "interior.cell": supported, }; diff --git a/packages/common/src/scopeSupportFacets/python.ts b/packages/common/src/scopeSupportFacets/python.ts index 7df0aa9c71..b46466c93f 100644 --- a/packages/common/src/scopeSupportFacets/python.ts +++ b/packages/common/src/scopeSupportFacets/python.ts @@ -38,6 +38,12 @@ export const pythonScopeSupport: LanguageScopeSupportFacetMap = { "branch.try": supported, "branch.loop": supported, + class: supported, + + "interior.class": supported, + "interior.function": supported, + "interior.lambda": supported, + element: notApplicable, tags: notApplicable, attribute: notApplicable, diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index 8c3341452b..623e2b7393 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -689,6 +689,31 @@ export const scopeSupportFacetInfos: Record< isIteration: true, }, + "interior.class": { + description: "The body of a class", + scopeType: "interior", + }, + "interior.function": { + description: "The body of a function", + scopeType: "interior", + }, + "interior.lambda": { + description: "The body of a lambda/anonymous function", + scopeType: "interior", + }, + "interior.element": { + description: "The interior/children of an xml element", + scopeType: "interior", + }, + "interior.command": { + description: "The body of a Talon command", + scopeType: "interior", + }, + "interior.cell": { + description: "The body of code cell in markdown", + scopeType: "interior", + }, + notebookCell: { description: "A cell in a notebook or a markdown code block", scopeType: "notebookCell", diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index 11d1d4bd5e..c4ad039323 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -171,6 +171,13 @@ export const scopeSupportFacets = [ "type.typeArgument", "type.typeArgument.iteration", + "interior.class", + "interior.function", + "interior.lambda", + "interior.element", + "interior.command", + "interior.cell", + "notebookCell", // FIXME: Still in legacy diff --git a/packages/common/src/scopeSupportFacets/talon.ts b/packages/common/src/scopeSupportFacets/talon.ts index 6b64079b1a..80fb6239b4 100644 --- a/packages/common/src/scopeSupportFacets/talon.ts +++ b/packages/common/src/scopeSupportFacets/talon.ts @@ -5,4 +5,5 @@ const { supported } = ScopeSupportFacetLevel; export const talonScopeSupport: LanguageScopeSupportFacetMap = { command: supported, + "interior.command": supported, }; diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index e09d94d230..ceed7ba859 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -154,6 +154,7 @@ export const simpleScopeTypeTypes = [ "functionName", "ifStatement", "instance", + "interior", "list", "map", "name", @@ -254,6 +255,8 @@ export interface SurroundingPairInteriorScopeType { delimiter: SurroundingPairName; // If true don't yield multiline pairs requireSingleLine?: boolean; + // If true the domain will be the full pair instead of just the interior + allowWeakContainment?: boolean; } export interface OneOfScopeType { diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.test.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.test.ts index 535c330d40..08c54a7e92 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.test.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.test.ts @@ -7,12 +7,12 @@ const testCases: { name: string; isOk: boolean; content: string }[] = [ { name: "Scope captures", isOk: true, - content: "(if_statement) @statement @ifStatement @comment", + content: "(if_statement) @statement @ifStatement @comment @interior", }, { name: "Relationships", isOk: true, - content: "(if_statement) @statement.domain @statement.interior @_.removal", + content: "(if_statement) @statement.domain @_.removal", }, { name: "Position captures", @@ -24,7 +24,7 @@ const testCases: { name: string; isOk: boolean; content: string }[] = [ name: "Range captures", isOk: true, content: - "(if_statement) @statement.start @statement.start.endOf @statement.removal.start @statement.interior.start.endOf", + "(if_statement) @statement.start @statement.start.endOf @statement.removal.start", }, { name: "Dummy capture", diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts index c32737f8f5..14a02a7f28 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts @@ -10,7 +10,6 @@ const positionSuffixes = ["startOf", "endOf"]; const rangeRelationships = [ "domain", "removal", - "interior", "iteration", "iteration.domain", ]; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index ec1c37508f..ff21e29aa8 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -8,20 +8,24 @@ import type { ModifierStage } from "../PipelineStages.types"; import { ModifyIfConditionStage } from "./ConditionalModifierStages"; export class InteriorOnlyStage implements ModifierStage { - private containingSurroundingPairIfNoInteriorStage: ModifierStage; - constructor( private modifierStageFactory: ModifierStageFactory, private modifier: InteriorOnlyModifier, - ) { - this.containingSurroundingPairIfNoInteriorStage = - getContainingSurroundingPairIfNoInteriorStage(this.modifierStageFactory); - } + ) {} run(target: Target): Target[] { - return this.containingSurroundingPairIfNoInteriorStage - .run(target) - .flatMap((target) => target.getInterior()!); + const interior = target.getInterior(); + + if (interior != null) { + return interior; + } + + const containingInteriorStage = this.modifierStageFactory.create({ + type: "containingScope", + scopeType: { type: "interior" }, + }); + + return containingInteriorStage.run(target); } } @@ -43,19 +47,6 @@ export class ExcludeInteriorStage implements ModifierStage { } } -function getContainingSurroundingPairIfNoInteriorStage( - modifierStageFactory: ModifierStageFactory, -): ModifierStage { - return new ModifyIfConditionStage( - modifierStageFactory, - { - type: "containingScope", - scopeType: { type: "surroundingPair", delimiter: "any" }, - }, - (target) => target.getInterior() == null, - ); -} - export function getContainingSurroundingPairIfNoBoundaryStage( modifierStageFactory: ModifierStageFactory, ): ModifierStage { diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 50152f21ef..657981240c 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -20,15 +20,16 @@ import { NonWhitespaceSequenceScopeHandler, UrlScopeHandler, } from "./RegexScopeHandler"; +import type { ComplexScopeType, ScopeHandler } from "./scopeHandler.types"; import type { ScopeHandlerFactory } from "./ScopeHandlerFactory"; import { SentenceScopeHandler } from "./SentenceScopeHandler/SentenceScopeHandler"; import { SurroundingPairInteriorScopeHandler, SurroundingPairScopeHandler, } from "./SurroundingPairScopeHandler"; +import { InteriorScopeHandler } from "./SurroundingPairScopeHandler/InteriorScopeHandler"; import { TokenScopeHandler } from "./TokenScopeHandler"; import { WordScopeHandler } from "./WordScopeHandler/WordScopeHandler"; -import type { ComplexScopeType, ScopeHandler } from "./scopeHandler.types"; /** * Returns a scope handler for the given scope type and language id, or @@ -119,6 +120,13 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { scopeType, languageId, ); + case "interior": + return new InteriorScopeHandler( + this, + this.languageDefinitions, + scopeType, + languageId, + ); case "custom": return scopeType.scopeHandler; case "oneOf": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts new file mode 100644 index 0000000000..d8cc20c39d --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -0,0 +1,77 @@ +import type { + Direction, + Position, + ScopeType, + SimpleScopeType, + TextEditor, +} from "@cursorless/common"; +import type { LanguageDefinitions } from "../../../../languages/LanguageDefinitions"; +import { BaseScopeHandler } from "../BaseScopeHandler"; +import { OneOfScopeHandler } from "../OneOfScopeHandler"; +import type { TargetScope } from "../scope.types"; +import type { + ComplexScopeType, + ScopeIteratorRequirements, +} from "../scopeHandler.types"; +import { type ScopeHandler } from "../scopeHandler.types"; +import type { ScopeHandlerFactory } from "../ScopeHandlerFactory"; + +export class InteriorScopeHandler extends BaseScopeHandler { + public scopeType: ScopeType = { type: "interior" }; + protected isHierarchical = true; + private scopeHandler: ScopeHandler; + + get iterationScopeType(): ScopeType | ComplexScopeType { + return this.scopeHandler.iterationScopeType; + } + + constructor( + scopeHandlerFactory: ScopeHandlerFactory, + languageDefinitions: LanguageDefinitions, + _scopeType: SimpleScopeType, + languageId: string, + ) { + super(); + + this.scopeHandler = (() => { + const pairInteriorScopeHandler = scopeHandlerFactory.create( + { + type: "surroundingPairInterior", + delimiter: "any", + allowWeakContainment: true, + }, + languageId, + ); + + const languageScopeHandler = languageDefinitions + .get(languageId) + ?.getScopeHandler(this.scopeType); + + if (languageScopeHandler == null) { + return pairInteriorScopeHandler; + } + + return OneOfScopeHandler.createFromScopeHandlers( + scopeHandlerFactory, + { + type: "oneOf", + scopeTypes: [ + languageScopeHandler.scopeType, + pairInteriorScopeHandler.scopeType!, + ], + }, + [languageScopeHandler, pairInteriorScopeHandler], + languageId, + ); + })(); + } + + generateScopeCandidates( + editor: TextEditor, + position: Position, + direction: Direction, + hints: ScopeIteratorRequirements, + ): Iterable { + return this.scopeHandler.generateScopes(editor, position, direction, hints); + } +} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts index feb91a40a5..c237606a8c 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts @@ -21,7 +21,7 @@ export class SurroundingPairInteriorScopeHandler extends BaseScopeHandler { { type: "surroundingPair", delimiter: this.scopeType.delimiter, - requireStrongContainment: true, + requireStrongContainment: !this.scopeType.allowWeakContainment, }, this.languageId, ); diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index a1b982a5cd..4ca19be2c2 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -126,6 +126,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "functionName": case "ifStatement": case "instance": + case "interior": case "list": case "map": case "name": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index c9a2661173..fdfe9ee304 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -104,6 +104,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { textFragment: isPrivate("text fragment"), disqualifyDelimiter: isPrivate("disqualify delimiter"), pairDelimiter: isPrivate("pair delimiter"), + interior: isPrivate("inside"), ["private.fieldAccess"]: isPrivate("access"), ["private.switchStatementSubject"]: isPrivate("subject"), }, diff --git a/queries/go.scm b/queries/go.scm index 73a0315319..598ce90fa4 100644 --- a/queries/go.scm +++ b/queries/go.scm @@ -202,11 +202,11 @@ name: (_) @functionName body: (block . - "{" @namedFunction.interior.start.endOf - "}" @namedFunction.interior.end.startOf + "{" @interior.start.endOf + "}" @interior.end.startOf . )? -) @namedFunction @functionName.domain +) @namedFunction @functionName.domain @interior.domain ;; method declaration ;; func (X) foo() {} @@ -214,21 +214,21 @@ name: (_) @functionName body: (block . - "{" @namedFunction.interior.start.endOf - "}" @namedFunction.interior.end.startOf + "{" @interior.start.endOf + "}" @interior.end.startOf . ) -) @namedFunction @functionName.domain +) @namedFunction @functionName.domain @interior.domain ;; func literal (func_literal body: (block . - "{" @anonymousFunction.interior.start.endOf @namedFunction.interior.start.endOf - "}" @anonymousFunction.interior.end.startOf @namedFunction.interior.end.startOf + "{" @interior.start.endOf + "}" @interior.end.startOf . ) -) @anonymousFunction @namedFunction +) @anonymousFunction @namedFunction @interior.domain ;; switch-based branch diff --git a/queries/html.scm b/queries/html.scm index 70b457e14c..3559b325dc 100644 --- a/queries/html.scm +++ b/queries/html.scm @@ -68,9 +68,9 @@ ;;! ^^^^^^^^^^^^^^^ ;;! ^^^^ (_ - (start_tag) @xmlElement.interior.start.endOf - (end_tag) @xmlElement.interior.end.startOf -) @xmlElement + (start_tag) @interior.start.endOf + (end_tag) @interior.end.startOf +) @xmlElement @interior.domain ;;!! text ;;! ^^^^^ ^^^^^^ diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm index 6cad71af0d..358b44c65d 100644 --- a/queries/javascript.jsx.scm +++ b/queries/javascript.jsx.scm @@ -3,9 +3,12 @@ ;;! ### ;;! *** ( - (jsx_element) @xmlElement @_.interior @_.iteration - (#child-range! @_.interior 0 -1 true true) - (#child-range! @_.iteration 0 -1 true true) + (jsx_element) @xmlElement @interior @interior.domain + (#child-range! @interior 0 -1 true true) +) +( + (jsx_element) @xmlElement.iteration @xmlElement.iteration.domain + (#child-range! @xmlElement.iteration 0 -1 true true) ) ;;!! bar diff --git a/queries/latex.scm b/queries/latex.scm index a67decdc20..2b044cd777 100644 --- a/queries/latex.scm +++ b/queries/latex.scm @@ -16,8 +16,8 @@ (subparagraph) @subParagraph (_ - (begin) @xmlStartTag @environment.interior.start.endOf @xmlElement.interior.start.endOf - (end) @xmlEndTag @environment.interior.end.startOf @xmlElement.interior.end.startOf + (begin) @xmlStartTag @interior.start.endOf + (end) @xmlEndTag @interior.end.startOf ) @environment @xmlElement @_.domain (_ diff --git a/queries/lua.scm b/queries/lua.scm index 19ba91145f..c861b2f181 100644 --- a/queries/lua.scm +++ b/queries/lua.scm @@ -78,8 +78,10 @@ ;;!! (if_statement "if" @branch.start - consequence: (_) @branch.end @branch.interior -) @ifStatement @branch.iteration @condition.iteration + consequence: (_) @branch.end @interior +) @ifStatement @interior.domain + +(if_statement) @branch.iteration @condition.iteration ;;!! if x < y then ;;!! print("x smaller") @@ -90,12 +92,12 @@ ;;!! end [ (elseif_statement - consequence: (_) @branch.interior + consequence: (_) @interior ) (else_statement - body: (_) @branch.interior + body: (_) @interior ) -] @branch @_.domain +] @branch @interior.domain ;;!! while i <= 5 do ;;! ^^^^^^ @@ -115,12 +117,21 @@ ;; Lists and maps (table_constructor - "{" @_.interior.start.endOf @value.iteration.start.endOf @collectionKey.iteration.start.endOf + "{" @interior.start.endOf + (field + name: (_) + ) + "}" @interior.end.startOf +) @map @interior.domain + +(table_constructor + "{" @value.iteration.start.endOf @collectionKey.iteration.start.endOf (field name: (_) ) - "}" @_.interior.end.startOf @value.iteration.end.startOf @collectionKey.iteration.end.startOf -) @map + "}" @value.iteration.end.startOf @collectionKey.iteration.end.startOf +) + ;;!! a = { foo = "bar" } ;;! ^^^-------- ;;! xxxxxx----- @@ -137,12 +148,12 @@ ;;!! a = { "1", "2", "3" } ;;! ^^^^^^^^^^^^^^^^^ (table_constructor - "{" @_.interior.start.endOf + "{" @interior.start.endOf (field !name ) - "}" @_.interior.end.startOf -) @list + "}" @interior.end.startOf +) @list @interior.domain ;; Strings @@ -215,8 +226,8 @@ ;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (function_declaration name: (_) @functionName - body: (_)? @namedFunction.interior -) @functionName.domain @namedFunction + body: (_)? @interior +) @namedFunction @_.domain ;; inside lambda: ;;!! __add = function(a, b) return a + b end @@ -226,8 +237,8 @@ ;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (function_definition !name - body: (_)? @_.interior -) @anonymousFunction + body: (_)? @interior +) @anonymousFunction @interior.domain ;; Names and values diff --git a/queries/markdown.scm b/queries/markdown.scm index 1f33979a6f..f518623c66 100644 --- a/queries/markdown.scm +++ b/queries/markdown.scm @@ -53,11 +53,11 @@ ;;! ^^^ ( (fenced_code_block - (fenced_code_block_delimiter) @_.interior.start.endOf + (fenced_code_block_delimiter) @interior.start.endOf . (block_continuation) - (fenced_code_block_delimiter) @_.interior.end.startOf - ) @notebookCell + (fenced_code_block_delimiter) @interior.end.startOf + ) @notebookCell @interior.domain (#trim-end! @notebookCell) (#insertion-delimiter! @notebookCell "\n\n") ) @@ -71,9 +71,9 @@ ;;! ^^^ ( (fenced_code_block - (info_string) @_.interior.start.endOf - (fenced_code_block_delimiter) @_.interior.end.startOf - ) @notebookCell + (info_string) @interior.start.endOf + (fenced_code_block_delimiter) @interior.end.startOf + ) @notebookCell @interior.domain (#trim-end! @notebookCell) (#insertion-delimiter! @notebookCell "\n\n") ) diff --git a/queries/python.scm b/queries/python.scm index 6a98b0dbcd..a0fbec64af 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -282,30 +282,30 @@ ( (function_definition name: (_) @functionName - body: (_) @namedFunction.interior - ) @namedFunction @functionName.domain + body: (_) @interior + ) @namedFunction @functionName.domain @interior.domain (#not-parent-type? @namedFunction decorated_definition) ) (decorated_definition (function_definition name: (_) @functionName - body: (_) @namedFunction.interior + body: (_) @interior ) -) @namedFunction @functionName.domain +) @namedFunction @functionName.domain @interior.domain ( (class_definition name: (_) @className - body: (_) @class.interior - ) @class @className.domain + body: (_) @interior + ) @class @className.domain @interior.domain (#not-parent-type? @class decorated_definition) ) (decorated_definition (class_definition name: (_) @className - body: (_) @class.interior + body: (_) @interior ) -) @class @className.domain +) @class @className.domain @interior.domain (module) @className.iteration @class.iteration (module) @statement.iteration @@ -362,8 +362,8 @@ ;;!! lambda _: pass ;;! ^^^^^^^^^^^^^^ (lambda - body: (_) @anonymousFunction.interior -) @anonymousFunction + body: (_) @interior +) @anonymousFunction @interior.domain ;;!! match value: ;;! ^^^^^ diff --git a/queries/talon.scm b/queries/talon.scm index 726533ffac..2e2aa12d93 100644 --- a/queries/talon.scm +++ b/queries/talon.scm @@ -113,8 +113,8 @@ ;;! ^^^^^^^^^^^^^^^^ ( (command_declaration - right: (_) @_.interior - ) @command + right: (_) @interior + ) @command @interior.domain (#insertion-delimiter! @command "\n") ) diff --git a/queries/xml.scm b/queries/xml.scm index ec7b93628b..e509419245 100644 --- a/queries/xml.scm +++ b/queries/xml.scm @@ -45,9 +45,9 @@ ;;! ^^^^^^^^^^^^^^^ ;;! ^^^^ (element - (STag) @xmlElement.interior.start.endOf - (ETag) @xmlElement.interior.end.startOf -) @xmlElement + (STag) @interior.start.endOf + (ETag) @interior.end.startOf +) @xmlElement @interior.domain ;;!! text ;;! ^^^^^ ^^^^^^ From bacfe2ae5451f3336ee1e87d6efb3bbf68a946f3 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 14:47:26 +0100 Subject: [PATCH 02/39] More migrations --- data/fixtures/scopes/latex/element.scope | 10 --- data/fixtures/scopes/latex/environment.scope | 10 --- .../scopes/latex/interior.element.scope | 45 ++++++++++++ data/fixtures/scopes/lua/branch.if.scope | 12 ---- .../fixtures/scopes/lua/interior.branch.scope | 70 +++++++++++++++++++ .../scopes/lua/interior.function.scope | 37 ++++++++++ data/fixtures/scopes/lua/map.scope | 7 -- data/fixtures/scopes/lua/namedFunction.scope | 9 --- .../common/src/scopeSupportFacets/latex.ts | 1 + packages/common/src/scopeSupportFacets/lua.ts | 2 + .../scopeSupportFacetInfos.ts | 5 +- .../scopeSupportFacets.types.ts | 1 + queries/lua.scm | 16 ++--- 13 files changed, 165 insertions(+), 60 deletions(-) create mode 100644 data/fixtures/scopes/latex/interior.element.scope create mode 100644 data/fixtures/scopes/lua/interior.branch.scope create mode 100644 data/fixtures/scopes/lua/interior.function.scope diff --git a/data/fixtures/scopes/latex/element.scope b/data/fixtures/scopes/latex/element.scope index ce63430e4d..8d938b3d62 100644 --- a/data/fixtures/scopes/latex/element.scope +++ b/data/fixtures/scopes/latex/element.scope @@ -12,14 +12,4 @@ 2| \end{quote} -----------< -[Interior: Content] = 1:4-1:9 - >-----< -1| Hello -[Interior: Removal] = 0:13-2:0 - > -0| \begin{quote} -1| Hello -2| \end{quote} - < - [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/latex/environment.scope b/data/fixtures/scopes/latex/environment.scope index ce63430e4d..8d938b3d62 100644 --- a/data/fixtures/scopes/latex/environment.scope +++ b/data/fixtures/scopes/latex/environment.scope @@ -12,14 +12,4 @@ 2| \end{quote} -----------< -[Interior: Content] = 1:4-1:9 - >-----< -1| Hello -[Interior: Removal] = 0:13-2:0 - > -0| \begin{quote} -1| Hello -2| \end{quote} - < - [Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/latex/interior.element.scope b/data/fixtures/scopes/latex/interior.element.scope new file mode 100644 index 0000000000..a291b2bf48 --- /dev/null +++ b/data/fixtures/scopes/latex/interior.element.scope @@ -0,0 +1,45 @@ +\begin{quote} + Hello +\end{quote} +--- + +[#1 Content] = +[#1 Removal] = 0:13-2:0 + > +0| \begin{quote} +1| Hello +2| \end{quote} + < + +[#1 Domain] = 0:0-2:11 + >------------- +0| \begin{quote} +1| Hello +2| \end{quote} + -----------< + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:7-0:12 + >-----< +0| \begin{quote} + +[#2 Domain] = 0:6-0:13 + >-------< +0| \begin{quote} + +[#2 Insertion delimiter] = " " + + +[#3 Content] = +[#3 Removal] = 2:5-2:10 + >-----< +2| \end{quote} + +[#3 Domain] = 2:4-2:11 + >-------< +2| \end{quote} + +[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/branch.if.scope b/data/fixtures/scopes/lua/branch.if.scope index 20e001180e..8da7e804b5 100644 --- a/data/fixtures/scopes/lua/branch.if.scope +++ b/data/fixtures/scopes/lua/branch.if.scope @@ -15,10 +15,6 @@ end 1| print("x is less than y") -----------------------------< -[#1 Interior] = 1:4-1:29 - >-------------------------< -1| print("x is less than y") - [#1 Insertion delimiter] = "\n" @@ -30,10 +26,6 @@ end 3| print("x is greater than y") --------------------------------< -[#2 Interior] = 3:4-3:32 - >----------------------------< -3| print("x is greater than y") - [#2 Insertion delimiter] = "\n" @@ -45,8 +37,4 @@ end 5| print("x is equal to y") ----------------------------< -[#3 Interior] = 5:4-5:28 - >------------------------< -5| print("x is equal to y") - [#3 Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/lua/interior.branch.scope b/data/fixtures/scopes/lua/interior.branch.scope new file mode 100644 index 0000000000..2b122e0a6f --- /dev/null +++ b/data/fixtures/scopes/lua/interior.branch.scope @@ -0,0 +1,70 @@ +if x < y then + a = 1 +elseif x > y then + a = 2 +else + a = 3 +end +--- + +[#1 Content] = 1:4-1:9 + >-----< +1| a = 1 + +[#1 Removal] = 1:0-1:9 + >---------< +1| a = 1 + +[#1 Leading delimiter] = 1:0-1:4 + >----< +1| a = 1 + +[#1 Domain] = 0:0-1:9 + >------------- +0| if x < y then +1| a = 1 + ---------< + +[#1 Insertion delimiter] = " " + + +[#2 Content] = 3:4-3:9 + >-----< +3| a = 2 + +[#2 Removal] = 3:0-3:9 + >---------< +3| a = 2 + +[#2 Leading delimiter] = 3:0-3:4 + >----< +3| a = 2 + +[#2 Domain] = 2:0-3:9 + >----------------- +2| elseif x > y then +3| a = 2 + ---------< + +[#2 Insertion delimiter] = " " + + +[#3 Content] = 5:4-5:9 + >-----< +5| a = 3 + +[#3 Removal] = 5:0-5:9 + >---------< +5| a = 3 + +[#3 Leading delimiter] = 5:0-5:4 + >----< +5| a = 3 + +[#3 Domain] = 4:0-5:9 + >---- +4| else +5| a = 3 + ---------< + +[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/interior.function.scope b/data/fixtures/scopes/lua/interior.function.scope new file mode 100644 index 0000000000..7daf2e6f93 --- /dev/null +++ b/data/fixtures/scopes/lua/interior.function.scope @@ -0,0 +1,37 @@ +function makeCounter() + local count = 0 +end +--- + +[#1 Content] = 1:4-1:19 + >---------------< +1| local count = 0 + +[#1 Removal] = 1:0-1:19 + >-------------------< +1| local count = 0 + +[#1 Leading delimiter] = 1:0-1:4 + >----< +1| local count = 0 + +[#1 Domain] = 0:0-2:3 + >---------------------- +0| function makeCounter() +1| local count = 0 +2| end + ---< + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:21-0:21 + >< +0| function makeCounter() + +[#2 Domain] = 0:20-0:22 + >--< +0| function makeCounter() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/map.scope b/data/fixtures/scopes/lua/map.scope index cbb86ec2a4..afca7544f1 100644 --- a/data/fixtures/scopes/lua/map.scope +++ b/data/fixtures/scopes/lua/map.scope @@ -14,11 +14,4 @@ foo = { bar = "a", baz = "b" } >-< 0| foo = { bar = "a", baz = "b" } -[Interior: Content] = 0:8-0:28 - >--------------------< -0| foo = { bar = "a", baz = "b" } -[Interior: Removal] = 0:7-0:29 - >----------------------< -0| foo = { bar = "a", baz = "b" } - [Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/namedFunction.scope b/data/fixtures/scopes/lua/namedFunction.scope index a869646a86..d14715737a 100644 --- a/data/fixtures/scopes/lua/namedFunction.scope +++ b/data/fixtures/scopes/lua/namedFunction.scope @@ -20,13 +20,4 @@ end 6| end ---< -[Interior] = 1:4-5:7 - >--------------- -1| local count = 0 -2| return function() -3| count = count + 1 -4| return count -5| end - -------< - [Insertion delimiter] = "\n\n" diff --git a/packages/common/src/scopeSupportFacets/latex.ts b/packages/common/src/scopeSupportFacets/latex.ts index db73bc677b..64d72b34e3 100644 --- a/packages/common/src/scopeSupportFacets/latex.ts +++ b/packages/common/src/scopeSupportFacets/latex.ts @@ -13,4 +13,5 @@ export const latexScopeSupport: LanguageScopeSupportFacetMap = { tags: supported, environment: supported, disqualifyDelimiter: supported, + "interior.element": supported, }; diff --git a/packages/common/src/scopeSupportFacets/lua.ts b/packages/common/src/scopeSupportFacets/lua.ts index 5b515b1aff..601d8c8d6d 100644 --- a/packages/common/src/scopeSupportFacets/lua.ts +++ b/packages/common/src/scopeSupportFacets/lua.ts @@ -15,4 +15,6 @@ export const luaScopeSupport: LanguageScopeSupportFacetMap = { "branch.if": supported, namedFunction: supported, disqualifyDelimiter: supported, + "interior.function": supported, + "interior.branch": supported, }; diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index 623e2b7393..81e1a9847c 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -701,6 +701,10 @@ export const scopeSupportFacetInfos: Record< description: "The body of a lambda/anonymous function", scopeType: "interior", }, + "interior.branch": { + description: "The body of an if/elif/else branch", + scopeType: "interior", + }, "interior.element": { description: "The interior/children of an xml element", scopeType: "interior", @@ -713,7 +717,6 @@ export const scopeSupportFacetInfos: Record< description: "The body of code cell in markdown", scopeType: "interior", }, - notebookCell: { description: "A cell in a notebook or a markdown code block", scopeType: "notebookCell", diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index c4ad039323..ed656c95a8 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -174,6 +174,7 @@ export const scopeSupportFacets = [ "interior.class", "interior.function", "interior.lambda", + "interior.branch", "interior.element", "interior.command", "interior.cell", diff --git a/queries/lua.scm b/queries/lua.scm index c861b2f181..0a5a728a7e 100644 --- a/queries/lua.scm +++ b/queries/lua.scm @@ -77,11 +77,9 @@ ;;!! (if_statement - "if" @branch.start - consequence: (_) @branch.end @interior -) @ifStatement @interior.domain - -(if_statement) @branch.iteration @condition.iteration + "if" @branch.start @interior.domain.start + consequence: (_) @branch.end @interior @interior.domain.end +) @ifStatement @branch.iteration @condition.iteration ;;!! if x < y then ;;!! print("x smaller") @@ -117,12 +115,10 @@ ;; Lists and maps (table_constructor - "{" @interior.start.endOf (field name: (_) ) - "}" @interior.end.startOf -) @map @interior.domain +) @map (table_constructor "{" @value.iteration.start.endOf @collectionKey.iteration.start.endOf @@ -148,12 +144,10 @@ ;;!! a = { "1", "2", "3" } ;;! ^^^^^^^^^^^^^^^^^ (table_constructor - "{" @interior.start.endOf (field !name ) - "}" @interior.end.startOf -) @list @interior.domain +) @list ;; Strings From 5026b89c7f2ad8b0d219df11babf73955512ddc9 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 14:51:26 +0100 Subject: [PATCH 03/39] Added test --- .../recorded/modifiers/changeInside.yml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 data/fixtures/recorded/modifiers/changeInside.yml diff --git a/data/fixtures/recorded/modifiers/changeInside.yml b/data/fixtures/recorded/modifiers/changeInside.yml new file mode 100644 index 0000000000..32454197af --- /dev/null +++ b/data/fixtures/recorded/modifiers/changeInside.yml @@ -0,0 +1,26 @@ +languageId: python +command: + version: 7 + spokenForm: change inside + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + usePrePhraseSnapshot: false +initialState: + documentContents: |- + def testing(): + pass + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: |- + def testing(): + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} From 57f50b12518d37bd52adf8a7ff0e76891e83dfef Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 15:45:29 +0100 Subject: [PATCH 04/39] Fixes --- .../command/PartialTargetDescriptor.types.ts | 1 + .../processTargets/modifiers/InteriorStage.ts | 4 ++- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 1 + .../InteriorScopeHandler.ts | 30 ++++++++++++------- .../src/scopeProviders/ScopeInfoProvider.ts | 1 + .../spokenForms/defaultSpokenFormMapCore.ts | 1 + 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index ceed7ba859..25bab4d720 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -155,6 +155,7 @@ export const simpleScopeTypeTypes = [ "ifStatement", "instance", "interior", + "interiorTreeOnly", "list", "map", "name", diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index ff21e29aa8..6b851a1c0f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -22,7 +22,9 @@ export class InteriorOnlyStage implements ModifierStage { const containingInteriorStage = this.modifierStageFactory.create({ type: "containingScope", - scopeType: { type: "interior" }, + scopeType: { + type: target.hasExplicitScopeType ? "interiorTreeOnly" : "interior", + }, }); return containingInteriorStage.run(target); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 657981240c..1208a3d1d1 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -121,6 +121,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { languageId, ); case "interior": + case "interiorTreeOnly": return new InteriorScopeHandler( this, this.languageDefinitions, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index d8cc20c39d..c21376ed85 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -1,9 +1,10 @@ -import type { - Direction, - Position, - ScopeType, - SimpleScopeType, - TextEditor, +import { + NoContainingScopeError, + type Direction, + type Position, + type ScopeType, + type SimpleScopeType, + type TextEditor, } from "@cursorless/common"; import type { LanguageDefinitions } from "../../../../languages/LanguageDefinitions"; import { BaseScopeHandler } from "../BaseScopeHandler"; @@ -28,12 +29,23 @@ export class InteriorScopeHandler extends BaseScopeHandler { constructor( scopeHandlerFactory: ScopeHandlerFactory, languageDefinitions: LanguageDefinitions, - _scopeType: SimpleScopeType, + scopeType: SimpleScopeType, languageId: string, ) { super(); this.scopeHandler = (() => { + const languageScopeHandler = languageDefinitions + .get(languageId) + ?.getScopeHandler(this.scopeType); + + if (scopeType.type === "interiorTreeOnly") { + if (languageScopeHandler == null) { + throw new NoContainingScopeError(this.scopeType.type); + } + return languageScopeHandler; + } + const pairInteriorScopeHandler = scopeHandlerFactory.create( { type: "surroundingPairInterior", @@ -43,10 +55,6 @@ export class InteriorScopeHandler extends BaseScopeHandler { languageId, ); - const languageScopeHandler = languageDefinitions - .get(languageId) - ?.getScopeHandler(this.scopeType); - if (languageScopeHandler == null) { return pairInteriorScopeHandler; } diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index 4ca19be2c2..3813676283 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -127,6 +127,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "ifStatement": case "instance": case "interior": + case "interiorTreeOnly": case "list": case "map": case "name": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index fdfe9ee304..e3b2f3591e 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -105,6 +105,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { disqualifyDelimiter: isPrivate("disqualify delimiter"), pairDelimiter: isPrivate("pair delimiter"), interior: isPrivate("inside"), + interiorTreeOnly: isPrivate("inside tree only"), ["private.fieldAccess"]: isPrivate("access"), ["private.switchStatementSubject"]: isPrivate("subject"), }, From c76b06c2de661b1d3fb344c5aafaf11160ec94c3 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 15:46:46 +0100 Subject: [PATCH 05/39] Changed to main --- queries/javascript.jsx.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm index 358b44c65d..2825fcff08 100644 --- a/queries/javascript.jsx.scm +++ b/queries/javascript.jsx.scm @@ -7,7 +7,7 @@ (#child-range! @interior 0 -1 true true) ) ( - (jsx_element) @xmlElement.iteration @xmlElement.iteration.domain + (jsx_element) @xmlElement.iteration (#child-range! @xmlElement.iteration 0 -1 true true) ) From 261bf5ccd8ac971f3727fa519123fd37ee5c099a Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 15:47:47 +0100 Subject: [PATCH 06/39] Clean up --- queries/javascript.jsx.scm | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm index 2825fcff08..f3c44b92d2 100644 --- a/queries/javascript.jsx.scm +++ b/queries/javascript.jsx.scm @@ -3,11 +3,8 @@ ;;! ### ;;! *** ( - (jsx_element) @xmlElement @interior @interior.domain + (jsx_element) @xmlElement @xmlElement.iteration @interior (#child-range! @interior 0 -1 true true) -) -( - (jsx_element) @xmlElement.iteration (#child-range! @xmlElement.iteration 0 -1 true true) ) From fd17e7c4c50feb1d1c8b5e559b5f956064113080 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 16:03:47 +0100 Subject: [PATCH 07/39] Use interior target --- .../TreeSitterScopeHandler.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts index 36ee018eb7..d179ac2c25 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts @@ -1,6 +1,7 @@ import type { SimpleScopeType, TextEditor } from "@cursorless/common"; import type { TreeSitterQuery } from "../../../../languages/TreeSitterQuery"; import type { QueryMatch } from "../../../../languages/TreeSitterQuery/QueryCapture"; +import { InteriorTarget } from "../../../targets"; import { ScopeTypeTarget } from "../../../targets/ScopeTypeTarget"; import type { CustomScopeType } from "../scopeHandler.types"; import type { ExtendedTargetScope } from "./BaseTreeSitterScopeHandler"; @@ -81,6 +82,21 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { true, )?.with(contentRange.end); + if (scopeTypeType === "interior") { + return { + editor, + domain, + allowMultiple, + getTargets: (isReversed) => [ + new InteriorTarget({ + editor, + isReversed, + fullInteriorRange: contentRange, + }), + ], + }; + } + return { editor, domain, From df0c5ab3765f8fc4a14e620bec21dd7b78a5125e Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 16:30:20 +0100 Subject: [PATCH 08/39] More testing --- .../scopeHandlers/FallbackScopeHandler.ts | 29 ++++++++------ .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 2 +- .../InteriorScopeHandler.ts | 28 +++++++------- .../TreeSitterScopeHandler.ts | 38 ++++++++----------- .../LegacyContainingSyntaxScopeStage.ts | 2 - .../processTargets/targets/ScopeTypeTarget.ts | 19 +--------- 6 files changed, 49 insertions(+), 69 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts index 869d4993b3..8a9368a372 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts @@ -24,11 +24,23 @@ export class FallbackScopeHandler extends BaseScopeHandler { ); } - constructor( - public scopeHandlerFactory: ScopeHandlerFactory, - private fallbackScopeType: FallbackScopeType, - private languageId: string, - ) { + static create( + scopeHandlerFactory: ScopeHandlerFactory, + scopeType: FallbackScopeType, + languageId: string, + ): ScopeHandler { + const scopeHandlers: ScopeHandler[] = scopeType.scopeTypes.map( + (scopeType) => scopeHandlerFactory.create(scopeType, languageId), + ); + + return this.createFromScopeHandlers(scopeHandlers); + } + + static createFromScopeHandlers(scopeHandlers: ScopeHandler[]): ScopeHandler { + return new FallbackScopeHandler(scopeHandlers); + } + + private constructor(private scopeHandlers: ScopeHandler[]) { super(); } @@ -38,12 +50,7 @@ export class FallbackScopeHandler extends BaseScopeHandler { direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { - const scopeHandlers: ScopeHandler[] = this.fallbackScopeType.scopeTypes.map( - (scopeType) => - this.scopeHandlerFactory.create(scopeType, this.languageId), - ); - - for (const scopeHandler of scopeHandlers) { + for (const scopeHandler of this.scopeHandlers) { yield* scopeHandler.generateScopes(editor, position, direction, hints); } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 1208a3d1d1..0c6ab2e67f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -133,7 +133,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { case "oneOf": return OneOfScopeHandler.create(this, scopeType, languageId); case "fallback": - return new FallbackScopeHandler(this, scopeType, languageId); + return FallbackScopeHandler.create(this, scopeType, languageId); case "conditional": return new ConditionalScopeHandler(this, scopeType, languageId); case "instance": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index c21376ed85..6943cbcc26 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -1,5 +1,4 @@ import { - NoContainingScopeError, type Direction, type Position, type ScopeType, @@ -8,6 +7,7 @@ import { } from "@cursorless/common"; import type { LanguageDefinitions } from "../../../../languages/LanguageDefinitions"; import { BaseScopeHandler } from "../BaseScopeHandler"; +import { FallbackScopeHandler } from "../FallbackScopeHandler"; import { OneOfScopeHandler } from "../OneOfScopeHandler"; import type { TargetScope } from "../scope.types"; import type { @@ -27,25 +27,14 @@ export class InteriorScopeHandler extends BaseScopeHandler { } constructor( - scopeHandlerFactory: ScopeHandlerFactory, + private scopeHandlerFactory: ScopeHandlerFactory, languageDefinitions: LanguageDefinitions, scopeType: SimpleScopeType, - languageId: string, + private languageId: string, ) { super(); this.scopeHandler = (() => { - const languageScopeHandler = languageDefinitions - .get(languageId) - ?.getScopeHandler(this.scopeType); - - if (scopeType.type === "interiorTreeOnly") { - if (languageScopeHandler == null) { - throw new NoContainingScopeError(this.scopeType.type); - } - return languageScopeHandler; - } - const pairInteriorScopeHandler = scopeHandlerFactory.create( { type: "surroundingPairInterior", @@ -55,10 +44,21 @@ export class InteriorScopeHandler extends BaseScopeHandler { languageId, ); + const languageScopeHandler = languageDefinitions + .get(languageId) + ?.getScopeHandler(this.scopeType); + if (languageScopeHandler == null) { return pairInteriorScopeHandler; } + if (scopeType.type === "interiorTreeOnly") { + return FallbackScopeHandler.createFromScopeHandlers([ + languageScopeHandler, + pairInteriorScopeHandler, + ]); + } + return OneOfScopeHandler.createFromScopeHandlers( scopeHandlerFactory, { diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts index d179ac2c25..dae42f207e 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts @@ -52,14 +52,22 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { const domain = getRelatedRange(match, scopeTypeType, "domain", true) ?? contentRange; - const removalRange = getRelatedRange(match, scopeTypeType, "removal", true); + if (scopeTypeType === "interior") { + return { + editor, + domain, + allowMultiple, + getTargets: (isReversed) => [ + new InteriorTarget({ + editor, + isReversed, + fullInteriorRange: contentRange, + }), + ], + }; + } - const interiorRange = getRelatedRange( - match, - scopeTypeType, - "interior", - true, - ); + const removalRange = getRelatedRange(match, scopeTypeType, "removal", true); const prefixRange = getRelatedRange( match, @@ -82,21 +90,6 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { true, )?.with(contentRange.end); - if (scopeTypeType === "interior") { - return { - editor, - domain, - allowMultiple, - getTargets: (isReversed) => [ - new InteriorTarget({ - editor, - isReversed, - fullInteriorRange: contentRange, - }), - ], - }; - } - return { editor, domain, @@ -111,7 +104,6 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { removalRange, leadingDelimiterRange, trailingDelimiterRange, - interiorRange, insertionDelimiter, }), ], diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts index d3ba647c4a..e3beaa6f02 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeTypeStages/LegacyContainingSyntaxScopeStage.ts @@ -65,7 +65,6 @@ export class LegacyContainingSyntaxScopeStage implements ModifierStage { leadingDelimiterRange, trailingDelimiterRange, removalRange, - interiorRange, } = scope.context; if ( @@ -85,7 +84,6 @@ export class LegacyContainingSyntaxScopeStage implements ModifierStage { isReversed: target.isReversed, contentRange: contentSelection, removalRange: removalRange, - interiorRange: interiorRange, insertionDelimiter: containingListDelimiter, leadingDelimiterRange, trailingDelimiterRange, diff --git a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts index ca757840fe..5b460bda1b 100644 --- a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts @@ -1,9 +1,8 @@ import type { Range, SimpleScopeTypeType } from "@cursorless/common"; +import type { Target } from "../../typings/target.types"; import type { CommonTargetParameters } from "./BaseTarget"; import { BaseTarget } from "./BaseTarget"; -import { InteriorTarget } from "./InteriorTarget"; import { PlainTarget } from "./PlainTarget"; -import type { Target } from "../../typings/target.types"; import { createContinuousRange, createContinuousRangeFromRanges, @@ -20,7 +19,6 @@ export interface ScopeTypeTargetParameters extends CommonTargetParameters { readonly insertionDelimiter?: string; readonly prefixRange?: Range; readonly removalRange?: Range; - readonly interiorRange?: Range; readonly leadingDelimiterRange?: Range; readonly trailingDelimiterRange?: Range; } @@ -29,7 +27,6 @@ export class ScopeTypeTarget extends BaseTarget { type = "ScopeTypeTarget"; private scopeTypeType_: SimpleScopeTypeType; private removalRange_?: Range; - private interiorRange_?: Range; private leadingDelimiterRange_?: Range; private trailingDelimiterRange_?: Range; private hasDelimiterRange_: boolean; @@ -40,7 +37,6 @@ export class ScopeTypeTarget extends BaseTarget { super(parameters); this.scopeTypeType_ = parameters.scopeTypeType; this.removalRange_ = parameters.removalRange; - this.interiorRange_ = parameters.interiorRange; this.leadingDelimiterRange_ = parameters.leadingDelimiterRange; this.trailingDelimiterRange_ = parameters.trailingDelimiterRange; this.prefixRange = parameters.prefixRange; @@ -79,19 +75,6 @@ export class ScopeTypeTarget extends BaseTarget { return undefined; } - getInterior() { - if (this.interiorRange_ == null) { - return super.getInterior(); - } - return [ - new InteriorTarget({ - editor: this.editor, - isReversed: this.isReversed, - fullInteriorRange: this.interiorRange_, - }), - ]; - } - getRemovalRange(): Range { return this.removalRange_ != null ? this.removalRange_ From 16895d949145310a51d43f15a5b8ffca702197de Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 17:09:19 +0100 Subject: [PATCH 09/39] Update query --- queries/javascript.jsx.scm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm index f3c44b92d2..2825fcff08 100644 --- a/queries/javascript.jsx.scm +++ b/queries/javascript.jsx.scm @@ -3,8 +3,11 @@ ;;! ### ;;! *** ( - (jsx_element) @xmlElement @xmlElement.iteration @interior + (jsx_element) @xmlElement @interior @interior.domain (#child-range! @interior 0 -1 true true) +) +( + (jsx_element) @xmlElement.iteration (#child-range! @xmlElement.iteration 0 -1 true true) ) From b573ebc4c2f4abeed926e3390cf19e31e3e2b54b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 17:44:16 +0100 Subject: [PATCH 10/39] update tests --- .../scopes/latex/interior.element.scope | 5 ++- .../fixtures/scopes/lua/interior.branch.scope | 33 ++++--------------- .../scopes/lua/interior.function.scope | 11 ++----- .../scopes/markdown/interior.cell.scope | 12 +++++-- .../scopes/python/interior.class.scope | 11 ++----- .../scopes/python/interior.function.scope | 11 ++----- .../scopes/python/interior.function2.scope | 11 ++----- .../scopes/python/interior.lambda.scope | 11 ++----- .../scopes/python/interior.lambda2.scope | 11 ++----- .../scopes/talon/interior.command.scope | 11 ++----- .../src/scopeSupportFacets/javascript.ts | 3 +- .../scopeSupportFacetInfos.ts | 1 + .../processTargets/targets/InteriorTarget.ts | 8 ++--- .../processTargets/targets/ScopeTypeTarget.ts | 11 ++----- .../targets/util/createContinuousRange.ts | 2 +- 15 files changed, 41 insertions(+), 111 deletions(-) diff --git a/data/fixtures/scopes/latex/interior.element.scope b/data/fixtures/scopes/latex/interior.element.scope index a291b2bf48..a3235c56a0 100644 --- a/data/fixtures/scopes/latex/interior.element.scope +++ b/data/fixtures/scopes/latex/interior.element.scope @@ -3,7 +3,10 @@ \end{quote} --- -[#1 Content] = +[#1 Content] = 1:4-1:9 + >-----< +1| Hello + [#1 Removal] = 0:13-2:0 > 0| \begin{quote} diff --git a/data/fixtures/scopes/lua/interior.branch.scope b/data/fixtures/scopes/lua/interior.branch.scope index 2b122e0a6f..2ce1cb35f8 100644 --- a/data/fixtures/scopes/lua/interior.branch.scope +++ b/data/fixtures/scopes/lua/interior.branch.scope @@ -7,18 +7,11 @@ else end --- -[#1 Content] = 1:4-1:9 +[#1 Content] = +[#1 Removal] = 1:4-1:9 >-----< 1| a = 1 -[#1 Removal] = 1:0-1:9 - >---------< -1| a = 1 - -[#1 Leading delimiter] = 1:0-1:4 - >----< -1| a = 1 - [#1 Domain] = 0:0-1:9 >------------- 0| if x < y then @@ -28,18 +21,11 @@ end [#1 Insertion delimiter] = " " -[#2 Content] = 3:4-3:9 +[#2 Content] = +[#2 Removal] = 3:4-3:9 >-----< 3| a = 2 -[#2 Removal] = 3:0-3:9 - >---------< -3| a = 2 - -[#2 Leading delimiter] = 3:0-3:4 - >----< -3| a = 2 - [#2 Domain] = 2:0-3:9 >----------------- 2| elseif x > y then @@ -49,18 +35,11 @@ end [#2 Insertion delimiter] = " " -[#3 Content] = 5:4-5:9 +[#3 Content] = +[#3 Removal] = 5:4-5:9 >-----< 5| a = 3 -[#3 Removal] = 5:0-5:9 - >---------< -5| a = 3 - -[#3 Leading delimiter] = 5:0-5:4 - >----< -5| a = 3 - [#3 Domain] = 4:0-5:9 >---- 4| else diff --git a/data/fixtures/scopes/lua/interior.function.scope b/data/fixtures/scopes/lua/interior.function.scope index 7daf2e6f93..4880d538f5 100644 --- a/data/fixtures/scopes/lua/interior.function.scope +++ b/data/fixtures/scopes/lua/interior.function.scope @@ -3,18 +3,11 @@ function makeCounter() end --- -[#1 Content] = 1:4-1:19 +[#1 Content] = +[#1 Removal] = 1:4-1:19 >---------------< 1| local count = 0 -[#1 Removal] = 1:0-1:19 - >-------------------< -1| local count = 0 - -[#1 Leading delimiter] = 1:0-1:4 - >----< -1| local count = 0 - [#1 Domain] = 0:0-2:3 >---------------------- 0| function makeCounter() diff --git a/data/fixtures/scopes/markdown/interior.cell.scope b/data/fixtures/scopes/markdown/interior.cell.scope index cab45f9f98..51679096c5 100644 --- a/data/fixtures/scopes/markdown/interior.cell.scope +++ b/data/fixtures/scopes/markdown/interior.cell.scope @@ -21,7 +21,12 @@ hello [#1 Insertion delimiter] = " " -[#2 Content] = +[#2 Content] = 1:0-2:8 + >---------- +1| def foo(): +2| pass + --------< + [#2 Removal] = 0:9-3:0 > 0| ```python @@ -104,7 +109,10 @@ hello [#6 Insertion delimiter] = " " -[#7 Content] = +[#7 Content] = 6:0-6:5 + >-----< +6| hello + [#7 Removal] = 5:3-7:0 > 5| ``` diff --git a/data/fixtures/scopes/python/interior.class.scope b/data/fixtures/scopes/python/interior.class.scope index 1ad5b8a943..0248108e3e 100644 --- a/data/fixtures/scopes/python/interior.class.scope +++ b/data/fixtures/scopes/python/interior.class.scope @@ -2,18 +2,11 @@ class MyClass: pass --- -[Content] = 1:4-1:8 +[Content] = +[Removal] = 1:4-1:8 >----< 1| pass -[Removal] = 1:0-1:8 - >--------< -1| pass - -[Leading delimiter] = 1:0-1:4 - >----< -1| pass - [Domain] = 0:0-1:8 >-------------- 0| class MyClass: diff --git a/data/fixtures/scopes/python/interior.function.scope b/data/fixtures/scopes/python/interior.function.scope index b5e6a0404a..9acf9a45a3 100644 --- a/data/fixtures/scopes/python/interior.function.scope +++ b/data/fixtures/scopes/python/interior.function.scope @@ -2,18 +2,11 @@ def foo(): pass --- -[#1 Content] = 1:4-1:8 +[#1 Content] = +[#1 Removal] = 1:4-1:8 >----< 1| pass -[#1 Removal] = 1:0-1:8 - >--------< -1| pass - -[#1 Leading delimiter] = 1:0-1:4 - >----< -1| pass - [#1 Domain] = 0:0-1:8 >---------- 0| def foo(): diff --git a/data/fixtures/scopes/python/interior.function2.scope b/data/fixtures/scopes/python/interior.function2.scope index a96e9e5bd2..d3b7564582 100644 --- a/data/fixtures/scopes/python/interior.function2.scope +++ b/data/fixtures/scopes/python/interior.function2.scope @@ -3,18 +3,11 @@ def bar(value: string) -> str: return value --- -[#1 Content] = 2:4-2:16 +[#1 Content] = +[#1 Removal] = 2:4-2:16 >------------< 2| return value -[#1 Removal] = 2:0-2:16 - >----------------< -2| return value - -[#1 Leading delimiter] = 2:0-2:4 - >----< -2| return value - [#1 Domain] = 0:0-2:16 >---- 0| @foo diff --git a/data/fixtures/scopes/python/interior.lambda.scope b/data/fixtures/scopes/python/interior.lambda.scope index af3d0efee5..a889bc2430 100644 --- a/data/fixtures/scopes/python/interior.lambda.scope +++ b/data/fixtures/scopes/python/interior.lambda.scope @@ -1,18 +1,11 @@ lambda: pass --- -[Content] = 0:8-0:12 +[Content] = +[Removal] = 0:8-0:12 >----< 0| lambda: pass -[Removal] = 0:7-0:12 - >-----< -0| lambda: pass - -[Leading delimiter] = 0:7-0:8 - >-< -0| lambda: pass - [Domain] = 0:0-0:12 >------------< 0| lambda: pass diff --git a/data/fixtures/scopes/python/interior.lambda2.scope b/data/fixtures/scopes/python/interior.lambda2.scope index 76ce203320..af724a71b6 100644 --- a/data/fixtures/scopes/python/interior.lambda2.scope +++ b/data/fixtures/scopes/python/interior.lambda2.scope @@ -1,18 +1,11 @@ lambda x: x --- -[Content] = 0:10-0:11 +[Content] = +[Removal] = 0:10-0:11 >-< 0| lambda x: x -[Removal] = 0:9-0:11 - >--< -0| lambda x: x - -[Leading delimiter] = 0:9-0:10 - >-< -0| lambda x: x - [Domain] = 0:0-0:11 >-----------< 0| lambda x: x diff --git a/data/fixtures/scopes/talon/interior.command.scope b/data/fixtures/scopes/talon/interior.command.scope index 9f5429cc96..3b1f340e92 100644 --- a/data/fixtures/scopes/talon/interior.command.scope +++ b/data/fixtures/scopes/talon/interior.command.scope @@ -2,18 +2,11 @@ do something: a = 2 --- -[Content] = 1:4-1:9 +[Content] = +[Removal] = 1:4-1:9 >-----< 1| a = 2 -[Removal] = 1:0-1:9 - >---------< -1| a = 2 - -[Leading delimiter] = 1:0-1:4 - >----< -1| a = 2 - [Domain] = 0:0-1:9 >------------- 0| do something: diff --git a/packages/common/src/scopeSupportFacets/javascript.ts b/packages/common/src/scopeSupportFacets/javascript.ts index c579ae4b58..bb4ac12161 100644 --- a/packages/common/src/scopeSupportFacets/javascript.ts +++ b/packages/common/src/scopeSupportFacets/javascript.ts @@ -114,8 +114,6 @@ export const javascriptCoreScopeSupport: LanguageScopeSupportFacetMap = { "value.field": supported, "collectionItem.unenclosed": supported, - - "interior.element": supported, }; export const javascriptJsxScopeSupport: LanguageScopeSupportFacetMap = { @@ -126,6 +124,7 @@ export const javascriptJsxScopeSupport: LanguageScopeSupportFacetMap = { attribute: supported, "key.attribute": supported, "value.attribute": supported, + "interior.element": supported, }; export const javascriptScopeSupport: LanguageScopeSupportFacetMap = { diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index 81e1a9847c..27d3207d0f 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -717,6 +717,7 @@ export const scopeSupportFacetInfos: Record< description: "The body of code cell in markdown", scopeType: "interior", }, + notebookCell: { description: "A cell in a notebook or a markdown code block", scopeType: "notebookCell", diff --git a/packages/cursorless-engine/src/processTargets/targets/InteriorTarget.ts b/packages/cursorless-engine/src/processTargets/targets/InteriorTarget.ts index d7a38da9ef..afdcc3f9b3 100644 --- a/packages/cursorless-engine/src/processTargets/targets/InteriorTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/InteriorTarget.ts @@ -1,8 +1,7 @@ import type { Range } from "@cursorless/common"; +import { shrinkRangeToFitContent } from "../../util/selectionUtils"; import type { MinimumTargetParameters } from "./BaseTarget"; import { BaseTarget } from "./BaseTarget"; -import { shrinkRangeToFitContent } from "../../util/selectionUtils"; -import { createContinuousRangeFromRanges } from "./util/createContinuousRange"; export interface InteriorTargetParameters extends MinimumTargetParameters { readonly fullInteriorRange: Range; @@ -42,11 +41,8 @@ export class InteriorTarget extends BaseTarget { return new InteriorTarget({ ...this.getCloneParameters(), isReversed, - fullInteriorRange: createContinuousRangeFromRanges( - this.fullInteriorRange, + fullInteriorRange: this.fullInteriorRange.union( endTarget.fullInteriorRange, - true, - true, ), }); } diff --git a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts index 5b460bda1b..9622731d2a 100644 --- a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts @@ -3,10 +3,6 @@ import type { Target } from "../../typings/target.types"; import type { CommonTargetParameters } from "./BaseTarget"; import { BaseTarget } from "./BaseTarget"; import { PlainTarget } from "./PlainTarget"; -import { - createContinuousRange, - createContinuousRangeFromRanges, -} from "./util/createContinuousRange"; import { getDelimitedSequenceRemovalRange } from "./util/insertionRemovalBehaviors/DelimitedSequenceInsertionRemovalBehavior"; import { getTokenLeadingDelimiterTarget, @@ -93,11 +89,8 @@ export class ScopeTypeTarget extends BaseTarget { const contentRemovalRange = this.removalRange_ != null || endTarget.removalRange_ != null - ? createContinuousRangeFromRanges( - this.removalRange_ ?? this.contentRange, + ? (this.removalRange_ ?? this.contentRange).union( endTarget.removalRange_ ?? endTarget.contentRange, - true, - true, ) : undefined; @@ -107,7 +100,7 @@ export class ScopeTypeTarget extends BaseTarget { leadingDelimiterRange: this.leadingDelimiterRange_, trailingDelimiterRange: endTarget.trailingDelimiterRange_, removalRange: contentRemovalRange, - contentRange: createContinuousRange(this, endTarget, true, true), + contentRange: this.contentRange.union(endTarget.contentRange), }); } diff --git a/packages/cursorless-engine/src/processTargets/targets/util/createContinuousRange.ts b/packages/cursorless-engine/src/processTargets/targets/util/createContinuousRange.ts index 02a2777f34..f9a904bf32 100644 --- a/packages/cursorless-engine/src/processTargets/targets/util/createContinuousRange.ts +++ b/packages/cursorless-engine/src/processTargets/targets/util/createContinuousRange.ts @@ -15,7 +15,7 @@ export function createContinuousRange( ); } -export function createContinuousRangeFromRanges( +function createContinuousRangeFromRanges( startRange: Range, endRange: Range, includeStart: boolean, From f51daaf913af522b21907f3816b69d98f42a75a5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 17:50:08 +0100 Subject: [PATCH 11/39] Rename --- .../common/src/types/command/PartialTargetDescriptor.types.ts | 2 +- .../src/processTargets/modifiers/InteriorStage.ts | 2 +- .../modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts | 2 +- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 2 +- .../cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts | 2 +- .../src/spokenForms/defaultSpokenFormMapCore.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 25bab4d720..e568f4d4a8 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -155,7 +155,7 @@ export const simpleScopeTypeTypes = [ "ifStatement", "instance", "interior", - "interiorTreeOnly", + "interiorFallback", "list", "map", "name", diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 6b851a1c0f..74c8b6bf84 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -23,7 +23,7 @@ export class InteriorOnlyStage implements ModifierStage { const containingInteriorStage = this.modifierStageFactory.create({ type: "containingScope", scopeType: { - type: target.hasExplicitScopeType ? "interiorTreeOnly" : "interior", + type: target.hasExplicitScopeType ? "interiorFallback" : "interior", }, }); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 0c6ab2e67f..21f657606a 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -121,7 +121,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { languageId, ); case "interior": - case "interiorTreeOnly": + case "interiorFallback": return new InteriorScopeHandler( this, this.languageDefinitions, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 6943cbcc26..90c842a4e5 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -52,7 +52,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { return pairInteriorScopeHandler; } - if (scopeType.type === "interiorTreeOnly") { + if (scopeType.type === "interiorFallback") { return FallbackScopeHandler.createFromScopeHandlers([ languageScopeHandler, pairInteriorScopeHandler, diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index 3813676283..c3afd7b66f 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -127,7 +127,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "ifStatement": case "instance": case "interior": - case "interiorTreeOnly": + case "interiorFallback": case "list": case "map": case "name": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index e3b2f3591e..2c879c58a5 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -105,7 +105,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { disqualifyDelimiter: isPrivate("disqualify delimiter"), pairDelimiter: isPrivate("pair delimiter"), interior: isPrivate("inside"), - interiorTreeOnly: isPrivate("inside tree only"), + interiorFallback: isPrivate("inside fallback"), ["private.fieldAccess"]: isPrivate("access"), ["private.switchStatementSubject"]: isPrivate("subject"), }, From c8e3d086e5d27dc2d06ebfdc4eb740bac15c215c Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 26 Jan 2025 17:54:38 +0100 Subject: [PATCH 12/39] Clean up --- .../src/processTargets/modifiers/ContainingScopeStage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts index bf74445f54..dff36ba8a7 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts @@ -52,7 +52,7 @@ export class ContainingScopeStage implements ModifierStage { ); if (containingScopes == null) { - throw new NoContainingScopeError(this.modifier.scopeType.type); + throw new NoContainingScopeError(scopeType.type); } return containingScopes; From edb168622bf07aa07edaf4873461815e743e102c Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 28 Jan 2025 22:26:58 +0100 Subject: [PATCH 13/39] Update test --- .../scopes/markdown/interior.cell.scope | 112 +++++------------- 1 file changed, 32 insertions(+), 80 deletions(-) diff --git a/data/fixtures/scopes/markdown/interior.cell.scope b/data/fixtures/scopes/markdown/interior.cell.scope index 51679096c5..9bf5718ecd 100644 --- a/data/fixtures/scopes/markdown/interior.cell.scope +++ b/data/fixtures/scopes/markdown/interior.cell.scope @@ -9,156 +9,108 @@ hello --- -[#1 Content] = -[#1 Removal] = 0:1-0:1 - >< -0| ```python - -[#1 Domain] = 0:0-0:2 - >--< +[#1 Content] = 0:3-2:8 + >------ 0| ```python - -[#1 Insertion delimiter] = " " - - -[#2 Content] = 1:0-2:8 - >---------- 1| def foo(): 2| pass --------< -[#2 Removal] = 0:9-3:0 - > +[#1 Removal] = 0:3-3:0 + >------ 0| ```python 1| def foo(): 2| pass 3| ``` < -[#2 Domain] = 0:0-4:0 +[#1 Domain] = 0:0-3:3 >--------- 0| ```python 1| def foo(): 2| pass 3| ``` -4| - < + ---< -[#2 Insertion delimiter] = " " +[#1 Insertion delimiter] = " " -[#3 Content] = 0:3-2:8 - >------ -0| ```python +[#2 Content] = 1:0-2:8 + >---------- 1| def foo(): 2| pass --------< -[#3 Removal] = 0:3-3:0 - >------ +[#2 Removal] = 0:9-3:0 + > 0| ```python 1| def foo(): 2| pass 3| ``` < -[#3 Domain] = 0:2-3:1 - >------- +[#2 Domain] = 0:0-4:0 + >--------- 0| ```python 1| def foo(): 2| pass 3| ``` - -< +4| + < -[#3 Insertion delimiter] = " " +[#2 Insertion delimiter] = " " -[#4 Content] = -[#4 Removal] = 1:8-1:8 +[#3 Content] = +[#3 Removal] = 1:8-1:8 >< 1| def foo(): -[#4 Domain] = 1:7-1:9 +[#3 Domain] = 1:7-1:9 >--< 1| def foo(): -[#4 Insertion delimiter] = " " - - -[#5 Content] = -[#5 Removal] = 3:2-3:2 - >< -3| ``` - -[#5 Domain] = 3:1-3:3 - >--< -3| ``` - -[#5 Insertion delimiter] = " " - - -[#6 Content] = -[#6 Removal] = 5:1-5:1 - >< -5| ``` - -[#6 Domain] = 5:0-5:2 - >--< -5| ``` - -[#6 Insertion delimiter] = " " +[#3 Insertion delimiter] = " " -[#7 Content] = 6:0-6:5 +[#4 Content] = 6:0-6:5 >-----< 6| hello -[#7 Removal] = 5:3-7:0 +[#4 Removal] = 5:3-7:0 > 5| ``` 6| hello 7| ``` < -[#7 Domain] = 5:0-8:0 +[#4 Domain] = 5:0-7:3 >--- 5| ``` 6| hello 7| ``` -8| - < + ---< -[#7 Insertion delimiter] = " " +[#4 Insertion delimiter] = " " -[#8 Content] = 6:0-6:5 +[#5 Content] = 6:0-6:5 >-----< 6| hello -[#8 Removal] = 5:3-7:0 +[#5 Removal] = 5:3-7:0 > 5| ``` 6| hello 7| ``` < -[#8 Domain] = 5:2-7:1 - >- +[#5 Domain] = 5:0-8:0 + >--- 5| ``` 6| hello 7| ``` - -< - -[#8 Insertion delimiter] = " " - - -[#9 Content] = -[#9 Removal] = 7:2-7:2 - >< -7| ``` - -[#9 Domain] = 7:1-7:3 - >--< -7| ``` +8| + < -[#9 Insertion delimiter] = " " +[#5 Insertion delimiter] = " " From eb2e10caeba06bed055ceacdf3df4422541e496f Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 29 Jan 2025 11:57:31 +0100 Subject: [PATCH 14/39] Added tests --- .../modifiers/interior/changeInside.yml | 22 +++++++++++++++++ .../modifiers/interior/changeInside2.yml | 22 +++++++++++++++++ .../interior/changeInsideElement.yml | 24 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 data/fixtures/recorded/modifiers/interior/changeInside.yml create mode 100644 data/fixtures/recorded/modifiers/interior/changeInside2.yml create mode 100644 data/fixtures/recorded/modifiers/interior/changeInsideElement.yml diff --git a/data/fixtures/recorded/modifiers/interior/changeInside.yml b/data/fixtures/recorded/modifiers/interior/changeInside.yml new file mode 100644 index 0000000000..f8c61d871c --- /dev/null +++ b/data/fixtures/recorded/modifiers/interior/changeInside.yml @@ -0,0 +1,22 @@ +languageId: html +command: + version: 7 + spokenForm: change inside + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + usePrePhraseSnapshot: false +initialState: + documentContents:
content
+ selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: <>content + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/data/fixtures/recorded/modifiers/interior/changeInside2.yml b/data/fixtures/recorded/modifiers/interior/changeInside2.yml new file mode 100644 index 0000000000..ef90304905 --- /dev/null +++ b/data/fixtures/recorded/modifiers/interior/changeInside2.yml @@ -0,0 +1,22 @@ +languageId: html +command: + version: 7 + spokenForm: change inside + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + usePrePhraseSnapshot: false +initialState: + documentContents:
content
+ selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + marks: {} +finalState: + documentContents:
+ selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} diff --git a/data/fixtures/recorded/modifiers/interior/changeInsideElement.yml b/data/fixtures/recorded/modifiers/interior/changeInsideElement.yml new file mode 100644 index 0000000000..07635004f1 --- /dev/null +++ b/data/fixtures/recorded/modifiers/interior/changeInsideElement.yml @@ -0,0 +1,24 @@ +languageId: html +command: + version: 7 + spokenForm: change inside element + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + - type: containingScope + scopeType: {type: xmlElement} + usePrePhraseSnapshot: false +initialState: + documentContents:
content
+ selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents:
+ selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} From 2fe2d069ccb280e9da6d489a3cbb252cd74bf3d1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 29 Jan 2025 13:33:35 +0100 Subject: [PATCH 15/39] Refactoring --- .../modifiers/interior/changeInside3.yml | 22 ++++++++++ .../modifiers/interior/changeInsideToken.yml | 20 +++++++++ .../command/PartialTargetDescriptor.types.ts | 2 +- .../ModifierStageFactoryImpl.ts | 2 +- .../processTargets/modifiers/InteriorStage.ts | 44 ++++++++++++++----- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 2 +- .../InteriorScopeHandler.ts | 22 +++++----- .../src/scopeProviders/ScopeInfoProvider.ts | 2 +- .../spokenForms/defaultSpokenFormMapCore.ts | 2 +- 9 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 data/fixtures/recorded/modifiers/interior/changeInside3.yml create mode 100644 data/fixtures/recorded/modifiers/interior/changeInsideToken.yml diff --git a/data/fixtures/recorded/modifiers/interior/changeInside3.yml b/data/fixtures/recorded/modifiers/interior/changeInside3.yml new file mode 100644 index 0000000000..aa0638a03f --- /dev/null +++ b/data/fixtures/recorded/modifiers/interior/changeInside3.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 7 + spokenForm: change inside + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + usePrePhraseSnapshot: false +initialState: + documentContents: (hello) + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + marks: {} +finalState: + documentContents: () + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml new file mode 100644 index 0000000000..fe52048f9d --- /dev/null +++ b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml @@ -0,0 +1,20 @@ +languageId: plaintext +command: + version: 7 + spokenForm: change inside token + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: false +initialState: + documentContents: (hello) + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 6769622c78..5e82c0f680 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -156,7 +156,7 @@ export const simpleScopeTypeTypes = [ "ifStatement", "instance", "interior", - "interiorFallback", + "interiorParseTree", "list", "map", "name", diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index 15a1f14c47..c8f78202a8 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -57,7 +57,7 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { case "toRawSelection": return new RawSelectionStage(modifier); case "interiorOnly": - return new InteriorOnlyStage(this, modifier); + return new InteriorOnlyStage(this.scopeHandlerFactory, modifier); case "excludeInterior": return new ExcludeInteriorStage(this, modifier); case "leading": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 74c8b6bf84..94bfce0729 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -1,15 +1,18 @@ -import type { - ExcludeInteriorModifier, - InteriorOnlyModifier, +import { + NoContainingScopeError, + type ExcludeInteriorModifier, + type InteriorOnlyModifier, + type ScopeType, } from "@cursorless/common"; import type { Target } from "../../typings/target.types"; import type { ModifierStageFactory } from "../ModifierStageFactory"; import type { ModifierStage } from "../PipelineStages.types"; import { ModifyIfConditionStage } from "./ConditionalModifierStages"; +import type { ScopeHandlerFactory } from "./scopeHandlers"; export class InteriorOnlyStage implements ModifierStage { constructor( - private modifierStageFactory: ModifierStageFactory, + private scopeHandlerFactory: ScopeHandlerFactory, private modifier: InteriorOnlyModifier, ) {} @@ -20,14 +23,35 @@ export class InteriorOnlyStage implements ModifierStage { return interior; } - const containingInteriorStage = this.modifierStageFactory.create({ - type: "containingScope", - scopeType: { - type: target.hasExplicitScopeType ? "interiorFallback" : "interior", + const { editor, isReversed, contentRange } = target; + const scopeType: ScopeType = { type: "interior" }; + + const scopeHandler = this.scopeHandlerFactory.create( + { type: target.hasExplicitScopeType ? "interiorParseTree" : "interior" }, + editor.document.languageId, + ); + + const scopes = scopeHandler.generateScopes( + editor, + contentRange.start, + "forward", + { + containment: "required", + allowAdjacentScopes: true, + skipAncestorScopes: true, + distalPosition: contentRange.end, }, - }); + ); + + const targets = Array.from(scopes).flatMap((scope) => + scope.getTargets(isReversed), + ); + + if (targets.length === 0) { + throw new NoContainingScopeError(scopeType.type); + } - return containingInteriorStage.run(target); + return targets; } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 21f657606a..5e8a8a1857 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -121,7 +121,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { languageId, ); case "interior": - case "interiorFallback": + case "interiorParseTree": return new InteriorScopeHandler( this, this.languageDefinitions, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 90c842a4e5..00ada78b65 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -35,6 +35,17 @@ export class InteriorScopeHandler extends BaseScopeHandler { super(); this.scopeHandler = (() => { + const languageScopeHandler = languageDefinitions + .get(languageId) + ?.getScopeHandler(this.scopeType); + + if (scopeType.type === "interiorParseTree") { + if (languageScopeHandler != null) { + return languageScopeHandler; + } + return FallbackScopeHandler.createFromScopeHandlers([]); + } + const pairInteriorScopeHandler = scopeHandlerFactory.create( { type: "surroundingPairInterior", @@ -44,21 +55,10 @@ export class InteriorScopeHandler extends BaseScopeHandler { languageId, ); - const languageScopeHandler = languageDefinitions - .get(languageId) - ?.getScopeHandler(this.scopeType); - if (languageScopeHandler == null) { return pairInteriorScopeHandler; } - if (scopeType.type === "interiorFallback") { - return FallbackScopeHandler.createFromScopeHandlers([ - languageScopeHandler, - pairInteriorScopeHandler, - ]); - } - return OneOfScopeHandler.createFromScopeHandlers( scopeHandlerFactory, { diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index c3afd7b66f..ad1c2206f3 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -127,7 +127,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "ifStatement": case "instance": case "interior": - case "interiorFallback": + case "interiorParseTree": case "list": case "map": case "name": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 88ad41e1d9..e6ea9d6253 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -106,7 +106,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { disqualifyDelimiter: isPrivate("disqualify delimiter"), pairDelimiter: isPrivate("pair delimiter"), interior: isPrivate("inside"), - interiorFallback: isPrivate("inside fallback"), + interiorParseTree: isPrivate("inside parse tree"), ["private.fieldAccess"]: isPrivate("access"), ["private.switchStatementSubject"]: isPrivate("subject"), }, From 1d28b12e235112e9910e08a43a6bfa307372b242 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 29 Jan 2025 13:50:08 +0100 Subject: [PATCH 16/39] Refactor --- .../scopeSupportFacets/scopeSupportFacetInfos.ts | 14 +++++++------- .../types/command/PartialTargetDescriptor.types.ts | 10 ++++++++-- .../primitiveTargetToSpokenForm.ts | 3 +++ .../src/languages/LanguageDefinition.ts | 3 +-- .../TreeSitterQuery/validateQueryCaptures.ts | 2 +- .../src/processTargets/modifiers/InteriorStage.ts | 7 +++++-- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 1 - .../InteriorScopeHandler.ts | 6 +++--- .../TreeSitterIterationScopeHandler.ts | 8 ++------ .../TreeSitterScopeHandler.ts | 4 ++-- .../src/processTargets/targets/ScopeTypeTarget.ts | 8 ++++---- .../src/scopeProviders/ScopeInfoProvider.ts | 1 - .../src/spokenForms/defaultSpokenFormMapCore.ts | 2 -- 13 files changed, 36 insertions(+), 33 deletions(-) diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index e8dd73144f..3392687be2 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -695,31 +695,31 @@ export const scopeSupportFacetInfos: Record< "interior.class": { description: "The body of a class", - scopeType: "interior", + scopeType: { type: "interior" }, }, "interior.function": { description: "The body of a function", - scopeType: "interior", + scopeType: { type: "interior" }, }, "interior.lambda": { description: "The body of a lambda/anonymous function", - scopeType: "interior", + scopeType: { type: "interior" }, }, "interior.branch": { description: "The body of an if/elif/else branch", - scopeType: "interior", + scopeType: { type: "interior" }, }, "interior.element": { description: "The interior/children of an xml element", - scopeType: "interior", + scopeType: { type: "interior" }, }, "interior.command": { description: "The body of a Talon command", - scopeType: "interior", + scopeType: { type: "interior" }, }, "interior.cell": { description: "The body of code cell in markdown", - scopeType: "interior", + scopeType: { type: "interior" }, }, notebookCell: { diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 5e82c0f680..ec18748fab 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -155,8 +155,6 @@ export const simpleScopeTypeTypes = [ "functionName", "ifStatement", "instance", - "interior", - "interiorParseTree", "list", "map", "name", @@ -229,6 +227,11 @@ export interface CustomRegexScopeType { flags?: string; } +export interface InteriorScopeType { + type: "interior"; + parseTreeOnly?: boolean; +} + export type SurroundingPairDirection = "left" | "right"; export interface SurroundingPairScopeType { @@ -276,9 +279,12 @@ export type ScopeType = | SurroundingPairScopeType | SurroundingPairInteriorScopeType | CustomRegexScopeType + | InteriorScopeType | OneOfScopeType | GlyphScopeType; +export type ScopeTypeType = ScopeType["type"]; + export interface ContainingSurroundingPairModifier extends ContainingScopeModifier { scopeType: SurroundingPairScopeType; diff --git a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index a2046e7838..9b657aedb8 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -267,6 +267,9 @@ export class PrimitiveTargetSpokenFormGenerator { } ); + case "interior": + return this.spokenFormMap.simpleModifier.interiorOnly; + default: return this.spokenFormMap.simpleScopeTypeType[scopeType.type]; } diff --git a/packages/cursorless-engine/src/languages/LanguageDefinition.ts b/packages/cursorless-engine/src/languages/LanguageDefinition.ts index 93e93fb6d0..3471f04841 100644 --- a/packages/cursorless-engine/src/languages/LanguageDefinition.ts +++ b/packages/cursorless-engine/src/languages/LanguageDefinition.ts @@ -1,7 +1,6 @@ import type { RawTreeSitterQueryProvider, ScopeType, - SimpleScopeType, SimpleScopeTypeType, TreeSitter, } from "@cursorless/common"; @@ -78,7 +77,7 @@ export class LanguageDefinition { return undefined; } - return new TreeSitterScopeHandler(this.query, scopeType as SimpleScopeType); + return new TreeSitterScopeHandler(this.query, scopeType); } /** diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts index 89763b319b..594b755129 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/validateQueryCaptures.ts @@ -2,7 +2,7 @@ import { showError, simpleScopeTypeTypes } from "@cursorless/common"; import { ide } from "../../singletons/ide.singleton"; const wildcard = "_"; -const captureNames = [wildcard, ...simpleScopeTypeTypes]; +const captureNames = [wildcard, "interior", ...simpleScopeTypeTypes]; const positionRelationships = ["prefix", "leading", "trailing"]; const positionSuffixes = [ diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 94bfce0729..651c2e57dc 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -24,10 +24,13 @@ export class InteriorOnlyStage implements ModifierStage { } const { editor, isReversed, contentRange } = target; - const scopeType: ScopeType = { type: "interior" }; + const scopeType: ScopeType = { + type: "interior", + parseTreeOnly: target.hasExplicitScopeType, + }; const scopeHandler = this.scopeHandlerFactory.create( - { type: target.hasExplicitScopeType ? "interiorParseTree" : "interior" }, + scopeType, editor.document.languageId, ); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 5e8a8a1857..8c9dc3c46d 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -121,7 +121,6 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { languageId, ); case "interior": - case "interiorParseTree": return new InteriorScopeHandler( this, this.languageDefinitions, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 00ada78b65..8d3234f45f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -1,8 +1,8 @@ import { type Direction, + type InteriorScopeType, type Position, type ScopeType, - type SimpleScopeType, type TextEditor, } from "@cursorless/common"; import type { LanguageDefinitions } from "../../../../languages/LanguageDefinitions"; @@ -29,7 +29,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { constructor( private scopeHandlerFactory: ScopeHandlerFactory, languageDefinitions: LanguageDefinitions, - scopeType: SimpleScopeType, + scopeType: InteriorScopeType, private languageId: string, ) { super(); @@ -39,7 +39,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { .get(languageId) ?.getScopeHandler(this.scopeType); - if (scopeType.type === "interiorParseTree") { + if (scopeType.parseTreeOnly) { if (languageScopeHandler != null) { return languageScopeHandler; } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts index 55c299c4cb..47f067f4ab 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts @@ -1,8 +1,4 @@ -import type { - ScopeType, - SimpleScopeType, - TextEditor, -} from "@cursorless/common"; +import type { ScopeType, TextEditor } from "@cursorless/common"; import type { TreeSitterQuery } from "../../../../languages/TreeSitterQuery"; import type { QueryMatch } from "../../../../languages/TreeSitterQuery/QueryCapture"; import { PlainTarget } from "../../../targets"; @@ -26,7 +22,7 @@ export class TreeSitterIterationScopeHandler extends BaseTreeSitterScopeHandler constructor( query: TreeSitterQuery, /** The scope type for which we are the iteration scope */ - private iterateeScopeType: SimpleScopeType, + private iterateeScopeType: ScopeType, ) { super(query); } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts index 68ebcb9d5d..4665e28e91 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts @@ -1,4 +1,4 @@ -import type { SimpleScopeType, TextEditor } from "@cursorless/common"; +import type { ScopeType, TextEditor } from "@cursorless/common"; import type { TreeSitterQuery } from "../../../../languages/TreeSitterQuery"; import type { QueryMatch } from "../../../../languages/TreeSitterQuery/QueryCapture"; import { InteriorTarget } from "../../../targets"; @@ -18,7 +18,7 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { constructor( query: TreeSitterQuery, - public scopeType: SimpleScopeType, + public scopeType: ScopeType, ) { super(query); } diff --git a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts index 9622731d2a..f54ec18d2a 100644 --- a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts @@ -1,4 +1,4 @@ -import type { Range, SimpleScopeTypeType } from "@cursorless/common"; +import type { Range, ScopeTypeType } from "@cursorless/common"; import type { Target } from "../../typings/target.types"; import type { CommonTargetParameters } from "./BaseTarget"; import { BaseTarget } from "./BaseTarget"; @@ -11,7 +11,7 @@ import { } from "./util/insertionRemovalBehaviors/TokenInsertionRemovalBehavior"; export interface ScopeTypeTargetParameters extends CommonTargetParameters { - readonly scopeTypeType: SimpleScopeTypeType; + readonly scopeTypeType: ScopeTypeType; readonly insertionDelimiter?: string; readonly prefixRange?: Range; readonly removalRange?: Range; @@ -21,7 +21,7 @@ export interface ScopeTypeTargetParameters extends CommonTargetParameters { export class ScopeTypeTarget extends BaseTarget { type = "ScopeTypeTarget"; - private scopeTypeType_: SimpleScopeTypeType; + private scopeTypeType_: ScopeTypeType; private removalRange_?: Range; private leadingDelimiterRange_?: Range; private trailingDelimiterRange_?: Range; @@ -118,7 +118,7 @@ export class ScopeTypeTarget extends BaseTarget { } } -function getInsertionDelimiter(scopeType: SimpleScopeTypeType): string { +function getInsertionDelimiter(scopeType: ScopeTypeType): string { switch (scopeType) { case "class": case "namedFunction": diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index ad1c2206f3..4ca19be2c2 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -127,7 +127,6 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "ifStatement": case "instance": case "interior": - case "interiorParseTree": case "list": case "map": case "name": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index e6ea9d6253..40c2794fdc 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -105,8 +105,6 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { textFragment: isPrivate("text fragment"), disqualifyDelimiter: isPrivate("disqualify delimiter"), pairDelimiter: isPrivate("pair delimiter"), - interior: isPrivate("inside"), - interiorParseTree: isPrivate("inside parse tree"), ["private.fieldAccess"]: isPrivate("access"), ["private.switchStatementSubject"]: isPrivate("subject"), }, From bdea8477c5e7102c9e20bea64c94d5ecfe5d0263 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 29 Jan 2025 14:09:47 +0100 Subject: [PATCH 17/39] Simplification --- .../modifiers/interior/changeInside4.yml | 22 ++++++++++ .../ModifierStageFactoryImpl.ts | 2 +- .../processTargets/modifiers/InteriorStage.ts | 42 ++++--------------- 3 files changed, 31 insertions(+), 35 deletions(-) create mode 100644 data/fixtures/recorded/modifiers/interior/changeInside4.yml diff --git a/data/fixtures/recorded/modifiers/interior/changeInside4.yml b/data/fixtures/recorded/modifiers/interior/changeInside4.yml new file mode 100644 index 0000000000..362d8c265c --- /dev/null +++ b/data/fixtures/recorded/modifiers/interior/changeInside4.yml @@ -0,0 +1,22 @@ +languageId: html +command: + version: 7 + spokenForm: change inside + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: interiorOnly} + usePrePhraseSnapshot: false +initialState: + documentContents:
content
(hello) + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 23} + marks: {} +finalState: + documentContents:
) + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index c8f78202a8..15a1f14c47 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -57,7 +57,7 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { case "toRawSelection": return new RawSelectionStage(modifier); case "interiorOnly": - return new InteriorOnlyStage(this.scopeHandlerFactory, modifier); + return new InteriorOnlyStage(this, modifier); case "excludeInterior": return new ExcludeInteriorStage(this, modifier); case "leading": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 651c2e57dc..6ead03e5a2 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -1,18 +1,15 @@ import { - NoContainingScopeError, type ExcludeInteriorModifier, type InteriorOnlyModifier, - type ScopeType, } from "@cursorless/common"; import type { Target } from "../../typings/target.types"; import type { ModifierStageFactory } from "../ModifierStageFactory"; import type { ModifierStage } from "../PipelineStages.types"; import { ModifyIfConditionStage } from "./ConditionalModifierStages"; -import type { ScopeHandlerFactory } from "./scopeHandlers"; export class InteriorOnlyStage implements ModifierStage { constructor( - private scopeHandlerFactory: ScopeHandlerFactory, + private modifierHandlerFactory: ModifierStageFactory, private modifier: InteriorOnlyModifier, ) {} @@ -23,38 +20,15 @@ export class InteriorOnlyStage implements ModifierStage { return interior; } - const { editor, isReversed, contentRange } = target; - const scopeType: ScopeType = { - type: "interior", - parseTreeOnly: target.hasExplicitScopeType, - }; - - const scopeHandler = this.scopeHandlerFactory.create( - scopeType, - editor.document.languageId, - ); - - const scopes = scopeHandler.generateScopes( - editor, - contentRange.start, - "forward", - { - containment: "required", - allowAdjacentScopes: true, - skipAncestorScopes: true, - distalPosition: contentRange.end, + const containingModifier = this.modifierHandlerFactory.create({ + type: "containingScope", + scopeType: { + type: "interior", + parseTreeOnly: target.hasExplicitScopeType, }, - ); - - const targets = Array.from(scopes).flatMap((scope) => - scope.getTargets(isReversed), - ); - - if (targets.length === 0) { - throw new NoContainingScopeError(scopeType.type); - } + }); - return targets; + return containingModifier.run(target); } } From 3f95faf882797ed983987b8d98e4ca8511229410 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 29 Jan 2025 15:17:33 +0100 Subject: [PATCH 18/39] Update test --- .../modifiers/interior/changeInsideToken.yml | 6 +++++- .../types/command/PartialTargetDescriptor.types.ts | 2 +- .../src/processTargets/modifiers/InteriorStage.ts | 2 +- .../InteriorScopeHandler.ts | 14 +++++++------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml index fe52048f9d..aee4b5d2d5 100644 --- a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml +++ b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml @@ -17,4 +17,8 @@ initialState: - anchor: {line: 0, character: 3} active: {line: 0, character: 3} marks: {} -thrownError: {name: NoContainingScopeError} +finalState: + documentContents: () + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index ec18748fab..0e83a05bf9 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -229,7 +229,7 @@ export interface CustomRegexScopeType { export interface InteriorScopeType { type: "interior"; - parseTreeOnly?: boolean; + useFallback?: boolean; } export type SurroundingPairDirection = "left" | "right"; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 6ead03e5a2..88a36ab19b 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -24,7 +24,7 @@ export class InteriorOnlyStage implements ModifierStage { type: "containingScope", scopeType: { type: "interior", - parseTreeOnly: target.hasExplicitScopeType, + useFallback: target.hasExplicitScopeType, }, }); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 8d3234f45f..b3ad752489 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -39,13 +39,6 @@ export class InteriorScopeHandler extends BaseScopeHandler { .get(languageId) ?.getScopeHandler(this.scopeType); - if (scopeType.parseTreeOnly) { - if (languageScopeHandler != null) { - return languageScopeHandler; - } - return FallbackScopeHandler.createFromScopeHandlers([]); - } - const pairInteriorScopeHandler = scopeHandlerFactory.create( { type: "surroundingPairInterior", @@ -59,6 +52,13 @@ export class InteriorScopeHandler extends BaseScopeHandler { return pairInteriorScopeHandler; } + if (scopeType.useFallback) { + return FallbackScopeHandler.createFromScopeHandlers([ + languageScopeHandler, + pairInteriorScopeHandler, + ]); + } + return OneOfScopeHandler.createFromScopeHandlers( scopeHandlerFactory, { From d558ac7307d4f2380594c595af5f54b44edae0a0 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 08:54:45 +0100 Subject: [PATCH 19/39] More fixes --- .../modifiers/interior/changeInsideToken.yml | 6 +-- .../command/PartialTargetDescriptor.types.ts | 2 +- .../processTargets/modifiers/InteriorStage.ts | 2 +- .../InteriorScopeHandler.ts | 39 +++++++++++++------ 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml index aee4b5d2d5..fe52048f9d 100644 --- a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml +++ b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml @@ -17,8 +17,4 @@ initialState: - anchor: {line: 0, character: 3} active: {line: 0, character: 3} marks: {} -finalState: - documentContents: () - selections: - - anchor: {line: 0, character: 1} - active: {line: 0, character: 1} +thrownError: {name: NoContainingScopeError} diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 0e83a05bf9..c1b2f9810e 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -229,7 +229,7 @@ export interface CustomRegexScopeType { export interface InteriorScopeType { type: "interior"; - useFallback?: boolean; + languageDefinitionOnly?: boolean; } export type SurroundingPairDirection = "left" | "right"; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 88a36ab19b..dd6ff42617 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -24,7 +24,7 @@ export class InteriorOnlyStage implements ModifierStage { type: "containingScope", scopeType: { type: "interior", - useFallback: target.hasExplicitScopeType, + languageDefinitionOnly: target.hasExplicitScopeType, }, }); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index b3ad752489..3e9b332799 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -1,4 +1,5 @@ import { + Range, type Direction, type InteriorScopeType, type Position, @@ -18,7 +19,6 @@ import { type ScopeHandler } from "../scopeHandler.types"; import type { ScopeHandlerFactory } from "../ScopeHandlerFactory"; export class InteriorScopeHandler extends BaseScopeHandler { - public scopeType: ScopeType = { type: "interior" }; protected isHierarchical = true; private scopeHandler: ScopeHandler; @@ -29,7 +29,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { constructor( private scopeHandlerFactory: ScopeHandlerFactory, languageDefinitions: LanguageDefinitions, - scopeType: InteriorScopeType, + public scopeType: InteriorScopeType, private languageId: string, ) { super(); @@ -39,6 +39,13 @@ export class InteriorScopeHandler extends BaseScopeHandler { .get(languageId) ?.getScopeHandler(this.scopeType); + if (scopeType.languageDefinitionOnly) { + if (languageScopeHandler == null) { + return FallbackScopeHandler.createFromScopeHandlers([]); + } + return languageScopeHandler; + } + const pairInteriorScopeHandler = scopeHandlerFactory.create( { type: "surroundingPairInterior", @@ -52,13 +59,6 @@ export class InteriorScopeHandler extends BaseScopeHandler { return pairInteriorScopeHandler; } - if (scopeType.useFallback) { - return FallbackScopeHandler.createFromScopeHandlers([ - languageScopeHandler, - pairInteriorScopeHandler, - ]); - } - return OneOfScopeHandler.createFromScopeHandlers( scopeHandlerFactory, { @@ -74,12 +74,29 @@ export class InteriorScopeHandler extends BaseScopeHandler { })(); } - generateScopeCandidates( + *generateScopeCandidates( editor: TextEditor, position: Position, direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { - return this.scopeHandler.generateScopes(editor, position, direction, hints); + const scopes = this.scopeHandler.generateScopes( + editor, + position, + direction, + hints, + ); + + if (!this.scopeType.languageDefinitionOnly) { + yield* scopes; + } + + const domain = new Range(position, hints.distalPosition); + + for (const scope of scopes) { + if (domain.contains(scope.domain)) { + yield scope; + } + } } } From ac3685977248d46301dd1c802ed20eb74beb4395 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 09:01:15 +0100 Subject: [PATCH 20/39] Added comments --- .../command/PartialTargetDescriptor.types.ts | 2 +- .../processTargets/modifiers/InteriorStage.ts | 2 +- .../InteriorScopeHandler.ts | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index c1b2f9810e..4f74edcd3a 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -229,7 +229,7 @@ export interface CustomRegexScopeType { export interface InteriorScopeType { type: "interior"; - languageDefinitionOnly?: boolean; + explicitScopeType?: boolean; } export type SurroundingPairDirection = "left" | "right"; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index dd6ff42617..8b3fefbe4e 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -24,7 +24,7 @@ export class InteriorOnlyStage implements ModifierStage { type: "containingScope", scopeType: { type: "interior", - languageDefinitionOnly: target.hasExplicitScopeType, + explicitScopeType: target.hasExplicitScopeType, }, }); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 3e9b332799..e1d1cc8a4c 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -39,7 +39,11 @@ export class InteriorScopeHandler extends BaseScopeHandler { .get(languageId) ?.getScopeHandler(this.scopeType); - if (scopeType.languageDefinitionOnly) { + // If the scope type is explicit (ie, the user has specified a scope + // type), then we don't want to include matching pairs. The user might + // have said something like "inside element" and then we don't want to + // yield `
`. + if (scopeType.explicitScopeType) { if (languageScopeHandler == null) { return FallbackScopeHandler.createFromScopeHandlers([]); } @@ -87,14 +91,20 @@ export class InteriorScopeHandler extends BaseScopeHandler { hints, ); - if (!this.scopeType.languageDefinitionOnly) { + // The user haven specified an explicit scope types. Just yield all matching scopes. + if (!this.scopeType.explicitScopeType) { yield* scopes; } - const domain = new Range(position, hints.distalPosition); + const targetDomain = new Range(position, hints.distalPosition); + // If the user has specified an explicit scope type, then we only want to + // yield scopes that is contained within the target domain. For example if + // the user said "inside token", then we don't want to yield scopes that are + // larger than the token. The definition of an interior is that it's + // inside the scope. for (const scope of scopes) { - if (domain.contains(scope.domain)) { + if (targetDomain.contains(scope.domain)) { yield scope; } } From 0fb7d86583fc6f25171fe3d4fc53c77edecd536b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 09:05:20 +0100 Subject: [PATCH 21/39] Update comments --- .../InteriorScopeHandler.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index e1d1cc8a4c..4210eba50b 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -42,7 +42,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { // If the scope type is explicit (ie, the user has specified a scope // type), then we don't want to include matching pairs. The user might // have said something like "inside element" and then we don't want to - // yield `
`. + // yield the interior of the `
` pair. if (scopeType.explicitScopeType) { if (languageScopeHandler == null) { return FallbackScopeHandler.createFromScopeHandlers([]); @@ -91,18 +91,17 @@ export class InteriorScopeHandler extends BaseScopeHandler { hints, ); - // The user haven specified an explicit scope types. Just yield all matching scopes. + // No explicit scope type. Just yield all matching scopes. if (!this.scopeType.explicitScopeType) { yield* scopes; } const targetDomain = new Range(position, hints.distalPosition); - // If the user has specified an explicit scope type, then we only want to - // yield scopes that is contained within the target domain. For example if - // the user said "inside token", then we don't want to yield scopes that are - // larger than the token. The definition of an interior is that it's - // inside the scope. + // For an explicit scope type we only yield scopes that are contained within + // the target domain. E.g the user said "inside token", then we don't want + // to yield scopes that are larger than the token. The definition of an + // interior is that it's inside the scope. for (const scope of scopes) { if (targetDomain.contains(scope.domain)) { yield scope; From b8565a72f48ccc9e18bbcd7d34cf0b2a5b623cf9 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 09:12:04 +0100 Subject: [PATCH 22/39] More cleanup --- .../scopeHandlers/FallbackScopeHandler.ts | 29 +++++++------------ .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 2 +- .../InteriorScopeHandler.ts | 12 +++++--- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts index 8a9368a372..869d4993b3 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts @@ -24,23 +24,11 @@ export class FallbackScopeHandler extends BaseScopeHandler { ); } - static create( - scopeHandlerFactory: ScopeHandlerFactory, - scopeType: FallbackScopeType, - languageId: string, - ): ScopeHandler { - const scopeHandlers: ScopeHandler[] = scopeType.scopeTypes.map( - (scopeType) => scopeHandlerFactory.create(scopeType, languageId), - ); - - return this.createFromScopeHandlers(scopeHandlers); - } - - static createFromScopeHandlers(scopeHandlers: ScopeHandler[]): ScopeHandler { - return new FallbackScopeHandler(scopeHandlers); - } - - private constructor(private scopeHandlers: ScopeHandler[]) { + constructor( + public scopeHandlerFactory: ScopeHandlerFactory, + private fallbackScopeType: FallbackScopeType, + private languageId: string, + ) { super(); } @@ -50,7 +38,12 @@ export class FallbackScopeHandler extends BaseScopeHandler { direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { - for (const scopeHandler of this.scopeHandlers) { + const scopeHandlers: ScopeHandler[] = this.fallbackScopeType.scopeTypes.map( + (scopeType) => + this.scopeHandlerFactory.create(scopeType, this.languageId), + ); + + for (const scopeHandler of scopeHandlers) { yield* scopeHandler.generateScopes(editor, position, direction, hints); } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 8c9dc3c46d..657981240c 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -132,7 +132,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { case "oneOf": return OneOfScopeHandler.create(this, scopeType, languageId); case "fallback": - return FallbackScopeHandler.create(this, scopeType, languageId); + return new FallbackScopeHandler(this, scopeType, languageId); case "conditional": return new ConditionalScopeHandler(this, scopeType, languageId); case "instance": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 4210eba50b..a4f99968b0 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -1,4 +1,5 @@ import { + NoContainingScopeError, Range, type Direction, type InteriorScopeType, @@ -8,7 +9,6 @@ import { } from "@cursorless/common"; import type { LanguageDefinitions } from "../../../../languages/LanguageDefinitions"; import { BaseScopeHandler } from "../BaseScopeHandler"; -import { FallbackScopeHandler } from "../FallbackScopeHandler"; import { OneOfScopeHandler } from "../OneOfScopeHandler"; import type { TargetScope } from "../scope.types"; import type { @@ -20,10 +20,10 @@ import type { ScopeHandlerFactory } from "../ScopeHandlerFactory"; export class InteriorScopeHandler extends BaseScopeHandler { protected isHierarchical = true; - private scopeHandler: ScopeHandler; + private scopeHandler: ScopeHandler | undefined; get iterationScopeType(): ScopeType | ComplexScopeType { - return this.scopeHandler.iterationScopeType; + throw new NoContainingScopeError("Iteration scope for interior"); } constructor( @@ -45,7 +45,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { // yield the interior of the `
` pair. if (scopeType.explicitScopeType) { if (languageScopeHandler == null) { - return FallbackScopeHandler.createFromScopeHandlers([]); + return undefined; } return languageScopeHandler; } @@ -84,6 +84,10 @@ export class InteriorScopeHandler extends BaseScopeHandler { direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { + if (this.scopeHandler == null) { + return; + } + const scopes = this.scopeHandler.generateScopes( editor, position, From 7a9620f4ece9d85aa25f3f7068b4d4ad63426f1e Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 09:20:29 +0100 Subject: [PATCH 23/39] More clean up --- .../command/PartialTargetDescriptor.types.ts | 2 + .../InteriorScopeHandler.ts | 105 +++++++++--------- 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 4f74edcd3a..5728a12a35 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -229,6 +229,8 @@ export interface CustomRegexScopeType { export interface InteriorScopeType { type: "interior"; + + // The user has specified a scope type. eg "inside element". explicitScopeType?: boolean; } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index a4f99968b0..0494caea6f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -20,62 +20,20 @@ import type { ScopeHandlerFactory } from "../ScopeHandlerFactory"; export class InteriorScopeHandler extends BaseScopeHandler { protected isHierarchical = true; - private scopeHandler: ScopeHandler | undefined; - - get iterationScopeType(): ScopeType | ComplexScopeType { - throw new NoContainingScopeError("Iteration scope for interior"); - } constructor( private scopeHandlerFactory: ScopeHandlerFactory, - languageDefinitions: LanguageDefinitions, - public scopeType: InteriorScopeType, + private languageDefinitions: LanguageDefinitions, + public readonly scopeType: InteriorScopeType, private languageId: string, ) { super(); + } - this.scopeHandler = (() => { - const languageScopeHandler = languageDefinitions - .get(languageId) - ?.getScopeHandler(this.scopeType); - - // If the scope type is explicit (ie, the user has specified a scope - // type), then we don't want to include matching pairs. The user might - // have said something like "inside element" and then we don't want to - // yield the interior of the `
` pair. - if (scopeType.explicitScopeType) { - if (languageScopeHandler == null) { - return undefined; - } - return languageScopeHandler; - } - - const pairInteriorScopeHandler = scopeHandlerFactory.create( - { - type: "surroundingPairInterior", - delimiter: "any", - allowWeakContainment: true, - }, - languageId, - ); - - if (languageScopeHandler == null) { - return pairInteriorScopeHandler; - } - - return OneOfScopeHandler.createFromScopeHandlers( - scopeHandlerFactory, - { - type: "oneOf", - scopeTypes: [ - languageScopeHandler.scopeType, - pairInteriorScopeHandler.scopeType!, - ], - }, - [languageScopeHandler, pairInteriorScopeHandler], - languageId, - ); - })(); + get iterationScopeType(): ScopeType | ComplexScopeType { + throw new NoContainingScopeError( + "Iteration scope for InteriorScopeHandler", + ); } *generateScopeCandidates( @@ -84,11 +42,13 @@ export class InteriorScopeHandler extends BaseScopeHandler { direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { - if (this.scopeHandler == null) { + const scopeHandler = this.getScopeHandler(); + + if (scopeHandler == null) { return; } - const scopes = this.scopeHandler.generateScopes( + const scopes = scopeHandler.generateScopes( editor, position, direction, @@ -112,4 +72,47 @@ export class InteriorScopeHandler extends BaseScopeHandler { } } } + + private getScopeHandler(): ScopeHandler | undefined { + const languageScopeHandler = this.languageDefinitions + .get(this.languageId) + ?.getScopeHandler(this.scopeType); + + // If the scope type is explicit (ie, the user has specified a scope + // type), then we don't want to include matching pairs. The user might + // have said something like "inside element" and then we don't want to + // yield the interior of the `
` pair. + if (this.scopeType.explicitScopeType) { + if (languageScopeHandler == null) { + return undefined; + } + return languageScopeHandler; + } + + const pairInteriorScopeHandler = this.scopeHandlerFactory.create( + { + type: "surroundingPairInterior", + delimiter: "any", + allowWeakContainment: true, + }, + this.languageId, + ); + + if (languageScopeHandler == null) { + return pairInteriorScopeHandler; + } + + return OneOfScopeHandler.createFromScopeHandlers( + this.scopeHandlerFactory, + { + type: "oneOf", + scopeTypes: [ + languageScopeHandler.scopeType, + pairInteriorScopeHandler.scopeType!, + ], + }, + [languageScopeHandler, pairInteriorScopeHandler], + this.languageId, + ); + } } From 913f8e8cf9e437a2d4c50fab17f9d3930b3a2c69 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 09:21:37 +0100 Subject: [PATCH 24/39] padding --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 0494caea6f..1dc94bf484 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -66,6 +66,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { // the target domain. E.g the user said "inside token", then we don't want // to yield scopes that are larger than the token. The definition of an // interior is that it's inside the scope. + for (const scope of scopes) { if (targetDomain.contains(scope.domain)) { yield scope; @@ -82,6 +83,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { // type), then we don't want to include matching pairs. The user might // have said something like "inside element" and then we don't want to // yield the interior of the `
` pair. + if (this.scopeType.explicitScopeType) { if (languageScopeHandler == null) { return undefined; From 40540dc40857e1aa4ea404615e3822138ed29beb Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 09:23:30 +0100 Subject: [PATCH 25/39] comment --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 1dc94bf484..571ba1eb4a 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -55,7 +55,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { hints, ); - // No explicit scope type. Just yield all matching scopes. + // No explicit scope type. Just yield all scopes. if (!this.scopeType.explicitScopeType) { yield* scopes; } From 3da4d51567cfaa52fce9a2a8612306be8c47d7b4 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 12:44:42 +0100 Subject: [PATCH 26/39] Fix --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 571ba1eb4a..c9c51e6fc5 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -58,6 +58,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { // No explicit scope type. Just yield all scopes. if (!this.scopeType.explicitScopeType) { yield* scopes; + return; } const targetDomain = new Range(position, hints.distalPosition); From 9742579a39f73859768090ed296cddfa9180bcf1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 13:34:38 +0100 Subject: [PATCH 27/39] Refactoring --- .../implicitExpansion/clearCoreToken.yml | 6 +- .../modifiers/interior/changeInsideToken.yml | 20 ----- .../src/languages/LanguageDefinition.ts | 3 +- .../scopeHandlers/FallbackScopeHandler.ts | 35 +++++--- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 2 +- .../InteriorScopeHandler.ts | 90 ++++++++++++------- .../SurroundingPairInteriorScopeHandler.ts | 2 +- .../TreeSitterIterationScopeHandler.ts | 8 +- .../TreeSitterScopeHandler.ts | 20 +---- .../processTargets/targets/ScopeTypeTarget.ts | 8 +- 10 files changed, 97 insertions(+), 97 deletions(-) delete mode 100644 data/fixtures/recorded/modifiers/interior/changeInsideToken.yml diff --git a/data/fixtures/recorded/implicitExpansion/clearCoreToken.yml b/data/fixtures/recorded/implicitExpansion/clearCoreToken.yml index 0c5670ce0a..42794a31e2 100644 --- a/data/fixtures/recorded/implicitExpansion/clearCoreToken.yml +++ b/data/fixtures/recorded/implicitExpansion/clearCoreToken.yml @@ -17,8 +17,4 @@ initialState: - anchor: {line: 0, character: 1} active: {line: 0, character: 1} marks: {} -finalState: - documentContents: () - selections: - - anchor: {line: 0, character: 1} - active: {line: 0, character: 1} +thrownError: {name: NoContainingScopeError} diff --git a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml b/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml deleted file mode 100644 index fe52048f9d..0000000000 --- a/data/fixtures/recorded/modifiers/interior/changeInsideToken.yml +++ /dev/null @@ -1,20 +0,0 @@ -languageId: plaintext -command: - version: 7 - spokenForm: change inside token - action: - name: clearAndSetSelection - target: - type: primitive - modifiers: - - {type: interiorOnly} - - type: containingScope - scopeType: {type: token} - usePrePhraseSnapshot: false -initialState: - documentContents: (hello) - selections: - - anchor: {line: 0, character: 3} - active: {line: 0, character: 3} - marks: {} -thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-engine/src/languages/LanguageDefinition.ts b/packages/cursorless-engine/src/languages/LanguageDefinition.ts index 3471f04841..93e93fb6d0 100644 --- a/packages/cursorless-engine/src/languages/LanguageDefinition.ts +++ b/packages/cursorless-engine/src/languages/LanguageDefinition.ts @@ -1,6 +1,7 @@ import type { RawTreeSitterQueryProvider, ScopeType, + SimpleScopeType, SimpleScopeTypeType, TreeSitter, } from "@cursorless/common"; @@ -77,7 +78,7 @@ export class LanguageDefinition { return undefined; } - return new TreeSitterScopeHandler(this.query, scopeType); + return new TreeSitterScopeHandler(this.query, scopeType as SimpleScopeType); } /** diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts index 869d4993b3..911bfaafe9 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts @@ -18,32 +18,39 @@ export class FallbackScopeHandler extends BaseScopeHandler { public scopeType = undefined; protected isHierarchical = true; - get iterationScopeType(): ScopeType { - throw new NoContainingScopeError( - "Iteration scope for FallbackScopeHandler", + static create( + scopeHandlerFactory: ScopeHandlerFactory, + scopeType: FallbackScopeType, + languageId: string, + ): ScopeHandler { + const scopeHandlers: ScopeHandler[] = scopeType.scopeTypes.map( + (scopeType) => scopeHandlerFactory.create(scopeType, languageId), ); + + return this.createFromScopeHandlers(scopeHandlers); } - constructor( - public scopeHandlerFactory: ScopeHandlerFactory, - private fallbackScopeType: FallbackScopeType, - private languageId: string, - ) { + static createFromScopeHandlers(scopeHandlers: ScopeHandler[]): ScopeHandler { + return new FallbackScopeHandler(scopeHandlers); + } + + private constructor(private scopeHandlers: ScopeHandler[]) { super(); } + get iterationScopeType(): ScopeType { + throw new NoContainingScopeError( + "Iteration scope for FallbackScopeHandler", + ); + } + *generateScopeCandidates( editor: TextEditor, position: Position, direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { - const scopeHandlers: ScopeHandler[] = this.fallbackScopeType.scopeTypes.map( - (scopeType) => - this.scopeHandlerFactory.create(scopeType, this.languageId), - ); - - for (const scopeHandler of scopeHandlers) { + for (const scopeHandler of this.scopeHandlers) { yield* scopeHandler.generateScopes(editor, position, direction, hints); } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 657981240c..8c9dc3c46d 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -132,7 +132,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { case "oneOf": return OneOfScopeHandler.create(this, scopeType, languageId); case "fallback": - return new FallbackScopeHandler(this, scopeType, languageId); + return FallbackScopeHandler.create(this, scopeType, languageId); case "conditional": return new ConditionalScopeHandler(this, scopeType, languageId); case "instance": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index c9c51e6fc5..3fc86a5ad1 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -8,7 +8,10 @@ import { type TextEditor, } from "@cursorless/common"; import type { LanguageDefinitions } from "../../../../languages/LanguageDefinitions"; +import type { Target } from "../../../../typings/target.types"; +import { InteriorTarget } from "../../../targets"; import { BaseScopeHandler } from "../BaseScopeHandler"; +import { FallbackScopeHandler } from "../FallbackScopeHandler"; import { OneOfScopeHandler } from "../OneOfScopeHandler"; import type { TargetScope } from "../scope.types"; import type { @@ -42,6 +45,7 @@ export class InteriorScopeHandler extends BaseScopeHandler { direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { + const targetDomain = new Range(position, hints.distalPosition); const scopeHandler = this.getScopeHandler(); if (scopeHandler == null) { @@ -55,22 +59,9 @@ export class InteriorScopeHandler extends BaseScopeHandler { hints, ); - // No explicit scope type. Just yield all scopes. - if (!this.scopeType.explicitScopeType) { - yield* scopes; - return; - } - - const targetDomain = new Range(position, hints.distalPosition); - - // For an explicit scope type we only yield scopes that are contained within - // the target domain. E.g the user said "inside token", then we don't want - // to yield scopes that are larger than the token. The definition of an - // interior is that it's inside the scope. - for (const scope of scopes) { - if (targetDomain.contains(scope.domain)) { - yield scope; + if (this.shouldYield(targetDomain, scope)) { + yield createInteriorScope(scope); } } } @@ -80,29 +71,31 @@ export class InteriorScopeHandler extends BaseScopeHandler { .get(this.languageId) ?.getScopeHandler(this.scopeType); + const pairScopeHandler = this.scopeHandlerFactory.create( + { + type: "surroundingPair", + delimiter: "any", + }, + this.languageId, + ); + // If the scope type is explicit (ie, the user has specified a scope - // type), then we don't want to include matching pairs. The user might + // type), then we want to prioritize language scopes. The user might // have said something like "inside element" and then we don't want to - // yield the interior of the `
` pair. + // yield the interior of the `
` pair first. if (this.scopeType.explicitScopeType) { if (languageScopeHandler == null) { - return undefined; + return pairScopeHandler; } - return languageScopeHandler; + return FallbackScopeHandler.createFromScopeHandlers([ + languageScopeHandler, + pairScopeHandler, + ]); } - const pairInteriorScopeHandler = this.scopeHandlerFactory.create( - { - type: "surroundingPairInterior", - delimiter: "any", - allowWeakContainment: true, - }, - this.languageId, - ); - if (languageScopeHandler == null) { - return pairInteriorScopeHandler; + return pairScopeHandler; } return OneOfScopeHandler.createFromScopeHandlers( @@ -111,11 +104,46 @@ export class InteriorScopeHandler extends BaseScopeHandler { type: "oneOf", scopeTypes: [ languageScopeHandler.scopeType, - pairInteriorScopeHandler.scopeType!, + pairScopeHandler.scopeType!, ], }, - [languageScopeHandler, pairInteriorScopeHandler], + [languageScopeHandler, pairScopeHandler], this.languageId, ); } + + private shouldYield(targetDomain: Range, scope: TargetScope): boolean { + // For an explicit scope type we only yield scopes that are contained within + // the target domain. E.g the user said "inside token", then we don't want + // to yield scopes that are larger than the token. The definition of an + // interior is that it's inside the scope. + + return ( + !this.scopeType.explicitScopeType || targetDomain.contains(scope.domain) + ); + } +} + +function createInteriorScope(scope: TargetScope): TargetScope { + return { + editor: scope.editor, + domain: scope.domain, + getTargets(isReversed) { + return scope.getTargets(isReversed).flatMap(createInteriorTarget); + }, + }; +} + +function createInteriorTarget(target: Target): Target[] { + const interior = target.getInterior(); + if (interior != null) { + return interior; + } + return [ + new InteriorTarget({ + editor: target.editor, + isReversed: target.isReversed, + fullInteriorRange: target.contentRange, + }), + ]; } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts index c237606a8c..feb91a40a5 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairInteriorScopeHandler.ts @@ -21,7 +21,7 @@ export class SurroundingPairInteriorScopeHandler extends BaseScopeHandler { { type: "surroundingPair", delimiter: this.scopeType.delimiter, - requireStrongContainment: !this.scopeType.allowWeakContainment, + requireStrongContainment: true, }, this.languageId, ); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts index 47f067f4ab..55c299c4cb 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterIterationScopeHandler.ts @@ -1,4 +1,8 @@ -import type { ScopeType, TextEditor } from "@cursorless/common"; +import type { + ScopeType, + SimpleScopeType, + TextEditor, +} from "@cursorless/common"; import type { TreeSitterQuery } from "../../../../languages/TreeSitterQuery"; import type { QueryMatch } from "../../../../languages/TreeSitterQuery/QueryCapture"; import { PlainTarget } from "../../../targets"; @@ -22,7 +26,7 @@ export class TreeSitterIterationScopeHandler extends BaseTreeSitterScopeHandler constructor( query: TreeSitterQuery, /** The scope type for which we are the iteration scope */ - private iterateeScopeType: ScopeType, + private iterateeScopeType: SimpleScopeType, ) { super(query); } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts index 4665e28e91..1fead109cd 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/TreeSitterScopeHandler.ts @@ -1,7 +1,6 @@ -import type { ScopeType, TextEditor } from "@cursorless/common"; +import type { SimpleScopeType, TextEditor } from "@cursorless/common"; import type { TreeSitterQuery } from "../../../../languages/TreeSitterQuery"; import type { QueryMatch } from "../../../../languages/TreeSitterQuery/QueryCapture"; -import { InteriorTarget } from "../../../targets"; import { ScopeTypeTarget } from "../../../targets/ScopeTypeTarget"; import type { CustomScopeType } from "../scopeHandler.types"; import { getCollectionItemRemovalRange } from "../util/getCollectionItemRemovalRange"; @@ -18,7 +17,7 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { constructor( query: TreeSitterQuery, - public scopeType: ScopeType, + public scopeType: SimpleScopeType, ) { super(query); } @@ -54,21 +53,6 @@ export class TreeSitterScopeHandler extends BaseTreeSitterScopeHandler { const domain = getRelatedRange(match, scopeTypeType, "domain", true) ?? contentRange; - if (scopeTypeType === "interior") { - return { - editor, - domain, - allowMultiple, - getTargets: (isReversed) => [ - new InteriorTarget({ - editor, - isReversed, - fullInteriorRange: contentRange, - }), - ], - }; - } - const prefixRange = getRelatedRange( match, scopeTypeType, diff --git a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts index f54ec18d2a..9622731d2a 100644 --- a/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts @@ -1,4 +1,4 @@ -import type { Range, ScopeTypeType } from "@cursorless/common"; +import type { Range, SimpleScopeTypeType } from "@cursorless/common"; import type { Target } from "../../typings/target.types"; import type { CommonTargetParameters } from "./BaseTarget"; import { BaseTarget } from "./BaseTarget"; @@ -11,7 +11,7 @@ import { } from "./util/insertionRemovalBehaviors/TokenInsertionRemovalBehavior"; export interface ScopeTypeTargetParameters extends CommonTargetParameters { - readonly scopeTypeType: ScopeTypeType; + readonly scopeTypeType: SimpleScopeTypeType; readonly insertionDelimiter?: string; readonly prefixRange?: Range; readonly removalRange?: Range; @@ -21,7 +21,7 @@ export interface ScopeTypeTargetParameters extends CommonTargetParameters { export class ScopeTypeTarget extends BaseTarget { type = "ScopeTypeTarget"; - private scopeTypeType_: ScopeTypeType; + private scopeTypeType_: SimpleScopeTypeType; private removalRange_?: Range; private leadingDelimiterRange_?: Range; private trailingDelimiterRange_?: Range; @@ -118,7 +118,7 @@ export class ScopeTypeTarget extends BaseTarget { } } -function getInsertionDelimiter(scopeType: ScopeTypeType): string { +function getInsertionDelimiter(scopeType: SimpleScopeTypeType): string { switch (scopeType) { case "class": case "namedFunction": From c107e8ac449ca7dae73fa3efcddfa88f00b4e69c Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 13:38:43 +0100 Subject: [PATCH 28/39] unused code --- .../common/src/types/command/PartialTargetDescriptor.types.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 5728a12a35..c086815acb 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -262,8 +262,6 @@ export interface SurroundingPairInteriorScopeType { delimiter: SurroundingPairName; // If true don't yield multiline pairs requireSingleLine?: boolean; - // If true the domain will be the full pair instead of just the interior - allowWeakContainment?: boolean; } export interface OneOfScopeType { @@ -285,8 +283,6 @@ export type ScopeType = | OneOfScopeType | GlyphScopeType; -export type ScopeTypeType = ScopeType["type"]; - export interface ContainingSurroundingPairModifier extends ContainingScopeModifier { scopeType: SurroundingPairScopeType; From 1f92e81f77570d5c68358823f8f7ced88890ddcf Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 13:41:35 +0100 Subject: [PATCH 29/39] type import --- .../src/processTargets/modifiers/InteriorStage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts index 8b3fefbe4e..3ae2d9899b 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/InteriorStage.ts @@ -1,6 +1,6 @@ -import { - type ExcludeInteriorModifier, - type InteriorOnlyModifier, +import type { + ExcludeInteriorModifier, + InteriorOnlyModifier, } from "@cursorless/common"; import type { Target } from "../../typings/target.types"; import type { ModifierStageFactory } from "../ModifierStageFactory"; From 2bf249694be64ba3a84fb59f9b0d17ca168e3e43 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 13:51:14 +0100 Subject: [PATCH 30/39] Added textual interior scope test --- .../scopes/html/interior.element.scope | 35 ++++++++++--------- .../javascript.jsx/interior.element.scope | 35 ++++++++++--------- .../textual/interior.surroundingPair.scope | 16 +++++++++ .../scopeSupportFacets.types.ts | 1 + .../textualScopeSupportFacetInfos.ts | 6 ++++ 5 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 data/fixtures/scopes/textual/interior.surroundingPair.scope diff --git a/data/fixtures/scopes/html/interior.element.scope b/data/fixtures/scopes/html/interior.element.scope index 0a0fca3688..9682fcd74b 100644 --- a/data/fixtures/scopes/html/interior.element.scope +++ b/data/fixtures/scopes/html/interior.element.scope @@ -1,37 +1,40 @@ -
text
+
text
--- [#1 Content] = [#1 Removal] = 0:1-0:4 >---< -0|
text
+0|
text
[#1 Domain] = 0:0-0:5 >-----< -0|
text
+0|
text
[#1 Insertion delimiter] = " " -[#2 Content] = -[#2 Removal] = 0:5-0:9 - >----< -0|
text
+[#2 Content] = 0:6-0:10 + >----< +0|
text
-[#2 Domain] = 0:0-0:15 - >---------------< -0|
text
+[#2 Removal] = 0:5-0:11 + >------< +0|
text
+ +[#2 Domain] = 0:0-0:17 + >-----------------< +0|
text
[#2 Insertion delimiter] = " " [#3 Content] = -[#3 Removal] = 0:11-0:14 - >---< -0|
text
+[#3 Removal] = 0:13-0:16 + >---< +0|
text
-[#3 Domain] = 0:9-0:15 - >------< -0|
text
+[#3 Domain] = 0:11-0:17 + >------< +0|
text
[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/javascript.jsx/interior.element.scope b/data/fixtures/scopes/javascript.jsx/interior.element.scope index 0a0fca3688..9682fcd74b 100644 --- a/data/fixtures/scopes/javascript.jsx/interior.element.scope +++ b/data/fixtures/scopes/javascript.jsx/interior.element.scope @@ -1,37 +1,40 @@ -
text
+
text
--- [#1 Content] = [#1 Removal] = 0:1-0:4 >---< -0|
text
+0|
text
[#1 Domain] = 0:0-0:5 >-----< -0|
text
+0|
text
[#1 Insertion delimiter] = " " -[#2 Content] = -[#2 Removal] = 0:5-0:9 - >----< -0|
text
+[#2 Content] = 0:6-0:10 + >----< +0|
text
-[#2 Domain] = 0:0-0:15 - >---------------< -0|
text
+[#2 Removal] = 0:5-0:11 + >------< +0|
text
+ +[#2 Domain] = 0:0-0:17 + >-----------------< +0|
text
[#2 Insertion delimiter] = " " [#3 Content] = -[#3 Removal] = 0:11-0:14 - >---< -0|
text
+[#3 Removal] = 0:13-0:16 + >---< +0|
text
-[#3 Domain] = 0:9-0:15 - >------< -0|
text
+[#3 Domain] = 0:11-0:17 + >------< +0|
text
[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/textual/interior.surroundingPair.scope b/data/fixtures/scopes/textual/interior.surroundingPair.scope new file mode 100644 index 0000000000..24f970f757 --- /dev/null +++ b/data/fixtures/scopes/textual/interior.surroundingPair.scope @@ -0,0 +1,16 @@ +( text ) +--- + +[Content] = 0:2-0:6 + >----< +0| ( text ) + +[Removal] = 0:1-0:7 + >------< +0| ( text ) + +[Domain] = 0:0-0:8 + >--------< +0| ( text ) + +[Insertion delimiter] = " " diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index 7691b13121..9fe14a9379 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -219,6 +219,7 @@ export type TextualScopeSupportFacet = | "url" | "surroundingPair" | "surroundingPair.iteration" + | "interior.surroundingPair" | "collectionItem.textual" | "collectionItem.textual.iteration"; diff --git a/packages/common/src/scopeSupportFacets/textualScopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/textualScopeSupportFacetInfos.ts index 86c34b5abf..8fe3e2de83 100644 --- a/packages/common/src/scopeSupportFacets/textualScopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/textualScopeSupportFacetInfos.ts @@ -83,6 +83,12 @@ export const textualScopeSupportFacetInfos: Record< }, isIteration: true, }, + "interior.surroundingPair": { + description: "The interior scope of a surrounding pair", + scopeType: { + type: "interior", + }, + }, "collectionItem.textual": { description: "A text based collection item", scopeType: { From 93e46b0b0cfe753faae8dc1b94cc88c0b0986ed7 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 13:52:35 +0100 Subject: [PATCH 31/39] Rename --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 3fc86a5ad1..f32555a56f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -129,12 +129,12 @@ function createInteriorScope(scope: TargetScope): TargetScope { editor: scope.editor, domain: scope.domain, getTargets(isReversed) { - return scope.getTargets(isReversed).flatMap(createInteriorTarget); + return scope.getTargets(isReversed).flatMap(createInteriorTargets); }, }; } -function createInteriorTarget(target: Target): Target[] { +function createInteriorTargets(target: Target): Target[] { const interior = target.getInterior(); if (interior != null) { return interior; From b9d1dad5f0e1b6eae88cda745b56ecabdf287227 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Thu, 30 Jan 2025 09:00:04 -0800 Subject: [PATCH 32/39] Apply suggestions from code review --- .../common/src/scopeSupportFacets/scopeSupportFacetInfos.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index 4d80e24d17..f39280ee06 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -722,7 +722,7 @@ export const scopeSupportFacetInfos: Record< scopeType: { type: "interior" }, }, "interior.element": { - description: "The interior/children of an xml element", + description: "The interior/children of an XML element", scopeType: { type: "interior" }, }, "interior.command": { @@ -730,7 +730,7 @@ export const scopeSupportFacetInfos: Record< scopeType: { type: "interior" }, }, "interior.cell": { - description: "The body of code cell in markdown", + description: "The body of a code cell in markdown", scopeType: { type: "interior" }, }, From eade3b5eb86220b609930b1519a1f3bdf96211fe Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Thu, 30 Jan 2025 09:05:48 -0800 Subject: [PATCH 33/39] Update packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index f32555a56f..d3f8249913 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -83,7 +83,6 @@ export class InteriorScopeHandler extends BaseScopeHandler { // type), then we want to prioritize language scopes. The user might // have said something like "inside element" and then we don't want to // yield the interior of the `
` pair first. - if (this.scopeType.explicitScopeType) { if (languageScopeHandler == null) { return pairScopeHandler; From dc32fc816d4c55011118d0cac27f784ebafe519f Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 18:09:47 +0100 Subject: [PATCH 34/39] Update comments --- .../InteriorScopeHandler.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index d3f8249913..d075f27918 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -79,10 +79,12 @@ export class InteriorScopeHandler extends BaseScopeHandler { this.languageId, ); - // If the scope type is explicit (ie, the user has specified a scope - // type), then we want to prioritize language scopes. The user might - // have said something like "inside element" and then we don't want to - // yield the interior of the `
` pair first. + /* + If the scope type is explicit (ie, the user has specified a scope + type), then we want to prioritize language scopes. The user might + have said something like "inside element" and then we don't want to + yield the interior of the `
` pair first. + */ if (this.scopeType.explicitScopeType) { if (languageScopeHandler == null) { return pairScopeHandler; @@ -112,10 +114,12 @@ export class InteriorScopeHandler extends BaseScopeHandler { } private shouldYield(targetDomain: Range, scope: TargetScope): boolean { - // For an explicit scope type we only yield scopes that are contained within - // the target domain. E.g the user said "inside token", then we don't want - // to yield scopes that are larger than the token. The definition of an - // interior is that it's inside the scope. + /* + For an explicit scope type we only yield scopes that are contained within + the target domain. E.g the user said "inside token", then we don't want + to yield scopes that are larger than the token. The definition of an + interior is that it's inside the scope. + */ return ( !this.scopeType.explicitScopeType || targetDomain.contains(scope.domain) From 111b40c2fa543293ffca78e7a492e1ea910bc79d Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 18:10:02 +0100 Subject: [PATCH 35/39] cleanup --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index d075f27918..be213149d9 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -120,7 +120,6 @@ export class InteriorScopeHandler extends BaseScopeHandler { to yield scopes that are larger than the token. The definition of an interior is that it's inside the scope. */ - return ( !this.scopeType.explicitScopeType || targetDomain.contains(scope.domain) ); From 5c444eb895b4f9c173f042f796ac4faa2cf6a7a7 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 18:12:34 +0100 Subject: [PATCH 36/39] revert comments --- .../InteriorScopeHandler.ts | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index be213149d9..60b5902972 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -79,12 +79,10 @@ export class InteriorScopeHandler extends BaseScopeHandler { this.languageId, ); - /* - If the scope type is explicit (ie, the user has specified a scope - type), then we want to prioritize language scopes. The user might - have said something like "inside element" and then we don't want to - yield the interior of the `
` pair first. - */ + // If the scope type is explicit (ie, the user has specified a scope + // type), then we want to prioritize language scopes. The user might + // have said something like "inside element" and then we don't want to + // yield the interior of the `
` pair first. if (this.scopeType.explicitScopeType) { if (languageScopeHandler == null) { return pairScopeHandler; @@ -114,12 +112,10 @@ export class InteriorScopeHandler extends BaseScopeHandler { } private shouldYield(targetDomain: Range, scope: TargetScope): boolean { - /* - For an explicit scope type we only yield scopes that are contained within - the target domain. E.g the user said "inside token", then we don't want - to yield scopes that are larger than the token. The definition of an - interior is that it's inside the scope. - */ + // For an explicit scope type we only yield scopes that are contained within + // the target domain. E.g the user said "inside token", then we don't want + // to yield scopes that are larger than the token. The definition of an + // interior is that it's inside the scope. return ( !this.scopeType.explicitScopeType || targetDomain.contains(scope.domain) ); From de5d6242573a915ccdbfd18705d1619c963247f3 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Thu, 30 Jan 2025 09:18:10 -0800 Subject: [PATCH 37/39] Update packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 60b5902972..89fcfce9fa 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -113,9 +113,8 @@ export class InteriorScopeHandler extends BaseScopeHandler { private shouldYield(targetDomain: Range, scope: TargetScope): boolean { // For an explicit scope type we only yield scopes that are contained within - // the target domain. E.g the user said "inside token", then we don't want - // to yield scopes that are larger than the token. The definition of an - // interior is that it's inside the scope. + // the target domain. For example, if the user said "inside token", we don't + // want to yield scopes that are larger than the token. return ( !this.scopeType.explicitScopeType || targetDomain.contains(scope.domain) ); From bd096f82cb336731f6add2e077e70057eead2928 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Thu, 30 Jan 2025 09:24:28 -0800 Subject: [PATCH 38/39] Update packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 89fcfce9fa..4080e582fb 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -80,9 +80,10 @@ export class InteriorScopeHandler extends BaseScopeHandler { ); // If the scope type is explicit (ie, the user has specified a scope - // type), then we want to prioritize language scopes. The user might - // have said something like "inside element" and then we don't want to - // yield the interior of the `
` pair first. + // type), then we want to prioritize language scopes. For example, + // if the user says "inside element" inside a `
` tag, the angle + // brackets of the tag are also a surrounding pair which should have + // lower priority. if (this.scopeType.explicitScopeType) { if (languageScopeHandler == null) { return pairScopeHandler; From 2678ffc3138db776c559adf7de0c0181e38c8958 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 30 Jan 2025 19:09:02 +0100 Subject: [PATCH 39/39] Merge fix --- .../SurroundingPairScopeHandler/InteriorScopeHandler.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts index 4080e582fb..56406abec3 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/InteriorScopeHandler.ts @@ -100,15 +100,8 @@ export class InteriorScopeHandler extends BaseScopeHandler { return OneOfScopeHandler.createFromScopeHandlers( this.scopeHandlerFactory, - { - type: "oneOf", - scopeTypes: [ - languageScopeHandler.scopeType, - pairScopeHandler.scopeType!, - ], - }, - [languageScopeHandler, pairScopeHandler], this.languageId, + [languageScopeHandler, pairScopeHandler], ); }