mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 17:24:06 +00:00
Auto merge of #15374 - jmintb:extern_crate, r=Veykril
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?
This commit is contained in:
commit
a3892f0ed9
@ -12,6 +12,7 @@ use triomphe::Arc;
|
||||
use crate::{
|
||||
body::scope::{ExprScopes, ScopeId},
|
||||
builtin_type::BuiltinType,
|
||||
data::ExternCrateDeclData,
|
||||
db::DefDatabase,
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
hir::{BindingId, ExprId, LabelId},
|
||||
@ -451,6 +452,7 @@ impl Resolver {
|
||||
def_map[module_id].scope.entries().for_each(|(name, def)| {
|
||||
res.add_per_ns(name, def);
|
||||
});
|
||||
|
||||
def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| {
|
||||
macs.iter().for_each(|&mac| {
|
||||
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
|
||||
@ -474,6 +476,23 @@ impl Resolver {
|
||||
res.map
|
||||
}
|
||||
|
||||
pub fn extern_crate_decls_in_scope<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn DefDatabase,
|
||||
) -> impl Iterator<Item = Name> + 'a {
|
||||
self.module_scope.def_map[self.module_scope.module_id]
|
||||
.scope
|
||||
.extern_crate_decls()
|
||||
.map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone())
|
||||
}
|
||||
|
||||
pub fn extern_crates_in_scope<'a>(&'a self) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
|
||||
self.module_scope
|
||||
.def_map
|
||||
.extern_prelude()
|
||||
.map(|(name, module_id)| (name.clone(), module_id.0.into()))
|
||||
}
|
||||
|
||||
pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
|
||||
// FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
|
||||
// aliased traits are NOT brought in scope (unless also aliased).
|
||||
|
@ -1722,6 +1722,14 @@ impl SemanticsScope<'_> {
|
||||
|name, id| cb(name, id.into()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
|
||||
self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
|
||||
}
|
||||
|
||||
pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ {
|
||||
self.resolver.extern_crate_decls_in_scope(self.db.upcast())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -20,6 +20,7 @@ pub(crate) mod r#type;
|
||||
pub(crate) mod use_;
|
||||
pub(crate) mod vis;
|
||||
pub(crate) mod env_vars;
|
||||
pub(crate) mod extern_crate;
|
||||
|
||||
use std::iter;
|
||||
|
||||
@ -739,6 +740,7 @@ pub(super) fn complete_name_ref(
|
||||
}
|
||||
}
|
||||
}
|
||||
NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx),
|
||||
NameRefKind::DotAccess(dot_access) => {
|
||||
flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
|
||||
dot::complete_dot(acc, ctx, dot_access);
|
||||
|
71
crates/ide-completion/src/completions/extern_crate.rs
Normal file
71
crates/ide-completion/src/completions/extern_crate.rs
Normal file
@ -0,0 +1,71 @@
|
||||
//! Completion for extern crates
|
||||
|
||||
use hir::{HasAttrs, Name};
|
||||
use ide_db::SymbolKind;
|
||||
|
||||
use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};
|
||||
|
||||
use super::Completions;
|
||||
|
||||
pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) {
|
||||
let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect();
|
||||
|
||||
for (name, module) in ctx.scope.extern_crates() {
|
||||
if imported_extern_crates.contains(&name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut item = CompletionItem::new(
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Module),
|
||||
ctx.source_range(),
|
||||
name.to_smol_str(),
|
||||
);
|
||||
item.set_documentation(module.docs(ctx.db));
|
||||
|
||||
item.add_to(acc, ctx.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
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
@ -351,6 +351,7 @@ pub(super) enum NameRefKind {
|
||||
expr: ast::RecordExpr,
|
||||
},
|
||||
Pattern(PatternContext),
|
||||
ExternCrate,
|
||||
}
|
||||
|
||||
/// The identifier we are currently completing.
|
||||
|
@ -624,6 +624,10 @@ fn classify_name_ref(
|
||||
});
|
||||
return Some(make_res(kind));
|
||||
},
|
||||
ast::ExternCrate(_) => {
|
||||
let kind = NameRefKind::ExternCrate;
|
||||
return Some(make_res(kind));
|
||||
},
|
||||
ast::MethodCallExpr(method) => {
|
||||
let receiver = find_opt_node_in_file(original_file, method.receiver());
|
||||
let kind = NameRefKind::DotAccess(DotAccess {
|
||||
|
Loading…
Reference in New Issue
Block a user