Skip to content

Commit ba3b7c7

Browse files
committed
feat: better completions for extern blcoks
1 parent e2dd95f commit ba3b7c7

File tree

5 files changed

+104
-18
lines changed

5 files changed

+104
-18
lines changed

src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option
7676
let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
7777
let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
7878

79-
let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
79+
let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock { .. }));
80+
let in_unsafe_extern_block =
81+
matches!(kind, Some(ItemListKind::ExternBlock { is_unsafe: true }));
82+
8083
let in_trait = matches!(kind, Some(ItemListKind::Trait));
8184
let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl(_)));
8285
let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
@@ -85,29 +88,39 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option
8588
let no_vis_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
8689
let has_unsafe_kw = ctx.qualifier_ctx.unsafe_tok.is_some();
8790
let has_async_kw = ctx.qualifier_ctx.async_tok.is_some();
91+
let has_safe_kw = ctx.qualifier_ctx.safe_tok.is_some();
8892

8993
// We handle completions for trait-impls in [`item_list::trait_impl`]
9094
if in_trait_impl {
9195
return;
9296
}
9397

9498
// Some keywords are invalid after non-vis qualifiers, so we handle them first.
95-
if has_unsafe_kw || has_async_kw {
96-
if !has_unsafe_kw {
97-
add_keyword("unsafe", "unsafe $0");
98-
}
99-
if !has_async_kw {
100-
add_keyword("async", "async $0");
101-
}
99+
if has_unsafe_kw || has_async_kw || has_safe_kw {
100+
if in_extern_block {
101+
add_keyword("fn", "fn $1($2);");
102+
add_keyword("static", "static $1: $2;");
103+
} else {
104+
if !has_unsafe_kw {
105+
add_keyword("unsafe", "unsafe $0");
106+
}
107+
if !has_async_kw {
108+
add_keyword("async", "async $0");
109+
}
102110

103-
if in_item_list || in_assoc_non_trait_impl {
104-
add_keyword("fn", "fn $1($2) {\n $0\n}");
105-
}
111+
if in_item_list || in_assoc_non_trait_impl {
112+
add_keyword("fn", "fn $1($2) {\n $0\n}");
113+
}
106114

107-
if has_unsafe_kw && in_item_list {
108-
add_keyword("trait", "trait $1 {\n $0\n}");
109-
if no_vis_qualifiers {
110-
add_keyword("impl", "impl $1 {\n $0\n}");
115+
if has_unsafe_kw && in_item_list {
116+
add_keyword("trait", "trait $1 {\n $0\n}");
117+
if no_vis_qualifiers {
118+
add_keyword("impl", "impl $1 {\n $0\n}");
119+
}
120+
}
121+
122+
if !has_async_kw && no_vis_qualifiers && in_item_list {
123+
add_keyword("extern", "extern $0");
111124
}
112125
}
113126

@@ -138,6 +151,11 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option
138151
}
139152

140153
if in_extern_block {
154+
add_keyword("unsafe", "unsafe $0");
155+
if in_unsafe_extern_block {
156+
add_keyword("safe", "safe $0");
157+
}
158+
141159
add_keyword("fn", "fn $1($2);");
142160
add_keyword("static", "static $1: $2;");
143161
} else {

src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ mod tests {
5858
r"fn my_fn() { unsafe $0 }",
5959
expect![[r#"
6060
kw async
61+
kw extern
6162
kw fn
6263
kw impl
6364
kw trait

src/tools/rust-analyzer/crates/ide-completion/src/context.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,16 @@ pub(crate) struct QualifierCtx {
4848
// TODO: Add try_tok and default_tok
4949
pub(crate) async_tok: Option<SyntaxToken>,
5050
pub(crate) unsafe_tok: Option<SyntaxToken>,
51+
pub(crate) safe_tok: Option<SyntaxToken>,
5152
pub(crate) vis_node: Option<ast::Visibility>,
5253
}
5354

5455
impl QualifierCtx {
5556
pub(crate) fn none(&self) -> bool {
56-
self.async_tok.is_none() && self.unsafe_tok.is_none() && self.vis_node.is_none()
57+
self.async_tok.is_none()
58+
&& self.unsafe_tok.is_none()
59+
&& self.safe_tok.is_none()
60+
&& self.vis_node.is_none()
5761
}
5862
}
5963

@@ -229,7 +233,7 @@ pub(crate) enum ItemListKind {
229233
Impl,
230234
TraitImpl(Option<ast::Impl>),
231235
Trait,
232-
ExternBlock,
236+
ExternBlock { is_unsafe: bool },
233237
}
234238

235239
#[derive(Debug)]

src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,14 @@ fn classify_name_ref(
11081108
},
11091109
None => return None,
11101110
} },
1111-
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
1111+
ast::ExternItemList(it) => {
1112+
let exn_blk = it.syntax().parent().and_then(ast::ExternBlock::cast);
1113+
PathKind::Item {
1114+
kind: ItemListKind::ExternBlock {
1115+
is_unsafe: exn_blk.and_then(|it| it.unsafe_token()).is_some(),
1116+
}
1117+
}
1118+
},
11121119
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
11131120
_ => return None,
11141121
}
@@ -1310,6 +1317,7 @@ fn classify_name_ref(
13101317
match token.kind() {
13111318
SyntaxKind::UNSAFE_KW => qualifier_ctx.unsafe_tok = Some(token),
13121319
SyntaxKind::ASYNC_KW => qualifier_ctx.async_tok = Some(token),
1320+
SyntaxKind::SAFE_KW => qualifier_ctx.safe_tok = Some(token),
13131321
_ => {}
13141322
}
13151323
}

src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ fn after_unsafe_token() {
124124
r#"unsafe $0"#,
125125
expect![[r#"
126126
kw async
127+
kw extern
127128
kw fn
128129
kw impl
129130
kw trait
@@ -495,3 +496,57 @@ type O = $0;
495496
",
496497
)
497498
}
499+
500+
#[test]
501+
fn inside_extern_blocks() {
502+
// Should suggest `fn`, `static`, `unsafe`
503+
check(
504+
r#"extern { $0 }"#,
505+
expect![[r#"
506+
ma makro!(…) macro_rules! makro
507+
md module
508+
kw crate::
509+
kw fn
510+
kw pub
511+
kw pub(crate)
512+
kw pub(super)
513+
kw self::
514+
kw static
515+
kw unsafe
516+
"#]],
517+
);
518+
519+
// Should suggest `fn`, `static`, `safe`, `unsafe`
520+
check(
521+
r#"unsafe extern { $0 }"#,
522+
expect![[r#"
523+
ma makro!(…) macro_rules! makro
524+
md module
525+
kw crate::
526+
kw fn
527+
kw pub
528+
kw pub(crate)
529+
kw pub(super)
530+
kw safe
531+
kw self::
532+
kw static
533+
kw unsafe
534+
"#]],
535+
);
536+
537+
check(
538+
r#"unsafe extern { pub safe $0 }"#,
539+
expect![[r#"
540+
kw fn
541+
kw static
542+
"#]],
543+
);
544+
545+
check(
546+
r#"unsafe extern { pub unsafe $0 }"#,
547+
expect![[r#"
548+
kw fn
549+
kw static
550+
"#]],
551+
)
552+
}

0 commit comments

Comments
 (0)