X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Fide_completion%2Fsrc%2Fcompletions%2Fpostfix.rs;h=e8e0c7ea9f1d1b65d7e66e8a3a206d4bcefe3a09;hb=82fccb971e49d6d8945b7764dd9e9ed883f6148f;hp=b35d97e425211ac844b985d2dbd2de2f0f8f75c4;hpb=f87debcf87c16690e8d5e185a35d03a402a2d5bf;p=rust.git diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index b35d97e4252..e8e0c7ea9f1 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs @@ -2,9 +2,9 @@ mod format_like; -use hir::Documentation; +use hir::{Documentation, HasAttrs}; use ide_db::{ - helpers::{insert_use::ImportScope, FamousDefs, SnippetCap}, + helpers::{insert_use::ImportScope, SnippetCap}, ty_filter::TryEnum, }; use syntax::{ @@ -15,11 +15,9 @@ use text_edit::TextEdit; use crate::{ - completions::postfix::format_like::add_format_like_completions, - context::CompletionContext, - item::{Builder, CompletionKind}, - patterns::ImmediateLocation, - CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope, + completions::postfix::format_like::add_format_like_completions, context::CompletionContext, + item::Builder, patterns::ImmediateLocation, CompletionItem, CompletionItemKind, + CompletionRelevance, Completions, SnippetScope, }; pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { @@ -45,8 +43,9 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { // Suggest .await syntax for types that implement Future trait if receiver_ty.impls_future(ctx.db) { - let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await"); - item.kind(CompletionItemKind::Keyword).detail("expr.await"); + let mut item = + CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await"); + item.detail("expr.await"); item.add_to(acc); } @@ -55,7 +54,26 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { None => return, }; - let postfix_snippet = build_postfix_snippet_builder(ctx, cap, &dot_receiver); + let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) { + Some(it) => it, + None => return, + }; + + if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { + if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { + if let &[hir::AssocItem::Function(drop_fn)] = &*drop_trait.items(ctx.db) { + cov_mark::hit!(postfix_drop_completion); + // FIXME: check that `drop` is in scope, use fully qualified path if it isn't/if shadowed + let mut item = postfix_snippet( + "drop", + "fn drop(&mut self)", + &format!("drop($0{})", receiver_text), + ); + item.set_documentation(drop_fn.docs(ctx.db)); + item.add_to(acc); + } + } + } if !ctx.config.snippets.is_empty() { add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); @@ -105,7 +123,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { ) .add_to(acc); postfix_snippet("not", "!expr", &format!("!{}", receiver_text)).add_to(acc); - } else if let Some(trait_) = FamousDefs(&ctx.sema, ctx.krate).core_iter_IntoIterator() { + } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { if receiver_ty.impls_trait(ctx.db, trait_, &[]) { postfix_snippet( "for", @@ -123,7 +141,10 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { // so it's better to consider references now to avoid breaking the compilation let dot_receiver = include_references(dot_receiver); let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal); - let postfix_snippet = build_postfix_snippet_builder(ctx, cap, &dot_receiver); + let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) { + Some(it) => it, + None => return, + }; match try_enum { Some(try_enum) => match try_enum { @@ -158,10 +179,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { } postfix_snippet("box", "Box::new(expr)", &format!("Box::new({})", receiver_text)).add_to(acc); - postfix_snippet("ok", "Ok(expr)", &format!("Ok({})", receiver_text)).add_to(acc); - postfix_snippet("err", "Err(expr)", &format!("Err({})", receiver_text)).add_to(acc); - postfix_snippet("some", "Some(expr)", &format!("Some({})", receiver_text)).add_to(acc); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); + postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); // fixme postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{})", receiver_text)).add_to(acc); postfix_snippet("call", "function(expr)", &format!("${{1}}({})", receiver_text)).add_to(acc); @@ -200,27 +218,41 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr { resulting_element } -fn build_postfix_snippet_builder<'a>( - ctx: &'a CompletionContext, +fn build_postfix_snippet_builder<'ctx>( + ctx: &'ctx CompletionContext, cap: SnippetCap, - receiver: &'a ast::Expr, -) -> impl Fn(&str, &str, &str) -> Builder + 'a { + receiver: &'ctx ast::Expr, +) -> Option Builder + 'ctx> { let receiver_syntax = receiver.syntax(); - let receiver_range = ctx.sema.original_range(receiver_syntax).range; + let receiver_range = ctx.sema.original_range_opt(receiver_syntax)?.range; + if ctx.source_range().end() < receiver_range.start() { + // This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping. + return None; + } let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); - move |label, detail, snippet| { - let edit = TextEdit::replace(delete_range, snippet.to_string()); - let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label); - item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit); - if ctx.original_token.text() == label { - let relevance = - CompletionRelevance { exact_postfix_snippet_match: true, ..Default::default() }; - item.set_relevance(relevance); - } + // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that + // can't be annotated for the closure, hence fix it by constructing it without the Option first + fn build<'ctx>( + ctx: &'ctx CompletionContext, + cap: SnippetCap, + delete_range: TextRange, + ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx { + move |label, detail, snippet| { + let edit = TextEdit::replace(delete_range, snippet.to_string()); + let mut item = + CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label); + item.detail(detail).snippet_edit(cap, edit); + if ctx.original_token.text() == label { + let relevance = + CompletionRelevance { exact_postfix_snippet_match: true, ..Default::default() }; + item.set_relevance(relevance); + } - item + item + } } + Some(build(ctx, cap, delete_range)) } fn add_custom_postfix_completions( @@ -229,8 +261,7 @@ fn add_custom_postfix_completions( postfix_snippet: impl Fn(&str, &str, &str) -> Builder, receiver_text: &str, ) -> Option<()> { - let import_scope = - ImportScope::find_insert_use_container_with_macros(&ctx.token.parent()?, &ctx.sema)?; + let import_scope = ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?; ctx.config.postfix_snippets().filter(|(_, snip)| snip.scope == SnippetScope::Expr).for_each( |(trigger, snippet)| { let imports = match snippet.imports(ctx, &import_scope) { @@ -255,12 +286,12 @@ mod tests { use expect_test::{expect, Expect}; use crate::{ - tests::{check_edit, check_edit_with_config, filtered_completion_list, TEST_CONFIG}, - CompletionConfig, CompletionKind, Snippet, + tests::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG}, + CompletionConfig, Snippet, }; fn check(ra_fixture: &str, expect: Expect) { - let actual = filtered_completion_list(ra_fixture, CompletionKind::Postfix); + let actual = completion_list(ra_fixture); expect.assert_eq(&actual) } @@ -281,9 +312,6 @@ fn main() { sn refm &mut expr sn match match expr {} sn box Box::new(expr) - sn ok Ok(expr) - sn err Err(expr) - sn some Some(expr) sn dbg dbg!(expr) sn dbgr dbg!(&expr) sn call function(expr) @@ -314,9 +342,6 @@ fn main() { sn refm &mut expr sn match match expr {} sn box Box::new(expr) - sn ok Ok(expr) - sn err Err(expr) - sn some Some(expr) sn dbg dbg!(expr) sn dbgr dbg!(&expr) sn call function(expr) @@ -338,9 +363,6 @@ fn main() { sn refm &mut expr sn match match expr {} sn box Box::new(expr) - sn ok Ok(expr) - sn err Err(expr) - sn some Some(expr) sn dbg dbg!(expr) sn dbgr dbg!(&expr) sn call function(expr) @@ -367,9 +389,6 @@ fn main() { sn refm &mut expr sn match match expr {} sn box Box::new(expr) - sn ok Ok(expr) - sn err Err(expr) - sn some Some(expr) sn dbg dbg!(expr) sn dbgr dbg!(&expr) sn call function(expr)