Skip to content

Commit 38f2010

Browse files
committed
Implement extern crate completion
1 parent 8202b5a commit 38f2010

File tree

6 files changed

+109
-0
lines changed

6 files changed

+109
-0
lines changed

crates/hir-def/src/resolver.rs

+4
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,10 @@ impl Resolver {
479479
res.map
480480
}
481481

482+
pub fn extern_crates_in_scope(&self) -> Vec<Name> {
483+
self.module_scope.def_map.extern_prelude().map(|(name, _)| name.clone()).collect()
484+
}
485+
482486
pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
483487
// FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
484488
// aliased traits are NOT brought in scope (unless also aliased).

crates/hir/src/semantics.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,10 @@ impl SemanticsScope<'_> {
17021702
|name, id| cb(name, id.into()),
17031703
)
17041704
}
1705+
1706+
pub fn extern_crates(&self) -> Vec<Name> {
1707+
self.resolver.extern_crates_in_scope()
1708+
}
17051709
}
17061710

17071711
#[derive(Debug)]

crates/ide-completion/src/completions.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub(crate) mod r#type;
2020
pub(crate) mod use_;
2121
pub(crate) mod vis;
2222
pub(crate) mod env_vars;
23+
pub(crate) mod extern_crate;
2324

2425
use std::iter;
2526

@@ -737,6 +738,7 @@ pub(super) fn complete_name_ref(
737738
}
738739
}
739740
}
741+
NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx, nameref.as_ref()),
740742
NameRefKind::DotAccess(dot_access) => {
741743
flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
742744
dot::complete_dot(acc, ctx, dot_access);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use ide_db::{FxHashSet, SymbolKind};
2+
use syntax::{
3+
ast::{self, NameRef},
4+
AstNode,
5+
};
6+
7+
use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};
8+
9+
use super::Completions;
10+
11+
pub(crate) fn complete_extern_crate(
12+
acc: &mut Completions,
13+
ctx: &CompletionContext<'_>,
14+
name_ref: Option<&NameRef>,
15+
) {
16+
let imported_extern_crates: FxHashSet<String> = ctx
17+
.token
18+
.parent_ancestors()
19+
.find_map(ast::SourceFile::cast)
20+
.map(|src_file| {
21+
src_file
22+
.syntax()
23+
.children()
24+
.into_iter()
25+
.filter_map(ast::ExternCrate::cast)
26+
.filter_map(|node| node.name_ref())
27+
.map(|name| name.to_string())
28+
.collect()
29+
})
30+
.unwrap_or_default();
31+
32+
let current_txt =
33+
name_ref.as_ref().map(|name_ref| name_ref.to_string()).unwrap_or(String::new());
34+
35+
for name in ctx.scope.extern_crates() {
36+
if (!current_txt.is_empty() && !name.to_smol_str().starts_with(&current_txt))
37+
|| imported_extern_crates.contains(name.to_smol_str().as_str())
38+
{
39+
continue;
40+
}
41+
42+
CompletionItem::new(
43+
CompletionItemKind::SymbolKind(SymbolKind::Module),
44+
ctx.source_range(),
45+
name.to_smol_str(),
46+
)
47+
.add_to(acc, ctx.db);
48+
}
49+
}
50+
51+
#[cfg(test)]
52+
mod test {
53+
use crate::tests::completion_list_no_kw;
54+
55+
#[test]
56+
fn can_complete_extern_crate() {
57+
let case = r#"
58+
//- /lib.rs crate:other_crate_a
59+
// nothing here
60+
//- /other_crate_b.rs crate:other_crate_b
61+
pub mod good_mod{}
62+
//- /lib.rs crate:crate_c
63+
// nothing here
64+
//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,crate_c
65+
extern crate crate_c;
66+
extern crate oth$0
67+
mod other_mod {}
68+
"#;
69+
70+
let completion_list = completion_list_no_kw(case);
71+
72+
assert_eq!("md other_crate_a\n".to_string(), completion_list);
73+
}
74+
75+
#[test]
76+
fn will_not_complete_existing_import() {
77+
let case = r#"
78+
//- /lib.rs crate:other_crate_a
79+
// nothing here
80+
//- /lib.rs crate:crate_c
81+
// nothing here
82+
//- /lib.rs crate:other_crate_b
83+
//
84+
//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b,crate_c
85+
extern crate other_crate_b;
86+
extern crate oth$0
87+
mod other_mod {}
88+
"#;
89+
90+
let completion_list = completion_list_no_kw(case);
91+
92+
assert_eq!("md other_crate_a\n".to_string(), completion_list);
93+
}
94+
}

crates/ide-completion/src/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ pub(super) enum NameRefKind {
301301
expr: ast::RecordExpr,
302302
},
303303
Pattern(PatternContext),
304+
ExternCrate,
304305
}
305306

306307
/// The identifier we are currently completing.

crates/ide-completion/src/context/analysis.rs

+4
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,10 @@ fn classify_name_ref(
624624
});
625625
return Some(make_res(kind));
626626
},
627+
ast::ExternCrate(_) => {
628+
let kind = NameRefKind::ExternCrate;
629+
return Some(make_res(kind));
630+
},
627631
ast::MethodCallExpr(method) => {
628632
let receiver = find_opt_node_in_file(original_file, method.receiver());
629633
let kind = NameRefKind::DotAccess(DotAccess {

0 commit comments

Comments
 (0)