Skip to content

Commit

Permalink
Implement extern crate completion
Browse files Browse the repository at this point in the history
  • Loading branch information
jmintb committed Aug 2, 2023
1 parent 8202b5a commit 38f2010
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 0 deletions.
4 changes: 4 additions & 0 deletions crates/hir-def/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,10 @@ impl Resolver {
res.map
}

pub fn extern_crates_in_scope(&self) -> Vec<Name> {
self.module_scope.def_map.extern_prelude().map(|(name, _)| name.clone()).collect()
}

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
4 changes: 4 additions & 0 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,10 @@ impl SemanticsScope<'_> {
|name, id| cb(name, id.into()),
)
}

pub fn extern_crates(&self) -> Vec<Name> {
self.resolver.extern_crates_in_scope()
}
}

#[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 @@ -737,6 +738,7 @@ pub(super) fn complete_name_ref(
}
}
}
NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx, nameref.as_ref()),
NameRefKind::DotAccess(dot_access) => {
flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
dot::complete_dot(acc, ctx, dot_access);
Expand Down
94 changes: 94 additions & 0 deletions crates/ide-completion/src/completions/extern_crate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use ide_db::{FxHashSet, SymbolKind};
use syntax::{
ast::{self, NameRef},
AstNode,
};

use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};

use super::Completions;

pub(crate) fn complete_extern_crate(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
name_ref: Option<&NameRef>,
) {
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();

let current_txt =
name_ref.as_ref().map(|name_ref| name_ref.to_string()).unwrap_or(String::new());

for name in ctx.scope.extern_crates() {
if (!current_txt.is_empty() && !name.to_smol_str().starts_with(&current_txt))
|| imported_extern_crates.contains(name.to_smol_str().as_str())
{
continue;
}

CompletionItem::new(
CompletionItemKind::SymbolKind(SymbolKind::Module),
ctx.source_range(),
name.to_smol_str(),
)
.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,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);
}
}
1 change: 1 addition & 0 deletions crates/ide-completion/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,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

0 comments on commit 38f2010

Please sign in to comment.