diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 931b92dec3f..d020b49cde5 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -4,6 +4,7 @@ pub(crate) mod attribute; pub(crate) mod dot; pub(crate) mod expr; pub(crate) mod extern_abi; +pub(crate) mod field; pub(crate) mod flyimport; pub(crate) mod fn_param; pub(crate) mod format_string; diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs new file mode 100644 index 00000000000..d81e48cbabd --- /dev/null +++ b/crates/ide-completion/src/completions/field.rs @@ -0,0 +1,53 @@ +//! Completion of field list position. + +use crate::{ + context::{IdentContext, NameContext, NameKind, NameRefContext, PathCompletionCtx, PathKind}, + CompletionContext, CompletionItem, CompletionItemKind, Completions, +}; + +pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext) { + match &ctx.ident_ctx { + IdentContext::Name(NameContext { kind: NameKind::RecordField, .. }) + | IdentContext::NameRef(NameRefContext { + path_ctx: + Some(PathCompletionCtx { + has_macro_bang: false, + is_absolute_path: false, + qualifier: None, + parent: None, + kind: PathKind::Type { in_tuple_struct: true }, + has_type_args: false, + .. + }), + .. + }) => { + if ctx.qualifier_ctx.vis_node.is_none() { + let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet); + add_keyword("pub(crate)", "pub(crate)"); + add_keyword("pub(super)", "pub(super)"); + add_keyword("pub", "pub"); + } + } + _ => return, + } +} + +pub(super) fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) { + let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw); + + match ctx.config.snippet_cap { + Some(cap) => { + if snippet.ends_with('}') && ctx.incomplete_let { + // complete block expression snippets with a trailing semicolon, if inside an incomplete let + cov_mark::hit!(let_semi); + item.insert_snippet(cap, format!("{};", snippet)); + } else { + item.insert_snippet(cap, snippet); + } + } + None => { + item.insert_text(if snippet.contains('$') { kw } else { snippet }); + } + }; + item.add_to(acc); +} diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 873db300b84..c3bf298bc6e 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -160,7 +160,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) (_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true, // and so are macros(except for attributes) ( - PathKind::Expr { .. } | PathKind::Type | PathKind::Item { .. } | PathKind::Pat, + PathKind::Expr { .. } + | PathKind::Type { .. } + | PathKind::Item { .. } + | PathKind::Pat, ItemInNs::Macros(mac), ) => mac.is_fn_like(ctx.db), (PathKind::Item { .. }, _) => true, @@ -170,14 +173,14 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) (PathKind::Pat, ItemInNs::Types(_)) => true, (PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)), - (PathKind::Type, ItemInNs::Types(ty)) => { + (PathKind::Type { .. }, ItemInNs::Types(ty)) => { if matches!(ctx.completion_location, Some(ImmediateLocation::TypeBound)) { matches!(ty, ModuleDef::Trait(_)) } else { true } } - (PathKind::Type, ItemInNs::Values(_)) => false, + (PathKind::Type { .. }, ItemInNs::Values(_)) => false, (PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(ctx.db), (PathKind::Attr { .. }, _) => false, diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 91414c8bf6a..bc8c070c14d 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -18,9 +18,12 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) } let (&is_absolute_path, qualifier) = match ctx.path_context() { - Some(PathCompletionCtx { kind: PathKind::Type, is_absolute_path, qualifier, .. }) => { - (is_absolute_path, qualifier) - } + Some(PathCompletionCtx { + kind: PathKind::Type { .. }, + is_absolute_path, + qualifier, + .. + }) => (is_absolute_path, qualifier), _ => return, }; diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 4eac86162a3..a4b38d3f246 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -49,7 +49,9 @@ pub(super) enum PathKind { in_block_expr: bool, in_loop_body: bool, }, - Type, + Type { + in_tuple_struct: bool, + }, Attr { kind: AttrKind, annotated_item_kind: Option, @@ -1222,7 +1224,9 @@ impl<'a> CompletionContext<'a> { // using Option> as extra controlflow let kind = match_ast! { match it { - ast::PathType(_) => Some(PathKind::Type), + ast::PathType(it) => Some(PathKind::Type { + in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind())) + }), ast::PathExpr(it) => { if let Some(p) = it.syntax().parent() { if ast::ExprStmt::can_cast(p.kind()) { @@ -1262,7 +1266,7 @@ impl<'a> CompletionContext<'a> { let parent = it.syntax().parent(); match parent.as_ref().map(|it| it.kind()) { Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat), - Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type), + Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type { in_tuple_struct: false }), Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }), Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) { Some(it) => match_ast! { diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 9659efad619..c100dd63eac 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -158,6 +158,7 @@ pub fn completions( completions::dot::complete_dot(acc, ctx); completions::expr::complete_expr_path(acc, ctx); completions::extern_abi::complete_extern_abi(acc, ctx); + completions::field::complete_field_list(acc, ctx); completions::flyimport::import_on_the_fly(acc, ctx); completions::fn_param::complete_fn_param(acc, ctx); completions::format_string::format_string(acc, ctx); diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index d51bc517d65..ca2b3ad3435 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -286,7 +286,7 @@ fn render_resolution_simple_( // Add `<>` for generic types let type_path_no_ty_args = matches!( ctx.completion.path_context(), - Some(PathCompletionCtx { kind: PathKind::Type, has_type_args: false, .. }) + Some(PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. }) ) && ctx.completion.config.callable.is_some(); if type_path_no_ty_args { if let Some(cap) = ctx.snippet_cap() { diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 5e1fbfa4a21..0be51b0e3ff 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -202,7 +202,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool { Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => { return false } - Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type, .. }) => { + Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }) => { cov_mark::hit!(no_parens_in_use_item); return false; } diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index d1f5d2a33c0..9e50e00ab73 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -100,7 +100,6 @@ fn after_fn_name() { #[test] fn before_record_field() { - // FIXME: This should emit visibility qualifiers check( r#" struct Foo { @@ -108,6 +107,10 @@ struct Foo { pub f: i32, } "#, - expect![[r#""#]], + expect![[r#" + kw pub + kw pub(crate) + kw pub(super) + "#]], ) } diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 76942110f88..1e5e86eef59 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -38,14 +38,13 @@ struct Foo<'lt, T, const C: usize> { #[test] fn tuple_struct_field() { - // FIXME: This should emit visibility qualifiers check( r#" struct Foo<'lt, T, const C: usize>(f$0); "#, expect![[r#" en Enum - ma makro!(…) macro_rules! makro + ma makro!(…) macro_rules! makro md module sp Self st Foo<…> @@ -57,6 +56,9 @@ struct Foo<'lt, T, const C: usize>(f$0); un Union bt u32 kw crate:: + kw pub + kw pub(crate) + kw pub(super) kw self:: kw super:: "#]],