-
Notifications
You must be signed in to change notification settings - Fork 17
Emit R6Class methods as workspace symbols #861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -267,6 +267,7 @@ fn completions_from_workspace_arguments( | |||
return Ok(None); | |||
}, | |||
indexer::IndexEntryData::Variable { .. } => return Ok(None), | |||
indexer::IndexEntryData::Method { .. } => return Ok(None), | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worth trying to give these a unique name? Like, trying to sniff out the R6 class name to include as well?
Imagine if I implemented two of three of these similar interfaces in the same file, then I'd have no way of telling them apart
What does Rust do for something like this?
https://github.com/DavisVaughan/almanac/blob/main/R/cache-rrule.R
https://github.com/DavisVaughan/almanac/blob/main/R/cache-radjusted.R

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rust doesn't emit methods as Workspace symbols so this issue doesn't arise.
I think the file name is good enough to disambiguate most of the time? But we could probably make your suggestion work like this: MyClass$get_events()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that would be kind of cool to also make it distinct as an R6 method? Maybe no parens, so MyClass$get_events
for m in self.cursor.matches(&self.query, node, contents) { | ||
for cap in m.captures.iter() { | ||
let cap_name = &self.query.capture_names()[cap.index as usize]; | ||
if *cap_name == capture_name { | ||
result.push(cap.node); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want cursor.matches()
or the flat cursor.captures()
?
If I remember right, captures()
is a flat list, but matches()
has some structure that it doesn't look like you use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm that sounded plausible but it looks like I can't access the capture name with the captures()
iterator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No matter what you use, I think you still get the name via self.query.capture_names()[cap.index as usize]
, this just avoids the double loop?
crates/ark/src/lsp/indexer.rs
Outdated
if crate::treesitter::node_is_call(&rhs, "R6Class", contents) || | ||
crate::treesitter::node_is_namespaced_call(&rhs, "R6", "R6Class", contents) | ||
{ | ||
index_r6_class(path, contents, &rhs, entries)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should probably early return after this right? If not, maybe a comment about why fallthrough is good?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've renamed to make it clear only methods are indexed, and added a comment explaining the fallthrough:
index_r6_class_methods(path, contents, &rhs, entries)?;
// Fallthrough to index the variable to which the R6 class is assigned
crates/ark/src/lsp/indexer.rs
Outdated
// Tree-sitter query to match individual methods in R6Class public/private lists | ||
let query_str = r#" | ||
(argument | ||
name: (identifier) @access | ||
value: (call | ||
function: (identifier) @_list_fn | ||
arguments: (arguments | ||
(argument | ||
name: (identifier) @method_name | ||
value: (function_definition) @method_fn | ||
) | ||
) | ||
) | ||
(#match? @access "public|private") | ||
(#eq? @_list_fn "list") | ||
) | ||
"#; | ||
let mut ts_query = crate::treesitter::TSQuery::new(query_str)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I will note is that tree-sitter recommends that you "compile" your static Query
objects exactly once ahead of time, because they do take a non zero amount of overhead to parse and compile.
Would it be possible to rearrange things to end up with a Lazy<Query>
that is initialized once and then fed to your TSQuery
helper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that we then need to split the query and the query cursor because the latter is stateful. I wasn't too worried about the overhead and thought I'd favour ergonomics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we then need to split the query and the query cursor
tbh that sounds like a feature and is how tree-sitter is supposed to work
// We'll switch from Rope to String in the near future so let's not | ||
// worry about this conversion now | ||
let contents_str = contents.to_string(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, should be nicer all around
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you wanted to be super fancy I believe you could implement TextProvider
for Rope
It's probably not worth it but i think the idea is that you can somehow provide some iterator when you don't have a complete contiguous buffer available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it might be easier than that? It looks like they already provide an implementation of TextProvider
for FnMut(Node) -> <iterator over bytes>
So you could probably provide something like
mut |node: Node| {
iter::once(contents.node_slice(&node).unwrap().to_string().as_bytes())
}
which would materialize a node
worth of text in to_string()
but not the whole document
It's not allowed to fail so I guess you have to unwrap()
May or may not be worth it, just a thought
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I didn't look into these more complex setups since we're going to switch to a flat document string soon that's straightforward to iterate over.
ee3801d
to
95bf05d
Compare
Addresses posit-dev/positron#6549
Add a bunch of tests for the workspace indexers which were not tested before.
Add a new indexed type
Method
. This is used to include R6Class methods in workspace symbols but exclusing them from completions.Indexers now push symbols onto a list instead of returning a single symbol to their callers. This allows a single indexer to handle multiple symbols.
The assignment handler now pushes R6Class method symbols in addition to the assigned object.
QA Notes
Add the following to a file:
These symbols should now be available as Workspace symbols (
#
prefix in command palette):initialize()
,foo()
,bar()
and you should be able to jump straight to the definition of these methods from any file.The symbols starting with
not_indexed
should not be available.All this is tested on the backend side.