Skip to content

Commit 3a0a708

Browse files
bors[bot]matklad
andauthored
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]>
2 parents 601f89f + 5fd5de4 commit 3a0a708

File tree

9 files changed

+141
-63
lines changed

9 files changed

+141
-63
lines changed

crates/ra_ide/src/completion.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! FIXME: write short doc here
22
3+
mod completion_config;
34
mod completion_item;
45
mod completion_context;
56
mod presentation;
@@ -28,27 +29,11 @@ use crate::{
2829
FilePosition,
2930
};
3031

31-
pub use crate::completion::completion_item::{
32-
CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
32+
pub use crate::completion::{
33+
completion_config::CompletionConfig,
34+
completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
3335
};
3436

35-
#[derive(Clone, Debug, PartialEq, Eq)]
36-
pub struct CompletionConfig {
37-
pub enable_postfix_completions: bool,
38-
pub add_call_parenthesis: bool,
39-
pub add_call_argument_snippets: bool,
40-
}
41-
42-
impl 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-
5237
/// Main entry point for completion. We run completion as a two-phase process.
5338
///
5439
/// First, we look at the position and collect a so-called `CompletionContext.

crates/ra_ide/src/completion/complete_keyword.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
4242
}
4343

4444
fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45-
CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46-
.kind(CompletionItemKind::Keyword)
47-
.insert_snippet(snippet)
48-
.build()
45+
let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46+
.kind(CompletionItemKind::Keyword);
47+
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()
4953
}
5054

5155
pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {

crates/ra_ide/src/completion/complete_postfix.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use ra_syntax::{
66
};
77
use ra_text_edit::TextEdit;
88

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

36+
let cap = match ctx.config.snippet_cap {
37+
Some(it) => it,
38+
None => return,
39+
};
40+
3541
if receiver_ty.is_bool() || receiver_ty.is_unknown() {
3642
postfix_snippet(
3743
ctx,
44+
cap,
3845
&dot_receiver,
3946
"if",
4047
"if expr {}",
@@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
4350
.add_to(acc);
4451
postfix_snippet(
4552
ctx,
53+
cap,
4654
&dot_receiver,
4755
"while",
4856
"while expr {}",
@@ -52,11 +60,20 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
5260
}
5361

5462
// !&&&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);
5665

57-
postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
58-
postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
66+
postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
5967
.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);
6077

6178
// The rest of the postfix completions create an expression that moves an argument,
6279
// 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) {
6683

6784
postfix_snippet(
6885
ctx,
86+
cap,
6987
&dot_receiver,
7088
"match",
7189
"match expr {}",
@@ -75,15 +93,23 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
7593

7694
postfix_snippet(
7795
ctx,
96+
cap,
7897
&dot_receiver,
7998
"box",
8099
"Box::new(expr)",
81100
&format!("Box::new({})", receiver_text),
82101
)
83102
.add_to(acc);
84103

85-
postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text))
86-
.add_to(acc);
104+
postfix_snippet(
105+
ctx,
106+
cap,
107+
&dot_receiver,
108+
"dbg",
109+
"dbg!(expr)",
110+
&format!("dbg!({})", receiver_text),
111+
)
112+
.add_to(acc);
87113
}
88114

89115
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 {
108134

109135
fn postfix_snippet(
110136
ctx: &CompletionContext,
137+
cap: SnippetCap,
111138
receiver: &ast::Expr,
112139
label: &str,
113140
detail: &str,
@@ -121,7 +148,7 @@ fn postfix_snippet(
121148
};
122149
CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
123150
.detail(detail)
124-
.snippet_edit(edit)
151+
.snippet_edit(cap, edit)
125152
}
126153

127154
#[cfg(test)]

crates/ra_ide/src/completion/complete_snippet.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
11
//! FIXME: write short doc here
22
33
use crate::completion::{
4-
completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
5-
CompletionKind, Completions,
4+
completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5+
CompletionItemKind, CompletionKind, Completions,
66
};
77

8-
fn snippet(ctx: &CompletionContext, label: &str, snippet: &str) -> Builder {
8+
fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
99
CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10-
.insert_snippet(snippet)
10+
.insert_snippet(cap, snippet)
1111
.kind(CompletionItemKind::Snippet)
1212
}
1313

1414
pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
1515
if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
1616
return;
1717
}
18+
let cap = match ctx.config.snippet_cap {
19+
Some(it) => it,
20+
None => return,
21+
};
1822

19-
snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
20-
snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
23+
snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
24+
snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
2125
}
2226

