diff options
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_keyword.rs | 43 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/complete_path.rs | 14 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/complete_scope.rs | 38 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_context.rs | 22 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_item.rs | 29 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 3 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 3 |
7 files changed, 120 insertions, 32 deletions
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs index d1e0a20a8..2869e67e0 100644 --- a/crates/ra_analysis/src/completion/complete_keyword.rs +++ b/crates/ra_analysis/src/completion/complete_keyword.rs | |||
@@ -35,7 +35,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
35 | acc.add(keyword("continue", "continue")); | 35 | acc.add(keyword("continue", "continue")); |
36 | acc.add(keyword("break", "break")); | 36 | acc.add(keyword("break", "break")); |
37 | } | 37 | } |
38 | acc.add_all(complete_return(fn_def, ctx.is_stmt)); | 38 | acc.add_all(complete_return(fn_def, ctx.can_be_stmt)); |
39 | } | 39 | } |
40 | 40 | ||
41 | fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool { | 41 | fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool { |
@@ -57,8 +57,8 @@ fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool { | |||
57 | false | 57 | false |
58 | } | 58 | } |
59 | 59 | ||
60 | fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem> { | 60 | fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> { |
61 | let snip = match (is_stmt, fn_def.ret_type().is_some()) { | 61 | let snip = match (can_be_stmt, fn_def.ret_type().is_some()) { |
62 | (true, true) => "return $0;", | 62 | (true, true) => "return $0;", |
63 | (true, false) => "return;", | 63 | (true, false) => "return;", |
64 | (false, true) => "return $0", | 64 | (false, true) => "return $0", |
@@ -75,7 +75,7 @@ mod tests { | |||
75 | } | 75 | } |
76 | 76 | ||
77 | #[test] | 77 | #[test] |
78 | fn test_completion_kewords() { | 78 | fn completes_various_keywords_in_function() { |
79 | check_keyword_completion( | 79 | check_keyword_completion( |
80 | r" | 80 | r" |
81 | fn quux() { | 81 | fn quux() { |
@@ -87,13 +87,13 @@ mod tests { | |||
87 | match "match $0 {}" | 87 | match "match $0 {}" |
88 | while "while $0 {}" | 88 | while "while $0 {}" |
89 | loop "loop {$0}" | 89 | loop "loop {$0}" |
90 | return "return" | 90 | return "return;" |
91 | "#, | 91 | "#, |
92 | ); | 92 | ); |
93 | } | 93 | } |
94 | 94 | ||
95 | #[test] | 95 | #[test] |
96 | fn test_completion_else() { | 96 | fn completes_else_after_if() { |
97 | check_keyword_completion( | 97 | check_keyword_completion( |
98 | r" | 98 | r" |
99 | fn quux() { | 99 | fn quux() { |
@@ -109,7 +109,7 @@ mod tests { | |||
109 | loop "loop {$0}" | 109 | loop "loop {$0}" |
110 | else "else {$0}" | 110 | else "else {$0}" |
111 | else if "else if $0 {}" | 111 | else if "else if $0 {}" |
112 | return "return" | 112 | return "return;" |
113 | "#, | 113 | "#, |
114 | ); | 114 | ); |
115 | } | 115 | } |
@@ -149,7 +149,7 @@ mod tests { | |||
149 | } | 149 | } |
150 | 150 | ||
151 | #[test] | 151 | #[test] |
152 | fn test_completion_return_no_stmt() { | 152 | fn dont_add_semi_after_return_if_not_a_statement() { |
153 | check_keyword_completion( | 153 | check_keyword_completion( |
154 | r" | 154 | r" |
155 | fn quux() -> i32 { | 155 | fn quux() -> i32 { |
@@ -169,7 +169,27 @@ mod tests { | |||
169 | } | 169 | } |
170 | 170 | ||
171 | #[test] | 171 | #[test] |
172 | fn test_continue_break_completion() { | 172 | fn last_return_in_block_has_semi() { |
173 | check_keyword_completion( | ||
174 | r" | ||
175 | fn quux() -> i32 { | ||
176 | if condition { | ||
177 | <|> | ||
178 | } | ||
179 | } | ||
180 | ", | ||
181 | r#" | ||
182 | if "if $0 {}" | ||
183 | match "match $0 {}" | ||
184 | while "while $0 {}" | ||
185 | loop "loop {$0}" | ||
186 | return "return $0;" | ||
187 | "#, | ||
188 | ); | ||
189 | } | ||
190 | |||
191 | #[test] | ||
192 | fn completes_break_and_continue_in_loops() { | ||
173 | check_keyword_completion( | 193 | check_keyword_completion( |
174 | r" | 194 | r" |
175 | fn quux() -> i32 { | 195 | fn quux() -> i32 { |
@@ -183,9 +203,10 @@ mod tests { | |||
183 | loop "loop {$0}" | 203 | loop "loop {$0}" |
184 | continue "continue" | 204 | continue "continue" |
185 | break "break" | 205 | break "break" |
186 | return "return $0" | 206 | return "return $0;" |
187 | "#, | 207 | "#, |
188 | ); | 208 | ); |
209 | // No completion: lambda isolates control flow | ||
189 | check_keyword_completion( | 210 | check_keyword_completion( |
190 | r" | 211 | r" |
191 | fn quux() -> i32 { | 212 | fn quux() -> i32 { |
@@ -197,7 +218,7 @@ mod tests { | |||
197 | match "match $0 {}" | 218 | match "match $0 {}" |
198 | while "while $0 {}" | 219 | while "while $0 {}" |
199 | loop "loop {$0}" | 220 | loop "loop {$0}" |
200 | return "return $0" | 221 | return "return $0;" |
201 | "#, | 222 | "#, |
202 | ); | 223 | ); |
203 | } | 224 | } |
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index c73a083a4..4723a65a6 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs | |||
@@ -17,7 +17,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C | |||
17 | let module_scope = module.scope(ctx.db)?; | 17 | let module_scope = module.scope(ctx.db)?; |
18 | module_scope.entries().for_each(|(name, res)| { | 18 | module_scope.entries().for_each(|(name, res)| { |
19 | CompletionItem::new(CompletionKind::Reference, name.to_string()) | 19 | CompletionItem::new(CompletionKind::Reference, name.to_string()) |
20 | .from_resolution(ctx.db, res) | 20 | .from_resolution(ctx, res) |
21 | .add_to(acc) | 21 | .add_to(acc) |
22 | }); | 22 | }); |
23 | } | 23 | } |
@@ -113,4 +113,16 @@ mod tests { | |||
113 | "Foo;Bar", | 113 | "Foo;Bar", |
114 | ); | 114 | ); |
115 | } | 115 | } |
116 | |||
117 | #[test] | ||
118 | fn dont_render_function_parens_in_use_item() { | ||
119 | check_reference_completion( | ||
120 | " | ||
121 | //- /lib.rs | ||
122 | mod m { pub fn foo() {} } | ||
123 | use crate::m::f<|>; | ||
124 | ", | ||
125 | "foo", | ||
126 | ) | ||
127 | } | ||
116 | } | 128 | } |
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs index 514fd2f88..daf666505 100644 --- a/crates/ra_analysis/src/completion/complete_scope.rs +++ b/crates/ra_analysis/src/completion/complete_scope.rs | |||
@@ -34,7 +34,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> | |||
34 | }) | 34 | }) |
35 | .for_each(|(name, res)| { | 35 | .for_each(|(name, res)| { |
36 | CompletionItem::new(CompletionKind::Reference, name.to_string()) | 36 | CompletionItem::new(CompletionKind::Reference, name.to_string()) |
37 | .from_resolution(ctx.db, res) | 37 | .from_resolution(ctx, res) |
38 | .add_to(acc) | 38 | .add_to(acc) |
39 | }); | 39 | }); |
40 | Ok(()) | 40 | Ok(()) |
@@ -74,7 +74,7 @@ mod tests { | |||
74 | let z = (); | 74 | let z = (); |
75 | } | 75 | } |
76 | ", | 76 | ", |
77 | "y;x;quux", | 77 | r#"y;x;quux "quux($0)""#, |
78 | ); | 78 | ); |
79 | } | 79 | } |
80 | 80 | ||
@@ -92,7 +92,7 @@ mod tests { | |||
92 | } | 92 | } |
93 | } | 93 | } |
94 | ", | 94 | ", |
95 | "b;a;quux", | 95 | r#"b;a;quux "quux()$0""#, |
96 | ); | 96 | ); |
97 | } | 97 | } |
98 | 98 | ||
@@ -106,7 +106,7 @@ mod tests { | |||
106 | } | 106 | } |
107 | } | 107 | } |
108 | ", | 108 | ", |
109 | "x;quux", | 109 | r#"x;quux "quux()$0""#, |
110 | ); | 110 | ); |
111 | } | 111 | } |
112 | 112 | ||
@@ -120,7 +120,7 @@ mod tests { | |||
120 | <|> | 120 | <|> |
121 | } | 121 | } |
122 | ", | 122 | ", |
123 | "quux;Foo;Baz", | 123 | r#"quux "quux()$0";Foo;Baz"#, |
124 | ); | 124 | ); |
125 | } | 125 | } |
126 | 126 | ||
@@ -134,7 +134,7 @@ mod tests { | |||
134 | fn quux() { <|> } | 134 | fn quux() { <|> } |
135 | } | 135 | } |
136 | ", | 136 | ", |
137 | "quux;Bar", | 137 | r#"quux "quux()$0";Bar"#, |
138 | ); | 138 | ); |
139 | } | 139 | } |
140 | 140 | ||
@@ -145,12 +145,12 @@ mod tests { | |||
145 | struct Foo; | 145 | struct Foo; |
146 | fn x() -> <|> | 146 | fn x() -> <|> |
147 | ", | 147 | ", |
148 | "Foo;x", | 148 | r#"Foo;x "x()$0""#, |
149 | ) | 149 | ) |
150 | } | 150 | } |
151 | 151 | ||
152 | #[test] | 152 | #[test] |
153 | fn dont_show_to_completions_for_shadowing() { | 153 | fn dont_show_both_completions_for_shadowing() { |
154 | check_reference_completion( | 154 | check_reference_completion( |
155 | r" | 155 | r" |
156 | fn foo() -> { | 156 | fn foo() -> { |
@@ -161,7 +161,7 @@ mod tests { | |||
161 | } | 161 | } |
162 | } | 162 | } |
163 | ", | 163 | ", |
164 | "bar;foo", | 164 | r#"bar;foo "foo()$0""#, |
165 | ) | 165 | ) |
166 | } | 166 | } |
167 | 167 | ||
@@ -169,4 +169,24 @@ mod tests { | |||
169 | fn completes_self_in_methods() { | 169 | fn completes_self_in_methods() { |
170 | check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") | 170 | check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") |
171 | } | 171 | } |
172 | |||
173 | #[test] | ||
174 | fn inserts_parens_for_function_calls() { | ||
175 | check_reference_completion( | ||
176 | r" | ||
177 | fn no_args() {} | ||
178 | fn main() { no_<|> } | ||
179 | ", | ||
180 | r#"no_args "no_args()$0" | ||
181 | main "main()$0""#, | ||
182 | ); | ||
183 | check_reference_completion( | ||
184 | r" | ||
185 | fn with_args(x: i32, y: String) {} | ||
186 | fn main() { with_<|> } | ||
187 | ", | ||
188 | r#"main "main()$0" | ||
189 | with_args "with_args($0)""#, | ||
190 | ); | ||
191 | } | ||
172 | } | 192 | } |
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 71bf7fd32..4685c9328 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs | |||
@@ -24,13 +24,15 @@ pub(super) struct CompletionContext<'a> { | |||
24 | pub(super) module: Option<hir::Module>, | 24 | pub(super) module: Option<hir::Module>, |
25 | pub(super) function: Option<hir::Function>, | 25 | pub(super) function: Option<hir::Function>, |
26 | pub(super) function_syntax: Option<ast::FnDef<'a>>, | 26 | pub(super) function_syntax: Option<ast::FnDef<'a>>, |
27 | pub(super) use_item_syntax: Option<ast::UseItem<'a>>, | ||
27 | pub(super) is_param: bool, | 28 | pub(super) is_param: bool, |
28 | /// A single-indent path, like `foo`. | 29 | /// A single-indent path, like `foo`. |
29 | pub(super) is_trivial_path: bool, | 30 | pub(super) is_trivial_path: bool, |
30 | /// If not a trivial, path, the prefix (qualifier). | 31 | /// If not a trivial, path, the prefix (qualifier). |
31 | pub(super) path_prefix: Option<hir::Path>, | 32 | pub(super) path_prefix: Option<hir::Path>, |
32 | pub(super) after_if: bool, | 33 | pub(super) after_if: bool, |
33 | pub(super) is_stmt: bool, | 34 | /// `true` if we are a statement or a last expr in the block. |
35 | pub(super) can_be_stmt: bool, | ||
34 | /// Something is typed at the "top" level, in module or impl/trait. | 36 | /// Something is typed at the "top" level, in module or impl/trait. |
35 | pub(super) is_new_item: bool, | 37 | pub(super) is_new_item: bool, |
36 | /// The receiver if this is a field or method access, i.e. writing something.<|> | 38 | /// The receiver if this is a field or method access, i.e. writing something.<|> |
@@ -55,11 +57,12 @@ impl<'a> CompletionContext<'a> { | |||
55 | module, | 57 | module, |
56 | function: None, | 58 | function: None, |
57 | function_syntax: None, | 59 | function_syntax: None, |
60 | use_item_syntax: None, | ||
58 | is_param: false, | 61 | is_param: false, |
59 | is_trivial_path: false, | 62 | is_trivial_path: false, |
60 | path_prefix: None, | 63 | path_prefix: None, |
61 | after_if: false, | 64 | after_if: false, |
62 | is_stmt: false, | 65 | can_be_stmt: false, |
63 | is_new_item: false, | 66 | is_new_item: false, |
64 | dot_receiver: None, | 67 | dot_receiver: None, |
65 | is_method_call: false, | 68 | is_method_call: false, |
@@ -114,6 +117,8 @@ impl<'a> CompletionContext<'a> { | |||
114 | _ => (), | 117 | _ => (), |
115 | } | 118 | } |
116 | 119 | ||
120 | self.use_item_syntax = self.leaf.ancestors().find_map(ast::UseItem::cast); | ||
121 | |||
117 | self.function_syntax = self | 122 | self.function_syntax = self |
118 | .leaf | 123 | .leaf |
119 | .ancestors() | 124 | .ancestors() |
@@ -143,13 +148,22 @@ impl<'a> CompletionContext<'a> { | |||
143 | if path.qualifier().is_none() { | 148 | if path.qualifier().is_none() { |
144 | self.is_trivial_path = true; | 149 | self.is_trivial_path = true; |
145 | 150 | ||
146 | self.is_stmt = match name_ref | 151 | self.can_be_stmt = match name_ref |
147 | .syntax() | 152 | .syntax() |
148 | .ancestors() | 153 | .ancestors() |
149 | .filter_map(ast::ExprStmt::cast) | 154 | .filter_map(ast::ExprStmt::cast) |
150 | .next() | 155 | .next() |
151 | { | 156 | { |
152 | None => false, | 157 | None => { |
158 | name_ref | ||
159 | .syntax() | ||
160 | .ancestors() | ||
161 | .filter_map(ast::Block::cast) | ||
162 | .next() | ||
163 | .and_then(|block| block.expr()) | ||
164 | .map(|e| e.syntax().range()) | ||
165 | == Some(name_ref.syntax().range()) | ||
166 | } | ||
153 | Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(), | 167 | Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(), |
154 | }; | 168 | }; |
155 | 169 | ||
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 1d294c553..cd4d529f9 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use crate::db; | ||
2 | |||
3 | use hir::PerNs; | 1 | use hir::PerNs; |
4 | 2 | ||
3 | use crate::completion::CompletionContext; | ||
4 | |||
5 | /// `CompletionItem` describes a single completion variant in the editor pop-up. | 5 | /// `CompletionItem` describes a single completion variant in the editor pop-up. |
6 | /// It is basically a POD with various properties. To construct a | 6 | /// It is basically a POD with various properties. To construct a |
7 | /// `CompletionItem`, use `new` method and the `Builder` struct. | 7 | /// `CompletionItem`, use `new` method and the `Builder` struct. |
@@ -118,12 +118,12 @@ impl Builder { | |||
118 | self.kind = Some(kind); | 118 | self.kind = Some(kind); |
119 | self | 119 | self |
120 | } | 120 | } |
121 | pub(crate) fn from_resolution( | 121 | pub(super) fn from_resolution( |
122 | mut self, | 122 | mut self, |
123 | db: &db::RootDatabase, | 123 | ctx: &CompletionContext, |
124 | resolution: &hir::Resolution, | 124 | resolution: &hir::Resolution, |
125 | ) -> Builder { | 125 | ) -> Builder { |
126 | let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); | 126 | let resolved = resolution.def_id.and_then(|d| d.resolve(ctx.db).ok()); |
127 | let kind = match resolved { | 127 | let kind = match resolved { |
128 | PerNs { | 128 | PerNs { |
129 | types: Some(hir::Def::Module(..)), | 129 | types: Some(hir::Def::Module(..)), |
@@ -138,14 +138,29 @@ impl Builder { | |||
138 | .. | 138 | .. |
139 | } => CompletionItemKind::Enum, | 139 | } => CompletionItemKind::Enum, |
140 | PerNs { | 140 | PerNs { |
141 | values: Some(hir::Def::Function(..)), | 141 | values: Some(hir::Def::Function(function)), |
142 | .. | 142 | .. |
143 | } => CompletionItemKind::Function, | 143 | } => return self.from_function(ctx, function), |
144 | _ => return self, | 144 | _ => return self, |
145 | }; | 145 | }; |
146 | self.kind = Some(kind); | 146 | self.kind = Some(kind); |
147 | self | 147 | self |
148 | } | 148 | } |
149 | |||
150 | fn from_function(mut self, ctx: &CompletionContext, function: hir::Function) -> Builder { | ||
151 | // If not an import, add parenthesis automatically. | ||
152 | if ctx.use_item_syntax.is_none() { | ||
153 | if let Some(sig_info) = function.signature_info(ctx.db) { | ||
154 | if sig_info.params.is_empty() { | ||
155 | self.snippet = Some(format!("{}()$0", self.label)); | ||
156 | } else { | ||
157 | self.snippet = Some(format!("{}($0)", self.label)); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | self.kind = Some(CompletionItemKind::Function); | ||
162 | self | ||
163 | } | ||
149 | } | 164 | } |
150 | 165 | ||
151 | impl Into<CompletionItem> for Builder { | 166 | impl Into<CompletionItem> for Builder { |
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index fcb4cd957..bff2e00c9 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -140,6 +140,9 @@ impl fmt::Debug for AnalysisImpl { | |||
140 | } | 140 | } |
141 | 141 | ||
142 | impl AnalysisImpl { | 142 | impl AnalysisImpl { |
143 | pub fn file_text(&self, file_id: FileId) -> Arc<String> { | ||
144 | self.db.file_text(file_id) | ||
145 | } | ||
143 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { | 146 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { |
144 | self.db.source_file(file_id) | 147 | self.db.source_file(file_id) |
145 | } | 148 | } |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 67b1c1482..9f5e9f358 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -274,6 +274,9 @@ pub struct Analysis { | |||
274 | } | 274 | } |
275 | 275 | ||
276 | impl Analysis { | 276 | impl Analysis { |
277 | pub fn file_text(&self, file_id: FileId) -> Arc<String> { | ||
278 | self.imp.file_text(file_id) | ||
279 | } | ||
277 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { | 280 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { |
278 | self.imp.file_syntax(file_id).clone() | 281 | self.imp.file_syntax(file_id).clone() |
279 | } | 282 | } |