aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-24 01:27:38 +0100
committerGitHub <[email protected]>2020-04-24 01:27:38 +0100
commit3a0a7081f4db293599bce5fab124cf258a946cb2 (patch)
tree2b3d64141d9797715914e2d26496eee7adb38a11 /crates
parent601f89f2cb085ab7e638f034088f32b9428a0611 (diff)
parent5fd5de4061362aa1066cb9a47aa9cb79eab38e47 (diff)
Merge #4116
4116: Make sure that adding a snippet requires corresponding capability r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/completion.rs23
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs12
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs39
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs18
-rw-r--r--crates/ra_ide/src/completion/completion_config.rs29
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs9
-rw-r--r--crates/ra_ide/src/completion/presentation.rs47
-rw-r--r--crates/rust-analyzer/src/config.rs1
9 files changed, 141 insertions, 63 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 19bc4321c..f0e02180b 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -1,5 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod completion_config;
3mod completion_item; 4mod completion_item;
4mod completion_context; 5mod completion_context;
5mod presentation; 6mod presentation;
@@ -28,27 +29,11 @@ use crate::{
28 FilePosition, 29 FilePosition,
29}; 30};
30 31
31pub use crate::completion::completion_item::{ 32pub use crate::completion::{
32 CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, 33 completion_config::CompletionConfig,
34 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
33}; 35};
34 36
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct CompletionConfig {
37 pub enable_postfix_completions: bool,
38 pub add_call_parenthesis: bool,
39 pub add_call_argument_snippets: bool,
40}
41
42impl Default for CompletionConfig {
43 fn default() -> Self {
44 CompletionConfig {
45 enable_postfix_completions: true,
46 add_call_parenthesis: true,
47 add_call_argument_snippets: true,
48 }
49 }
50}
51
52/// Main entry point for completion. We run completion as a two-phase process. 37/// Main entry point for completion. We run completion as a two-phase process.
53/// 38///
54/// First, we look at the position and collect a so-called `CompletionContext. 39/// First, we look at the position and collect a so-called `CompletionContext.
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 38f9c34e7..adefb290e 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -42,10 +42,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
42} 42}
43 43
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46 .kind(CompletionItemKind::Keyword) 46 .kind(CompletionItemKind::Keyword);
47 .insert_snippet(snippet) 47
48 .build() 48 match ctx.config.snippet_cap {
49 Some(cap) => res.insert_snippet(cap, snippet),
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
51 }
52 .build()
49} 53}
50 54
51pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 29c2881c6..8d397b0fe 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -6,6 +6,7 @@ use ra_syntax::{
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
9use crate::{ 10use crate::{
10 completion::{ 11 completion::{
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
@@ -32,9 +33,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
32 None => return, 33 None => return,
33 }; 34 };
34 35
36 let cap = match ctx.config.snippet_cap {
37 Some(it) => it,
38 None => return,
39 };
40
35 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 41 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
36 postfix_snippet( 42 postfix_snippet(
37 ctx, 43 ctx,
44 cap,
38 &dot_receiver, 45 &dot_receiver,
39 "if", 46 "if",
40 "if expr {}", 47 "if expr {}",
@@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
43 .add_to(acc); 50 .add_to(acc);
44 postfix_snippet( 51 postfix_snippet(
45 ctx, 52 ctx,
53 cap,
46 &dot_receiver, 54 &dot_receiver,
47 "while", 55 "while",
48 "while expr {}", 56 "while expr {}",
@@ -52,11 +60,20 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
52 } 60 }
53 61
54 // !&&&42 is a compiler error, ergo process it before considering the references 62 // !&&&42 is a compiler error, ergo process it before considering the references
55 postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); 63 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
64 .add_to(acc);
56 65
57 postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); 66 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
58 postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
59 .add_to(acc); 67 .add_to(acc);
68 postfix_snippet(
69 ctx,
70 cap,
71 &dot_receiver,
72 "refm",
73 "&mut expr",
74 &format!("&mut {}", receiver_text),
75 )
76 .add_to(acc);
60 77
61 // The rest of the postfix completions create an expression that moves an argument, 78 // The rest of the postfix completions create an expression that moves an argument,
62 // so it's better to consider references now to avoid breaking the compilation 79 // so it's better to consider references now to avoid breaking the compilation
@@ -66,6 +83,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
66 83
67 postfix_snippet( 84 postfix_snippet(
68 ctx, 85 ctx,
86 cap,
69 &dot_receiver, 87 &dot_receiver,
70 "match", 88 "match",
71 "match expr {}", 89 "match expr {}",
@@ -75,6 +93,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
75 93
76 postfix_snippet( 94 postfix_snippet(
77 ctx, 95 ctx,
96 cap,
78 &dot_receiver, 97 &dot_receiver,
79 "box", 98 "box",
80 "Box::new(expr)", 99 "Box::new(expr)",
@@ -82,8 +101,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
82 ) 101 )
83 .add_to(acc); 102 .add_to(acc);
84 103
85 postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)) 104 postfix_snippet(
86 .add_to(acc); 105 ctx,
106 cap,
107 &dot_receiver,
108 "dbg",
109 "dbg!(expr)",
110 &format!("dbg!({})", receiver_text),
111 )
112 .add_to(acc);
87} 113}
88 114
89fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 115fn 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 {
108 134
109fn postfix_snippet( 135fn postfix_snippet(
110 ctx: &CompletionContext, 136 ctx: &CompletionContext,
137 cap: SnippetCap,
111 receiver: &ast::Expr, 138 receiver: &ast::Expr,
112 label: &str, 139 label: &str,
113 detail: &str, 140 detail: &str,
@@ -121,7 +148,7 @@ fn postfix_snippet(
121 }; 148 };
122 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 149 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
123 .detail(detail) 150 .detail(detail)
124 .snippet_edit(edit) 151 .snippet_edit(cap, edit)
125} 152}
126 153
127#[cfg(test)] 154#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index f731e9b9a..4bccfbfed 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -1,13 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::completion::{ 3use crate::completion::{
4 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, 4 completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5 CompletionKind, Completions, 5 CompletionItemKind, CompletionKind, Completions,
6}; 6};
7 7
8fn snippet(ctx: &CompletionContext, label: &str, snippet: &str) -> Builder { 8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10 .insert_snippet(snippet) 10 .insert_snippet(cap, snippet)
11 .kind(CompletionItemKind::Snippet) 11 .kind(CompletionItemKind::Snippet)
12} 12}
13 13
@@ -15,17 +15,27 @@ pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { 15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
16 return; 16 return;
17 } 17 }
18 let cap = match ctx.config.snippet_cap {
19 Some(it) => it,
20 None => return,
21 };
18 22
19 snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 23 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
20 snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 24 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
21} 25}
22 26
23pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 27pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
24 if !ctx.is_new_item { 28 if !ctx.is_new_item {
25 return; 29 return;
26 } 30 }
31 let cap = match ctx.config.snippet_cap {
32 Some(it) => it,
33 None => return,
34 };
35
27 snippet( 36 snippet(
28 ctx, 37 ctx,
38 cap,
29 "Test function", 39 "Test function",
30 "\ 40 "\
31#[test] 41#[test]
@@ -36,8 +46,8 @@ fn ${1:feature}() {
36 .lookup_by("tfn") 46 .lookup_by("tfn")
37 .add_to(acc); 47 .add_to(acc);
38 48
39 snippet(ctx, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 49 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
40 snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); 50 snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
41} 51}
42 52
43#[cfg(test)] 53#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 2ec0e7ce9..c39943252 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -122,7 +122,7 @@ fn add_function_impl(
122 ctx: &CompletionContext, 122 ctx: &CompletionContext,
123 func: &hir::Function, 123 func: &hir::Function,
124) { 124) {
125 let display = FunctionSignature::from_hir(ctx.db, *func); 125 let signature = FunctionSignature::from_hir(ctx.db, *func);
126 126
127 let fn_name = func.name(ctx.db).to_string(); 127 let fn_name = func.name(ctx.db).to_string();
128 128
@@ -141,12 +141,20 @@ fn add_function_impl(
141 } else { 141 } else {
142 CompletionItemKind::Function 142 CompletionItemKind::Function
143 }; 143 };
144
145 let snippet = format!("{} {{\n $0\n}}", display);
146
147 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); 144 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
148 145
149 builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); 146 match ctx.config.snippet_cap {
147 Some(cap) => {
148 let snippet = format!("{} {{\n $0\n}}", signature);
149 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
150 }
151 None => {
152 let header = format!("{} {{", signature);
153 builder.text_edit(TextEdit::replace(range, header))
154 }
155 }
156 .kind(completion_kind)
157 .add_to(acc);
150} 158}
151 159
152fn add_type_alias_impl( 160fn add_type_alias_impl(
diff --git a/crates/ra_ide/src/completion/completion_config.rs b/crates/ra_ide/src/completion/completion_config.rs
new file mode 100644
index 000000000..6cf7ed6e4
--- /dev/null
+++ b/crates/ra_ide/src/completion/completion_config.rs
@@ -0,0 +1,29 @@
1//! Settings for tweaking completion.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig {
9 pub enable_postfix_completions: bool,
10 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub struct SnippetCap {
17 _private: (),
18}
19
20impl Default for CompletionConfig {
21 fn default() -> Self {
22 CompletionConfig {
23 enable_postfix_completions: true,
24 add_call_parenthesis: true,
25 add_call_argument_snippets: true,
26 snippet_cap: Some(SnippetCap { _private: () }),
27 }
28 }
29}
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index edbf4a5b7..fb06cc125 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -2,6 +2,7 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use super::completion_config::SnippetCap;
5use hir::Documentation; 6use hir::Documentation;
6use ra_syntax::TextRange; 7use ra_syntax::TextRange;
7use ra_text_edit::TextEdit; 8use ra_text_edit::TextEdit;
@@ -270,7 +271,11 @@ impl Builder {
270 self.insert_text = Some(insert_text.into()); 271 self.insert_text = Some(insert_text.into());
271 self 272 self
272 } 273 }
273 pub(crate) fn insert_snippet(mut self, snippet: impl Into<String>) -> Builder { 274 pub(crate) fn insert_snippet(
275 mut self,
276 _cap: SnippetCap,
277 snippet: impl Into<String>,
278 ) -> Builder {
274 self.insert_text_format = InsertTextFormat::Snippet; 279 self.insert_text_format = InsertTextFormat::Snippet;
275 self.insert_text(snippet) 280 self.insert_text(snippet)
276 } 281 }
@@ -282,7 +287,7 @@ impl Builder {
282 self.text_edit = Some(edit); 287 self.text_edit = Some(edit);
283 self 288 self
284 } 289 }
285 pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder { 290 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
286 self.insert_text_format = InsertTextFormat::Snippet; 291 self.insert_text_format = InsertTextFormat::Snippet;
287 self.text_edit(edit) 292 self.text_edit(edit)
288 } 293 }
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 78df9cbdb..5e2b8b920 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -114,17 +114,19 @@ impl Completions {
114 114
115 // Add `<>` for generic types 115 // Add `<>` for generic types
116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { 116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
117 let has_non_default_type_params = match resolution { 117 if let Some(cap) = ctx.config.snippet_cap {
118 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 118 let has_non_default_type_params = match resolution {
119 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 119 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
120 _ => false, 120 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
121 }; 121 _ => false,
122 if has_non_default_type_params { 122 };
123 tested_by!(inserts_angle_brackets_for_generics); 123 if has_non_default_type_params {
124 completion_item = completion_item 124 tested_by!(inserts_angle_brackets_for_generics);
125 .lookup_by(local_name.clone()) 125 completion_item = completion_item
126 .label(format!("{}<…>", local_name)) 126 .lookup_by(local_name.clone())
127 .insert_snippet(format!("{}<$0>", local_name)); 127 .label(format!("{}<…>", local_name))
128 .insert_snippet(cap, format!("{}<$0>", local_name));
129 }
128 } 130 }
129 } 131 }
130 132
@@ -184,13 +186,16 @@ impl Completions {
184 .set_deprecated(is_deprecated(macro_, ctx.db)) 186 .set_deprecated(is_deprecated(macro_, ctx.db))
185 .detail(detail); 187 .detail(detail);
186 188
187 builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call { 189 builder = match ctx.config.snippet_cap {
188 tested_by!(dont_insert_macro_call_parens_unncessary); 190 Some(cap) if ctx.use_item_syntax.is_none() && !ctx.is_macro_call => {
189 builder.insert_text(name) 191 let macro_braces_to_insert =
190 } else { 192 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
191 let macro_braces_to_insert = 193 builder.insert_snippet(cap, macro_declaration + macro_braces_to_insert)
192 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); 194 }
193 builder.insert_snippet(macro_declaration + macro_braces_to_insert) 195 _ => {
196 tested_by!(dont_insert_macro_call_parens_unncessary);
197 builder.insert_text(name)
198 }
194 }; 199 };
195 200
196 self.add(builder); 201 self.add(builder);
@@ -366,6 +371,10 @@ impl Builder {
366 if ctx.use_item_syntax.is_some() || ctx.is_call { 371 if ctx.use_item_syntax.is_some() || ctx.is_call {
367 return self; 372 return self;
368 } 373 }
374 let cap = match ctx.config.snippet_cap {
375 Some(it) => it,
376 None => return self,
377 };
369 // If not an import, add parenthesis automatically. 378 // If not an import, add parenthesis automatically.
370 tested_by!(inserts_parens_for_function_calls); 379 tested_by!(inserts_parens_for_function_calls);
371 380
@@ -387,7 +396,7 @@ impl Builder {
387 396
388 (snippet, format!("{}(…)", name)) 397 (snippet, format!("{}(…)", name))
389 }; 398 };
390 self.lookup_by(name).label(label).insert_snippet(snippet) 399 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
391 } 400 }
392} 401}
393 402
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 642c34574..33d7c95a8 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -104,6 +104,7 @@ impl Default for Config {
104 enable_postfix_completions: true, 104 enable_postfix_completions: true,
105 add_call_parenthesis: true, 105 add_call_parenthesis: true,
106 add_call_argument_snippets: true, 106 add_call_argument_snippets: true,
107 ..CompletionConfig::default()
107 }, 108 },
108 call_info_full: true, 109 call_info_full: true,
109 } 110 }