Skip to content

feat: Implement extern crate completion #15374

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

Merged
merged 1 commit into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions crates/hir-def/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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)));
Expand All @@ -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).
Expand Down
8 changes: 8 additions & 0 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 2 additions & 0 deletions crates/ide-completion/src/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
71 changes: 71 additions & 0 deletions crates/ide-completion/src/completions/extern_crate.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Member

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)


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);
}
}
1 change: 1 addition & 0 deletions crates/ide-completion/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ pub(super) enum NameRefKind {
expr: ast::RecordExpr,
},
Pattern(PatternContext),
ExternCrate,
}

/// The identifier we are currently completing.
Expand Down
4 changes: 4 additions & 0 deletions crates/ide-completion/src/context/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down