Make sure that adding a snippet requires corresponding capability

This commit is contained in:
Aleksey Kladov 2020-04-24 02:26:38 +02:00
parent b3050bded1
commit 5fd5de4061
6 changed files with 108 additions and 45 deletions

View File

@ -42,10 +42,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
} }
fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
.kind(CompletionItemKind::Keyword) .kind(CompletionItemKind::Keyword);
.insert_snippet(snippet)
.build() match ctx.config.snippet_cap {
Some(cap) => res.insert_snippet(cap, snippet),
_ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
}
.build()
} }
pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {

View File

@ -6,6 +6,7 @@ use ra_syntax::{
}; };
use ra_text_edit::TextEdit; use ra_text_edit::TextEdit;
use super::completion_config::SnippetCap;
use crate::{ use crate::{
completion::{ completion::{
completion_context::CompletionContext, completion_context::CompletionContext,
@ -32,9 +33,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
None => return, None => return,
}; };
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
};
if receiver_ty.is_bool() || receiver_ty.is_unknown() { if receiver_ty.is_bool() || receiver_ty.is_unknown() {
postfix_snippet( postfix_snippet(
ctx, ctx,
cap,
&dot_receiver, &dot_receiver,
"if", "if",
"if expr {}", "if expr {}",
@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
.add_to(acc); .add_to(acc);
postfix_snippet( postfix_snippet(
ctx, ctx,
cap,
&dot_receiver, &dot_receiver,
"while", "while",
"while expr {}", "while expr {}",
@ -52,12 +60,21 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
} }
// !&&&42 is a compiler error, ergo process it before considering the references // !&&&42 is a compiler error, ergo process it before considering the references
postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
.add_to(acc); .add_to(acc);
postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
.add_to(acc);
postfix_snippet(
ctx,
cap,
&dot_receiver,
"refm",
"&mut expr",
&format!("&mut {}", receiver_text),
)
.add_to(acc);
// The rest of the postfix completions create an expression that moves an argument, // The rest of the postfix completions create an expression that moves an argument,
// 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);
@ -66,6 +83,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
postfix_snippet( postfix_snippet(
ctx, ctx,
cap,
&dot_receiver, &dot_receiver,
"match", "match",
"match expr {}", "match expr {}",
@ -75,6 +93,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
postfix_snippet( postfix_snippet(
ctx, ctx,
cap,
&dot_receiver, &dot_receiver,
"box", "box",
"Box::new(expr)", "Box::new(expr)",
@ -82,8 +101,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
) )
.add_to(acc); .add_to(acc);
postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)) postfix_snippet(
.add_to(acc); ctx,
cap,
&dot_receiver,
"dbg",
"dbg!(expr)",
&format!("dbg!({})", receiver_text),
)
.add_to(acc);
} }
fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
@ -108,6 +134,7 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {
fn postfix_snippet( fn postfix_snippet(
ctx: &CompletionContext, ctx: &CompletionContext,
cap: SnippetCap,
receiver: &ast::Expr, receiver: &ast::Expr,
label: &str, label: &str,
detail: &str, detail: &str,
@ -121,7 +148,7 @@ fn postfix_snippet(
}; };
CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
.detail(detail) .detail(detail)
.snippet_edit(edit) .snippet_edit(cap, edit)
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,13 +1,13 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use crate::completion::{ use crate::completion::{
completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
CompletionKind, Completions, CompletionItemKind, CompletionKind, Completions,
}; };
fn snippet(ctx: &CompletionContext, label: &str, snippet: &str) -> Builder { fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
.insert_snippet(snippet) .insert_snippet(cap, snippet)
.kind(CompletionItemKind::Snippet) .kind(CompletionItemKind::Snippet)
} }
@ -15,17 +15,27 @@ pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
return; return;
} }
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
};
snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
} }
pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.is_new_item { if !ctx.is_new_item {
return; return;
} }
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
};
snippet( snippet(
ctx, ctx,
cap,
"Test function", "Test function",
"\ "\
#[test] #[test]
@ -36,8 +46,8 @@ fn ${1:feature}() {
.lookup_by("tfn") .lookup_by("tfn")
.add_to(acc); .add_to(acc);
snippet(ctx, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
} }
#[cfg(test)] #[cfg(test)]

View File

@ -122,7 +122,7 @@ fn add_function_impl(
ctx: &CompletionContext, ctx: &CompletionContext,
func: &hir::Function, func: &hir::Function,
) { ) {
let display = FunctionSignature::from_hir(ctx.db, *func); let signature = FunctionSignature::from_hir(ctx.db, *func);
let fn_name = func.name(ctx.db).to_string(); let fn_name = func.name(ctx.db).to_string();
@ -141,12 +141,20 @@ fn add_function_impl(
} else { } else {
CompletionItemKind::Function CompletionItemKind::Function
}; };
let snippet = format!("{} {{\n $0\n}}", display);
let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); match ctx.config.snippet_cap {
Some(cap) => {
let snippet = format!("{} {{\n $0\n}}", signature);
builder.snippet_edit(cap, TextEdit::replace(range, snippet))
}
None => {
let header = format!("{} {{", signature);
builder.text_edit(TextEdit::replace(range, header))
}
}
.kind(completion_kind)
.add_to(acc);
} }
fn add_type_alias_impl( fn add_type_alias_impl(

View File

@ -2,6 +2,7 @@
use std::fmt; use std::fmt;
use super::completion_config::SnippetCap;
use hir::Documentation; use hir::Documentation;
use ra_syntax::TextRange; use ra_syntax::TextRange;
use ra_text_edit::TextEdit; use ra_text_edit::TextEdit;
@ -270,7 +271,11 @@ impl Builder {
self.insert_text = Some(insert_text.into()); self.insert_text = Some(insert_text.into());
self self
} }
pub(crate) fn insert_snippet(mut self, snippet: impl Into<String>) -> Builder { pub(crate) fn insert_snippet(
mut self,
_cap: SnippetCap,
snippet: impl Into<String>,
) -> Builder {
self.insert_text_format = InsertTextFormat::Snippet; self.insert_text_format = InsertTextFormat::Snippet;
self.insert_text(snippet) self.insert_text(snippet)
} }
@ -282,7 +287,7 @@ impl Builder {
self.text_edit = Some(edit); self.text_edit = Some(edit);
self self
} }
pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder { pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
self.insert_text_format = InsertTextFormat::Snippet; self.insert_text_format = InsertTextFormat::Snippet;
self.text_edit(edit) self.text_edit(edit)
} }

