Fix postfix completions panicking

This commit is contained in:
Lukas Wirth 2021-10-25 15:22:29 +02:00
parent 4c582ffe9f
commit a932935d4e
2 changed files with 36 additions and 18 deletions

View file

@ -55,7 +55,10 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
None => return, 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 !ctx.config.snippets.is_empty() { if !ctx.config.snippets.is_empty() {
add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
@ -123,7 +126,10 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
// so it's better to consider references now to avoid breaking the compilation // so it's better to consider references now to avoid breaking the compilation
let dot_receiver = include_references(dot_receiver); let dot_receiver = include_references(dot_receiver);
let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal); 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 { match try_enum {
Some(try_enum) => match try_enum { Some(try_enum) => match try_enum {
@ -200,27 +206,36 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {
resulting_element resulting_element
} }
fn build_postfix_snippet_builder<'a>( fn build_postfix_snippet_builder<'ctx>(
ctx: &'a CompletionContext, ctx: &'ctx CompletionContext,
cap: SnippetCap, cap: SnippetCap,
receiver: &'a ast::Expr, receiver: &'ctx ast::Expr,
) -> impl Fn(&str, &str, &str) -> Builder + 'a { ) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {
let receiver_syntax = receiver.syntax(); 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;
let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
move |label, detail, snippet| { // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that
let edit = TextEdit::replace(delete_range, snippet.to_string()); // can't be annotated for the closure, hence fix it by constructing it without the Option first
let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label); fn build<'ctx>(
item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit); ctx: &'ctx CompletionContext,
if ctx.original_token.text() == label { cap: SnippetCap,
let relevance = delete_range: TextRange,
CompletionRelevance { exact_postfix_snippet_match: true, ..Default::default() }; ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {
item.set_relevance(relevance); 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);
}
item item
}
} }
Some(build(ctx, cap, delete_range))
} }
fn add_custom_postfix_completions( fn add_custom_postfix_completions(

View file

@ -49,7 +49,10 @@ pub(crate) fn add_format_like_completions(
None => return, 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,
};
let mut parser = FormatStrParser::new(input); let mut parser = FormatStrParser::new(input);
if parser.parse().is_ok() { if parser.parse().is_ok() {