diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 1e0b7711667..a11652ca302 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -46,12 +46,14 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { return; } match ctx.path_context() { - Some(PathCompletionCtx { - is_absolute_path: false, - qualifier: None, - kind: PathKind::Expr { .. }, - .. - }) if !ctx.is_path_disallowed() => {} + Some( + path_ctx @ PathCompletionCtx { + is_absolute_path: false, + qualifier: None, + kind: PathKind::Expr { .. }, + .. + }, + ) if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {} _ => return, } diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 23f47523d66..7c3296a0b31 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -11,32 +11,38 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) { let _p = profile::span("complete_expr_path"); - if ctx.is_path_disallowed() { - return; - } - let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update, after_if_expr) = - match ctx.nameref_ctx() { - Some(NameRefContext { - path_ctx: - Some(PathCompletionCtx { - kind: PathKind::Expr { in_block_expr, in_loop_body, after_if_expr }, - is_absolute_path, - qualifier, - .. - }), - record_expr, - .. - }) => ( - *is_absolute_path, - qualifier, - *in_block_expr, - *in_loop_body, - record_expr.as_ref().map_or(false, |&(_, it)| it), - *after_if_expr, - ), - _ => return, - }; + let ( + is_absolute_path, + qualifier, + in_block_expr, + in_loop_body, + is_func_update, + after_if_expr, + wants_mut_token, + ) = match ctx.nameref_ctx() { + Some(NameRefContext { + path_ctx: + Some(PathCompletionCtx { + kind: + PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }, + is_absolute_path, + qualifier, + .. + }), + record_expr, + .. + }) if ctx.qualifier_ctx.none() => ( + *is_absolute_path, + qualifier, + *in_block_expr, + *in_loop_body, + record_expr.as_ref().map_or(false, |&(_, it)| it), + *after_if_expr, + ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false), + ), + _ => return, + }; let scope_def_applicable = |def| { use hir::{GenericParam::*, ModuleDef::*}; @@ -164,12 +170,43 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) None if is_absolute_path => acc.add_crate_roots(ctx), None => { acc.add_nameref_keywords_with_colon(ctx); - if let Some(hir::Adt::Enum(e)) = + if let Some(adt) = ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) { - super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { - acc.add_qualified_enum_variant(ctx, variant, path) - }); + let self_ty = + (|| ctx.sema.to_def(ctx.impl_def.as_ref()?)?.self_ty(ctx.db).as_adt())(); + let complete_self = self_ty == Some(adt); + + match adt { + hir::Adt::Struct(strukt) => { + let path = ctx + .module + .find_use_path(ctx.db, hir::ModuleDef::from(strukt)) + .filter(|it| it.len() > 1); + + acc.add_struct_literal(ctx, strukt, path, None); + + if complete_self { + acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE)); + } + } + hir::Adt::Union(un) => { + let path = ctx + .module + .find_use_path(ctx.db, hir::ModuleDef::from(un)) + .filter(|it| it.len() > 1); + + acc.add_union_literal(ctx, un, path, None); + if complete_self { + acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE)); + } + } + hir::Adt::Enum(e) => { + super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { + acc.add_qualified_enum_variant(ctx, variant, path) + }); + } + } } ctx.process_all_names(&mut |name, def| { if scope_def_applicable(def) { @@ -180,20 +217,18 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) if !is_func_update { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); - if ctx.expects_expression() { - if !in_block_expr { - add_keyword("unsafe", "unsafe {\n $0\n}"); - } - add_keyword("match", "match $1 {\n $0\n}"); - add_keyword("while", "while $1 {\n $0\n}"); - add_keyword("while let", "while let $1 = $2 {\n $0\n}"); - add_keyword("loop", "loop {\n $0\n}"); - add_keyword("if", "if $1 {\n $0\n}"); - add_keyword("if let", "if let $1 = $2 {\n $0\n}"); - add_keyword("for", "for $1 in $2 {\n $0\n}"); - add_keyword("true", "true"); - add_keyword("false", "false"); + if !in_block_expr { + add_keyword("unsafe", "unsafe {\n $0\n}"); } + add_keyword("match", "match $1 {\n $0\n}"); + add_keyword("while", "while $1 {\n $0\n}"); + add_keyword("while let", "while let $1 = $2 {\n $0\n}"); + add_keyword("loop", "loop {\n $0\n}"); + add_keyword("if", "if $1 {\n $0\n}"); + add_keyword("if let", "if let $1 = $2 {\n $0\n}"); + add_keyword("for", "for $1 in $2 {\n $0\n}"); + add_keyword("true", "true"); + add_keyword("false", "false"); if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) @@ -207,7 +242,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) add_keyword("else if", "else if $1 {\n $0\n}"); } - if ctx.expects_ident_ref_expr() { + if wants_mut_token { add_keyword("mut", "mut "); } diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 22068096ba0..901f7519d25 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -8,7 +8,7 @@ use syntax::{AstNode, SyntaxNode, T}; use crate::{ - context::{CompletionContext, PathKind}, + context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PatternContext}, patterns::ImmediateLocation, render::{render_resolution_with_import, RenderContext}, }; @@ -110,16 +110,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) if !ctx.config.enable_imports_on_the_fly { return None; } - if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use | PathKind::Item { .. })) - || ctx.is_path_disallowed() - { - return None; - } - // FIXME: This should be encoded in a different way - if ctx.pattern_ctx.is_none() && ctx.path_context().is_none() && !ctx.has_dot_receiver() { - // completion inside `ast::Name` of a item declaration - return None; - } + let path_kind = match ctx.nameref_ctx() { + Some(NameRefContext { path_ctx: Some(PathCompletionCtx { kind, .. }), .. }) + if matches!( + kind, + PathKind::Expr { .. } + | PathKind::Type { .. } + | PathKind::Attr { .. } + | PathKind::Derive + | PathKind::Pat + ) => + { + Some(kind) + } + Some(NameRefContext { dot_access: Some(_), .. }) => None, + None if matches!(ctx.pattern_ctx, Some(PatternContext { record_pat: None, .. })) => { + Some(&PathKind::Pat) + } + _ => return None, + }; + let potential_import_name = { let token_kind = ctx.token.kind(); if matches!(token_kind, T![.] | T![::]) { @@ -138,18 +148,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) return None; } - let path_kind = match ctx.path_kind() { - Some(kind) => Some(kind), - None if ctx.pattern_ctx.is_some() => Some(PathKind::Pat), - None => None, - }; let ns_filter = |import: &LocatedImport| { let path_kind = match path_kind { - Some(path_kind) => path_kind, - None => match import.original_item { - ItemInNs::Macros(mac) => return mac.is_fn_like(ctx.db), - _ => return true, - }, + Some(it) => it, + None => return true, }; match (path_kind, import.original_item) { // Aren't handled in flyimport diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 7c6d72a031e..d44bf0a6ab7 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -16,19 +16,23 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) return; } - let (&is_absolute_path, path_qualifier, kind) = match ctx.path_context() { - Some(PathCompletionCtx { - kind: PathKind::Item { kind }, - is_absolute_path, - qualifier, - .. - }) => (is_absolute_path, qualifier, Some(kind)), - Some(PathCompletionCtx { - kind: PathKind::Expr { in_block_expr: true, .. }, - is_absolute_path, - qualifier, - .. - }) => (is_absolute_path, qualifier, None), + let (&is_absolute_path, path_qualifier, kind, is_trivial_path) = match ctx.path_context() { + Some( + ctx @ PathCompletionCtx { + kind: PathKind::Item { kind }, + is_absolute_path, + qualifier, + .. + }, + ) => (is_absolute_path, qualifier, Some(kind), ctx.is_trivial_path()), + Some( + ctx @ PathCompletionCtx { + kind: PathKind::Expr { in_block_expr: true, .. }, + is_absolute_path, + qualifier, + .. + }, + ) => (is_absolute_path, qualifier, None, ctx.is_trivial_path()), _ => return, }; @@ -36,7 +40,9 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) trait_impl::complete_trait_impl(acc, ctx); } - add_keywords(acc, ctx, kind); + if is_trivial_path { + add_keywords(acc, ctx, kind); + } if kind.is_none() { // this is already handled by expression @@ -71,9 +77,6 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) } fn add_keywords(acc: &mut Completions, ctx: &CompletionContext, kind: Option<&ItemListKind>) { - if ctx.is_non_trivial_path() { - return; - } let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None); diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index e870ecc2295..65fa1191781 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -8,11 +8,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { let item = match ctx.nameref_ctx() { - Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) - if !ctx.is_non_trivial_path() => - { - item - } + Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) => item, _ => return, }; diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 6717ca0a0e2..65805dba1ce 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -71,43 +71,6 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Some(()) } -pub(crate) fn complete_record_literal( - acc: &mut Completions, - ctx: &CompletionContext, -) -> Option<()> { - if !ctx.expects_expression() { - return None; - } - - match ctx.expected_type.as_ref()?.as_adt()? { - hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => { - let path = ctx - .module - .find_use_path(ctx.db, hir::ModuleDef::from(strukt)) - .filter(|it| it.len() > 1); - - acc.add_struct_literal(ctx, strukt, path, None); - - let impl_ = ctx.impl_def.as_ref()?; - let impl_adt = ctx.sema.to_def(impl_)?.self_ty(ctx.db).as_adt()?; - if hir::Adt::Struct(strukt) == impl_adt { - acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE)); - } - } - hir::Adt::Union(un) if ctx.path_qual().is_none() => { - let path = ctx - .module - .find_use_path(ctx.db, hir::ModuleDef::from(un)) - .filter(|it| it.len() > 1); - - acc.add_union_literal(ctx, un, path, None); - } - _ => {} - }; - - Some(()) -} - #[cfg(test)] mod tests { use crate::tests::check_edit; diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index bc8c070c14d..9cf0b87ad6f 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -13,9 +13,6 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) { let _p = profile::span("complete_type_path"); - if ctx.is_path_disallowed() { - return; - } let (&is_absolute_path, qualifier) = match ctx.path_context() { Some(PathCompletionCtx { diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index d41ca88e890..02307def9e6 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -43,12 +43,13 @@ pub(crate) enum Visible { No, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub(super) enum PathKind { Expr { in_block_expr: bool, in_loop_body: bool, after_if_expr: bool, + ref_expr_parent: Option, }, Type { in_tuple_struct: bool, @@ -356,41 +357,14 @@ pub(crate) fn expects_generic_arg(&self) -> bool { matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_))) } - pub(crate) fn expects_ident_ref_expr(&self) -> bool { - matches!(self.completion_location, Some(ImmediateLocation::RefExpr)) - } - - // FIXME: This shouldn't exist - pub(crate) fn is_path_disallowed(&self) -> bool { - !self.qualifier_ctx.none() - || (matches!(self.name_ctx(), Some(NameContext { .. })) && self.pattern_ctx.is_none()) - || matches!(self.pattern_ctx, Some(PatternContext { record_pat: Some(_), .. })) - || matches!( - self.nameref_ctx(), - Some(NameRefContext { record_expr: Some((_, false)), .. }) - ) - } - pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> { self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref()) } - pub(crate) fn expects_expression(&self) -> bool { - matches!(self.path_context(), Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. })) - } - - pub(crate) fn is_non_trivial_path(&self) -> bool { - self.path_context().as_ref().map_or(false, |it| !it.is_trivial_path()) - } - pub(crate) fn path_qual(&self) -> Option<&ast::Path> { self.path_context().and_then(|it| it.qualifier.as_ref().map(|it| &it.path)) } - pub(crate) fn path_kind(&self) -> Option { - self.path_context().map(|it| it.kind) - } - /// Checks if an item is visible and not `doc(hidden)` at the completion site. pub(crate) fn is_visible(&self, item: &I) -> Visible where @@ -1210,8 +1184,10 @@ fn classify_name_ref( let in_block_expr = is_in_block(it.syntax()); let in_loop_body = is_in_loop_body(it.syntax()); let after_if_expr = after_if_expr(it.syntax().clone()); + let ref_expr_parent = path.as_single_name_ref() + .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast); - Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr }) + Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }) }, ast::TupleStructPat(it) => { path_ctx.has_call_parens = true; @@ -1261,7 +1237,9 @@ fn classify_name_ref( let in_block_expr = is_in_block(it.syntax()); let after_if_expr = after_if_expr(it.syntax().clone()); fill_record_expr(it.syntax()); - PathKind::Expr { in_block_expr, in_loop_body, after_if_expr } + let ref_expr_parent = path.as_single_name_ref() + .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast); + PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent } }); }, } @@ -1368,7 +1346,7 @@ fn classify_name_ref( } } - if let Some(PathKind::Item { .. }) = kind { + if let PathKind::Item { .. } = path_ctx.kind { if qualifier_ctx.none() { if let Some(t) = top.first_token() { if let Some(prev) = t diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 3269e4926a7..9dc367b0bcc 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -169,7 +169,6 @@ pub fn completions( completions::mod_::complete_mod(acc, ctx); completions::pattern::complete_pattern(acc, ctx); completions::postfix::complete_postfix(acc, ctx); - completions::record::complete_record_literal(acc, ctx); completions::record::complete_record(acc, ctx); completions::snippet::complete_expr_snippet(acc, ctx); completions::snippet::complete_item_snippet(acc, ctx); diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs index 9abbfaa4072..761c97b9a96 100644 --- a/crates/ide-completion/src/patterns.rs +++ b/crates/ide-completion/src/patterns.rs @@ -30,7 +30,6 @@ pub(crate) enum TypeAnnotation { /// from which file the nodes are. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum ImmediateLocation { - RefExpr, TypeBound, /// Original file ast node TypeAnnotation(TypeAnnotation), @@ -80,7 +79,6 @@ pub(crate) fn determine_location( let res = match_ast! { match parent { - ast::RefExpr(_) => ImmediateLocation::RefExpr, ast::TypeBound(_) => ImmediateLocation::TypeBound, ast::TypeBoundList(_) => ImmediateLocation::TypeBound, ast::GenericArgList(_) => sema @@ -248,30 +246,3 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { } None } - -#[cfg(test)] -mod tests { - use syntax::algo::find_node_at_offset; - - use crate::tests::position; - - use super::*; - - fn check_location(code: &str, loc: impl Into>) { - let (db, pos) = position(code); - - let sema = Semantics::new(&db); - let original_file = sema.parse(pos.file_id); - - let name_like = find_node_at_offset(original_file.syntax(), pos.offset).unwrap(); - assert_eq!( - determine_location(&sema, original_file.syntax(), pos.offset, &name_like), - loc.into() - ); - } - - #[test] - fn test_ref_expr_loc() { - check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); - } -} diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index ca2b3ad3435..942dc033687 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -1099,6 +1099,8 @@ fn go(world: &WorldSnapshot) { go(w$0) } "#, expect![[r#" lc world [type+name+local] + st WorldSnapshot {…} [] + st &WorldSnapshot {…} [type] st WorldSnapshot [] fn go(…) [] "#]], @@ -1197,6 +1199,8 @@ fn main() { lc s [name+local] lc &mut s [type+name+local] st S [] + st &mut S [type] + st S [] fn main() [] fn foo(…) [] "#]], @@ -1266,6 +1270,8 @@ fn main() { lc m [local] lc t [local] lc &t [type+local] + st S [] + st &S [type] st T [] st S [] fn main() [] @@ -1311,6 +1317,8 @@ fn main() { lc m [local] lc t [local] lc &mut t [type+local] + st S [] + st &mut S [type] st T [] st S [] fn main() [] @@ -1405,6 +1413,8 @@ fn main() { } "#, expect![[r#" + st S [] + st &S [type] st T [] st S [] fn main() [] diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index 5c862f013a7..de527860d8c 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -34,8 +34,8 @@ fn render( let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; let needs_bang = match completion.path_context() { - Some(&PathCompletionCtx { kind, has_macro_bang, .. }) => { - is_fn_like && kind != PathKind::Use && !has_macro_bang + Some(PathCompletionCtx { kind, has_macro_bang, .. }) => { + is_fn_like && *kind != PathKind::Use && !has_macro_bang } _ => is_fn_like, };