View File

@ -114,17 +114,19 @@ impl Completions {
// Add `<>` for generic types // Add `<>` for generic types
if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
let has_non_default_type_params = match resolution { if let Some(cap) = ctx.config.snippet_cap {
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), let has_non_default_type_params = match resolution {
ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
_ => false, ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
}; _ => false,
if has_non_default_type_params { };
tested_by!(inserts_angle_brackets_for_generics); if has_non_default_type_params {
completion_item = completion_item tested_by!(inserts_angle_brackets_for_generics);
.lookup_by(local_name.clone()) completion_item = completion_item
.label(format!("{}<…>", local_name)) .lookup_by(local_name.clone())
.insert_snippet(format!("{}<$0>", local_name)); .label(format!("{}<…>", local_name))
.insert_snippet(cap, format!("{}<$0>", local_name));
}
} }
} }
@ -184,13 +186,16 @@ impl Completions {
.set_deprecated(is_deprecated(macro_, ctx.db)) .set_deprecated(is_deprecated(macro_, ctx.db))
.detail(detail); .detail(detail);
builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call { builder = match ctx.config.snippet_cap {
tested_by!(dont_insert_macro_call_parens_unncessary); Some(cap) if ctx.use_item_syntax.is_none() && !ctx.is_macro_call => {
builder.insert_text(name) let macro_braces_to_insert =
} else { self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
let macro_braces_to_insert = builder.insert_snippet(cap, macro_declaration + macro_braces_to_insert)
self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); }
builder.insert_snippet(macro_declaration + macro_braces_to_insert) _ => {
tested_by!(dont_insert_macro_call_parens_unncessary);
builder.insert_text(name)
}
}; };
self.add(builder); self.add(builder);
@ -366,6 +371,10 @@ impl Builder {
if ctx.use_item_syntax.is_some() || ctx.is_call { if ctx.use_item_syntax.is_some() || ctx.is_call {
return self; return self;
} }
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return self,
};
// If not an import, add parenthesis automatically. // If not an import, add parenthesis automatically.
tested_by!(inserts_parens_for_function_calls); tested_by!(inserts_parens_for_function_calls);
@ -387,7 +396,7 @@ impl Builder {
(snippet, format!("{}(…)", name)) (snippet, format!("{}(…)", name))
}; };
self.lookup_by(name).label(label).insert_snippet(snippet) self.lookup_by(name).label(label).insert_snippet(cap, snippet)
} }
} }