Skip to content

Commit 3e705dd

Browse files
committed
Index R6 classes
1 parent cba4960 commit 3e705dd

File tree

4 files changed

+261
-1
lines changed

4 files changed

+261
-1
lines changed

crates/ark/src/lsp/indexer.rs

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ fn index_node(
225225
}
226226

227227
fn index_assignment(
228-
_path: &Path,
228+
path: &Path,
229229
contents: &Rope,
230230
node: &Node,
231231
entries: &mut Vec<IndexEntry>,
@@ -243,6 +243,16 @@ fn index_assignment(
243243
None => return Ok(()),
244244
};
245245

246+
let Some(rhs) = node.child_by_field_name("rhs") else {
247+
return Ok(());
248+
};
249+
250+
if crate::treesitter::node_is_call(&rhs, "R6Class", contents) ||
251+
crate::treesitter::node_is_namespaced_call(&rhs, "R6", "R6Class", contents)
252+
{
253+
index_r6_class(path, contents, &rhs, entries)?;
254+
}
255+
246256
let lhs_text = contents.node_slice(&lhs)?.to_string();
247257

248258
// The method matching is super hacky but let's wait until the typed API to
@@ -294,6 +304,73 @@ fn index_assignment(
294304
Ok(())
295305
}
296306

307+
fn index_r6_class(
308+
_path: &Path,
309+
contents: &Rope,
310+
node: &Node,
311+
entries: &mut Vec<IndexEntry>,
312+
) -> anyhow::Result<()> {
313+
let Some(args_node) = node.child_by_field_name("arguments") else {
314+
return Ok(());
315+
};
316+
317+
let mut cursor = args_node.walk();
318+
for arg in args_node.children(&mut cursor) {
319+
// Only consider `public = ` and `private = ` arguments
320+
let Some(arg_name) = arg.child_by_field_name("name") else {
321+
continue;
322+
};
323+
if !arg_name.is_identifier_or_string() {
324+
continue;
325+
}
326+
let arg_name_str = contents.node_slice(&arg_name)?;
327+
if arg_name_str != "public" && arg_name_str != "private" {
328+
continue;
329+
}
330+
331+
let Some(list_node) = arg.child_by_field_name("value") else {
332+
continue;
333+
};
334+
if !list_node.is_call() {
335+
continue;
336+
}
337+
338+
let Some(list_args) = list_node.child_by_field_name("arguments") else {
339+
return Ok(());
340+
};
341+
342+
let mut cursor = list_args.walk();
343+
for arg in list_args.children(&mut cursor) {
344+
if !arg.is_argument() {
345+
continue;
346+
}
347+
348+
let (Some(mtd_name), Some(mtd_value)) = (
349+
arg.child_by_field_name("name"),
350+
arg.child_by_field_name("value"),
351+
) else {
352+
continue;
353+
};
354+
if !mtd_name.is_identifier_or_string() || !mtd_value.is_function_definition() {
355+
continue;
356+
}
357+
358+
let name = contents.node_slice(&mtd_name)?.to_string();
359+
let start = convert_point_to_position(contents, mtd_name.start_position());
360+
let end = convert_point_to_position(contents, mtd_name.end_position());
361+
362+
// TODO!: Should be Method
363+
entries.push(IndexEntry {
364+
key: name.clone(),
365+
range: Range { start, end },
366+
data: IndexEntryData::Variable { name },
367+
});
368+
}
369+
}
370+
371+
Ok(())
372+
}
373+
297374
fn index_comment(
298375
_path: &Path,
299376
contents: &Rope,
@@ -422,6 +499,49 @@ x <- function() {
422499
# This inner section is not indexed ----
423500
}
424501
502+
"#
503+
);
504+
}
505+
506+
#[test]
507+
fn test_index_r6class() {
508+
test_index!(
509+
r#"
510+
class <- R6Class(
511+
public = list(
512+
initialize = function() {
513+
1
514+
},
515+
public_method = function() {
516+
2
517+
}
518+
),
519+
private = list(
520+
private_method = function() {
521+
1
522+
}
523+
),
524+
other = list(
525+
other_method = function() {
526+
1
527+
}
528+
)
529+
)
530+
"#
531+
);
532+
}
533+
534+
#[test]
535+
fn test_index_r6class_namespaced() {
536+
test_index!(
537+
r#"
538+
class <- R6::R6Class(
539+
public = list(
540+
initialize = function() {
541+
1
542+
},
543+
)
544+
)
425545
"#
426546
);
427547
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
source: crates/ark/src/lsp/indexer.rs
3+
expression: entries
4+
---
5+
[
6+
IndexEntry {
7+
key: "initialize",
8+
range: Range {
9+
start: Position {
10+
line: 3,
11+
character: 8,
12+
},
13+
end: Position {
14+
line: 3,
15+
character: 18,
16+
},
17+
},
18+
data: Variable {
19+
name: "initialize",
20+
},
21+
},
22+
IndexEntry {
23+
key: "public_method",
24+
range: Range {
25+
start: Position {
26+
line: 6,
27+
character: 8,
28+
},
29+
end: Position {
30+
line: 6,
31+
character: 21,
32+
},
33+
},
34+
data: Variable {
35+
name: "public_method",
36+
},
37+
},
38+
IndexEntry {
39+
key: "private_method",
40+
range: Range {
41+
start: Position {
42+
line: 11,
43+
character: 8,
44+
},
45+
end: Position {
46+
line: 11,
47+
character: 22,
48+
},
49+
},
50+
data: Variable {
51+
name: "private_method",
52+
},
53+
},
54+
IndexEntry {
55+
key: "class",
56+
range: Range {
57+
start: Position {
58+
line: 1,
59+
character: 0,
60+
},
61+
end: Position {
62+
line: 1,
63+
character: 5,
64+
},
65+
},
66+
data: Variable {
67+
name: "class",
68+
},
69+
},
70+
]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
source: crates/ark/src/lsp/indexer.rs
3+
expression: entries
4+
---
5+
[
6+
IndexEntry {
7+
key: "initialize",
8+
range: Range {
9+
start: Position {
10+
line: 3,
11+
character: 8,
12+
},
13+
end: Position {
14+
line: 3,
15+
character: 18,
16+
},
17+
},
18+
data: Variable {
19+
name: "initialize",
20+
},
21+
},
22+
IndexEntry {
23+
key: "class",
24+
range: Range {
25+
start: Position {
26+
line: 1,
27+
character: 0,
28+
},
29+
end: Position {
30+
line: 1,
31+
character: 5,
32+
},
33+
},
34+
data: Variable {
35+
name: "class",
36+
},
37+
},
38+
]

crates/ark/src/treesitter.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,38 @@ pub(crate) fn node_is_call(node: &Node, name: &str, contents: &ropey::Rope) -> b
465465
fun == name
466466
}
467467

468+
pub(crate) fn node_is_namespaced_call(
469+
node: &Node,
470+
namespace: &str,
471+
name: &str,
472+
contents: &ropey::Rope,
473+
) -> bool {
474+
if !node.is_call() {
475+
return false;
476+
}
477+
478+
let Some(op) = node.child_by_field_name("function") else {
479+
return false;
480+
};
481+
if !op.is_namespace_operator() {
482+
return false;
483+
}
484+
485+
let (Some(node_namespace), Some(node_name)) =
486+
(op.child_by_field_name("lhs"), op.child_by_field_name("rhs"))
487+
else {
488+
return false;
489+
};
490+
let Some(node_namespace) = node_text(&node_namespace, contents) else {
491+
return false;
492+
};
493+
let Some(node_name) = node_text(&node_name, contents) else {
494+
return false;
495+
};
496+
497+
node_namespace == namespace && node_name == name
498+
}
499+
468500
/// This function takes a [Node] that you suspect might be in a call argument position
469501
/// and walks up the tree, looking for the containing call node
470502
///

0 commit comments

Comments
 (0)