aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-01-15 12:51:20 +0000
committerGitHub <[email protected]>2021-01-15 12:51:20 +0000
commitd6a708b1eae170aee4a323ea6513bc4f2a1a5bbc (patch)
treee5b5f19b0207028a0ac99d7b211d843cba6f9b8c
parent054e2061521292a72748510f3f6cb7c8b1e8611b (diff)
parentf2ba2048d1afb816623d037f265f4445a2f44b54 (diff)
Merge #7281
7281: Insert `;` when completing keywords in let r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/completion/src/completions/keyword.rs106
-rw-r--r--crates/completion/src/context.rs18
2 files changed, 89 insertions, 35 deletions
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
index 425a688ff..c1af348dc 100644
--- a/crates/completion/src/completions/keyword.rs
+++ b/crates/completion/src/completions/keyword.rs
@@ -1,6 +1,6 @@
1//! Completes keywords. 1//! Completes keywords.
2 2
3use syntax::{ast, SyntaxKind}; 3use syntax::SyntaxKind;
4use test_utils::mark; 4use test_utils::mark;
5 5
6use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 6use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
@@ -86,8 +86,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
86 add_keyword(ctx, acc, "match", "match $0 {}"); 86 add_keyword(ctx, acc, "match", "match $0 {}");
87 add_keyword(ctx, acc, "while", "while $0 {}"); 87 add_keyword(ctx, acc, "while", "while $0 {}");
88 add_keyword(ctx, acc, "loop", "loop {$0}"); 88 add_keyword(ctx, acc, "loop", "loop {$0}");
89 add_keyword(ctx, acc, "if", "if "); 89 add_keyword(ctx, acc, "if", "if $0 {}");
90 add_keyword(ctx, acc, "if let", "if let "); 90 add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
91 } 91 }
92 92
93 if ctx.if_is_prev || ctx.block_expr_parent { 93 if ctx.if_is_prev || ctx.block_expr_parent {
@@ -143,47 +143,49 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
143 Some(it) => it, 143 Some(it) => it,
144 None => return, 144 None => return,
145 }; 145 };
146 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
147}
148
149fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
150 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
151 .kind(CompletionItemKind::Keyword);
152 146
153 match ctx.config.snippet_cap { 147 add_keyword(
154 Some(cap) => res.insert_snippet(cap, snippet), 148 ctx,
155 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }), 149 acc,
156 } 150 "return",
157 .build() 151 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
152 (true, true) => "return $0;",
153 (true, false) => "return;",
154 (false, true) => "return $0",
155 (false, false) => "return",
156 },
157 )
158} 158}
159 159
160fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 160fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
161 acc.add(keyword(ctx, kw, snippet)); 161 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
162} 162 .kind(CompletionItemKind::Keyword);
163 163 let builder = match ctx.config.snippet_cap {
164fn complete_return( 164 Some(cap) => {
165 ctx: &CompletionContext, 165 let tmp;
166 fn_def: &ast::Fn, 166 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
167 can_be_stmt: bool, 167 mark::hit!(let_semi);
168) -> Option<CompletionItem> { 168 tmp = format!("{};", snippet);
169 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) { 169 &tmp
170 (true, true) => "return $0;", 170 } else {
171 (true, false) => "return;", 171 snippet
172 (false, true) => "return $0", 172 };
173 (false, false) => "return", 173 builder.insert_snippet(cap, snippet)
174 }
175 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
174 }; 176 };
175 Some(keyword(ctx, "return", snip)) 177 acc.add(builder.build());
176} 178}
177 179
178#[cfg(test)] 180#[cfg(test)]
179mod tests { 181mod tests {
180 use expect_test::{expect, Expect}; 182 use expect_test::{expect, Expect};
183 use test_utils::mark;
181 184
182 use crate::{ 185 use crate::{
183 test_utils::{check_edit, completion_list}, 186 test_utils::{check_edit, completion_list},
184 CompletionKind, 187 CompletionKind,
185 }; 188 };
186 use test_utils::mark;
187 189
188 fn check(ra_fixture: &str, expect: Expect) { 190 fn check(ra_fixture: &str, expect: Expect) {
189 let actual = completion_list(ra_fixture, CompletionKind::Keyword); 191 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
@@ -609,4 +611,50 @@ fn foo() {
609 "#]], 611 "#]],
610 ); 612 );
611 } 613 }
614
615 #[test]
616 fn let_semi() {
617 mark::check!(let_semi);
618 check_edit(
619 "match",
620 r#"
621fn main() { let x = $0 }
622"#,
623 r#"
624fn main() { let x = match $0 {}; }
625"#,
626 );
627
628 check_edit(
629 "if",
630 r#"
631fn main() {
632 let x = $0
633 let y = 92;
634}
635"#,
636 r#"
637fn main() {
638 let x = if $0 {};
639 let y = 92;
640}
641"#,
642 );
643
644 check_edit(
645 "loop",
646 r#"
647fn main() {
648 let x = $0
649 bar();
650}
651"#,
652 r#"
653fn main() {
654 let x = loop {$0};
655 bar();
656}
657"#,
658 );
659 }
612} 660}
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index ebf28e887..d809460e2 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -92,6 +92,7 @@ pub(crate) struct CompletionContext<'a> {
92 pub(super) has_item_list_or_source_file_parent: bool, 92 pub(super) has_item_list_or_source_file_parent: bool,
93 pub(super) for_is_prev2: bool, 93 pub(super) for_is_prev2: bool,
94 pub(super) fn_is_prev: bool, 94 pub(super) fn_is_prev: bool,
95 pub(super) incomplete_let: bool,
95 pub(super) locals: Vec<(String, Local)>, 96 pub(super) locals: Vec<(String, Local)>,
96} 97}
97 98
@@ -132,9 +133,9 @@ impl<'a> CompletionContext<'a> {
132 scope, 133 scope,
133 db, 134 db,
134 config, 135 config,
136 position,
135 original_token, 137 original_token,
136 token, 138 token,
137 position,
138 krate, 139 krate,
139 expected_type: None, 140 expected_type: None,
140 name_ref_syntax: None, 141 name_ref_syntax: None,
@@ -155,30 +156,31 @@ impl<'a> CompletionContext<'a> {
155 is_expr: false, 156 is_expr: false,
156 is_new_item: false, 157 is_new_item: false,
157 dot_receiver: None, 158 dot_receiver: None,
159 dot_receiver_is_ambiguous_float_literal: false,
158 is_call: false, 160 is_call: false,
159 is_pattern_call: false, 161 is_pattern_call: false,
160 is_macro_call: false, 162 is_macro_call: false,
161 is_path_type: false, 163 is_path_type: false,
162 has_type_args: false, 164 has_type_args: false,
163 dot_receiver_is_ambiguous_float_literal: false,
164 attribute_under_caret: None, 165 attribute_under_caret: None,
165 mod_declaration_under_caret: None, 166 mod_declaration_under_caret: None,
166 unsafe_is_prev: false, 167 unsafe_is_prev: false,
167 in_loop_body: false, 168 if_is_prev: false,
168 ref_pat_parent: false,
169 bind_pat_parent: false,
170 block_expr_parent: false, 169 block_expr_parent: false,
170 bind_pat_parent: false,
171 ref_pat_parent: false,
172 in_loop_body: false,
171 has_trait_parent: false, 173 has_trait_parent: false,
172 has_impl_parent: false, 174 has_impl_parent: false,
173 inside_impl_trait_block: false, 175 inside_impl_trait_block: false,
174 has_field_list_parent: false, 176 has_field_list_parent: false,
175 trait_as_prev_sibling: false, 177 trait_as_prev_sibling: false,
176 impl_as_prev_sibling: false, 178 impl_as_prev_sibling: false,
177 if_is_prev: false,
178 is_match_arm: false, 179 is_match_arm: false,
179 has_item_list_or_source_file_parent: false, 180 has_item_list_or_source_file_parent: false,
180 for_is_prev2: false, 181 for_is_prev2: false,
181 fn_is_prev: false, 182 fn_is_prev: false,
183 incomplete_let: false,
182 locals, 184 locals,
183 }; 185 };
184 186
@@ -270,6 +272,10 @@ impl<'a> CompletionContext<'a> {
270 .filter(|module| module.item_list().is_none()); 272 .filter(|module| module.item_list().is_none());
271 self.for_is_prev2 = for_is_prev2(syntax_element.clone()); 273 self.for_is_prev2 = for_is_prev2(syntax_element.clone());
272 self.fn_is_prev = fn_is_prev(syntax_element.clone()); 274 self.fn_is_prev = fn_is_prev(syntax_element.clone());
275 self.incomplete_let =
276 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
277 it.syntax().text_range().end() == syntax_element.text_range().end()
278 });
273 } 279 }
274 280
275 fn fill( 281 fn fill(