-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
feat: Implement extern crate completion #15374
Conversation
38f2010
to
b56c5a2
Compare
crates/hir-def/src/resolver.rs
Outdated
pub fn extern_crates_in_scope(&self) -> Vec<Name> { | ||
self.module_scope.def_map.extern_prelude().map(|(name, _)| name.clone()).collect() |
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.
Should be no need to collect here and we are also interested in the module here
pub fn extern_crates_in_scope(&self) -> Vec<Name> { | |
self.module_scope.def_map.extern_prelude().map(|(name, _)| name.clone()).collect() | |
pub fn extern_crates_in_scope(&self) -> impl Iterator<Item = (Name, ModuleId)> { | |
self.module_scope.def_map.extern_prelude().map(|(name, mid)| (name.clone(), mid)) |
|
||
use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; | ||
|
||
use super::Completions; |
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.
nit, we prefer not to use super
imports, I assume this was copied from another file (we have a few rogue super imports lying around still)
let current_txt = | ||
name_ref.as_ref().map(|name_ref| name_ref.to_string()).unwrap_or(String::new()); |
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 don't need to filter the names ourselves, that's something the clients (text editors) are doing for us.
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.
Ah, that makes a lot of sense.
let imported_extern_crates: FxHashSet<String> = ctx | ||
.token | ||
.parent_ancestors() | ||
.find_map(ast::SourceFile::cast) | ||
.map(|src_file| { | ||
src_file | ||
.syntax() | ||
.children() | ||
.into_iter() | ||
.filter_map(ast::ExternCrate::cast) | ||
.filter_map(|node| node.name_ref()) | ||
.map(|name| name.to_string()) | ||
.collect() | ||
}) | ||
.unwrap_or_default(); |
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.
Going via syntax is pretty messy and error prone (given macros could create these as well), but we can't go the HIR route here right now as we don't record extern_crate
decls in the HIR yet. Can you put a FIXME above this something like FIXME: Collect extern crate names via HIR when possible
?
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 with #15377 we can make this work on the HIR
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.
Nice it felt like a hack haha.
I made a quick implementation using the resolver to perform external crate decl queries here. Am I on the right track or should I be finding the information somewhere else? I suspect a bit of adjustment somewhere might remove the need for querying extern crate decl data 🤔
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.
Yup, using the query is the correct approach
crates/hir/src/semantics.rs
Outdated
pub fn extern_crates(&self) -> Vec<Name> { | ||
self.resolver.extern_crates_in_scope() | ||
} |
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.
Similar stuff here, but map from ModuleId
to Module
pub fn extern_crates(&self) -> Vec<Name> { | |
self.resolver.extern_crates_in_scope() | |
} | |
pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> { | |
self.resolver.extern_crates_in_scope().map(...) | |
} |
CompletionItem::new( | ||
CompletionItemKind::SymbolKind(SymbolKind::Module), | ||
ctx.source_range(), | ||
name.to_smol_str(), | ||
) | ||
.add_to(acc, ctx.db); |
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.
With the scope.extern_crates()
changes above giving us the module as well we can add the module docs via .set_documentation(module.docs(db)
#[cfg(test)] | ||
mod test { | ||
use crate::tests::completion_list_no_kw; | ||
|
||
#[test] | ||
fn can_complete_extern_crate() { | ||
let case = r#" | ||
//- /lib.rs crate:other_crate_a | ||
// nothing here | ||
//- /other_crate_b.rs crate:other_crate_b | ||
pub mod good_mod{} | ||
//- /lib.rs crate:crate_c | ||
// nothing here | ||
//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,crate_c | ||
extern crate crate_c; | ||
extern crate oth$0 | ||
mod other_mod {} | ||
"#; | ||
|
||
let completion_list = completion_list_no_kw(case); | ||
|
||
assert_eq!("md other_crate_a\n".to_string(), completion_list); | ||
} | ||
|
||
#[test] | ||
fn will_not_complete_existing_import() { | ||
let case = r#" | ||
//- /lib.rs crate:other_crate_a | ||
// nothing here | ||
//- /lib.rs crate:crate_c | ||
// nothing here | ||
//- /lib.rs crate:other_crate_b | ||
// | ||
//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b,crate_c | ||
extern crate other_crate_b; | ||
extern crate oth$0 | ||
mod other_mod {} | ||
"#; | ||
|
||
let completion_list = completion_list_no_kw(case); | ||
|
||
assert_eq!("md other_crate_a\n".to_string(), completion_list); | ||
} | ||
} |
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.
Let's create a new module in the ide-completion/src/tests
folder instead, you can also check how a completion test is structured for this usually by taking a look at the use_tree
module in there for example.
e4ace6e
to
4764447
Compare
Thanks! |
I wasn't quite finished but I guess it was good enough haha. Thanks for the insightful feedback! |
Oh were you not? It looked fine to me 😅 |
feat: Implement extern crate completion Hi, this is a draft PR for #13002. I have basic completion working as well as a filter for existing extern crate imports in the same file. This is based on the tests, I have not actually tried this in an editor. Before going further I think this is a good point to stop and get feedback on the structure and approach I have taken so far. Let me know what you think :) I will make sure to add more tests, rebase commits and align with the code style guidelines before submitting a final version. A few specific questions : 1. Is there a better way to check for matching suggestions? right now I just test if an extern crate name starts with the current user input. 2. Am I creating the `CompletionItem` correctly? I noticed that `use_.rs` invokes a builder where as I do not. 3. When checking for existing extern crate imports the current implementation only looks at the current source file, is that sufficient?
I had not addressed this:
But if you think it looks fine then that is good enough for me 👍 |
☀️ Test successful - checks-actions |
👀 Test was successful, but fast-forwarding failed: 422 Update is not a fast forward |
Should be fast forward-able now. |
@bors r+ |
feat: Implement extern crate completion Hi, this is a draft PR for #13002. I have basic completion working as well as a filter for existing extern crate imports in the same file. This is based on the tests, I have not actually tried this in an editor. Before going further I think this is a good point to stop and get feedback on the structure and approach I have taken so far. Let me know what you think :) I will make sure to add more tests, rebase commits and align with the code style guidelines before submitting a final version. A few specific questions : 1. Is there a better way to check for matching suggestions? right now I just test if an extern crate name starts with the current user input. 2. Am I creating the `CompletionItem` correctly? I noticed that `use_.rs` invokes a builder where as I do not. 3. When checking for existing extern crate imports the current implementation only looks at the current source file, is that sufficient?
💔 Test failed - checks-actions |
Ah, there is a semantic conflict now when merging onto master. Could you rebase? @bors delegate+ |
@bors r=Veykril |
☀️ Test successful - checks-actions |
Hi, this is a draft PR for #13002.
I have basic completion working as well as a filter for existing extern crate imports in the same file. This is based on the tests, I have not actually tried this in an editor. Before going further I think this is a good point to stop and get feedback on the
structure and approach I have taken so far. Let me know what you think :)
I will make sure to add more tests, rebase commits and align with the code style guidelines before submitting a final version.
A few specific questions :
user input.
CompletionItem
correctly? I noticed thatuse_.rs
invokes a builder where as I do not.