From 47334fdece737959620822c93605d3c53efe3f1c Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 10:17:31 +0200 Subject: [PATCH 1/7] Added functionCall.chain facet --- .../scopes/c/functionCall.chain.scope | 19 ++++++++++++++ .../scopes/c/functionCallee.chain.scope | 25 +++++++++++++++++++ .../scopes/csharp/functionCall.chain.scope | 19 ++++++++++++++ .../scopes/csharp/functionCallee.chain.scope | 25 +++++++++++++++++++ .../scopes/csharp/functionCallee.method.scope | 6 ++--- .../scopes/csharp/functionCallee.scope | 6 ++--- .../scopes/java/functionCall.chain.scope | 19 ++++++++++++++ .../scopes/java/functionCallee.chain.scope | 25 +++++++++++++++++++ .../javascript.core/functionCall.chain.scope | 19 ++++++++++++++ .../functionCallee.chain.scope | 25 +++++++++++++++++++ .../scopes/lua/functionCall.chain.scope | 19 ++++++++++++++ .../scopes/lua/functionCall.chain2.scope | 19 ++++++++++++++ .../scopes/lua/functionCallee.chain.scope | 25 +++++++++++++++++++ .../scopes/lua/functionCallee.chain2.scope | 25 +++++++++++++++++++ .../scopes/python/functionCall.chain.scope | 19 ++++++++++++++ .../scopes/python/functionCallee.chain.scope | 25 +++++++++++++++++++ packages/common/src/scopeSupportFacets/c.ts | 2 ++ .../common/src/scopeSupportFacets/csharp.ts | 2 ++ packages/common/src/scopeSupportFacets/css.ts | 4 +++ .../common/src/scopeSupportFacets/html.ts | 2 ++ .../common/src/scopeSupportFacets/java.ts | 2 ++ .../src/scopeSupportFacets/javascript.ts | 2 ++ .../common/src/scopeSupportFacets/json.ts | 2 ++ packages/common/src/scopeSupportFacets/lua.ts | 2 ++ .../common/src/scopeSupportFacets/markdown.ts | 2 ++ .../common/src/scopeSupportFacets/python.ts | 2 ++ packages/common/src/scopeSupportFacets/r.ts | 2 ++ packages/common/src/scopeSupportFacets/scm.ts | 4 +++ .../scopeSupportFacetInfos.ts | 9 +++++++ .../scopeSupportFacets.types.ts | 2 ++ .../common/src/scopeSupportFacets/talon.ts | 4 +++ packages/common/src/scopeSupportFacets/xml.ts | 2 ++ .../common/src/scopeSupportFacets/yaml.ts | 2 ++ 33 files changed, 361 insertions(+), 6 deletions(-) create mode 100644 data/fixtures/scopes/c/functionCall.chain.scope create mode 100644 data/fixtures/scopes/c/functionCallee.chain.scope create mode 100644 data/fixtures/scopes/csharp/functionCall.chain.scope create mode 100644 data/fixtures/scopes/csharp/functionCallee.chain.scope create mode 100644 data/fixtures/scopes/java/functionCall.chain.scope create mode 100644 data/fixtures/scopes/java/functionCallee.chain.scope create mode 100644 data/fixtures/scopes/javascript.core/functionCall.chain.scope create mode 100644 data/fixtures/scopes/javascript.core/functionCallee.chain.scope create mode 100644 data/fixtures/scopes/lua/functionCall.chain.scope create mode 100644 data/fixtures/scopes/lua/functionCall.chain2.scope create mode 100644 data/fixtures/scopes/lua/functionCallee.chain.scope create mode 100644 data/fixtures/scopes/lua/functionCallee.chain2.scope create mode 100644 data/fixtures/scopes/python/functionCall.chain.scope create mode 100644 data/fixtures/scopes/python/functionCallee.chain.scope diff --git a/data/fixtures/scopes/c/functionCall.chain.scope b/data/fixtures/scopes/c/functionCall.chain.scope new file mode 100644 index 0000000000..6297108ea1 --- /dev/null +++ b/data/fixtures/scopes/c/functionCall.chain.scope @@ -0,0 +1,19 @@ +foo().bar(); +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/c/functionCallee.chain.scope b/data/fixtures/scopes/c/functionCallee.chain.scope new file mode 100644 index 0000000000..b4a1e073a1 --- /dev/null +++ b/data/fixtures/scopes/c/functionCallee.chain.scope @@ -0,0 +1,25 @@ +foo().bar(); +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo().bar(); + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo().bar(); + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/csharp/functionCall.chain.scope b/data/fixtures/scopes/csharp/functionCall.chain.scope new file mode 100644 index 0000000000..979d301b16 --- /dev/null +++ b/data/fixtures/scopes/csharp/functionCall.chain.scope @@ -0,0 +1,19 @@ +Foo().Bar(); +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| Foo().Bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| Foo().Bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/csharp/functionCallee.chain.scope b/data/fixtures/scopes/csharp/functionCallee.chain.scope new file mode 100644 index 0000000000..640b71bf60 --- /dev/null +++ b/data/fixtures/scopes/csharp/functionCallee.chain.scope @@ -0,0 +1,25 @@ +Foo().Bar(); +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| Foo().Bar(); + +[#1 Domain] = 0:0-0:5 + >-----< +0| Foo().Bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| Foo().Bar(); + +[#2 Domain] = 0:0-0:11 + >-----------< +0| Foo().Bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/csharp/functionCallee.method.scope b/data/fixtures/scopes/csharp/functionCallee.method.scope index f30f0f7536..2d5d171438 100644 --- a/data/fixtures/scopes/csharp/functionCallee.method.scope +++ b/data/fixtures/scopes/csharp/functionCallee.method.scope @@ -1,13 +1,13 @@ -foo.bar(); +Foo.Bar(); --- [Content] = [Removal] = 0:0-0:7 >-------< -0| foo.bar(); +0| Foo.Bar(); [Domain] = 0:0-0:9 >---------< -0| foo.bar(); +0| Foo.Bar(); [Insertion delimiter] = " " diff --git a/data/fixtures/scopes/csharp/functionCallee.scope b/data/fixtures/scopes/csharp/functionCallee.scope index 3071a48ae9..252e7fe961 100644 --- a/data/fixtures/scopes/csharp/functionCallee.scope +++ b/data/fixtures/scopes/csharp/functionCallee.scope @@ -1,13 +1,13 @@ -foo(); +Foo(); --- [Content] = [Removal] = 0:0-0:3 >---< -0| foo(); +0| Foo(); [Domain] = 0:0-0:5 >-----< -0| foo(); +0| Foo(); [Insertion delimiter] = " " diff --git a/data/fixtures/scopes/java/functionCall.chain.scope b/data/fixtures/scopes/java/functionCall.chain.scope new file mode 100644 index 0000000000..6297108ea1 --- /dev/null +++ b/data/fixtures/scopes/java/functionCall.chain.scope @@ -0,0 +1,19 @@ +foo().bar(); +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/java/functionCallee.chain.scope b/data/fixtures/scopes/java/functionCallee.chain.scope new file mode 100644 index 0000000000..b4a1e073a1 --- /dev/null +++ b/data/fixtures/scopes/java/functionCallee.chain.scope @@ -0,0 +1,25 @@ +foo().bar(); +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo().bar(); + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo().bar(); + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/javascript.core/functionCall.chain.scope b/data/fixtures/scopes/javascript.core/functionCall.chain.scope new file mode 100644 index 0000000000..6297108ea1 --- /dev/null +++ b/data/fixtures/scopes/javascript.core/functionCall.chain.scope @@ -0,0 +1,19 @@ +foo().bar(); +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/javascript.core/functionCallee.chain.scope b/data/fixtures/scopes/javascript.core/functionCallee.chain.scope new file mode 100644 index 0000000000..b4a1e073a1 --- /dev/null +++ b/data/fixtures/scopes/javascript.core/functionCallee.chain.scope @@ -0,0 +1,25 @@ +foo().bar(); +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo().bar(); + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar(); + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo().bar(); + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar(); + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/functionCall.chain.scope b/data/fixtures/scopes/lua/functionCall.chain.scope new file mode 100644 index 0000000000..c73434c70d --- /dev/null +++ b/data/fixtures/scopes/lua/functionCall.chain.scope @@ -0,0 +1,19 @@ +foo().bar() +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/functionCall.chain2.scope b/data/fixtures/scopes/lua/functionCall.chain2.scope new file mode 100644 index 0000000000..7e87ac4c3b --- /dev/null +++ b/data/fixtures/scopes/lua/functionCall.chain2.scope @@ -0,0 +1,19 @@ +foo():bar() +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo():bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo():bar() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/functionCallee.chain.scope b/data/fixtures/scopes/lua/functionCallee.chain.scope new file mode 100644 index 0000000000..b0b9872650 --- /dev/null +++ b/data/fixtures/scopes/lua/functionCallee.chain.scope @@ -0,0 +1,25 @@ +foo().bar() +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo().bar() + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo().bar() + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/lua/functionCallee.chain2.scope b/data/fixtures/scopes/lua/functionCallee.chain2.scope new file mode 100644 index 0000000000..da8ae230c1 --- /dev/null +++ b/data/fixtures/scopes/lua/functionCallee.chain2.scope @@ -0,0 +1,25 @@ +foo():bar() +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo():bar() + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo():bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo():bar() + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo():bar() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/functionCall.chain.scope b/data/fixtures/scopes/python/functionCall.chain.scope new file mode 100644 index 0000000000..c73434c70d --- /dev/null +++ b/data/fixtures/scopes/python/functionCall.chain.scope @@ -0,0 +1,19 @@ +foo().bar() +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/python/functionCallee.chain.scope b/data/fixtures/scopes/python/functionCallee.chain.scope new file mode 100644 index 0000000000..b0b9872650 --- /dev/null +++ b/data/fixtures/scopes/python/functionCallee.chain.scope @@ -0,0 +1,25 @@ +foo().bar() +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo().bar() + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo().bar() + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar() + +[#2 Insertion delimiter] = " " diff --git a/packages/common/src/scopeSupportFacets/c.ts b/packages/common/src/scopeSupportFacets/c.ts index 944f13664b..c20c7bc6e4 100644 --- a/packages/common/src/scopeSupportFacets/c.ts +++ b/packages/common/src/scopeSupportFacets/c.ts @@ -39,8 +39,10 @@ export const cCoreScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.method": supported, + "functionCallee.chain": supported, "argument.actual.singleLine": supported, "argument.actual.multiLine": supported, diff --git a/packages/common/src/scopeSupportFacets/csharp.ts b/packages/common/src/scopeSupportFacets/csharp.ts index 0d3d64074b..cbaf8f4924 100644 --- a/packages/common/src/scopeSupportFacets/csharp.ts +++ b/packages/common/src/scopeSupportFacets/csharp.ts @@ -20,9 +20,11 @@ export const csharpScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.constructor": supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.constructor": supported, "functionCallee.method": supported, + "functionCallee.chain": supported, namedFunction: supported, "namedFunction.iteration.document": supported, diff --git a/packages/common/src/scopeSupportFacets/css.ts b/packages/common/src/scopeSupportFacets/css.ts index e58f96723d..0ab9014eda 100644 --- a/packages/common/src/scopeSupportFacets/css.ts +++ b/packages/common/src/scopeSupportFacets/css.ts @@ -155,6 +155,10 @@ export const cssScopeSupport: LanguageScopeSupportFacetMap = { "value.return.lambda": notApplicable, "interior.lambda": notApplicable, + // Function call chain + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, + // Keyword argument "name.argument.actual": notApplicable, "name.argument.actual.iteration": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/html.ts b/packages/common/src/scopeSupportFacets/html.ts index dcf81e3748..a78a8bb97e 100644 --- a/packages/common/src/scopeSupportFacets/html.ts +++ b/packages/common/src/scopeSupportFacets/html.ts @@ -135,6 +135,8 @@ export const htmlScopeSupport: LanguageScopeSupportFacetMap = { // Function call functionCall: notApplicable, functionCallee: notApplicable, + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, "argumentList.actual.empty": notApplicable, "argumentList.actual.singleLine": notApplicable, "argumentList.actual.multiLine": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/java.ts b/packages/common/src/scopeSupportFacets/java.ts index 601bd52c74..f6f387a5c2 100644 --- a/packages/common/src/scopeSupportFacets/java.ts +++ b/packages/common/src/scopeSupportFacets/java.ts @@ -79,9 +79,11 @@ export const javaScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.constructor": supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.constructor": supported, "functionCallee.method": supported, + "functionCallee.chain": supported, "namedFunction.constructor": supported, "namedFunction.method": supported, diff --git a/packages/common/src/scopeSupportFacets/javascript.ts b/packages/common/src/scopeSupportFacets/javascript.ts index f7647f4ce6..2dcd0db13e 100644 --- a/packages/common/src/scopeSupportFacets/javascript.ts +++ b/packages/common/src/scopeSupportFacets/javascript.ts @@ -58,9 +58,11 @@ export const javascriptCoreScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.constructor": supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.constructor": supported, "functionCallee.method": supported, + "functionCallee.chain": supported, "argument.actual.singleLine": supported, "argument.actual.multiLine": supported, diff --git a/packages/common/src/scopeSupportFacets/json.ts b/packages/common/src/scopeSupportFacets/json.ts index 637f851562..4045d3d9ac 100644 --- a/packages/common/src/scopeSupportFacets/json.ts +++ b/packages/common/src/scopeSupportFacets/json.ts @@ -138,6 +138,8 @@ export const jsonScopeSupport: LanguageScopeSupportFacetMap = { // Function call functionCall: notApplicable, functionCallee: notApplicable, + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, "argumentList.actual.empty": notApplicable, "argumentList.actual.singleLine": notApplicable, "argumentList.actual.multiLine": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/lua.ts b/packages/common/src/scopeSupportFacets/lua.ts index 8410b459cc..2101dc2f5c 100644 --- a/packages/common/src/scopeSupportFacets/lua.ts +++ b/packages/common/src/scopeSupportFacets/lua.ts @@ -15,8 +15,10 @@ export const luaScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.method": supported, + "functionCallee.chain": supported, "key.attribute": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/markdown.ts b/packages/common/src/scopeSupportFacets/markdown.ts index 5d083c1d28..86e04dbfaa 100644 --- a/packages/common/src/scopeSupportFacets/markdown.ts +++ b/packages/common/src/scopeSupportFacets/markdown.ts @@ -136,6 +136,8 @@ export const markdownScopeSupport: LanguageScopeSupportFacetMap = { // Function call functionCall: notApplicable, functionCallee: notApplicable, + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, "argumentList.actual.empty": notApplicable, "argumentList.actual.singleLine": notApplicable, "argumentList.actual.multiLine": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/python.ts b/packages/common/src/scopeSupportFacets/python.ts index c28fb6a87d..58a17d62bd 100644 --- a/packages/common/src/scopeSupportFacets/python.ts +++ b/packages/common/src/scopeSupportFacets/python.ts @@ -200,9 +200,11 @@ export const pythonScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.constructor": supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.constructor": supported, "functionCallee.method": supported, + "functionCallee.chain": supported, disqualifyDelimiter: supported, pairDelimiter: supported, diff --git a/packages/common/src/scopeSupportFacets/r.ts b/packages/common/src/scopeSupportFacets/r.ts index 75833a1983..2fbf78b394 100644 --- a/packages/common/src/scopeSupportFacets/r.ts +++ b/packages/common/src/scopeSupportFacets/r.ts @@ -27,8 +27,10 @@ export const rScopeSupport: LanguageScopeSupportFacetMap = { functionCall: supported, "functionCall.method": supported, + "functionCall.chain": supported, functionCallee: supported, "functionCallee.method": supported, + "functionCallee.chain": supported, "comment.line": supported, ifStatement: supported, diff --git a/packages/common/src/scopeSupportFacets/scm.ts b/packages/common/src/scopeSupportFacets/scm.ts index a627cd6bb4..289b73d8b8 100644 --- a/packages/common/src/scopeSupportFacets/scm.ts +++ b/packages/common/src/scopeSupportFacets/scm.ts @@ -137,6 +137,10 @@ export const scmScopeSupport: LanguageScopeSupportFacetMap = { "value.return.lambda": notApplicable, "interior.lambda": notApplicable, + // Function call chain + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, + // Keyword argument "name.argument.actual": notApplicable, "name.argument.actual.iteration": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index 55fb92433b..daf1940fbc 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -225,6 +225,10 @@ export const scopeSupportFacetInfos: Record< description: "A method call", scopeType: "functionCall", }, + "functionCall.chain": { + description: "A chain of function calls, eg `foo().bar()`", + scopeType: "functionCall", + }, functionCallee: { description: "The function being called in a function call", scopeType: "functionCallee", @@ -239,6 +243,11 @@ export const scopeSupportFacetInfos: Record< "The function being called in a method call, including parent objects.", scopeType: "functionCallee", }, + "functionCallee.chain": { + description: + "The function being called in a chain of function calls, including parent objects.", + scopeType: "functionCallee", + }, "argument.actual.singleLine": { description: "A single line argument in a function call", diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index 8513746cb4..1a574409cb 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -72,9 +72,11 @@ export const scopeSupportFacets = [ "functionCall", "functionCall.constructor", "functionCall.method", + "functionCall.chain", "functionCallee", "functionCallee.constructor", "functionCallee.method", + "functionCallee.chain", "argument.actual.singleLine", "argument.actual.multiLine", diff --git a/packages/common/src/scopeSupportFacets/talon.ts b/packages/common/src/scopeSupportFacets/talon.ts index e7e524e56d..37942c443f 100644 --- a/packages/common/src/scopeSupportFacets/talon.ts +++ b/packages/common/src/scopeSupportFacets/talon.ts @@ -146,6 +146,10 @@ export const talonScopeSupport: LanguageScopeSupportFacetMap = { "argument.actual.multiLine": notApplicable, "argumentList.actual.multiLine": notApplicable, + // Function call chain + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, + // Anonymous function / lambda anonymousFunction: notApplicable, "argumentList.formal.lambda.empty": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/xml.ts b/packages/common/src/scopeSupportFacets/xml.ts index 1b7b88c1c9..17688c761c 100644 --- a/packages/common/src/scopeSupportFacets/xml.ts +++ b/packages/common/src/scopeSupportFacets/xml.ts @@ -135,6 +135,8 @@ export const xmlScopeSupport: LanguageScopeSupportFacetMap = { // Function call functionCall: notApplicable, functionCallee: notApplicable, + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, "argumentList.actual.empty": notApplicable, "argumentList.actual.singleLine": notApplicable, "argumentList.actual.multiLine": notApplicable, diff --git a/packages/common/src/scopeSupportFacets/yaml.ts b/packages/common/src/scopeSupportFacets/yaml.ts index 5ab4c73fb4..1579ce7d3e 100644 --- a/packages/common/src/scopeSupportFacets/yaml.ts +++ b/packages/common/src/scopeSupportFacets/yaml.ts @@ -144,6 +144,8 @@ export const yamlScopeSupport: LanguageScopeSupportFacetMap = { // Function call functionCall: notApplicable, functionCallee: notApplicable, + "functionCall.chain": notApplicable, + "functionCallee.chain": notApplicable, "argumentList.actual.empty": notApplicable, "argumentList.actual.singleLine": notApplicable, "argumentList.actual.multiLine": notApplicable, From 2390ec4cbd8727cadb19a683461ccbde7efe5fe7 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 14:29:44 +0200 Subject: [PATCH 2/7] r --- .../scopes/r/functionCall.chain.scope | 19 ++++++ .../scopes/r/functionCallee.chain.scope | 25 ++++++++ .../queryPredicateOperators.ts | 62 +++++++++++++++++++ queries/r.scm | 15 ++--- 4 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 data/fixtures/scopes/r/functionCall.chain.scope create mode 100644 data/fixtures/scopes/r/functionCallee.chain.scope diff --git a/data/fixtures/scopes/r/functionCall.chain.scope b/data/fixtures/scopes/r/functionCall.chain.scope new file mode 100644 index 0000000000..c73434c70d --- /dev/null +++ b/data/fixtures/scopes/r/functionCall.chain.scope @@ -0,0 +1,19 @@ +foo().bar() +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar() + +[#2 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/r/functionCallee.chain.scope b/data/fixtures/scopes/r/functionCallee.chain.scope new file mode 100644 index 0000000000..b0b9872650 --- /dev/null +++ b/data/fixtures/scopes/r/functionCallee.chain.scope @@ -0,0 +1,25 @@ +foo().bar() +--- + +[#1 Content] = +[#1 Removal] = 0:0-0:3 + >---< +0| foo().bar() + +[#1 Domain] = 0:0-0:5 + >-----< +0| foo().bar() + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = 0:0-0:9 + >---------< +0| foo().bar() + +[#2 Domain] = 0:0-0:11 + >-----------< +0| foo().bar() + +[#2 Insertion delimiter] = " " diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index eeac765dae..8a6059ef28 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -264,6 +264,67 @@ class GrowToNamedSiblings extends QueryPredicateOperator { return true; } } +/** + * A predicate operator that modifies the range of the match to grow to leading siblings of the same type. + * + * The `leadingSeparator` argument specificies the separator each node except the first sibling will be separated by. + * + * ``` + * (#call-chain! @foo ".") + * ``` + */ +class CallChain extends QueryPredicateOperator { + name = "call-chain!" as const; + schema = z.union([z.tuple([q.node]), z.tuple([q.node, q.string])]); + + run(nodeInfo: MutableQueryCapture, leadingSeparator: string) { + const { node, range, document } = nodeInfo; + + if (node.parent == null) { + throw Error("Node has no parent"); + } + + const { children } = node.parent; + const nodeIndex = children.findIndex((n) => n.id === node.id); + + if (nodeIndex === -1) { + throw Error("Node not found in parent"); + } + + let start = children[nodeIndex]; + let end = start; + + for (let i = nodeIndex; i > -1; --i) { + const child = children[i]; + + if (child.type !== node.type) { + break; + } + + start = child; + + const childRange = makeRangeFromPositions( + child.startPosition, + child.endPosition, + ); + + const childText = document.getText(childRange); + + if (!childText.startsWith(leadingSeparator)) { + break; + } + } + + if (start.id !== end.id) { + nodeInfo.range = makeRangeFromPositions( + start.startPosition, + end.endPosition, + ); + } + + return true; + } +} /** * A predicate operator that modifies the range of the match by trimming trailing whitespace, @@ -442,6 +503,7 @@ export const queryPredicateOperators = [ new CharacterRange(), new ShrinkToMatch(), new GrowToNamedSiblings(), + new CallChain(), new AllowMultiple(), new InsertionDelimiter(), new SingleOrMultilineDelimiter(), diff --git a/queries/r.scm b/queries/r.scm index 0de9ecff10..1f182edb0d 100644 --- a/queries/r.scm +++ b/queries/r.scm @@ -134,15 +134,16 @@ ;; Function calls ;;!! foo() ;;! ^^^^^ -;;! ----- -(call) @functionCall @argumentOrParameter.iteration.domain - -;;!! foo() ;;! ^^^ ;;! ----- -(call - (identifier) @functionCallee -) @_.domain +( + (call + (identifier) @functionCallee.end + ) @functionCall @functionCallee.start.startOf @functionCallee.domain + (#call-chain! @functionCall ".") + (#call-chain! @functionCallee.start.startOf ".") + (#call-chain! @functionCallee.domain ".") +) ;; Technically lists and arrays are just calls to the function `list` or `c` ;;!! list(1, 2, 3) From 1b0462e0761cbb4cf5d53ed6aaab53b3160dae18 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 14:37:30 +0200 Subject: [PATCH 3/7] Added text field to child nodes --- .../languages/TreeSitterQuery/QueryCapture.ts | 1 + .../queryPredicateOperators.ts | 36 ++++++++----------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts index 856bfadc15..0c4eba64b9 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts @@ -21,6 +21,7 @@ export interface SimpleSyntaxNode { interface SimpleChildSyntaxNode extends SimpleSyntaxNode { readonly startPosition: Point; readonly endPosition: Point; + readonly text: string; } /** diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 8a6059ef28..8ab7942ec5 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -1,11 +1,11 @@ -import type { Position } from "@cursorless/common"; -import { Range, adjustPosition } from "@cursorless/common"; +import { Position, Range, adjustPosition } from "@cursorless/common"; +import type { Point } from "web-tree-sitter"; import { z } from "zod"; +import { isEven } from "./isEven"; import { makeRangeFromPositions } from "./makeRangeFromPositions"; +import { q } from "./operatorArgumentSchemaTypes"; import type { MutableQueryCapture } from "./QueryCapture"; import { QueryPredicateOperator } from "./QueryPredicateOperator"; -import { isEven } from "./isEven"; -import { q } from "./operatorArgumentSchemaTypes"; /** * A predicate operator that returns true if the node is at an even index within @@ -226,7 +226,7 @@ class GrowToNamedSiblings extends QueryPredicateOperator { schema = z.union([z.tuple([q.node]), z.tuple([q.node, q.string])]); run(nodeInfo: MutableQueryCapture, notText?: string) { - const { node, range, document } = nodeInfo; + const { node, range } = nodeInfo; if (node.parent == null) { throw Error("Node has no parent"); @@ -234,7 +234,7 @@ class GrowToNamedSiblings extends QueryPredicateOperator { const { children } = node.parent; const nodeIndex = children.findIndex((n) => n.id === node.id); - let endPosition: Position | null = null; + let endPosition: Point | undefined; if (nodeIndex === -1) { throw Error("Node not found in parent"); @@ -245,20 +245,19 @@ class GrowToNamedSiblings extends QueryPredicateOperator { if (!child.isNamed) { break; } - const childRange = makeRangeFromPositions( - child.startPosition, - child.endPosition, - ); - if (notText != null && notText === document.getText(childRange)) { + if (notText != null && notText === child.text) { break; } - endPosition = childRange.end; + endPosition = child.endPosition; } if (endPosition != null) { - nodeInfo.range = new Range(range.start, endPosition); + nodeInfo.range = new Range( + range.start, + new Position(endPosition.row, endPosition.column), + ); } return true; @@ -278,7 +277,7 @@ class CallChain extends QueryPredicateOperator { schema = z.union([z.tuple([q.node]), z.tuple([q.node, q.string])]); run(nodeInfo: MutableQueryCapture, leadingSeparator: string) { - const { node, range, document } = nodeInfo; + const { node, range } = nodeInfo; if (node.parent == null) { throw Error("Node has no parent"); @@ -303,14 +302,7 @@ class CallChain extends QueryPredicateOperator { start = child; - const childRange = makeRangeFromPositions( - child.startPosition, - child.endPosition, - ); - - const childText = document.getText(childRange); - - if (!childText.startsWith(leadingSeparator)) { + if (!child.text.startsWith(leadingSeparator)) { break; } } From a0f928a9043c23823d11a28ba38d222683cca5c3 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 14:39:56 +0200 Subject: [PATCH 4/7] Clean up --- queries/r.scm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queries/r.scm b/queries/r.scm index 1f182edb0d..0f37c81fe9 100644 --- a/queries/r.scm +++ b/queries/r.scm @@ -149,8 +149,8 @@ ;;!! list(1, 2, 3) ;;! ^^^^^^^^^^^^^ (call - function: (identifier) @name - (#match? @name "^(c|list)$") + function: (identifier) @_dummy + (#match? @_dummy "^(c|list)$") ) @list (binary_operator From 992ee77b2428e8cfb029f08ed00a19d1215176a5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 14:40:34 +0200 Subject: [PATCH 5/7] Ran meta updater --- data/scopeSupportFacetInfos.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/scopeSupportFacetInfos.md b/data/scopeSupportFacetInfos.md index 3e78e4b016..c76c69a114 100644 --- a/data/scopeSupportFacetInfos.md +++ b/data/scopeSupportFacetInfos.md @@ -143,12 +143,14 @@ ### functionCall - `functionCall` A function call +- `functionCall.chain` A chain of function calls, eg `foo().bar()` - `functionCall.constructor` A constructor call - `functionCall.method` A method call ### functionCallee - `functionCallee` The function being called in a function call +- `functionCallee.chain` The function being called in a chain of function calls, including parent objects. - `functionCallee.constructor` The class being constructed in a class instantiation, including the `new` keyword. - `functionCallee.method` The function being called in a method call, including parent objects. From 04cb6065919a62a0755c4ff1f63d0dfe833d85ed Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 14:45:23 +0200 Subject: [PATCH 6/7] Remove unused variable --- .../src/languages/TreeSitterQuery/queryPredicateOperators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 8ab7942ec5..3bbbfebd24 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -277,7 +277,7 @@ class CallChain extends QueryPredicateOperator { schema = z.union([z.tuple([q.node]), z.tuple([q.node, q.string])]); run(nodeInfo: MutableQueryCapture, leadingSeparator: string) { - const { node, range } = nodeInfo; + const { node } = nodeInfo; if (node.parent == null) { throw Error("Node has no parent"); From 44a17902cc97bf2bc02c3b2f44dd675b29f0988c Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 16 Jul 2025 14:52:28 +0200 Subject: [PATCH 7/7] Clean up --- .../src/languages/TreeSitterQuery/queryPredicateOperators.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 3bbbfebd24..12e5559a0a 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -291,7 +291,6 @@ class CallChain extends QueryPredicateOperator { } let start = children[nodeIndex]; - let end = start; for (let i = nodeIndex; i > -1; --i) { const child = children[i]; @@ -307,10 +306,10 @@ class CallChain extends QueryPredicateOperator { } } - if (start.id !== end.id) { + if (start.id !== node.id) { nodeInfo.range = makeRangeFromPositions( start.startPosition, - end.endPosition, + children[nodeIndex].endPosition, ); }