Skip to content

Commit 5ec2eed

Browse files
committed
Rewrite method matching with TS query
1 parent 8387de0 commit 5ec2eed

File tree

2 files changed

+71
-52
lines changed

2 files changed

+71
-52
lines changed

crates/ark/src/lsp/indexer.rs

Lines changed: 31 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::lsp::traits::rope::RopeExt;
2929
use crate::treesitter::BinaryOperatorType;
3030
use crate::treesitter::NodeType;
3131
use crate::treesitter::NodeTypeExt;
32+
use crate::treesitter::TSQuery;
3233

3334
#[derive(Clone, Debug)]
3435
pub enum IndexEntryData {
@@ -314,61 +315,39 @@ fn index_r6_class(
314315
node: &Node,
315316
entries: &mut Vec<IndexEntry>,
316317
) -> anyhow::Result<()> {
317-
let Some(args_node) = node.child_by_field_name("arguments") else {
318-
return Ok(());
319-
};
320-
321-
let mut cursor = args_node.walk();
322-
for arg in args_node.children(&mut cursor) {
323-
// Only consider `public = ` and `private = ` arguments
324-
let Some(arg_name) = arg.child_by_field_name("name") else {
325-
continue;
326-
};
327-
if !arg_name.is_identifier_or_string() {
328-
continue;
329-
}
330-
let arg_name_str = contents.node_slice(&arg_name)?;
331-
if arg_name_str != "public" && arg_name_str != "private" {
332-
continue;
333-
}
334-
335-
let Some(list_node) = arg.child_by_field_name("value") else {
336-
continue;
337-
};
338-
if !list_node.is_call() {
339-
continue;
340-
}
341-
342-
let Some(list_args) = list_node.child_by_field_name("arguments") else {
343-
return Ok(());
344-
};
345-
346-
let mut cursor = list_args.walk();
347-
for arg in list_args.children(&mut cursor) {
348-
if !arg.is_argument() {
349-
continue;
350-
}
318+
// Tree-sitter query to match individual methods in R6Class public/private lists
319+
let query_str = r#"
320+
(argument
321+
name: (identifier) @access
322+
value: (call
323+
function: (identifier) @_list_fn
324+
arguments: (arguments
325+
(argument
326+
name: (identifier) @method_name
327+
value: (function_definition) @method_fn
328+
)
329+
)
330+
)
331+
(#match? @access "public|private")
332+
(#eq? @_list_fn "list")
333+
)
334+
"#;
335+
let mut ts_query = TSQuery::new(query_str)?;
351336

352-
let (Some(mtd_name), Some(mtd_value)) = (
353-
arg.child_by_field_name("name"),
354-
arg.child_by_field_name("value"),
355-
) else {
356-
continue;
357-
};
358-
if !mtd_name.is_identifier_or_string() || !mtd_value.is_function_definition() {
359-
continue;
360-
}
337+
// We'll switch from Rope to String in the near future so let's not
338+
// worry about this conversion now
339+
let contents_str = contents.to_string();
361340

362-
let name = contents.node_slice(&mtd_name)?.to_string();
363-
let start = convert_point_to_position(contents, mtd_name.start_position());
364-
let end = convert_point_to_position(contents, mtd_name.end_position());
341+
for method_node in ts_query.captures_for(*node, "method_name", contents_str.as_bytes()) {
342+
let name = contents.node_slice(&method_node)?.to_string();
343+
let start = convert_point_to_position(contents, method_node.start_position());
344+
let end = convert_point_to_position(contents, method_node.end_position());
365345

366-
entries.push(IndexEntry {
367-
key: name.clone(),
368-
range: Range { start, end },
369-
data: IndexEntryData::Method { name },
370-
});
371-
}
346+
entries.push(IndexEntry {
347+
key: name.clone(),
348+
range: Range { start, end },
349+
data: IndexEntryData::Method { name },
350+
});
372351
}
373352

374353
Ok(())

crates/ark/src/treesitter.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use anyhow::anyhow;
12
use tree_sitter::Node;
23

34
use crate::lsp::traits::node::NodeExt;
@@ -623,6 +624,45 @@ pub(crate) fn point_end_of_previous_row(
623624
}
624625
}
625626

627+
pub(crate) struct TSQuery {
628+
query: tree_sitter::Query,
629+
cursor: tree_sitter::QueryCursor,
630+
}
631+
632+
impl TSQuery {
633+
pub(crate) fn new(query_str: &str) -> anyhow::Result<Self> {
634+
let language = &tree_sitter_r::LANGUAGE.into();
635+
let query = tree_sitter::Query::new(language, query_str)
636+
.map_err(|err| anyhow!("Failed to compile query: {err}"))?;
637+
638+
let cursor = tree_sitter::QueryCursor::new();
639+
640+
Ok(Self { query, cursor })
641+
}
642+
643+
/// Match query against `contents` and collect all nodes captured with the
644+
/// given capture name
645+
pub(crate) fn captures_for<'tree>(
646+
&mut self,
647+
node: tree_sitter::Node<'tree>,
648+
capture_name: &str,
649+
contents: &[u8],
650+
) -> Vec<tree_sitter::Node<'tree>> {
651+
let mut result = Vec::new();
652+
653+
for m in self.cursor.matches(&self.query, node, contents) {
654+
for cap in m.captures.iter() {
655+
let cap_name = &self.query.capture_names()[cap.index as usize];
656+
if *cap_name == capture_name {
657+
result.push(cap.node);
658+
}
659+
}
660+
}
661+
662+
result
663+
}
664+
}
665+
626666
#[cfg(test)]
627667
mod tests {
628668
use ropey::Rope;

0 commit comments

Comments
 (0)