Skip to content

Commit b56c5a2

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

File tree

6 files changed

+111
-0
lines changed

6 files changed

+111
-0
lines changed

crates/hir-def/src/resolver.rs

Lines changed: 4 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

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

crates/ide-completion/src/context.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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)