diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 6c001bd16bfe..39048e44001e 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -87,6 +87,9 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option let in_block = kind.is_none(); let no_vis_qualifiers = ctx.qualifier_ctx.vis_node.is_none(); + let no_abi_qualifiers = ctx.qualifier_ctx.abi_node.is_none(); + let has_extern_kw = + ctx.qualifier_ctx.abi_node.as_ref().is_some_and(|it| it.string_token().is_none()); let has_unsafe_kw = ctx.qualifier_ctx.unsafe_tok.is_some(); let has_async_kw = ctx.qualifier_ctx.async_tok.is_some(); let has_safe_kw = ctx.qualifier_ctx.safe_tok.is_some(); @@ -118,7 +121,7 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option } } - if !has_async_kw && no_vis_qualifiers && in_item_list { + if !has_async_kw && no_vis_qualifiers && no_abi_qualifiers && in_item_list { add_keyword("extern", "extern $0"); } @@ -159,11 +162,14 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option add_keyword("static", "static $1: $2;"); } else { if !in_inherent_impl { - if !in_trait { + if !in_trait && no_abi_qualifiers { add_keyword("extern", "extern $0"); } add_keyword("type", "type $0"); } + if has_extern_kw { + add_keyword("crate", "crate $0;"); + } add_keyword("fn", "fn $1($2) {\n $0\n}"); add_keyword("unsafe", "unsafe $0"); diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 31a9a74aa890..23318e1d1991 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -53,6 +53,7 @@ pub(crate) struct QualifierCtx { pub(crate) unsafe_tok: Option, pub(crate) safe_tok: Option, pub(crate) vis_node: Option, + pub(crate) abi_node: Option, } impl QualifierCtx { @@ -61,6 +62,7 @@ impl QualifierCtx { && self.unsafe_tok.is_none() && self.safe_tok.is_none() && self.vis_node.is_none() + && self.abi_node.is_none() } } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index e84c0653364d..da9464f48a12 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1628,6 +1628,7 @@ fn classify_name_ref<'db>( } } qualifier_ctx.vis_node = error_node.children().find_map(ast::Visibility::cast); + qualifier_ctx.abi_node = error_node.children().find_map(ast::Abi::cast); } if let PathKind::Item { .. } = path_ctx.kind diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index ac32649d4ffb..9afc8b49d6bc 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/crates/ide-completion/src/tests/item_list.rs @@ -176,6 +176,105 @@ fn after_visibility_unsafe() { ); } +#[test] +fn after_abi() { + check_with_base_items( + r#"extern "C" $0"#, + expect![[r#" + kw async + kw const + kw enum + kw fn + kw impl + kw impl for + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + "#]], + ); + check_with_base_items( + r#"extern "C" f$0"#, + expect![[r#" + kw async + kw const + kw enum + kw fn + kw impl + kw impl for + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + "#]], + ); +} + +#[test] +fn after_extern_token() { + check_with_base_items( + r#"extern $0"#, + expect![[r#" + kw async + kw const + kw crate + kw enum + kw fn + kw impl + kw impl for + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + "#]], + ); + check_with_base_items( + r#"extern cr$0"#, + expect![[r#" + kw async + kw const + kw crate + kw enum + kw fn + kw impl + kw impl for + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + "#]], + ); + check_edit("crate", "extern $0", "extern crate $0;"); +} + #[test] fn in_impl_assoc_item_list() { check_with_base_items(