Skip to content

Make sure that adding a snippet requires corresponding capability #4116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 4 additions & 19 deletions crates/ra_ide/src/completion.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! FIXME: write short doc here

mod completion_config;
mod completion_item;
mod completion_context;
mod presentation;
Expand Down Expand Up @@ -28,27 +29,11 @@ use crate::{
FilePosition,
};

pub use crate::completion::completion_item::{
CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
pub use crate::completion::{
completion_config::CompletionConfig,
completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompletionConfig {
pub enable_postfix_completions: bool,
pub add_call_parenthesis: bool,
pub add_call_argument_snippets: bool,
}

impl Default for CompletionConfig {
fn default() -> Self {
CompletionConfig {
enable_postfix_completions: true,
add_call_parenthesis: true,
add_call_argument_snippets: true,
}
}
}

/// Main entry point for completion. We run completion as a two-phase process.
///
/// First, we look at the position and collect a so-called `CompletionContext.
Expand Down
12 changes: 8 additions & 4 deletions crates/ra_ide/src/completion/complete_keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
.kind(CompletionItemKind::Keyword)
.insert_snippet(snippet)
.build()
let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
.kind(CompletionItemKind::Keyword);

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) {
Expand Down
39 changes: 33 additions & 6 deletions crates/ra_ide/src/completion/complete_postfix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ra_syntax::{
};
use ra_text_edit::TextEdit;

use super::completion_config::SnippetCap;
use crate::{
completion::{
completion_context::CompletionContext,
Expand All @@ -32,9 +33,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
None => return,
};

let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
};

if receiver_ty.is_bool() || receiver_ty.is_unknown() {
postfix_snippet(
ctx,
cap,
&dot_receiver,
"if",
"if expr {}",
Expand All @@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
.add_to(acc);
postfix_snippet(
ctx,
cap,
&dot_receiver,
"while",
"while expr {}",
Expand All @@ -52,11 +60,20 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
}

// !&&&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))
.add_to(acc);

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))
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,
// so it's better to consider references now to avoid breaking the compilation
Expand All @@ -66,6 +83,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {

postfix_snippet(
ctx,
cap,
&dot_receiver,
"match",
"match expr {}",
Expand All @@ -75,15 +93,23 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {

postfix_snippet(
ctx,
cap,
&dot_receiver,
"box",
"Box::new(expr)",
&format!("Box::new({})", receiver_text),
)
.add_to(acc);

postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text))
.add_to(acc);
postfix_snippet(
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 {
Expand All @@ -108,6 +134,7 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {

fn postfix_snippet(
ctx: &CompletionContext,
cap: SnippetCap,
receiver: &ast::Expr,
label: &str,
detail: &str,
Expand All @@ -121,7 +148,7 @@ fn postfix_snippet(
};
CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
.detail(detail)
.snippet_edit(edit)
.snippet_edit(cap, edit)
}

#[cfg(test)]
Expand Down
26 changes: 18 additions & 8 deletions crates/ra_ide/src/completion/complete_snippet.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
//! FIXME: write short doc here

use crate::completion::{
completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
CompletionKind, Completions,
completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
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)
.insert_snippet(snippet)
.insert_snippet(cap, snippet)
.kind(CompletionItemKind::Snippet)
}

pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
return;
}
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
};

snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
snippet(ctx, cap, "pd", "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) {
if !ctx.is_new_item {
return;
}
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
};

snippet(
ctx,
cap,
"Test function",
"\
#[test]
Expand All @@ -36,8 +46,8 @@ fn ${1:feature}() {
.lookup_by("tfn")
.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, "pub(crate)", "pub(crate) $0").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, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
}

#[cfg(test)]
Expand Down
18 changes: 13 additions & 5 deletions crates/ra_ide/src/completion/complete_trait_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn add_function_impl(
ctx: &CompletionContext,
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();

Expand All @@ -141,12 +141,20 @@ fn add_function_impl(
} else {
CompletionItemKind::Function
};

let snippet = format!("{} {{\n $0\n}}", display);

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(
Expand Down
29 changes: 29 additions & 0 deletions crates/ra_ide/src/completion/completion_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Settings for tweaking completion.
//!
//! The fun thing here is `SnippetCap` -- this type can only be created in this
//! module, and we use to statically check that we only produce snippet
//! completions if we are allowed to.

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompletionConfig {
pub enable_postfix_completions: bool,
pub add_call_parenthesis: bool,
pub add_call_argument_snippets: bool,
pub snippet_cap: Option<SnippetCap>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SnippetCap {
_private: (),
}

impl Default for CompletionConfig {
fn default() -> Self {
CompletionConfig {
enable_postfix_completions: true,
add_call_parenthesis: true,
add_call_argument_snippets: true,
snippet_cap: Some(SnippetCap { _private: () }),
}
}
}
9 changes: 7 additions & 2 deletions crates/ra_ide/src/completion/completion_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::fmt;

use super::completion_config::SnippetCap;
use hir::Documentation;
use ra_syntax::TextRange;
use ra_text_edit::TextEdit;
Expand Down Expand Up @@ -270,7 +271,11 @@ impl Builder {
self.insert_text = Some(insert_text.into());
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(snippet)
}
Expand All @@ -282,7 +287,7 @@ impl Builder {
self.text_edit = Some(edit);
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.text_edit(edit)
}
Expand Down
47 changes: 28 additions & 19 deletions crates/ra_ide/src/completion/presentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,19 @@ impl Completions {

// Add `<>` for generic types
if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
let has_non_default_type_params = match resolution {
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
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);
completion_item = completion_item
.lookup_by(local_name.clone())
.label(format!("{}<…>", local_name))
.insert_snippet(format!("{}<$0>", local_name));
if let Some(cap) = ctx.config.snippet_cap {
let has_non_default_type_params = match resolution {
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
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);
completion_item = completion_item
.lookup_by(local_name.clone())
.label(format!("{}<…>", local_name))
.insert_snippet(cap, format!("{}<$0>", local_name));
}
}
}

Expand Down Expand Up @@ -184,13 +186,16 @@ impl Completions {
.set_deprecated(is_deprecated(macro_, ctx.db))
.detail(detail);

builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call {
tested_by!(dont_insert_macro_call_parens_unncessary);
builder.insert_text(name)
} else {
let 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)
builder = match ctx.config.snippet_cap {
Some(cap) if ctx.use_item_syntax.is_none() && !ctx.is_macro_call => {
let macro_braces_to_insert =
self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
builder.insert_snippet(cap, macro_declaration + macro_braces_to_insert)
}
_ => {
tested_by!(dont_insert_macro_call_parens_unncessary);
builder.insert_text(name)
}
};

self.add(builder);
Expand Down Expand Up @@ -366,6 +371,10 @@ impl Builder {
if ctx.use_item_syntax.is_some() || ctx.is_call {
return self;
}
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return self,
};
// If not an import, add parenthesis automatically.
tested_by!(inserts_parens_for_function_calls);

Expand All @@ -387,7 +396,7 @@ impl Builder {

(snippet, format!("{}(…)", name))
};
self.lookup_by(name).label(label).insert_snippet(snippet)
self.lookup_by(name).label(label).insert_snippet(cap, snippet)
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl Default for Config {
enable_postfix_completions: true,
add_call_parenthesis: true,
add_call_argument_snippets: true,
..CompletionConfig::default()
},
call_info_full: true,
}
Expand Down