Repository: cursorless-dev/cursorless
Pull request #2721: Enable initial r support
Author: login: BlueDrink9
Created: 2025-01-12T03:42:56Z | Merged: 2025-05-25T10:51:39Z

I'm still wrapping my head around treesitter to add the supported scopes, so I'll update this PR and add relevant tests once I do + once the r PR is added to vscode's parse tree

## Checklist

- [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)
- [x] I have not broken the cheatsheet



Diff:
diff --git a/data/fixtures/scopes/r/anonymousFunction.scope b/data/fixtures/scopes/r/anonymousFunction.scope
new file mode 100644
index 0000000000..1f2d152fd1
--- /dev/null
+++ b/data/fixtures/scopes/r/anonymousFunction.scope
@@ -0,0 +1,17 @@
+lapply(1:10, function(x) x^2)
+---
+
+[Content] =
+[Domain] = 0:13-0:28
+               >---------------<
+0| lapply(1:10, function(x) x^2)
+
+[Removal] = 0:12-0:28
+              >----------------<
+0| lapply(1:10, function(x) x^2)
+
+[Leading delimiter] = 0:12-0:13
+              >-<
+0| lapply(1:10, function(x) x^2)
+
+[Insertion delimiter] = "\n"
diff --git a/data/fixtures/scopes/r/argument.actual.iteration.scope b/data/fixtures/scopes/r/argument.actual.iteration.scope
new file mode 100644
index 0000000000..a1afb2339d
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.actual.iteration.scope
@@ -0,0 +1,17 @@
+foo(x, y)
+
+---
+
+[#1 Range] = 0:4-0:8
+      >----<
+0| foo(x, y)
+
+[#1 Domain] = 0:3-0:9
+     >------<
+0| foo(x, y)
+
+
+[#2 Range] =
+[#2 Domain] = 0:4-0:8
+      >----<
+0| foo(x, y)
diff --git a/data/fixtures/scopes/r/argument.actual.scope b/data/fixtures/scopes/r/argument.actual.scope
new file mode 100644
index 0000000000..b1247a308d
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.actual.scope
@@ -0,0 +1,33 @@
+func(x, y)
+---
+
+[#1 Content] =
+[#1 Domain] = 0:5-0:6
+       >-<
+0| func(x, y)
+
+[#1 Removal] = 0:5-0:8
+       >---<
+0| func(x, y)
+
+[#1 Trailing delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
+
+[#1 Insertion delimiter] = ", "
+
+
+[#2 Content] =
+[#2 Domain] = 0:8-0:9
+          >-<
+0| func(x, y)
+
+[#2 Removal] = 0:6-0:9
+        >---<
+0| func(x, y)
+
+[#2 Leading delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
+
+[#2 Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/argument.actual2.scope b/data/fixtures/scopes/r/argument.actual2.scope
new file mode 100644
index 0000000000..536e3fc939
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.actual2.scope
@@ -0,0 +1,10 @@
+func(1)
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:5-0:6
+       >-<
+0| func(1)
+
+[Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/argument.actual3.scope b/data/fixtures/scopes/r/argument.actual3.scope
new file mode 100644
index 0000000000..223eb2f607
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.actual3.scope
@@ -0,0 +1,10 @@
+func(...)
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:5-0:8
+       >---<
+0| func(...)
+
+[Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/argument.actual4.scope b/data/fixtures/scopes/r/argument.actual4.scope
new file mode 100644
index 0000000000..e1dde59bf3
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.actual4.scope
@@ -0,0 +1,10 @@
+func(x=1)
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:5-0:8
+       >---<
+0| func(x=1)
+
+[Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/argument.actual5.scope b/data/fixtures/scopes/r/argument.actual5.scope
new file mode 100644
index 0000000000..bf5bfbef8d
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.actual5.scope
@@ -0,0 +1,45 @@
+foo(
+       x,
+       y
+)
+
+---
+
+[#1 Content] =
+[#1 Domain] = 1:7-1:8
+         >-<
+1|        x,
+
+[#1 Removal] = 1:7-2:7
+         >--
+1|        x,
+2|        y
+   -------<
+
+[#1 Trailing delimiter] = 1:8-2:7
+          >-
+1|        x,
+2|        y
+   -------<
+
+[#1 Insertion delimiter] = ",\n"
+
+
+[#2 Content] =
+[#2 Domain] = 2:7-2:8
+         >-<
+2|        y
+
+[#2 Removal] = 1:8-2:8
+          >-
+1|        x,
+2|        y
+   --------<
+
+[#2 Leading delimiter] = 1:8-2:7
+          >-
+1|        x,
+2|        y
+   -------<
+
+[#2 Insertion delimiter] = ",\n"
diff --git a/data/fixtures/scopes/r/argument.formal.iteration.scope b/data/fixtures/scopes/r/argument.formal.iteration.scope
new file mode 100644
index 0000000000..7e813687bd
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.formal.iteration.scope
@@ -0,0 +1,17 @@
+abc <- function(x, y){}
+
+---
+
+[#1 Range] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){}
+
+[#1 Domain] = 0:0-0:23
+  >-----------------------<
+0| abc <- function(x, y){}
+
+
+[#2 Range] =
+[#2 Domain] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){}
diff --git a/data/fixtures/scopes/r/argument.formal.scope b/data/fixtures/scopes/r/argument.formal.scope
new file mode 100644
index 0000000000..321b8a8b4c
--- /dev/null
+++ b/data/fixtures/scopes/r/argument.formal.scope
@@ -0,0 +1,33 @@
+abc <- function(x, y){ }
+---
+
+[#1 Content] =
+[#1 Domain] = 0:16-0:17
+                  >-<
+0| abc <- function(x, y){ }
+
+[#1 Removal] = 0:16-0:19
+                  >---<
+0| abc <- function(x, y){ }
+
+[#1 Trailing delimiter] = 0:17-0:19
+                   >--<
+0| abc <- function(x, y){ }
+
+[#1 Insertion delimiter] = ", "
+
+
+[#2 Content] =
+[#2 Domain] = 0:19-0:20
+                     >-<
+0| abc <- function(x, y){ }
+
+[#2 Removal] = 0:17-0:20
+                   >---<
+0| abc <- function(x, y){ }
+
+[#2 Leading delimiter] = 0:17-0:19
+                   >--<
+0| abc <- function(x, y){ }
+
+[#2 Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/argumentList.formal.scope b/data/fixtures/scopes/r/argumentList.formal.scope
new file mode 100644
index 0000000000..54b7297520
--- /dev/null
+++ b/data/fixtures/scopes/r/argumentList.formal.scope
@@ -0,0 +1,25 @@
+abc <- function(x, y){ }
+---
+
+[#1 Content] =
+[#1 Removal] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){ }
+
+[#1 Domain] = 0:0-0:24
+  >------------------------<
+0| abc <- function(x, y){ }
+
+[#1 Insertion delimiter] = ", "
+
+
+[#2 Content] =
+[#2 Removal] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){ }
+
+[#2 Domain] = 0:7-0:24
+         >-----------------<
+0| abc <- function(x, y){ }
+
+[#2 Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/argumentList.formal2.scope b/data/fixtures/scopes/r/argumentList.formal2.scope
new file mode 100644
index 0000000000..ec4af72e55
--- /dev/null
+++ b/data/fixtures/scopes/r/argumentList.formal2.scope
@@ -0,0 +1,25 @@
+abc <- function(){ }
+---
+
+[#1 Content] =
+[#1 Removal] = 0:16-0:16
+                  ><
+0| abc <- function(){ }
+
+[#1 Domain] = 0:0-0:20
+  >--------------------<
+0| abc <- function(){ }
+
+[#1 Insertion delimiter] = ""
+
+
+[#2 Content] =
+[#2 Removal] = 0:16-0:16
+                  ><
+0| abc <- function(){ }
+
+[#2 Domain] = 0:7-0:20
+         >-------------<
+0| abc <- function(){ }
+
+[#2 Insertion delimiter] = ""
diff --git a/data/fixtures/scopes/r/argumentList.formal3.scope b/data/fixtures/scopes/r/argumentList.formal3.scope
new file mode 100644
index 0000000000..23f68f7633
--- /dev/null
+++ b/data/fixtures/scopes/r/argumentList.formal3.scope
@@ -0,0 +1,47 @@
+abc <- function(
+    x,
+    y
+){
+}
+---
+
+[#1 Content] =
+[#1 Removal] = 0:16-3:0
+                  >
+0| abc <- function(
+1|     x,
+2|     y
+3| ){
+   <
+
+[#1 Domain] = 0:0-4:1
+  >----------------
+0| abc <- function(
+1|     x,
+2|     y
+3| ){
+4| }
+   -<
+
+[#1 Insertion delimiter] = ",\n"
+
+
+[#2 Content] =
+[#2 Removal] = 0:16-3:0
+                  >
+0| abc <- function(
+1|     x,
+2|     y
+3| ){
+   <
+
+[#2 Domain] = 0:7-4:1
+         >---------
+0| abc <- function(
+1|     x,
+2|     y
+3| ){
+4| }
+   -<
+
+[#2 Insertion delimiter] = ",\n"
diff --git a/data/fixtures/scopes/r/argumentList.formal4.scope b/data/fixtures/scopes/r/argumentList.formal4.scope
new file mode 100644
index 0000000000..645eaf49a0
--- /dev/null
+++ b/data/fixtures/scopes/r/argumentList.formal4.scope
@@ -0,0 +1,13 @@
+function(x, y){ }
+---
+
+[Content] =
+[Removal] = 0:9-0:13
+           >----<
+0| function(x, y){ }
+
+[Domain] = 0:0-0:17
+  >-----------------<
+0| function(x, y){ }
+
+[Insertion delimiter] = ", "
diff --git a/data/fixtures/scopes/r/comment.line.scope b/data/fixtures/scopes/r/comment.line.scope
new file mode 100644
index 0000000000..42719cdbb8
--- /dev/null
+++ b/data/fixtures/scopes/r/comment.line.scope
@@ -0,0 +1,10 @@
+# Hello world
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:0-0:13
+  >-------------<
+0| # Hello world
+
+[Insertion delimiter] = "\n"
diff --git a/data/fixtures/scopes/r/functionCall.scope b/data/fixtures/scopes/r/functionCall.scope
new file mode 100644
index 0000000000..9d9be652fd
--- /dev/null
+++ b/data/fixtures/scopes/r/functionCall.scope
@@ -0,0 +1,10 @@
+lapply(1:10, function(x) x^2)
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:0-0:29
+  >-----------------------------<
+0| lapply(1:10, function(x) x^2)
+
+[Insertion delimiter] = " "
diff --git a/data/fixtures/scopes/r/functionCallee.scope b/data/fixtures/scopes/r/functionCallee.scope
new file mode 100644
index 0000000000..b9f1e38504
--- /dev/null
+++ b/data/fixtures/scopes/r/functionCallee.scope
@@ -0,0 +1,10 @@
+lapply(1:10, function(x) x^2)
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:0-0:6
+  >------<
+0| lapply(1:10, function(x) x^2)
+
+[Insertion delimiter] = " "
diff --git a/data/fixtures/scopes/r/functionName.scope b/data/fixtures/scopes/r/functionName.scope
new file mode 100644
index 0000000000..1ee4db97f0
--- /dev/null
+++ b/data/fixtures/scopes/r/functionName.scope
@@ -0,0 +1,25 @@
+abc <- function(arg){
+    TRUE
+}
+---
+
+[Content] = 0:0-0:3
+  >---<
+0| abc <- function(arg){
+
+[Removal] = 0:0-0:7
+  >-------<
+0| abc <- function(arg){
+
+[Trailing delimiter] = 0:3-0:7
+     >----<
+0| abc <- function(arg){
+
+[Domain] = 0:0-2:1
+  >---------------------
+0| abc <- function(arg){
+1|     TRUE
+2| }
+   -<
+
+[Insertion delimiter] = " "
diff --git a/data/fixtures/scopes/r/ifStatement.scope b/data/fixtures/scopes/r/ifStatement.scope
new file mode 100644
index 0000000000..9b1eee34b5
--- /dev/null
+++ b/data/fixtures/scopes/r/ifStatement.scope
@@ -0,0 +1,10 @@
+if(FALSE) 0
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:0-0:11
+  >-----------<
+0| if(FALSE) 0
+
+[Insertion delimiter] = "\n"
diff --git a/data/fixtures/scopes/r/ifStatement2.scope b/data/fixtures/scopes/r/ifStatement2.scope
new file mode 100644
index 0000000000..8a8eb3310f
--- /dev/null
+++ b/data/fixtures/scopes/r/ifStatement2.scope
@@ -0,0 +1,10 @@
+if(TRUE){ 1 }
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:0-0:13
+  >-------------<
+0| if(TRUE){ 1 }
+
+[Insertion delimiter] = "\n"
diff --git a/data/fixtures/scopes/r/ifStatement3.scope b/data/fixtures/scopes/r/ifStatement3.scope
new file mode 100644
index 0000000000..530fab0f41
--- /dev/null
+++ b/data/fixtures/scopes/r/ifStatement3.scope
@@ -0,0 +1,29 @@
+if(TRUE){
+    1
+} else {
+    0
+}
+
+---
+
+[Content] =
+[Domain] = 0:0-4:1
+  >---------
+0| if(TRUE){
+1|     1
+2| } else {
+3|     0
+4| }
+   -<
+
+[Removal] = 0:0-5:0
+  >---------
+0| if(TRUE){
+1|     1
+2| } else {
+3|     0
+4| }
+5|
+   <
+
+[Insertion delimiter] = "\n"
diff --git a/data/fixtures/scopes/r/name.assignment.scope b/data/fixtures/scopes/r/name.assignment.scope
new file mode 100644
index 0000000000..ea78f068a8
--- /dev/null
+++ b/data/fixtures/scopes/r/name.assignment.scope
@@ -0,0 +1,20 @@
+hello = "world"
+---
+
+[Content] = 0:0-0:5
+  >-----<
+0| hello = "world"
+
+[Removal] = 0:0-0:8
+  >--------<
+0| hello = "world"
+
+[Trailing delimiter] = 0:5-0:8
+       >---<
+0| hello = "world"
+
+[Domain] = 0:0-0:15
+  >---------------<
+0| hello = "world"
+
+[Insertion delimiter] = " "
diff --git a/data/fixtures/scopes/r/name.variable.scope b/data/fixtures/scopes/r/name.variable.scope
new file mode 100644
index 0000000000..fbc108739d
--- /dev/null
+++ b/data/fixtures/scopes/r/name.variable.scope
@@ -0,0 +1,20 @@
+hello <- "world"
+---
+
+[Content] = 0:0-0:5
+  >-----<
+0| hello <- "world"
+
+[Removal] = 0:0-0:9
+  >---------<
+0| hello <- "world"
+
+[Trailing delimiter] = 0:5-0:9
+       >----<
+0| hello <- "world"
+
+[Domain] = 0:0-0:16
+  >----------------<
+0| hello <- "world"
+
+[Insertion delimiter] = " "
diff --git a/data/fixtures/scopes/r/namedFunction.scope b/data/fixtures/scopes/r/namedFunction.scope
new file mode 100644
index 0000000000..ab33c053d4
--- /dev/null
+++ b/data/fixtures/scopes/r/namedFunction.scope
@@ -0,0 +1,10 @@
+foo <- function(bar){}
+---
+
+[Content] =
+[Removal] =
+[Domain] = 0:0-0:22
+  >----------------------<
+0| foo <- function(bar){}
+
+[Insertion delimiter] = "\n\n"
diff --git a/data/fixtures/scopes/r/value.assignment.scope b/data/fixtures/scopes/r/value.assignment.scope
new file mode 100644
index 0000000000..a21e094469
--- /dev/null
+++ b/data/fixtures/scopes/r/value.assignment.scope
@@ -0,0 +1,20 @@
+hello <- "world"
+---
+
+[Content] = 0:9-0:16
+           >-------<
+0| hello <- "world"
+
+[Removal] = 0:5-0:16
+       >-----------<
+0| hello <- "world"
+
+[Leading delimiter] = 0:5-0:9
+       >----<
+0| hello <- "world"
+
+[Domain] = 0:0-0:16
+  >----------------<
+0| hello <- "world"
+
+[Insertion delimiter] = " "
diff --git a/data/fixtures/scopes/r/value.variable.scope b/data/fixtures/scopes/r/value.variable.scope
new file mode 100644
index 0000000000..2d153b44d1
--- /dev/null
+++ b/data/fixtures/scopes/r/value.variable.scope
@@ -0,0 +1,20 @@
+hello = "world"
+---
+
+[Content] = 0:8-0:15
+          >-------<
+0| hello = "world"
+
+[Removal] = 0:5-0:15
+       >----------<
+0| hello = "world"
+
+[Leading delimiter] = 0:5-0:8
+       >---<
+0| hello = "world"
+
+[Domain] = 0:0-0:15
+  >---------------<
+0| hello = "world"
+
+[Insertion delimiter] = " "
diff --git a/packages/common/src/scopeSupportFacets/languageScopeSupport.ts b/packages/common/src/scopeSupportFacets/languageScopeSupport.ts
index 8a2c0124a6..ea98a324c7 100644
--- a/packages/common/src/scopeSupportFacets/languageScopeSupport.ts
+++ b/packages/common/src/scopeSupportFacets/languageScopeSupport.ts
@@ -18,6 +18,7 @@ import { luaScopeSupport } from "./lua";
 import { markdownScopeSupport } from "./markdown";
 import { phpScopeSupport } from "./php";
 import { pythonScopeSupport } from "./python";
+import { rScopeSupport } from "./r";
 import { rubyScopeSupport } from "./ruby";
 import { rustScopeSupport } from "./rust";
 import { scalaScopeSupport } from "./scala";
@@ -51,6 +52,7 @@ export const languageScopeSupport: StringRecord<LanguageScopeSupportFacetMap> =
     markdown: markdownScopeSupport,
     php: phpScopeSupport,
     python: pythonScopeSupport,
+    r: rScopeSupport,
     ruby: rubyScopeSupport,
     rust: rustScopeSupport,
     scala: scalaScopeSupport,
diff --git a/packages/common/src/scopeSupportFacets/r.ts b/packages/common/src/scopeSupportFacets/r.ts
new file mode 100644
index 0000000000..273ace97f4
--- /dev/null
+++ b/packages/common/src/scopeSupportFacets/r.ts
@@ -0,0 +1,102 @@
+import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";
+import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types";
+
+const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
+
+export const rScopeSupport: LanguageScopeSupportFacetMap = {
+  anonymousFunction: supported,
+  "argument.actual": supported,
+  "argument.formal": supported,
+  "argumentList.formal": supported,
+  "argument.actual.iteration": supported,
+  "argument.formal.iteration": supported,
+  "comment.line": supported,
+  functionCall: supported,
+  functionCallee: supported,
+  functionName: supported,
+  ifStatement: supported,
+  "name.assignment": supported,
+  "name.variable": supported,
+  namedFunction: supported,
+  "value.assignment": supported,
+  "value.variable": supported,
+
+  // Unsupported
+
+  "branch.if": unsupported,
+  "branch.if.iteration": unsupported,
+  "branch.loop": unsupported,
+  "branch.switchCase": unsupported,
+  "branch.switchCase.iteration": unsupported,
+  "branch.ternary": unsupported,
+  "branch.try": unsupported,
+  "branch.try.iteration": unsupported,
+  "condition.for": unsupported,
+  "condition.if": unsupported,
+  "condition.ternary": unsupported,
+  "condition.while": unsupported,
+  "interior.class": unsupported,
+  "interior.function": unsupported,
+  "interior.if": unsupported,
+  "interior.lambda": unsupported,
+  "interior.try": unsupported,
+  "key.attribute": unsupported,
+  "name.class": unsupported,
+  "name.field": unsupported,
+  "name.foreach": unsupported,
+  "name.function": unsupported,
+  "name.method": unsupported,
+  "statement.class": unsupported,
+  "string.multiLine": unsupported,
+  "string.singleLine": unsupported,
+  "value.attribute": unsupported,
+  "value.foreach": unsupported,
+  "value.return": unsupported,
+  attribute: unsupported,
+  class: unsupported,
+  className: unsupported,
+  environment: unsupported,
+  fieldAccess: unsupported,
+  map: unsupported,
+  regularExpression: unsupported,
+  switchStatementSubject: unsupported,
+
+  // Not applicable
+
+  "interior.cell": notApplicable,
+  "interior.command": notApplicable,
+  "interior.element": notApplicable,
+  "name.assignment.pattern": notApplicable,
+  // Not applicable for base language but might be useful for markdown or quarto
+  "section.iteration.document": notApplicable,
+  "section.iteration.parent": notApplicable,
+  "type.alias": notApplicable,
+  "type.argument.formal": notApplicable,
+  "type.argument.formal.constructor": notApplicable,
+  "type.argument.formal.constructor.iteration": notApplicable,
+  "type.argument.formal.iteration": notApplicable,
+  "type.argument.formal.method": notApplicable,
+  "type.argument.formal.method.iteration": notApplicable,
+  "type.cast": notApplicable,
+  "type.class": notApplicable,
+  "type.enum": notApplicable,
+  "type.field": notApplicable,
+  "type.field.iteration": notApplicable,
+  "type.foreach": notApplicable,
+  "type.interface": notApplicable,
+  "type.resource": notApplicable,
+  "type.resource.iteration": notApplicable,
+  "type.return": notApplicable,
+  "type.typeArgument": notApplicable,
+  "type.typeArgument.iteration": notApplicable,
+  "type.variable": notApplicable,
+  "value.typeAlias": notApplicable,
+  "value.variable.pattern": notApplicable,
+  command: notApplicable,
+  element: notApplicable,
+  endTag: notApplicable,
+  notebookCell: notApplicable,
+  section: notApplicable,
+  startTag: notApplicable,
+  tags: notApplicable,
+};
diff --git a/packages/cursorless-org-docs/src/docs/user/languages/r.mdx b/packages/cursorless-org-docs/src/docs/user/languages/r.mdx
new file mode 100644
index 0000000000..cc692d622a
--- /dev/null
+++ b/packages/cursorless-org-docs/src/docs/user/languages/r.mdx
@@ -0,0 +1,5 @@
+import { Language } from "./components/Language";
+
+# R
+
+<Language languageId="r"></Language>
diff --git a/queries/r.scm b/queries/r.scm
new file mode 100644
index 0000000000..529b144b90
--- /dev/null
+++ b/queries/r.scm
@@ -0,0 +1,194 @@
+;; https://github.com/r-lib/tree-sitter-r/blob/main/src/grammar.json
+;; https://github.com/r-lib/tree-sitter-r/blob/main/src/node-types.json
+
+[
+  (for_statement)
+  (if_statement)
+  (repeat_statement)
+  (function_definition)
+  (while_statement)
+] @statement
+
+;;!! # hello
+;;!  ^^^^^^^
+(comment) @comment
+
+;;!! if (x > 0) { }
+;;!  ^^^^^^^^^^^^^^
+(if_statement) @ifStatement
+
+;; ;;!! if (TRUE) { print("hello") } else { print("world") }
+;; ;;!      ^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+;; (if_statement
+;;   "if" @branch.start
+;;   (_) @condition
+;;   consequence: (braced_expression) @branch.end
+;;   alternative: (braced_expression)? @branch.end
+;; ) @branch.iteration
+
+;; named function
+;;!! abc <- function(x){ }
+;;!  ^^^^^^^^^^^^^^^^^^^^^
+(binary_operator
+  ;;!! abc <- function(x){ }
+  ;;!  ^^^
+  lhs: (identifier) @functionName
+  rhs: (function_definition
+    name: "function"
+    parameters: (parameters)
+    body: (braced_expression) @interior
+  ) @functionName.trailing.startOf
+) @namedFunction @namedFunction.domain @functionName.domain
+
+;; anonymous function
+;;!! function(x){ }
+;;!  ^^^^^^^^^^^^^^
+(function_definition) @anonymousFunction
+
+;; argument.actual
+;;!! foo("bar")
+;;!      ^^^^^
+(
+  (arguments
+    (
+      (argument) @_.leading.endOf
+      (comma)
+    )?
+    .
+    (argument) @argumentOrParameter
+    .
+    (
+      (comma)
+      (argument) @_.trailing.startOf
+    )?
+  ) @_dummy
+  (#single-or-multi-line-delimiter! @argumentOrParameter @_dummy ", " ",\n")
+)
+
+;; argument.formal
+;;!! function(a, b){}
+;;!           ^^^^
+(
+  (parameters
+    (
+      (parameter) @_.leading.endOf
+      (comma)
+    )?
+    .
+    (parameter) @argumentOrParameter
+    .
+    (
+      (comma)
+      (parameter) @_.trailing.startOf
+    )?
+  ) @_dummy
+  (#single-or-multi-line-delimiter! @argumentOrParameter @_dummy ", " ",\n")
+)
+
+[
+  ;; argument.actual
+  ;;!! foo(a, b)
+  ;;!      ^^^^
+  (call
+   arguments: (arguments
+     open: "(" @argumentList.start.endOf @argumentOrParameter.iteration.start.endOf
+     close: ")" @argumentList.end.startOf @argumentOrParameter.iteration.end.startOf
+     ) @_dummy @argumentOrParameter.iteration.domain
+   (#empty-single-multi-delimiter! @argumentList.start.endOf @_dummy "" ", " ",\n")
+  )
+
+  ;; argument.formal
+  ;;!! function(a, b){}
+  ;;!           ^^^^
+  (function_definition
+   parameters: (parameters
+     open: "(" @argumentList.start.endOf @argumentOrParameter.iteration.start.endOf
+     close: ")" @argumentList.end.startOf @argumentOrParameter.iteration.end.startOf
+     ) @_dummy
+   (#empty-single-multi-delimiter! @argumentList.start.endOf @_dummy "" ", " ",\n")
+  )
+] @argumentList.domain
+
+(arguments
+  "(" @argumentOrParameter.iteration.start.endOf
+  ")" @argumentOrParameter.iteration.end.startOf
+)
+
+(parameters
+  "(" @name.iteration.start.endOf @value.iteration.start.endOf @type.iteration.start.endOf
+  ")" @name.iteration.end.startOf @value.iteration.end.startOf @type.iteration.end.startOf
+)
+
+;;!! function(a, b){ }
+;;!           ^^^^
+;; For domain, have to handle either named or unnamed function using
+;; alternation, to ensure that the function name is part of the domain if it
+;; exists
+[
+  (binary_operator
+   (function_definition
+    parameters: (parameters
+      open: "(" @argumentList.start.endOf @argumentOrParameter.iteration.start.endOf
+      close: ")" @argumentList.end.startOf @argumentOrParameter.iteration.end.startOf
+      ) @_dummy
+    (#empty-single-multi-delimiter! @argumentList.start.endOf @_dummy "" ", " ",\n")
+   )
+  ) @argumentOrParameter.iteration.domain
+  (function_definition
+   parameters: (parameters
+     open: "(" @argumentList.start.endOf @argumentOrParameter.iteration.start.endOf
+     close: ")" @argumentList.end.startOf @argumentOrParameter.iteration.end.startOf
+     ) @_dummy
+   (#empty-single-multi-delimiter! @argumentList.start.endOf @_dummy "" ", " ",\n")
+  )
+] @argumentList.domain
+
+
+;; Function calls
+;;!! foo()
+;;!  ^^^^^
+;;!  -----
+(call) @functionCall @argumentOrParameter.iteration.domain
+
+;;!! foo()
+;;!  ^^^
+;;!  -----
+(call
+  (identifier) @functionCallee
+)
+
+;; Technically lists and arrays are just calls to the function `list` or `c`
+;;!! list(1, 2, 3)
+;;!  ^^^^^^^^^^^^^
+(call
+  function: (identifier) @functionName
+  (#match? @functionName "^(c|list)$")
+) @list
+
+(binary_operator
+  ;;!! hello <- "world"
+  ;;!  ^^^^^
+  ;;!  -----
+  lhs: (identifier) @name @value.leading.endOf
+  operator: ["<-" "="]
+  ;;!! hello <- "world"
+  ;;!           ^^^^^^^
+  ;;!  -----
+  rhs: (_) @value @name.trailing.startOf
+) @_.domain
+
+;;!! foo(hello)
+;;!      ^^^^^
+;;!  -----
+;; (identifier) @value
+
+;; ;; from https://github.com/r-lib/tree-sitter-r/blob/main/queries/highlights.scm
+;; ;; Plus magrittr operators
+;; operator: [ "?" ":=" "=" "<-" "<<-" "->" "->>"
+;;   "~" "|>" "||" "|" "&&" "&"
+;;   "<" "<=" ">" ">=" "==" "!="
+;;   "+" "-" "*" "/" "::" ":::"
+;;   "**" "^" "$" "@" ":" "%in%"
+;;   "%>%" "%<>%" "%T>%" "%$%"
+;;   "special"
+;; ] @disqualifyDelimiter


Reviews:
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T21:05:54Z
user: login: AndreasArvidsson
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-16T04:47:19Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T03:19:06Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T05:39:32Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T05:41:33Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T05:43:14Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T07:20:45Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T07:22:22Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T07:24:00Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T07:52:16Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T08:05:45Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T10:00:54Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T10:01:46Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T10:03:42Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T10:04:02Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T10:04:34Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T06:38:32Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T06:40:32Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T09:09:57Z
user: login: AndreasArvidsson
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-25T09:11:57Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-25T09:38:43Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T09:46:16Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T09:47:08Z
user: login: AndreasArvidsson
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-25T10:20:24Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T10:21:16Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-25T10:22:12Z
user: login: BlueDrink9
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-25T10:23:20Z
user: login: AndreasArvidsson
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-25T10:28:35Z
user: login: AndreasArvidsson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T10:29:24Z

Review comments:
diff_hunk: @@ -0,0 +1,19 @@
+abc <- function(arg){
+    TRUE
+}
+---
+
+[Content] =
+[Domain] = 0:0-0:3
+  >---<
+0| abc <- function(arg){
path: data/fixtures/scopes/r/functionName.scope
user: login: BlueDrink9
body: This is the only case that I'm unsure about and is likely to need review, I'm wondering if it would make more sense to include the operator in this for removal and insertion? Although I might struggle a little bit with adding them as custom delimiters
created_at: 2025-05-15T21:05:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+abc <- function(arg){
+    TRUE
+}
+---
+
+[Content] =
+[Domain] = 0:0-0:3
+  >---<
+0| abc <- function(arg){
path: data/fixtures/scopes/r/functionName.scope
user: login: AndreasArvidsson
body: `<-` should probably be added as a trailing range. That way it will be deleted if you delete the name.
We do the same thing with `=` in `value = 2`
created_at: 2025-05-16T04:38:33Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:0-0:5
+  >-----<
+0| hello <- "world"
+
+[#1 Removal] = 0:0-0:6
+  >------<
+0| hello <- "world"
+
+[#1 Trailing delimiter] = 0:5-0:6
+       >-<
+0| hello <- "world"
path: data/fixtures/scopes/r/name.assignment.scope
user: login: AndreasArvidsson
body: ` <- ` should be part off the trailing range. Have a look at how we do this in other languages.
created_at: 2025-05-16T04:41:58Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:0-0:5
+  >-----<
+0| hello <- "world"
+
+[#1 Removal] = 0:0-0:6
+  >------<
+0| hello <- "world"
+
+[#1 Trailing delimiter] = 0:5-0:6
+       >-<
+0| hello <- "world"
+
+[#1 Insertion delimiter] = " "
+
+
+[#2 Content] =
+[#2 Domain] = 1:0-1:5
+  >-----<
+1| hello = "world"
+
+[#2 Removal] = 1:0-1:6
+  >------<
+1| hello = "world"
+
+[#2 Trailing delimiter] = 1:5-1:6
+       >-<
+1| hello = "world"
path: data/fixtures/scopes/r/name.assignment.scope
user: login: AndreasArvidsson
body: ` = ` should be part of the trailing range
created_at: 2025-05-16T04:42:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:0-0:5
+  >-----<
+0| hello <- "world"
+
+[#1 Removal] = 0:0-0:6
+  >------<
+0| hello <- "world"
+
+[#1 Trailing delimiter] = 0:5-0:6
+       >-<
+0| hello <- "world"
path: data/fixtures/scopes/r/name.variable.scope
user: login: AndreasArvidsson
body: ` <- ` should be part of the trailing range
created_at: 2025-05-16T04:42:59Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:0-0:5
+  >-----<
+0| hello <- "world"
+
+[#1 Removal] = 0:0-0:6
+  >------<
+0| hello <- "world"
+
+[#1 Trailing delimiter] = 0:5-0:6
+       >-<
+0| hello <- "world"
+
+[#1 Insertion delimiter] = " "
+
+
+[#2 Content] =
+[#2 Domain] = 1:0-1:5
+  >-----<
+1| hello = "world"
+
+[#2 Removal] = 1:0-1:6
+  >------<
+1| hello = "world"
+
+[#2 Trailing delimiter] = 1:5-1:6
+       >-<
+1| hello = "world"
path: data/fixtures/scopes/r/name.variable.scope
user: login: AndreasArvidsson
body: ` = ` should be part of the trailing range
created_at: 2025-05-16T04:43:11Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:9-0:16
+           >-------<
+0| hello <- "world"
+
+[#1 Removal] = 0:8-0:16
+          >--------<
+0| hello <- "world"
+
+[#1 Leading delimiter] = 0:8-0:9
+          >-<
+0| hello <- "world"
path: data/fixtures/scopes/r/value.assignment.scope
user: login: AndreasArvidsson
body: ` <- ` should be part of the leading range
created_at: 2025-05-16T04:44:17Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:9-0:16
+           >-------<
+0| hello <- "world"
+
+[#1 Removal] = 0:8-0:16
+          >--------<
+0| hello <- "world"
+
+[#1 Leading delimiter] = 0:8-0:9
+          >-<
+0| hello <- "world"
+
+[#1 Insertion delimiter] = " "
+
+
+[#2 Content] =
+[#2 Domain] = 1:8-1:15
+          >-------<
+1| hello = "world"
+
+[#2 Removal] = 1:7-1:15
+         >--------<
+1| hello = "world"
+
+[#2 Leading delimiter] = 1:7-1:8
+         >-<
+1| hello = "world"
path: data/fixtures/scopes/r/value.assignment.scope
user: login: AndreasArvidsson
body: ` = ` should be part of the leading range
created_at: 2025-05-16T04:44:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:9-0:16
+           >-------<
+0| hello <- "world"
+
+[#1 Removal] = 0:8-0:16
+          >--------<
+0| hello <- "world"
+
+[#1 Leading delimiter] = 0:8-0:9
+          >-<
+0| hello <- "world"
path: data/fixtures/scopes/r/value.variable.scope
user: login: AndreasArvidsson
body: ` <- ` should be part of the leading range
created_at: 2025-05-16T04:44:46Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,34 @@
+hello <- "world"
+hello = "world"
+---
+
+[#1 Content] =
+[#1 Domain] = 0:9-0:16
+           >-------<
+0| hello <- "world"
+
+[#1 Removal] = 0:8-0:16
+          >--------<
+0| hello <- "world"
+
+[#1 Leading delimiter] = 0:8-0:9
+          >-<
+0| hello <- "world"
+
+[#1 Insertion delimiter] = " "
+
+
+[#2 Content] =
+[#2 Domain] = 1:8-1:15
+          >-------<
+1| hello = "world"
path: data/fixtures/scopes/r/value.variable.scope
user: login: AndreasArvidsson
body: ` = ` should be part of the leading range
created_at: 2025-05-16T04:44:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,189 @@
+import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";
+import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types";
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
+
+export const rScopeSupport: LanguageScopeSupportFacetMap = {
path: packages/common/src/scopeSupportFacets/r.ts
user: login: AndreasArvidsson
body: Could you please group these in supported/unsupported/notApplicable? Have a look at how we have done in other languages.
created_at: 2025-05-16T04:45:54Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,189 @@
+import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";
+import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types";
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
+
+export const rScopeSupport: LanguageScopeSupportFacetMap = {
+  "comment.line": supported,
+  "ifStatement": supported,
+  "argument.actual": supported,
+  "argumentList.formal": supported,
+  "condition.if": supported,
+  "condition.while": unsupported,
+
+  "namedFunction": supported,
+  "anonymousFunction": supported,
+  "functionName": supported,
+  "functionCall": supported,
+  "functionCallee": supported,
+
+  "name.assignment": supported,
+  "name.assignment.pattern": notApplicable,
+  "name.variable": supported,
+  "value.assignment": supported,
+  "value.variable": supported,
+  "value.variable.pattern": notApplicable,
+
+  // "interior.function": unsupported,
path: packages/common/src/scopeSupportFacets/r.ts
user: login: AndreasArvidsson
body: Before this is ready to merge I would like all commented facets like this to be removed
created_at: 2025-05-16T04:46:51Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,137 @@
+;; https://github.com/r-lib/tree-sitter-r/blob/main/src/grammar.json
+;; https://github.com/r-lib/tree-sitter-r/blob/main/src/node-types.json
+
+[
+  (for_statement)
+  (if_statement)
+  (repeat_statement)
+  (function_definition)
+  (while_statement)
+  (function_definition)
+] @statement
+
+;;!! # hello
+;;!  ^^^^^^^
+(comment) @comment
+
+;;!! if (x > 0) { }
+;;!  ^^^^^^^^^^^^^^
+(if_statement) @ifStatement
+
+;; ;;!! if (TRUE) { print("hello") } else { print("world") }
+;; ;;!      ^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+;; (if_statement
+;;   "if" @branch.start
+;;   (_) @condition
+;;   consequence: (braced_expression) @branch.end
+;;   alternative: (braced_expression)? @branch.end
+;; ) @branch.iteration
+
+;;!! foo(x)
+;;!      ^
+(argument) @argumentOrParameter
+;;!! foo(x)
+;;!  ^^^^^^
+(call) @functionCall
+
+;;!! function(x){ }
+;;!  ^^^^^^^^^^^^^^
+(function_definition) @anonymousFunction
+
+;;!! function("bar")
+;;!           ^^^^^
+(
+  (parameters
+    (_)? @_.leading.endOf
+    .
+    (_) @argumentOrParameter
+    .
+    (_)? @_.trailing.startOf
+  ) @_dummy
+  (#single-or-multi-line-delimiter! @argumentOrParameter @_dummy ", " ",\n")
+)
+(_
+  parameters: (_
+    "(" @argumentList.start.endOf @argumentOrParameter.iteration.start.endOf
+    ")" @argumentList.end.startOf @argumentOrParameter.iteration.end.startOf
+  ) @_dummy
+  (#empty-single-multi-delimiter! @argumentList.start.endOf @_dummy "" ", " ",\n")
+) @argumentList.domain @argumentOrParameter.iteration.domain
+
+(parameters
+  "(" @argumentOrParameter.iteration.start.endOf
+  ")" @argumentOrParameter.iteration.end.startOf
+) @argumentOrParameter.iteration.domain
+
+;;!! abc <- function(x){ }
+;;!  ^^^^^^^^^^^^^^^^^^^^^
+(binary_operator
+  ;;!! abc <- function(x){ }
+  ;;!  ^^^
+  lhs: (identifier) @functionName
+  rhs: (function_definition
+    name: "function"
+    parameters: (parameters)
+    body: (braced_expression) @interior
+  )
+) @namedFunction
+
+;;!! foo()
+;;!  ^^^^^
+;;!  -----
+(call) @functionCall
+
+;;!! foo()
+;;!  ^^^
+;;!  -----
+(call
+  (identifier) @functionCallee
+)
+
+;; Technically lists and arrays are just calls to the function `list` or `c`
+;; TODO: should this include the function name/brackets?
+;;!! list(1, 2, 3)
+;;!  ^^^^^^^^^^^^^
+(call
+  function: (identifier) @functionName
+  (#match? @functionName "^(c|list)$")
+) @list
+
+(binary_operator
+  ;;!! hello <- "world"
+  ;;!  ^^^^^
+  ;;!  -----
+  lhs: (identifier) @name
+  operator: "<-"
+  ;;!! hello <- "world"
+  ;;!           ^^^^^^^
+  ;;!  -----
+  rhs: (_) @value
+)
+(binary_operator
+  ;;!! hello <- "world"
+  ;;!  ^^^^^
+  ;;!  -----
+  lhs: (identifier) @name
+  operator: "="
+  ;;!! hello <- "world"
+  ;;!           ^^^^^^^
+  ;;!  -----
+  rhs: (_) @value
+)
path: queries/r.scm
user: login: BlueDrink9
body: I'm assuming there is no way to combine these to handle either "<-" or "=" as part of a single query?
created_at: 2025-05-22T10:46:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,33 @@
+func(x, y)
+---
+
+[#1 Content] =
+[#1 Domain] = 0:5-0:6
+       >-<
+0| func(x, y)
+
+[#1 Removal] = 0:5-0:8
+       >---<
+0| func(x, y)
+
+[#1 Trailing delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
path: data/fixtures/scopes/r/argument.actual.scope
user: login: BlueDrink9
body: I need help, I cannot figure out how to make the whitespace part of the trailing delimiter. This test currently fails
created_at: 2025-05-23T02:46:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,33 @@
+func(x, y)
+---
+
+[#1 Content] =
+[#1 Domain] = 0:5-0:6
+       >-<
+0| func(x, y)
+
+[#1 Removal] = 0:5-0:8
+       >---<
+0| func(x, y)
+
+[#1 Trailing delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
path: data/fixtures/scopes/r/argument.actual.scope
user: login: BlueDrink9
body: Part of me wonders if the parser is the issue here, it seems like the capture after an argument does not include the white space, which it seems to me it it does for other languages?
created_at: 2025-05-23T03:18:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,33 @@
+func(x, y)
+---
+
+[#1 Content] =
+[#1 Domain] = 0:5-0:6
+       >-<
+0| func(x, y)
+
+[#1 Removal] = 0:5-0:8
+       >---<
+0| func(x, y)
+
+[#1 Trailing delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
path: data/fixtures/scopes/r/argument.actual.scope
user: login: AndreasArvidsson
body: The problem is that the parse tree have `,` as a named node. This is quite unusual. Usually delimiters like `,`, `(` etc are not named. To fix this we can do the following:

```scm
(
  (arguments
    (
      (argument) @_.leading.endOf
      (comma)
    )?
    .
    (argument) @argumentOrParameter
    .
    (
      (comma)
      (argument) @_.trailing.startOf
    )?
  ) @_dummy
  (#not-type? @argumentOrParameter "comment")
  (#single-or-multi-line-delimiter! @argumentOrParameter @_dummy ", " ",\n")
)
```

created_at: 2025-05-23T05:39:32Z
author_association: MEMBER
diff_hunk: @@ -22,7 +22,7 @@ async function main() {
     }
 
     // Install extension dependencies
-    const subprocess = cp.spawn("code", args, {
+    const subprocess = cp.spawn("codium", args, {
path: packages/cursorless-vscode/src/scripts/initLaunchSandbox.ts
user: login: AndreasArvidsson
body: Please don't commit this
created_at: 2025-05-23T05:41:23Z
author_association: MEMBER
diff_hunk: @@ -1,3 +1,25 @@
+See description of facets in here,
path: packages/cursorless-org-docs/src/docs/contributing/adding-a-new-language.md
user: login: AndreasArvidsson
body: This change needs to be worked on. It looks like a bunch of separate thoughts. Did you really mean to commit it like this?
created_at: 2025-05-23T05:43:14Z
author_association: MEMBER
diff_hunk: @@ -22,7 +22,7 @@ async function main() {
     }
 
     // Install extension dependencies
-    const subprocess = cp.spawn("code", args, {
+    const subprocess = cp.spawn("codium", args, {
path: packages/cursorless-vscode/src/scripts/initLaunchSandbox.ts
user: login: BlueDrink9
body: oops!
created_at: 2025-05-23T07:20:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,3 +1,25 @@
+See description of facets in here,
path: packages/cursorless-org-docs/src/docs/contributing/adding-a-new-language.md
user: login: BlueDrink9
body: No I did not, sorry! That was just my todo list, I am not used to how VSCode auto stages all changes
created_at: 2025-05-23T07:22:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,3 +1,25 @@
+See description of facets in here,
path: packages/cursorless-org-docs/src/docs/contributing/adding-a-new-language.md
user: login: AndreasArvidsson
body: Vscode doesn't auto stage anything for me. Might be a setting. That sounds dangerous :D
created_at: 2025-05-23T07:24:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,33 @@
+func(x, y)
+---
+
+[#1 Content] =
+[#1 Domain] = 0:5-0:6
+       >-<
+0| func(x, y)
+
+[#1 Removal] = 0:5-0:8
+       >---<
+0| func(x, y)
+
+[#1 Trailing delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
path: data/fixtures/scopes/r/argument.actual.scope
user: login: BlueDrink9
body: Thanks, that worked!
created_at: 2025-05-23T07:52:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,33 @@
+func(x, y)
+---
+
+[#1 Content] =
+[#1 Domain] = 0:5-0:6
+       >-<
+0| func(x, y)
+
+[#1 Removal] = 0:5-0:8
+       >---<
+0| func(x, y)
+
+[#1 Trailing delimiter] = 0:6-0:8
+        >--<
+0| func(x, y)
path: data/fixtures/scopes/r/argument.actual.scope
user: login: AndreasArvidsson
body: Great. btw since we're not using the wild card anymore you can remove `  (#not-type? @argumentOrParameter "comment")`
created_at: 2025-05-23T08:05:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,16 @@
+
path: data/fixtures/scopes/r/ifStatement2.scope
user: login: AndreasArvidsson
body: Is there a particular reason for this leading empty line?
created_at: 2025-05-23T10:00:54Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,31 @@
+
path: data/fixtures/scopes/r/ifStatement3.scope
user: login: AndreasArvidsson
body: Same here with empty lines
created_at: 2025-05-23T10:01:46Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";
+import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types";
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
+
+export const rScopeSupport: LanguageScopeSupportFacetMap = {
+  "anonymousFunction": supported,
+  "argument.actual": supported,
+  "argumentList.formal": supported,
+  "comment.line": supported,
+  "functionCall": supported,
+  "functionCallee": supported,
+  "functionName": supported,
+  "ifStatement": supported,
+  "name.assignment": supported,
+  "name.variable": supported,
+  "namedFunction": supported,
+  "value.assignment": supported,
+  "value.variable": supported,
+
+  "branch.if": unsupported,
path: packages/common/src/scopeSupportFacets/r.ts
user: login: AndreasArvidsson
body: ```suggestion
  // Unsupported

  "branch.if": unsupported,
```
created_at: 2025-05-23T10:03:41Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";
+import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types";
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
+
+export const rScopeSupport: LanguageScopeSupportFacetMap = {
+  "anonymousFunction": supported,
+  "argument.actual": supported,
+  "argumentList.formal": supported,
+  "comment.line": supported,
+  "functionCall": supported,
+  "functionCallee": supported,
+  "functionName": supported,
+  "ifStatement": supported,
+  "name.assignment": supported,
+  "name.variable": supported,
+  "namedFunction": supported,
+  "value.assignment": supported,
+  "value.variable": supported,
+
+  "branch.if": unsupported,
+  "branch.if.iteration": unsupported,
+  "branch.loop": unsupported,
+  "branch.switchCase": unsupported,
+  "branch.switchCase.iteration": unsupported,
+  "branch.ternary": unsupported,
+  "branch.try": unsupported,
+  "branch.try.iteration": unsupported,
+  "condition.for": unsupported,
+  "condition.if": unsupported,
+  "condition.ternary": unsupported,
+  "condition.while": unsupported,
+  "interior.class": unsupported,
+  "interior.function": unsupported,
+  "interior.if": unsupported,
+  "interior.lambda": unsupported,
+  "interior.try": unsupported,
+  "key.attribute": unsupported,
+  "name.class": unsupported,
+  "name.field": unsupported,
+  "name.foreach": unsupported,
+  "name.function": unsupported,
+  "name.method": unsupported,
+  "statement.class": unsupported,
+  "string.multiLine": unsupported,
+  "string.singleLine": unsupported,
+  "value.attribute": unsupported,
+  "value.foreach": unsupported,
+  "value.return": unsupported,
+  attribute: unsupported,
+  class: unsupported,
+  className: unsupported,
+  environment: unsupported,
+  fieldAccess: unsupported,
+  map: unsupported,
+  regularExpression: unsupported,
+  switchStatementSubject: unsupported,
+
+  "interior.cell": notApplicable,
path: packages/common/src/scopeSupportFacets/r.ts
user: login: AndreasArvidsson
body: ```suggestion
  // Not applicable

  "interior.cell": notApplicable,
```
created_at: 2025-05-23T10:04:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+import type { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";
+import { ScopeSupportFacetLevel } from "./scopeSupportFacets.types";
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
path: packages/common/src/scopeSupportFacets/r.ts
user: login: AndreasArvidsson
body: ```suggestion
```
created_at: 2025-05-23T10:04:34Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,17 @@
+foo(x, y)
+
+---
+
+[#1 Range] = 0:4-0:8
+      >----<
+0| foo(x, y)
+
+[#1 Domain] = 0:0-0:9
+  >---------<
+0| foo(x, y)
path: data/fixtures/scopes/r/argument.actual.iteration.scope
user: login: AndreasArvidsson
body: The function name should not be part of the iteration scope
eg

https://github.com/cursorless-dev/cursorless/blob/97bf1d6651000087ea1762aec9bb5ca50bc1b00e/data/fixtures/scopes/javascript.core/argument.actual.iteration.scope

created_at: 2025-05-24T06:38:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,42 @@
+abc <- function(
path: data/fixtures/scopes/r/argument.formal.iteration.scope
user: login: AndreasArvidsson
body: You don't need a vertical list for the iteration scope. I just wanted to make sure that the insertion delimiter was correct for argument.
created_at: 2025-05-24T06:40:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,17 @@
+foo(x, y)
+
+---
+
+[#1 Range] = 0:4-0:8
+      >----<
+0| foo(x, y)
+
+[#1 Domain] = 0:0-0:9
+  >---------<
+0| foo(x, y)
path: data/fixtures/scopes/r/argument.actual.iteration.scope
user: login: AndreasArvidsson
body: The reason for this is when you use a function call as an argument. eg
```ts
foo(|bar(1), baz(2))
```
If your cursor is `|` and you say `"take every arg"` I would expect to get two selections with both bar and baz function calls. That is why we exclude the function name from the iteration domain.
created_at: 2025-05-25T09:09:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,17 @@
+abc <- function(x, y){}
+
+---
+
+[#1 Range] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){}
+
+[#1 Domain] = 0:15-0:21
+                 >------<
+0| abc <- function(x, y){}
path: data/fixtures/scopes/r/argument.formal.iteration.scope
user: login: AndreasArvidsson
body: That domain should include the entire function

eg
https://github.com/cursorless-dev/cursorless/blob/97bf1d6651000087ea1762aec9bb5ca50bc1b00e/data/fixtures/scopes/javascript.core/argument.formal.iteration.scope

created_at: 2025-05-25T09:11:36Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,17 @@
+abc <- function(x, y){}
+
+---
+
+[#1 Range] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){}
+
+[#1 Domain] = 0:15-0:21
+                 >------<
+0| abc <- function(x, y){}
path: data/fixtures/scopes/r/argument.formal.iteration.scope
user: login: BlueDrink9
body: That seems really inconsistent, how come it's different for formal versus actual? Fixed anyway
created_at: 2025-05-25T09:38:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,17 @@
+abc <- function(x, y){}
+
+---
+
+[#1 Range] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){}
+
+[#1 Domain] = 0:15-0:21
+                 >------<
+0| abc <- function(x, y){}
path: data/fixtures/scopes/r/argument.formal.iteration.scope
user: login: AndreasArvidsson
body: Have you read my response about that?
https://github.com/cursorless-dev/cursorless/pull/2721/files#r2106136228
created_at: 2025-05-25T09:46:16Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,17 @@
+abc <- function(x, y){}
+
+---
+
+[#1 Range] = 0:16-0:20
+                  >----<
+0| abc <- function(x, y){}
+
+[#1 Domain] = 0:15-0:21
+                 >------<
+0| abc <- function(x, y){}
path: data/fixtures/scopes/r/argument.formal.iteration.scope
user: login: AndreasArvidsson
body: You probably want to include the function name as well so that you can take all arguments from the function name
created_at: 2025-05-25T09:47:08Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+import { Language } from "./components/Language";
+
+# r
path: packages/cursorless-org-docs/src/docs/user/languages/r.mdx
user: login: AndreasArvidsson
body: ```suggestion
# R
```
created_at: 2025-05-25T10:21:15Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+import { Language } from "./components/Language";
+
+# r
path: packages/cursorless-org-docs/src/docs/user/languages/r.mdx
user: login: BlueDrink9
body: The test expected r, how come?
created_at: 2025-05-25T10:22:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+import { Language } from "./components/Language";
+
+# r
path: packages/cursorless-org-docs/src/docs/user/languages/r.mdx
user: login: BlueDrink9
body: Oh right this is just markdown, nvm

created_at: 2025-05-25T10:23:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+import { Language } from "./components/Language";
+
+# r
path: packages/cursorless-org-docs/src/docs/user/languages/r.mdx
user: login: AndreasArvidsson
body: That was probably the file name. The markdown title we are free to do whatever we want with.
created_at: 2025-05-25T10:29:24Z
author_association: MEMBER

PR comments:
user: login: BlueDrink9
created_at: 2025-05-13T21:36:12Z
body: I'm marking this as ready to make adding scopes easier. Once r support is included in cursorless, it will be easier for it to pick it up as an option for adding scopes
author_association: CONTRIBUTOR
user: login: AndreasArvidsson
created_at: 2025-05-14T07:02:23Z
body: Latest version of the parse tree is published and should be available in the marketplace very soon.
author_association: MEMBER
user: login: BlueDrink9
created_at: 2025-05-15T21:06:07Z
body: @AndreasArvidsson ready to review! Thank you again for your help yesterday
author_association: CONTRIBUTOR
user: login: AndreasArvidsson
created_at: 2025-05-16T04:36:14Z
body: Please add a scope test for an empty argument list as well. The difference is the insertion delimiter. Please have a look at how it's implemented in other languages.
author_association: MEMBER
user: login: AndreasArvidsson
created_at: 2025-05-16T04:41:06Z
body: I would recommend that you create multiple scope files for the same facet instead of adding multiple different code examples in the same file. It's much simpler to review it that way.
author_association: MEMBER
user: login: AndreasArvidsson
created_at: 2025-05-23T10:08:22Z
body: Looks like you're starting to get the hang of this :)

* Might be worth adding a test for a vertical argument list for both `argument.actual` and `argumentList.formal`.
* Looks like you're still missing `argument.formal`
* Don't forget to add iteration scopes for argument otherwise you can't do `every arg`
author_association: MEMBER
user: login: AndreasArvidsson
created_at: 2025-05-29T07:33:10Z
body: @BlueDrink9 Regarding iteration domain for arguments
https://github.com/cursorless-dev/cursorless/issues/2930
author_association: MEMBER