From 37e0e8af10514a2064915389f22743fb690e3f93 Mon Sep 17 00:00:00 2001 From: Jessie Chatham Spencer Date: Thu, 30 Mar 2023 08:53:36 +0200 Subject: [PATCH] Implement extern crate completion --- crates/hir-def/src/resolver.rs | 19 +++++ crates/hir/src/semantics.rs | 8 +++ crates/ide-completion/src/completions.rs | 2 + .../src/completions/extern_crate.rs | 71 +++++++++++++++++++ crates/ide-completion/src/context.rs | 1 + crates/ide-completion/src/context/analysis.rs | 4 ++ 6 files changed, 105 insertions(+) create mode 100644 crates/ide-completion/src/completions/extern_crate.rs diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 62576100ea4..2f9187009e2 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -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 + '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 + '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 { // FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of // aliased traits are NOT brought in scope (unless also aliased). diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 65ccb8b0733..b8d4ecd4414 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1722,6 +1722,14 @@ impl SemanticsScope<'_> { |name, id| cb(name, id.into()), ) } + + pub fn extern_crates(&self) -> impl Iterator + '_ { + self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id })) + } + + pub fn extern_crate_decls(&self) -> impl Iterator + '_ { + self.resolver.extern_crate_decls_in_scope(self.db.upcast()) + } } #[derive(Debug)] diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 7e2ecdbb858..f60ac150164 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -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); diff --git a/crates/ide-completion/src/completions/extern_crate.rs b/crates/ide-completion/src/completions/extern_crate.rs new file mode 100644 index 00000000000..0d0e143f5f6 --- /dev/null +++ b/crates/ide-completion/src/completions/extern_crate.rs @@ -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 = 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); + } +} diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 1fd635ba2e7..0da7ba6d000 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -351,6 +351,7 @@ pub(super) enum NameRefKind { expr: ast::RecordExpr, }, Pattern(PatternContext), + ExternCrate, } /// The identifier we are currently completing. diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index c66cb987fea..1e6b2f319aa 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -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 {