2327
pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
2428
if !ctx.is_new_item {
2529
return;
2630
}
31+
let cap = match ctx.config.snippet_cap {
32+
Some(it) => it,
33+
None => return,
34+
};
35+
2736
snippet(
2837
ctx,
38+
cap,
2939
"Test function",
3040
"\
3141
#[test]
@@ -36,8 +46,8 @@ fn ${1:feature}() {
3646
.lookup_by("tfn")
3747
.add_to(acc);
3848

39-
snippet(ctx, "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);
49+
snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
50+
snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
4151
}
4252

4353
#[cfg(test)]

crates/ra_ide/src/completion/complete_trait_impl.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ fn add_function_impl(
122122
ctx: &CompletionContext,
123123
func: &hir::Function,
124124
) {
125-
let display = FunctionSignature::from_hir(ctx.db, *func);
125+
let signature = FunctionSignature::from_hir(ctx.db, *func);
126126

127127
let fn_name = func.name(ctx.db).to_string();
128128

@@ -141,12 +141,20 @@ fn add_function_impl(
141141
} else {
142142
CompletionItemKind::Function
143143
};
144-
145-
let snippet = format!("{} {{\n $0\n}}", display);
146-
147144
let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
148145

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);
150158
}
151159

152160
fn add_type_alias_impl(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
8+
pub 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)]
16+
pub struct SnippetCap {
17+
_private: (),
18+
}
19+
20+
impl 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+
}

crates/ra_ide/src/completion/completion_item.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::fmt;
44

5+
use super::completion_config::SnippetCap;
56
use hir::Documentation;
67
use ra_syntax::TextRange;
78
use ra_text_edit::TextEdit;
@@ -270,7 +271,11 @@ impl Builder {
270271
self.insert_text = Some(insert_text.into());
271272
self
272273
}
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 {
274279
self.insert_text_format = InsertTextFormat::Snippet;
275280
self.insert_text(snippet)
276281
}
@@ -282,7 +287,7 @@ impl Builder {
282287
self.text_edit = Some(edit);
283288
self
284289
}
285-
pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder {
290+
pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
286291
self.insert_text_format = InsertTextFormat::Snippet;
287292
self.text_edit(edit)
288293
}

crates/ra_ide/src/completion/presentation.rs

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,19 @@ impl Completions {
114114

115115
// Add `<>` for generic types
116116
if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
117-
let has_non_default_type_params = match resolution {
118-
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
119-
ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
120-
_ => false,
121-
};
122-
if has_non_default_type_params {
123-
tested_by!(inserts_angle_brackets_for_generics);
124-
completion_item = completion_item
125-
.lookup_by(local_name.clone())
126-
.label(format!("{}<…>", local_name))
127-
.insert_snippet(format!("{}<$0>", local_name));
117+
if let Some(cap) = ctx.config.snippet_cap {
118+
let has_non_default_type_params = match resolution {
119+
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
120+
ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
121+
_ => false,
122+
};
123+
if has_non_default_type_params {
124+
tested_by!(inserts_angle_brackets_for_generics);
125+
completion_item = completion_item
126+
.lookup_by(local_name.clone())
127+
.label(format!("{}<…>", local_name))
128+
.insert_snippet(cap, format!("{}<$0>", local_name));
129+
}
128130
}
129131
}
130132

@@ -184,13 +186,16 @@ impl Completions {
184186
.set_deprecated(is_deprecated(macro_, ctx.db))
185187
.detail(detail);
186188

187-
builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call {
188-
tested_by!(dont_insert_macro_call_parens_unncessary);
189-
builder.insert_text(name)
190-
} else {
191-
let macro_braces_to_insert =
192-
self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
193-
builder.insert_snippet(macro_declaration + macro_braces_to_insert)
189+
builder = match ctx.config.snippet_cap {
190+
Some(cap) if ctx.use_item_syntax.is_none() && !ctx.is_macro_call => {
191+
let macro_braces_to_insert =
192+
self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
193+
builder.insert_snippet(cap, macro_declaration + macro_braces_to_insert)
194+
}
195+
_ => {
196+
tested_by!(dont_insert_macro_call_parens_unncessary);
197+
builder.insert_text(name)
198+
}
194199
};
195200

196201
self.add(builder);
@@ -366,6 +371,10 @@ impl Builder {
366371
if ctx.use_item_syntax.is_some() || ctx.is_call {
367372
return self;
368373
}
374+
let cap = match ctx.config.snippet_cap {
375+
Some(it) => it,
376+
None => return self,
377+
};
369378
// If not an import, add parenthesis automatically.
370379
tested_by!(inserts_parens_for_function_calls);
371380

@@ -387,7 +396,7 @@ impl Builder {
387396

388397
(snippet, format!("{}(…)", name))
389398
};
390-
self.lookup_by(name).label(label).insert_snippet(snippet)
399+
self.lookup_by(name).label(label).insert_snippet(cap, snippet)
391400
}
392401
}
393402

crates/rust-analyzer/src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ impl Default for Config {
104104
enable_postfix_completions: true,
105105
add_call_parenthesis: true,
106106
add_call_argument_snippets: true,
107+
..CompletionConfig::default()
107108
},
108109
call_info_full: true,
109110
}

0 commit comments

Comments
 (0)