aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs214
-rw-r--r--crates/ra_ide/src/call_info.rs549
-rw-r--r--crates/ra_ide/src/completion.rs91
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs861
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs994
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs131
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs1102
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs137
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs126
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs639
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs1506
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs546
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs106
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs464
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs1615
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs83
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs32
-rw-r--r--crates/ra_ide/src/completion/patterns.rs194
-rw-r--r--crates/ra_ide/src/completion/presentation.rs1639
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs111
-rw-r--r--crates/ra_ide/src/diagnostics.rs880
-rw-r--r--crates/ra_ide/src/display.rs29
-rw-r--r--crates/ra_ide/src/display/function_signature.rs120
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs134
-rw-r--r--crates/ra_ide/src/display/structure.rs429
-rw-r--r--crates/ra_ide/src/expand_macro.rs241
-rw-r--r--crates/ra_ide/src/extend_selection.rs12
-rw-r--r--crates/ra_ide/src/folding_ranges.rs174
-rw-r--r--crates/ra_ide/src/goto_definition.rs1164
-rw-r--r--crates/ra_ide/src/goto_implementation.rs197
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs132
-rw-r--r--crates/ra_ide/src/hover.rs2625
-rw-r--r--crates/ra_ide/src/inlay_hints.rs992
-rw-r--r--crates/ra_ide/src/join_lines.rs5
-rw-r--r--crates/ra_ide/src/lib.rs87
-rw-r--r--crates/ra_ide/src/markup.rs38
-rw-r--r--crates/ra_ide/src/matching_brace.rs33
-rw-r--r--crates/ra_ide/src/mock_analysis.rs214
-rw-r--r--crates/ra_ide/src/parent_module.rs13
-rw-r--r--crates/ra_ide/src/prime_caches.rs2
-rw-r--r--crates/ra_ide/src/references.rs515
-rw-r--r--crates/ra_ide/src/references/rename.rs1210
-rw-r--r--crates/ra_ide/src/runnables.rs898
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html41
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html84
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html101
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html42
-rw-r--r--crates/ra_ide/src/ssr.rs618
-rw-r--r--crates/ra_ide/src/status.rs28
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs333
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs188
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs18
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs201
-rw-r--r--crates/ra_ide/src/syntax_tree.rs12
-rw-r--r--crates/ra_ide/src/typing.rs44
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs15
57 files changed, 10368 insertions, 12652 deletions
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index defd8176f..cb7e62cd6 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -39,10 +39,11 @@ pub(crate) fn call_hierarchy(
39 39
40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { 40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
41 let sema = Semantics::new(db); 41 let sema = Semantics::new(db);
42
42 // 1. Find all refs 43 // 1. Find all refs
43 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. 44 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
44 // 3. Add ranges relative to the start of the fndef. 45 // 3. Add ranges relative to the start of the fndef.
45 let refs = references::find_all_refs(db, position, None)?; 46 let refs = references::find_all_refs(&sema, position, None)?;
46 47
47 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
48 49
@@ -96,7 +97,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
96 //FIXME: Type::as_callable is broken 97 //FIXME: Type::as_callable is broken
97 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; 98 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
98 match callable_def { 99 match callable_def {
99 hir::CallableDef::FunctionId(it) => { 100 hir::CallableDefId::FunctionId(it) => {
100 let fn_def: hir::Function = it.into(); 101 let fn_def: hir::Function = it.into();
101 let nav = fn_def.to_nav(db); 102 let nav = fn_def.to_nav(db);
102 Some(nav) 103 Some(nav)
@@ -145,12 +146,12 @@ mod tests {
145 use crate::mock_analysis::analysis_and_position; 146 use crate::mock_analysis::analysis_and_position;
146 147
147 fn check_hierarchy( 148 fn check_hierarchy(
148 fixture: &str, 149 ra_fixture: &str,
149 expected: &str, 150 expected: &str,
150 expected_incoming: &[&str], 151 expected_incoming: &[&str],
151 expected_outgoing: &[&str], 152 expected_outgoing: &[&str],
152 ) { 153 ) {
153 let (analysis, pos) = analysis_and_position(fixture); 154 let (analysis, pos) = analysis_and_position(ra_fixture);
154 155
155 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; 156 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
156 assert_eq!(navs.len(), 1); 157 assert_eq!(navs.len(), 1);
@@ -177,12 +178,12 @@ mod tests {
177 fn test_call_hierarchy_on_ref() { 178 fn test_call_hierarchy_on_ref() {
178 check_hierarchy( 179 check_hierarchy(
179 r#" 180 r#"
180 //- /lib.rs 181//- /lib.rs
181 fn callee() {} 182fn callee() {}
182 fn caller() { 183fn caller() {
183 call<|>ee(); 184 call<|>ee();
184 } 185}
185 "#, 186"#,
186 "callee FN_DEF FileId(1) 0..14 3..9", 187 "callee FN_DEF FileId(1) 0..14 3..9",
187 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"], 188 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"],
188 &[], 189 &[],
@@ -193,12 +194,12 @@ mod tests {
193 fn test_call_hierarchy_on_def() { 194 fn test_call_hierarchy_on_def() {
194 check_hierarchy( 195 check_hierarchy(
195 r#" 196 r#"
196 //- /lib.rs 197//- /lib.rs
197 fn call<|>ee() {} 198fn call<|>ee() {}
198 fn caller() { 199fn caller() {
199 callee(); 200 callee();
200 } 201}
201 "#, 202"#,
202 "callee FN_DEF FileId(1) 0..14 3..9", 203 "callee FN_DEF FileId(1) 0..14 3..9",
203 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"], 204 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"],
204 &[], 205 &[],
@@ -209,13 +210,13 @@ mod tests {
209 fn test_call_hierarchy_in_same_fn() { 210 fn test_call_hierarchy_in_same_fn() {
210 check_hierarchy( 211 check_hierarchy(
211 r#" 212 r#"
212 //- /lib.rs 213//- /lib.rs
213 fn callee() {} 214fn callee() {}
214 fn caller() { 215fn caller() {
215 call<|>ee(); 216 call<|>ee();
216 callee(); 217 callee();
217 } 218}
218 "#, 219"#,
219 "callee FN_DEF FileId(1) 0..14 3..9", 220 "callee FN_DEF FileId(1) 0..14 3..9",
220 &["caller FN_DEF FileId(1) 15..58 18..24 : [33..39, 47..53]"], 221 &["caller FN_DEF FileId(1) 15..58 18..24 : [33..39, 47..53]"],
221 &[], 222 &[],
@@ -226,20 +227,20 @@ mod tests {
226 fn test_call_hierarchy_in_different_fn() { 227 fn test_call_hierarchy_in_different_fn() {
227 check_hierarchy( 228 check_hierarchy(
228 r#" 229 r#"
229 //- /lib.rs 230//- /lib.rs
230 fn callee() {} 231fn callee() {}
231 fn caller1() { 232fn caller1() {
232 call<|>ee(); 233 call<|>ee();
233 } 234}
234 235
235 fn caller2() { 236fn caller2() {
236 callee(); 237 callee();
237 } 238}
238 "#, 239"#,
239 "callee FN_DEF FileId(1) 0..14 3..9", 240 "callee FN_DEF FileId(1) 0..14 3..9",
240 &[ 241 &[
241 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", 242 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
242 "caller2 FN_DEF FileId(1) 46..76 49..56 : [65..71]", 243 "caller2 FN_DEF FileId(1) 47..77 50..57 : [66..72]",
243 ], 244 ],
244 &[], 245 &[],
245 ); 246 );
@@ -249,26 +250,26 @@ mod tests {
249 fn test_call_hierarchy_in_tests_mod() { 250 fn test_call_hierarchy_in_tests_mod() {
250 check_hierarchy( 251 check_hierarchy(
251 r#" 252 r#"
252 //- /lib.rs cfg:test 253//- /lib.rs cfg:test
253 fn callee() {} 254fn callee() {}
254 fn caller1() { 255fn caller1() {
255 call<|>ee(); 256 call<|>ee();
256 } 257}
257 258
258 #[cfg(test)] 259#[cfg(test)]
259 mod tests { 260mod tests {
260 use super::*; 261 use super::*;
261 262
262 #[test] 263 #[test]
263 fn test_caller() { 264 fn test_caller() {
264 callee(); 265 callee();
265 } 266 }
266 } 267}
267 "#, 268"#,
268 "callee FN_DEF FileId(1) 0..14 3..9", 269 "callee FN_DEF FileId(1) 0..14 3..9",
269 &[ 270 &[
270 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", 271 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
271 "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]", 272 "test_caller FN_DEF FileId(1) 95..149 110..121 : [134..140]",
272 ], 273 ],
273 &[], 274 &[],
274 ); 275 );
@@ -278,19 +279,19 @@ mod tests {
278 fn test_call_hierarchy_in_different_files() { 279 fn test_call_hierarchy_in_different_files() {
279 check_hierarchy( 280 check_hierarchy(
280 r#" 281 r#"
281 //- /lib.rs 282//- /lib.rs
282 mod foo; 283mod foo;
283 use foo::callee; 284use foo::callee;
284 285
285 fn caller() { 286fn caller() {
286 call<|>ee(); 287 call<|>ee();
287 } 288}
288 289
289 //- /foo/mod.rs 290//- /foo/mod.rs
290 pub fn callee() {} 291pub fn callee() {}
291 "#, 292"#,
292 "callee FN_DEF FileId(2) 0..18 7..13", 293 "callee FN_DEF FileId(2) 0..18 7..13",
293 &["caller FN_DEF FileId(1) 26..55 29..35 : [44..50]"], 294 &["caller FN_DEF FileId(1) 27..56 30..36 : [45..51]"],
294 &[], 295 &[],
295 ); 296 );
296 } 297 }
@@ -299,13 +300,13 @@ mod tests {
299 fn test_call_hierarchy_outgoing() { 300 fn test_call_hierarchy_outgoing() {
300 check_hierarchy( 301 check_hierarchy(
301 r#" 302 r#"
302 //- /lib.rs 303//- /lib.rs
303 fn callee() {} 304fn callee() {}
304 fn call<|>er() { 305fn call<|>er() {
305 callee(); 306 callee();
306 callee(); 307 callee();
307 } 308}
308 "#, 309"#,
309 "caller FN_DEF FileId(1) 15..58 18..24", 310 "caller FN_DEF FileId(1) 15..58 18..24",
310 &[], 311 &[],
311 &["callee FN_DEF FileId(1) 0..14 3..9 : [33..39, 47..53]"], 312 &["callee FN_DEF FileId(1) 0..14 3..9 : [33..39, 47..53]"],
@@ -316,20 +317,20 @@ mod tests {
316 fn test_call_hierarchy_outgoing_in_different_files() { 317 fn test_call_hierarchy_outgoing_in_different_files() {
317 check_hierarchy( 318 check_hierarchy(
318 r#" 319 r#"
319 //- /lib.rs 320//- /lib.rs
320 mod foo; 321mod foo;
321 use foo::callee; 322use foo::callee;
322 323
323 fn call<|>er() { 324fn call<|>er() {
324 callee(); 325 callee();
325 } 326}
326 327
327 //- /foo/mod.rs 328//- /foo/mod.rs
328 pub fn callee() {} 329pub fn callee() {}
329 "#, 330"#,
330 "caller FN_DEF FileId(1) 26..55 29..35", 331 "caller FN_DEF FileId(1) 27..56 30..36",
331 &[], 332 &[],
332 &["callee FN_DEF FileId(2) 0..18 7..13 : [44..50]"], 333 &["callee FN_DEF FileId(2) 0..18 7..13 : [45..51]"],
333 ); 334 );
334 } 335 }
335 336
@@ -337,22 +338,59 @@ mod tests {
337 fn test_call_hierarchy_incoming_outgoing() { 338 fn test_call_hierarchy_incoming_outgoing() {
338 check_hierarchy( 339 check_hierarchy(
339 r#" 340 r#"
340 //- /lib.rs 341//- /lib.rs
341 fn caller1() { 342fn caller1() {
342 call<|>er2(); 343 call<|>er2();
343 } 344}
344 345
345 fn caller2() { 346fn caller2() {
346 caller3(); 347 caller3();
347 } 348}
348 349
349 fn caller3() { 350fn caller3() {
350 351
351 } 352}
352 "#, 353"#,
353 "caller2 FN_DEF FileId(1) 32..63 35..42", 354 "caller2 FN_DEF FileId(1) 33..64 36..43",
354 &["caller1 FN_DEF FileId(1) 0..31 3..10 : [19..26]"], 355 &["caller1 FN_DEF FileId(1) 0..31 3..10 : [19..26]"],
355 &["caller3 FN_DEF FileId(1) 64..80 67..74 : [51..58]"], 356 &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"],
357 );
358 }
359
360 #[test]
361 fn test_call_hierarchy_issue_5103() {
362 check_hierarchy(
363 r#"
364fn a() {
365 b()
366}
367
368fn b() {}
369
370fn main() {
371 a<|>()
372}
373"#,
374 "a FN_DEF FileId(1) 0..18 3..4",
375 &["main FN_DEF FileId(1) 31..52 34..38 : [47..48]"],
376 &["b FN_DEF FileId(1) 20..29 23..24 : [13..14]"],
377 );
378
379 check_hierarchy(
380 r#"
381fn a() {
382 b<|>()
383}
384
385fn b() {}
386
387fn main() {
388 a()
389}
390"#,
391 "b FN_DEF FileId(1) 20..29 23..24",
392 &["a FN_DEF FileId(1) 0..18 3..4 : [13..14]"],
393 &[],
356 ); 394 );
357 } 395 }
358} 396}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index aa039e6fc..1fe1c21de 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -7,7 +7,15 @@ use ra_syntax::{
7}; 7};
8use test_utils::mark; 8use test_utils::mark;
9 9
10use crate::{CallInfo, FilePosition, FunctionSignature}; 10use crate::{FilePosition, FunctionSignature};
11
12/// Contains information about a call site. Specifically the
13/// `FunctionSignature`and current parameter.
14#[derive(Debug)]
15pub struct CallInfo {
16 pub signature: FunctionSignature,
17 pub active_parameter: Option<usize>,
18}
11 19
12/// Computes parameter information for the given call expression. 20/// Computes parameter information for the given call expression.
13pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 21pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
@@ -40,43 +48,40 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
40 // Find the calling expression and it's NameRef 48 // Find the calling expression and it's NameRef
41 let calling_node = FnCallNode::with_node(&token.parent())?; 49 let calling_node = FnCallNode::with_node(&token.parent())?;
42 50
43 let (mut call_info, has_self) = match &calling_node { 51 let signature = match &calling_node {
44 FnCallNode::CallExpr(call) => { 52 FnCallNode::CallExpr(call) => {
45 //FIXME: Type::as_callable is broken 53 //FIXME: Type::as_callable is broken
46 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; 54 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
47 match callable_def { 55 match callable_def {
48 hir::CallableDef::FunctionId(it) => { 56 hir::CallableDefId::FunctionId(it) => {
49 let fn_def = it.into(); 57 let fn_def = it.into();
50 (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db)) 58 FunctionSignature::from_hir(sema.db, fn_def)
51 } 59 }
52 hir::CallableDef::StructId(it) => { 60 hir::CallableDefId::StructId(it) => {
53 (CallInfo::with_struct(sema.db, it.into())?, false) 61 FunctionSignature::from_struct(sema.db, it.into())?
54 } 62 }
55 hir::CallableDef::EnumVariantId(it) => { 63 hir::CallableDefId::EnumVariantId(it) => {
56 (CallInfo::with_enum_variant(sema.db, it.into())?, false) 64 FunctionSignature::from_enum_variant(sema.db, it.into())?
57 } 65 }
58 } 66 }
59 } 67 }
60 FnCallNode::MethodCallExpr(method_call) => { 68 FnCallNode::MethodCallExpr(method_call) => {
61 let function = sema.resolve_method_call(&method_call)?; 69 let function = sema.resolve_method_call(&method_call)?;
62 (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db)) 70 FunctionSignature::from_hir(sema.db, function)
63 } 71 }
64 FnCallNode::MacroCallExpr(macro_call) => { 72 FnCallNode::MacroCallExpr(macro_call) => {
65 let macro_def = sema.resolve_macro_call(&macro_call)?; 73 let macro_def = sema.resolve_macro_call(&macro_call)?;
66 (CallInfo::with_macro(sema.db, macro_def)?, false) 74 FunctionSignature::from_macro(sema.db, macro_def)?
67 } 75 }
68 }; 76 };
69 77
70 // If we have a calling expression let's find which argument we are on 78 // If we have a calling expression let's find which argument we are on
71 let num_params = call_info.parameters().len(); 79 let num_params = signature.parameters.len();
72 80
73 match num_params { 81 let active_parameter = match num_params {
74 0 => (), 82 0 => None,
75 1 => { 83 1 if signature.has_self_param => None,
76 if !has_self { 84 1 => Some(0),
77 call_info.active_parameter = Some(0);
78 }
79 }
80 _ => { 85 _ => {
81 if let Some(arg_list) = calling_node.arg_list() { 86 if let Some(arg_list) = calling_node.arg_list() {
82 // Number of arguments specified at the call site 87 // Number of arguments specified at the call site
@@ -93,22 +98,24 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
93 arg_list 98 arg_list
94 .args() 99 .args()
95 .take_while(|arg| { 100 .take_while(|arg| {
96 arg.syntax().text_range().end() < token.text_range().start() 101 arg.syntax().text_range().end() <= token.text_range().start()
97 }) 102 })
98 .count(), 103 .count(),
99 ); 104 );
100 105
101 // If we are in a method account for `self` 106 // If we are in a method account for `self`
102 if has_self { 107 if signature.has_self_param {
103 param += 1; 108 param += 1;
104 } 109 }
105 110
106 call_info.active_parameter = Some(param); 111 Some(param)
112 } else {
113 None
107 } 114 }
108 } 115 }
109 } 116 };
110 117
111 Some(call_info) 118 Some(CallInfo { signature, active_parameter })
112} 119}
113 120
114#[derive(Debug)] 121#[derive(Debug)]
@@ -181,201 +188,192 @@ impl CallInfo {
181 let res = ActiveParameter { ty, name }; 188 let res = ActiveParameter { ty, name };
182 Some(res) 189 Some(res)
183 } 190 }
184
185 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
186 let signature = FunctionSignature::from_hir(db, function);
187
188 CallInfo { signature, active_parameter: None }
189 }
190
191 fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
192 let signature = FunctionSignature::from_struct(db, st)?;
193
194 Some(CallInfo { signature, active_parameter: None })
195 }
196
197 fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
198 let signature = FunctionSignature::from_enum_variant(db, variant)?;
199
200 Some(CallInfo { signature, active_parameter: None })
201 }
202
203 fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
204 let signature = FunctionSignature::from_macro(db, macro_def)?;
205
206 Some(CallInfo { signature, active_parameter: None })
207 }
208
209 fn parameters(&self) -> &[String] {
210 &self.signature.parameters
211 }
212} 191}
213 192
214#[cfg(test)] 193#[cfg(test)]
215mod tests { 194mod tests {
195 use expect::{expect, Expect};
216 use test_utils::mark; 196 use test_utils::mark;
217 197
218 use crate::mock_analysis::single_file_with_position; 198 use crate::mock_analysis::analysis_and_position;
219
220 use super::*;
221
222 // These are only used when testing
223 impl CallInfo {
224 fn doc(&self) -> Option<hir::Documentation> {
225 self.signature.doc.clone()
226 }
227
228 fn label(&self) -> String {
229 self.signature.to_string()
230 }
231 }
232
233 fn call_info_helper(text: &str) -> Option<CallInfo> {
234 let (analysis, position) = single_file_with_position(text);
235 analysis.call_info(position).unwrap()
236 }
237
238 fn call_info(text: &str) -> CallInfo {
239 let info = call_info_helper(text);
240 assert!(info.is_some());
241 info.unwrap()
242 }
243 199
244 fn no_call_info(text: &str) { 200 fn check(ra_fixture: &str, expect: Expect) {
245 let info = call_info_helper(text); 201 let (analysis, position) = analysis_and_position(ra_fixture);
246 assert!(info.is_none()); 202 let call_info = analysis.call_info(position).unwrap();
203 let actual = match call_info {
204 Some(call_info) => {
205 let docs = match &call_info.signature.doc {
206 None => "".to_string(),
207 Some(docs) => format!("{}\n------\n", docs.as_str()),
208 };
209 let params = call_info
210 .signature
211 .parameters
212 .iter()
213 .enumerate()
214 .map(|(i, param)| {
215 if Some(i) == call_info.active_parameter {
216 format!("<{}>", param)
217 } else {
218 param.clone()
219 }
220 })
221 .collect::<Vec<_>>()
222 .join(", ");
223 format!("{}{}\n({})\n", docs, call_info.signature, params)
224 }
225 None => String::new(),
226 };
227 expect.assert_eq(&actual);
247 } 228 }
248 229
249 #[test] 230 #[test]
250 fn test_fn_signature_two_args_firstx() { 231 fn test_fn_signature_two_args() {
251 let info = call_info( 232 check(
252 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 233 r#"
253fn bar() { foo(<|>3, ); }"#, 234fn foo(x: u32, y: u32) -> u32 {x + y}
235fn bar() { foo(<|>3, ); }
236"#,
237 expect![[r#"
238 fn foo(x: u32, y: u32) -> u32
239 (<x: u32>, y: u32)
240 "#]],
254 ); 241 );
255 242 check(
256 assert_eq!(info.parameters(), ["x: u32", "y: u32"]); 243 r#"
257 assert_eq!(info.active_parameter, Some(0)); 244fn foo(x: u32, y: u32) -> u32 {x + y}
258 } 245fn bar() { foo(3<|>, ); }
259 246"#,
260 #[test] 247 expect![[r#"
261 fn test_fn_signature_two_args_second() { 248 fn foo(x: u32, y: u32) -> u32
262 let info = call_info( 249 (<x: u32>, y: u32)
263 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 250 "#]],
264fn bar() { foo(3, <|>); }"#, 251 );
252 check(
253 r#"
254fn foo(x: u32, y: u32) -> u32 {x + y}
255fn bar() { foo(3,<|> ); }
256"#,
257 expect![[r#"
258 fn foo(x: u32, y: u32) -> u32
259 (x: u32, <y: u32>)
260 "#]],
261 );
262 check(
263 r#"
264fn foo(x: u32, y: u32) -> u32 {x + y}
265fn bar() { foo(3, <|>); }
266"#,
267 expect![[r#"
268 fn foo(x: u32, y: u32) -> u32
269 (x: u32, <y: u32>)
270 "#]],
265 ); 271 );
266
267 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
268 assert_eq!(info.active_parameter, Some(1));
269 } 272 }
270 273
271 #[test] 274 #[test]
272 fn test_fn_signature_two_args_empty() { 275 fn test_fn_signature_two_args_empty() {
273 let info = call_info( 276 check(
274 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 277 r#"
275fn bar() { foo(<|>); }"#, 278fn foo(x: u32, y: u32) -> u32 {x + y}
279fn bar() { foo(<|>); }
280"#,
281 expect![[r#"
282 fn foo(x: u32, y: u32) -> u32
283 (<x: u32>, y: u32)
284 "#]],
276 ); 285 );
277
278 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
279 assert_eq!(info.active_parameter, Some(0));
280 } 286 }
281 287
282 #[test] 288 #[test]
283 fn test_fn_signature_two_args_first_generics() { 289 fn test_fn_signature_two_args_first_generics() {
284 let info = call_info( 290 check(
285 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
286fn bar() { foo(<|>3, ); }"#,
287 );
288
289 assert_eq!(info.parameters(), ["x: T", "y: U"]);
290 assert_eq!(
291 info.label(),
292 r#" 291 r#"
293fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 292fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
294where T: Copy + Display, 293 where T: Copy + Display, U: Debug
295 U: Debug 294{ x + y }
296 "# 295
297 .trim() 296fn bar() { foo(<|>3, ); }
297"#,
298 expect![[r#"
299 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
300 where T: Copy + Display,
301 U: Debug
302 (<x: T>, y: U)
303 "#]],
298 ); 304 );
299 assert_eq!(info.active_parameter, Some(0));
300 } 305 }
301 306
302 #[test] 307 #[test]
303 fn test_fn_signature_no_params() { 308 fn test_fn_signature_no_params() {
304 let info = call_info( 309 check(
305 r#"fn foo<T>() -> T where T: Copy + Display {}
306fn bar() { foo(<|>); }"#,
307 );
308
309 assert!(info.parameters().is_empty());
310 assert_eq!(
311 info.label(),
312 r#" 310 r#"
313fn foo<T>() -> T 311fn foo<T>() -> T where T: Copy + Display {}
314where T: Copy + Display 312fn bar() { foo(<|>); }
315 "# 313"#,
316 .trim() 314 expect![[r#"
315 fn foo<T>() -> T
316 where T: Copy + Display
317 ()
318 "#]],
317 ); 319 );
318 assert!(info.active_parameter.is_none());
319 } 320 }
320 321
321 #[test] 322 #[test]
322 fn test_fn_signature_for_impl() { 323 fn test_fn_signature_for_impl() {
323 let info = call_info( 324 check(
324 r#"struct F; impl F { pub fn new() { F{}} } 325 r#"
325fn bar() {let _ : F = F::new(<|>);}"#, 326struct F; impl F { pub fn new() { F{}} }
327fn bar() {let _ : F = F::new(<|>);}
328"#,
329 expect![[r#"
330 pub fn new()
331 ()
332 "#]],
326 ); 333 );
327
328 assert!(info.parameters().is_empty());
329 assert_eq!(info.active_parameter, None);
330 } 334 }
331 335
332 #[test] 336 #[test]
333 fn test_fn_signature_for_method_self() { 337 fn test_fn_signature_for_method_self() {
334 let info = call_info( 338 check(
335 r#"struct F; 339 r#"
336impl F { 340struct S;
337 pub fn new() -> F{ 341impl S { pub fn do_it(&self) {} }
338 F{}
339 }
340
341 pub fn do_it(&self) {}
342}
343 342
344fn bar() { 343fn bar() {
345 let f : F = F::new(); 344 let s: S = S;
346 f.do_it(<|>); 345 s.do_it(<|>);
347}"#, 346}
347"#,
348 expect![[r#"
349 pub fn do_it(&self)
350 (&self)
351 "#]],
348 ); 352 );
349
350 assert_eq!(info.parameters(), ["&self"]);
351 assert_eq!(info.active_parameter, None);
352 } 353 }
353 354
354 #[test] 355 #[test]
355 fn test_fn_signature_for_method_with_arg() { 356 fn test_fn_signature_for_method_with_arg() {
356 let info = call_info( 357 check(
357 r#"struct F; 358 r#"
358impl F { 359struct S;
359 pub fn new() -> F{ 360impl S { pub fn do_it(&self, x: i32) {} }
360 F{}
361 }
362
363 pub fn do_it(&self, x: i32) {}
364}
365 361
366fn bar() { 362fn bar() {
367 let f : F = F::new(); 363 let s: S = S;
368 f.do_it(<|>); 364 s.do_it(<|>);
369}"#, 365}
366"#,
367 expect![[r#"
368 pub fn do_it(&self, x: i32)
369 (&self, <x: i32>)
370 "#]],
370 ); 371 );
371
372 assert_eq!(info.parameters(), ["&self", "x: i32"]);
373 assert_eq!(info.active_parameter, Some(1));
374 } 372 }
375 373
376 #[test] 374 #[test]
377 fn test_fn_signature_with_docs_simple() { 375 fn test_fn_signature_with_docs_simple() {
378 let info = call_info( 376 check(
379 r#" 377 r#"
380/// test 378/// test
381// non-doc-comment 379// non-doc-comment
@@ -387,17 +385,18 @@ fn bar() {
387 let _ = foo(<|>); 385 let _ = foo(<|>);
388} 386}
389"#, 387"#,
388 expect![[r#"
389 test
390 ------
391 fn foo(j: u32) -> u32
392 (<j: u32>)
393 "#]],
390 ); 394 );
391
392 assert_eq!(info.parameters(), ["j: u32"]);
393 assert_eq!(info.active_parameter, Some(0));
394 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
395 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
396 } 395 }
397 396
398 #[test] 397 #[test]
399 fn test_fn_signature_with_docs() { 398 fn test_fn_signature_with_docs() {
400 let info = call_info( 399 check(
401 r#" 400 r#"
402/// Adds one to the number given. 401/// Adds one to the number given.
403/// 402///
@@ -415,31 +414,26 @@ pub fn add_one(x: i32) -> i32 {
415pub fn do() { 414pub fn do() {
416 add_one(<|> 415 add_one(<|>
417}"#, 416}"#,
418 ); 417 expect![[r##"
418 Adds one to the number given.
419 419
420 assert_eq!(info.parameters(), ["x: i32"]); 420 # Examples
421 assert_eq!(info.active_parameter, Some(0));
422 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
423 assert_eq!(
424 info.doc().map(|it| it.into()),
425 Some(
426 r#"Adds one to the number given.
427 421
428# Examples 422 ```
423 let five = 5;
429 424
430``` 425 assert_eq!(6, my_crate::add_one(5));
431let five = 5; 426 ```
432 427 ------
433assert_eq!(6, my_crate::add_one(5)); 428 pub fn add_one(x: i32) -> i32
434```"# 429 (<x: i32>)
435 .to_string() 430 "##]],
436 )
437 ); 431 );
438 } 432 }
439 433
440 #[test] 434 #[test]
441 fn test_fn_signature_with_docs_impl() { 435 fn test_fn_signature_with_docs_impl() {
442 let info = call_info( 436 check(
443 r#" 437 r#"
444struct addr; 438struct addr;
445impl addr { 439impl addr {
@@ -460,32 +454,28 @@ impl addr {
460pub fn do_it() { 454pub fn do_it() {
461 addr {}; 455 addr {};
462 addr::add_one(<|>); 456 addr::add_one(<|>);
463}"#, 457}
464 ); 458"#,
465 459 expect![[r##"
466 assert_eq!(info.parameters(), ["x: i32"]); 460 Adds one to the number given.
467 assert_eq!(info.active_parameter, Some(0));
468 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
469 assert_eq!(
470 info.doc().map(|it| it.into()),
471 Some(
472 r#"Adds one to the number given.
473 461
474# Examples 462 # Examples
475 463
476``` 464 ```
477let five = 5; 465 let five = 5;
478 466
479assert_eq!(6, my_crate::add_one(5)); 467 assert_eq!(6, my_crate::add_one(5));
480```"# 468 ```
481 .to_string() 469 ------
482 ) 470 pub fn add_one(x: i32) -> i32
471 (<x: i32>)
472 "##]],
483 ); 473 );
484 } 474 }
485 475
486 #[test] 476 #[test]
487 fn test_fn_signature_with_docs_from_actix() { 477 fn test_fn_signature_with_docs_from_actix() {
488 let info = call_info( 478 check(
489 r#" 479 r#"
490struct WriteHandler<E>; 480struct WriteHandler<E>;
491 481
@@ -509,101 +499,102 @@ impl<E> WriteHandler<E> {
509pub fn foo(mut r: WriteHandler<()>) { 499pub fn foo(mut r: WriteHandler<()>) {
510 r.finished(<|>); 500 r.finished(<|>);
511} 501}
512
513"#, 502"#,
514 ); 503 expect![[r#"
515 504 Method is called when writer finishes.
516 assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string()); 505
517 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); 506 By default this method stops actor's `Context`.
518 assert_eq!(info.active_parameter, Some(1)); 507 ------
519 assert_eq!( 508 fn finished(&mut self, ctx: &mut Self::Context)
520 info.doc().map(|it| it.into()), 509 (&mut self, <ctx: &mut Self::Context>)
521 Some( 510 "#]],
522 r#"Method is called when writer finishes.
523
524By default this method stops actor's `Context`."#
525 .to_string()
526 )
527 ); 511 );
528 } 512 }
529 513
530 #[test] 514 #[test]
531 fn call_info_bad_offset() { 515 fn call_info_bad_offset() {
532 mark::check!(call_info_bad_offset); 516 mark::check!(call_info_bad_offset);
533 let (analysis, position) = single_file_with_position( 517 check(
534 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 518 r#"
535 fn bar() { foo <|> (3, ); }"#, 519fn foo(x: u32, y: u32) -> u32 {x + y}
520fn bar() { foo <|> (3, ); }
521"#,
522 expect![[""]],
536 ); 523 );
537 let call_info = analysis.call_info(position).unwrap();
538 assert!(call_info.is_none());
539 } 524 }
540 525
541 #[test] 526 #[test]
542 fn test_nested_method_in_lamba() { 527 fn test_nested_method_in_lambda() {
543 let info = call_info( 528 check(
544 r#"struct Foo; 529 r#"
545 530struct Foo;
546impl Foo { 531impl Foo { fn bar(&self, _: u32) { } }
547 fn bar(&self, _: u32) { }
548}
549 532
550fn bar(_: u32) { } 533fn bar(_: u32) { }
551 534
552fn main() { 535fn main() {
553 let foo = Foo; 536 let foo = Foo;
554 std::thread::spawn(move || foo.bar(<|>)); 537 std::thread::spawn(move || foo.bar(<|>));
555}"#, 538}
539"#,
540 expect![[r#"
541 fn bar(&self, _: u32)
542 (&self, <_: u32>)
543 "#]],
556 ); 544 );
557
558 assert_eq!(info.parameters(), ["&self", "_: u32"]);
559 assert_eq!(info.active_parameter, Some(1));
560 assert_eq!(info.label(), "fn bar(&self, _: u32)");
561 } 545 }
562 546
563 #[test] 547 #[test]
564 fn works_for_tuple_structs() { 548 fn works_for_tuple_structs() {
565 let info = call_info( 549 check(
566 r#" 550 r#"
567/// A cool tuple struct 551/// A cool tuple struct
568struct TS(u32, i32); 552struct TS(u32, i32);
569fn main() { 553fn main() {
570 let s = TS(0, <|>); 554 let s = TS(0, <|>);
571}"#, 555}
556"#,
557 expect![[r#"
558 A cool tuple struct
559 ------
560 struct TS(u32, i32) -> TS
561 (u32, <i32>)
562 "#]],
572 ); 563 );
573
574 assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
575 assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
576 assert_eq!(info.active_parameter, Some(1));
577 } 564 }
578 565
579 #[test] 566 #[test]
580 fn generic_struct() { 567 fn generic_struct() {
581 let info = call_info( 568 check(
582 r#" 569 r#"
583struct TS<T>(T); 570struct TS<T>(T);
584fn main() { 571fn main() {
585 let s = TS(<|>); 572 let s = TS(<|>);
586}"#, 573}
574"#,
575 expect![[r#"
576 struct TS<T>(T) -> TS
577 (<T>)
578 "#]],
587 ); 579 );
588
589 assert_eq!(info.label(), "struct TS<T>(T) -> TS");
590 assert_eq!(info.active_parameter, Some(0));
591 } 580 }
592 581
593 #[test] 582 #[test]
594 fn cant_call_named_structs() { 583 fn cant_call_named_structs() {
595 no_call_info( 584 check(
596 r#" 585 r#"
597struct TS { x: u32, y: i32 } 586struct TS { x: u32, y: i32 }
598fn main() { 587fn main() {
599 let s = TS(<|>); 588 let s = TS(<|>);
600}"#, 589}
590"#,
591 expect![[""]],
601 ); 592 );
602 } 593 }
603 594
604 #[test] 595 #[test]
605 fn works_for_enum_variants() { 596 fn works_for_enum_variants() {
606 let info = call_info( 597 check(
607 r#" 598 r#"
608enum E { 599enum E {
609 /// A Variant 600 /// A Variant
@@ -617,17 +608,19 @@ enum E {
617fn main() { 608fn main() {
618 let a = E::A(<|>); 609 let a = E::A(<|>);
619} 610}
620 "#, 611"#,
612 expect![[r#"
613 A Variant
614 ------
615 E::A(0: i32)
616 (<0: i32>)
617 "#]],
621 ); 618 );
622
623 assert_eq!(info.label(), "E::A(0: i32)");
624 assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
625 assert_eq!(info.active_parameter, Some(0));
626 } 619 }
627 620
628 #[test] 621 #[test]
629 fn cant_call_enum_records() { 622 fn cant_call_enum_records() {
630 no_call_info( 623 check(
631 r#" 624 r#"
632enum E { 625enum E {
633 /// A Variant 626 /// A Variant
@@ -641,13 +634,14 @@ enum E {
641fn main() { 634fn main() {
642 let a = E::C(<|>); 635 let a = E::C(<|>);
643} 636}
644 "#, 637"#,
638 expect![[""]],
645 ); 639 );
646 } 640 }
647 641
648 #[test] 642 #[test]
649 fn fn_signature_for_macro() { 643 fn fn_signature_for_macro() {
650 let info = call_info( 644 check(
651 r#" 645 r#"
652/// empty macro 646/// empty macro
653macro_rules! foo { 647macro_rules! foo {
@@ -657,31 +651,30 @@ macro_rules! foo {
657fn f() { 651fn f() {
658 foo!(<|>); 652 foo!(<|>);
659} 653}
660 "#, 654"#,
655 expect![[r#"
656 empty macro
657 ------
658 foo!()
659 ()
660 "#]],
661 ); 661 );
662
663 assert_eq!(info.label(), "foo!()");
664 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
665 } 662 }
666 663
667 #[test] 664 #[test]
668 fn fn_signature_for_call_in_macro() { 665 fn fn_signature_for_call_in_macro() {
669 let info = call_info( 666 check(
670 r#" 667 r#"
671 macro_rules! id { 668macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
672 ($($tt:tt)*) => { $($tt)* } 669fn foo() { }
673 } 670id! {
674 fn foo() { 671 fn bar() { foo(<|>); }
675 672}
676 } 673"#,
677 id! { 674 expect![[r#"
678 fn bar() { 675 fn foo()
679 foo(<|>); 676 ()
680 } 677 "#]],
681 }
682 "#,
683 ); 678 );
684
685 assert_eq!(info.label(), "fn foo()");
686 } 679 }
687} 680}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index d890b69d2..f3a5e9573 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -2,6 +2,9 @@ mod completion_config;
2mod completion_item; 2mod completion_item;
3mod completion_context; 3mod completion_context;
4mod presentation; 4mod presentation;
5mod patterns;
6#[cfg(test)]
7mod test_utils;
5 8
6mod complete_attribute; 9mod complete_attribute;
7mod complete_dot; 10mod complete_dot;
@@ -15,8 +18,6 @@ mod complete_unqualified_path;
15mod complete_postfix; 18mod complete_postfix;
16mod complete_macro_in_item_position; 19mod complete_macro_in_item_position;
17mod complete_trait_impl; 20mod complete_trait_impl;
18#[cfg(test)]
19mod test_utils;
20 21
21use ra_ide_db::RootDatabase; 22use ra_ide_db::RootDatabase;
22 23
@@ -62,11 +63,11 @@ pub use crate::completion::{
62// There also snippet completions: 63// There also snippet completions:
63// 64//
64// .Expressions 65// .Expressions
65// - `pd` -> `println!("{:?}")` 66// - `pd` -> `eprintln!(" = {:?}", );")`
66// - `ppd` -> `println!("{:#?}")` 67// - `ppd` -> `eprintln!(" = {:#?}", );`
67// 68//
68// .Items 69// .Items
69// - `tfn` -> `#[test] fn f(){}` 70// - `tfn` -> `#[test] fn feature(){}`
70// - `tmod` -> 71// - `tmod` ->
71// ```rust 72// ```rust
72// #[cfg(test)] 73// #[cfg(test)]
@@ -74,7 +75,7 @@ pub use crate::completion::{
74// use super::*; 75// use super::*;
75// 76//
76// #[test] 77// #[test]
77// fn test_fn() {} 78// fn test_name() {}
78// } 79// }
79// ``` 80// ```
80 81
@@ -125,3 +126,81 @@ pub(crate) fn completions(
125 126
126 Some(acc) 127 Some(acc)
127} 128}
129
130#[cfg(test)]
131mod tests {
132 use crate::completion::completion_config::CompletionConfig;
133 use crate::mock_analysis::analysis_and_position;
134
135 struct DetailAndDocumentation<'a> {
136 detail: &'a str,
137 documentation: &'a str,
138 }
139
140 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(ra_fixture);
142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions {
145 if item.detail() == Some(expected.detail) {
146 let opt = item.documentation();
147 let doc = opt.as_ref().map(|it| it.as_str());
148 assert_eq!(doc, Some(expected.documentation));
149 return;
150 }
151 }
152 panic!("completion detail not found: {}", expected.detail)
153 }
154
155 #[test]
156 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
157 check_detail_and_documentation(
158 r#"
159 //- /lib.rs
160 macro_rules! bar {
161 () => {
162 struct Bar;
163 impl Bar {
164 #[doc = "Do the foo"]
165 fn foo(&self) {}
166 }
167 }
168 }
169
170 bar!();
171
172 fn foo() {
173 let bar = Bar;
174 bar.fo<|>;
175 }
176 "#,
177 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
178 );
179 }
180
181 #[test]
182 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
183 check_detail_and_documentation(
184 r#"
185 //- /lib.rs
186 macro_rules! bar {
187 () => {
188 struct Bar;
189 impl Bar {
190 /// Do the foo
191 fn foo(&self) {}
192 }
193 }
194 }
195
196 bar!();
197
198 fn foo() {
199 let bar = Bar;
200 bar.fo<|>;
201 }
202 "#,
203 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
204 );
205 }
206}
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index fb3f0b743..d268c92be 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -20,6 +20,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
20 { 20 {
21 complete_derive(acc, ctx, token_tree) 21 complete_derive(acc, ctx, token_tree)
22 } 22 }
23 (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {}
23 _ => complete_attribute_start(acc, ctx, attribute), 24 _ => complete_attribute_start(acc, ctx, attribute),
24 } 25 }
25 Some(()) 26 Some(())
@@ -34,6 +35,10 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
34 ) 35 )
35 .kind(CompletionItemKind::Attribute); 36 .kind(CompletionItemKind::Attribute);
36 37
38 if let Some(lookup) = attr_completion.lookup {
39 item = item.lookup_by(lookup);
40 }
41
37 match (attr_completion.snippet, ctx.config.snippet_cap) { 42 match (attr_completion.snippet, ctx.config.snippet_cap) {
38 (Some(snippet), Some(cap)) => { 43 (Some(snippet), Some(cap)) => {
39 item = item.insert_snippet(cap, snippet); 44 item = item.insert_snippet(cap, snippet);
@@ -41,7 +46,7 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
41 _ => {} 46 _ => {}
42 } 47 }
43 48
44 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner { 49 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
45 acc.add(item); 50 acc.add(item);
46 } 51 }
47 } 52 }
@@ -49,85 +54,74 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
49 54
50struct AttrCompletion { 55struct AttrCompletion {
51 label: &'static str, 56 label: &'static str,
57 lookup: Option<&'static str>,
58 snippet: Option<&'static str>,
59 prefer_inner: bool,
60}
61
62impl AttrCompletion {
63 const fn prefer_inner(self) -> AttrCompletion {
64 AttrCompletion { prefer_inner: true, ..self }
65 }
66}
67
68const fn attr(
69 label: &'static str,
70 lookup: Option<&'static str>,
52 snippet: Option<&'static str>, 71 snippet: Option<&'static str>,
53 should_be_inner: bool, 72) -> AttrCompletion {
73 AttrCompletion { label, lookup, snippet, prefer_inner: false }
54} 74}
55 75
56const ATTRIBUTES: &[AttrCompletion] = &[ 76const ATTRIBUTES: &[AttrCompletion] = &[
57 AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false }, 77 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
58 AttrCompletion { 78 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
59 label: "cfg_attr", 79 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
60 snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"), 80 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
61 should_be_inner: false, 81 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
62 }, 82 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
63 AttrCompletion { label: "cfg", snippet: Some("cfg(${0:predicate})"), should_be_inner: false }, 83 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
64 AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false }, 84 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
65 AttrCompletion { 85 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
66 label: "deprecated",
67 snippet: Some(r#"deprecated = "${0:reason}""#),
68 should_be_inner: false,
69 },
70 AttrCompletion {
71 label: "derive",
72 snippet: Some(r#"derive(${0:Debug})"#),
73 should_be_inner: false,
74 },
75 AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false },
76 AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true },
77 AttrCompletion { label: "forbid", snippet: Some("forbid(${0:lint})"), should_be_inner: false },
78 // FIXME: resolve through macro resolution? 86 // FIXME: resolve through macro resolution?
79 AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true }, 87 attr("global_allocator", None, None).prefer_inner(),
80 AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false }, 88 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
81 AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false }, 89 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
82 AttrCompletion { 90 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
83 label: "link_name", 91 attr("link", None, None),
84 snippet: Some(r#"link_name = "${0:symbol_name}""#), 92 attr("macro_export", None, None),
85 should_be_inner: false, 93 attr("macro_use", None, None),
86 }, 94 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
87 AttrCompletion { label: "link", snippet: None, should_be_inner: false }, 95 attr("no_mangle", None, None),
88 AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false }, 96 attr("no_std", None, None).prefer_inner(),
89 AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false }, 97 attr("non_exhaustive", None, None),
90 AttrCompletion { 98 attr("panic_handler", None, None).prefer_inner(),
91 label: "must_use", 99 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
92 snippet: Some(r#"must_use = "${0:reason}""#), 100 attr("proc_macro", None, None),
93 should_be_inner: false, 101 attr("proc_macro_attribute", None, None),
94 }, 102 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
95 AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false }, 103 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
96 AttrCompletion { label: "no_std", snippet: None, should_be_inner: true }, 104 .prefer_inner(),
97 AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false }, 105 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
98 AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true }, 106 attr(
99 AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false }, 107 "should_panic(…)",
100 AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false }, 108 Some("should_panic"),
101 AttrCompletion { label: "proc_macro_attribute", snippet: None, should_be_inner: false }, 109 Some(r#"should_panic(expected = "${0:reason}")"#),
102 AttrCompletion { 110 ),
103 label: "proc_macro_derive", 111 attr(
104 snippet: Some("proc_macro_derive(${0:Trait})"), 112 r#"target_feature = "…""#,
105 should_be_inner: false, 113 Some("target_feature"),
106 }, 114 Some("target_feature = \"${0:feature}\""),
107 AttrCompletion { 115 ),
108 label: "recursion_limit", 116 attr("test", None, None),
109 snippet: Some("recursion_limit = ${0:128}"), 117 attr("used", None, None),
110 should_be_inner: true, 118 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
111 }, 119 attr(
112 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, 120 r#"windows_subsystem = "…""#,
113 AttrCompletion { 121 Some("windows_subsystem"),
114 label: "should_panic", 122 Some(r#"windows_subsystem = "${0:subsystem}""#),
115 snippet: Some(r#"should_panic(expected = "${0:reason}")"#), 123 )
116 should_be_inner: false, 124 .prefer_inner(),
117 },
118 AttrCompletion {
119 label: "target_feature",
120 snippet: Some("target_feature = \"${0:feature}\""),
121 should_be_inner: false,
122 },
123 AttrCompletion { label: "test", snippet: None, should_be_inner: false },
124 AttrCompletion { label: "used", snippet: None, should_be_inner: false },
125 AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false },
126 AttrCompletion {
127 label: "windows_subsystem",
128 snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
129 should_be_inner: true,
130 },
131]; 125];
132 126
133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 127fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
@@ -201,7 +195,7 @@ fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>,
201 195
202fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 196fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
203 let mut result = FxHashSet::default(); 197 let mut result = FxHashSet::default();
204 ctx.scope().process_all_names(&mut |name, scope_def| { 198 ctx.scope.process_all_names(&mut |name, scope_def| {
205 if let hir::ScopeDef::MacroDef(mac) = scope_def { 199 if let hir::ScopeDef::MacroDef(mac) = scope_def {
206 if mac.is_derive_macro() { 200 if mac.is_derive_macro() {
207 result.insert(name.to_string()); 201 result.insert(name.to_string());
@@ -232,624 +226,147 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
232 226
233#[cfg(test)] 227#[cfg(test)]
234mod tests { 228mod tests {
235 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 229 use expect::{expect, Expect};
236 use insta::assert_debug_snapshot; 230
231 use crate::completion::{test_utils::completion_list, CompletionKind};
237 232
238 fn do_attr_completion(code: &str) -> Vec<CompletionItem> { 233 fn check(ra_fixture: &str, expect: Expect) {
239 do_completion(code, CompletionKind::Attribute) 234 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
235 expect.assert_eq(&actual);
240 } 236 }
241 237
242 #[test] 238 #[test]
243 fn empty_derive_completion() { 239 fn empty_derive_completion() {
244 assert_debug_snapshot!( 240 check(
245 do_attr_completion( 241 r#"
246 r" 242#[derive(<|>)]
247 #[derive(<|>)] 243struct Test {}
248 struct Test {} 244 "#,
249 ", 245 expect![[r#"
250 ), 246 at Clone
251 @r###" 247 at Copy, Clone
252 [ 248 at Debug
253 CompletionItem { 249 at Default
254 label: "Clone", 250 at Eq, PartialEq
255 source_range: 30..30, 251 at Hash
256 delete: 30..30, 252 at Ord, PartialOrd, Eq, PartialEq
257 insert: "Clone", 253 at PartialEq
258 kind: Attribute, 254 at PartialOrd, PartialEq
259 }, 255 "#]],
260 CompletionItem {
261 label: "Copy, Clone",
262 source_range: 30..30,
263 delete: 30..30,
264 insert: "Copy, Clone",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "Debug",
269 source_range: 30..30,
270 delete: 30..30,
271 insert: "Debug",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "Default",
276 source_range: 30..30,
277 delete: 30..30,
278 insert: "Default",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "Eq, PartialEq",
283 source_range: 30..30,
284 delete: 30..30,
285 insert: "Eq, PartialEq",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "Hash",
290 source_range: 30..30,
291 delete: 30..30,
292 insert: "Hash",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "Ord, PartialOrd, Eq, PartialEq",
297 source_range: 30..30,
298 delete: 30..30,
299 insert: "Ord, PartialOrd, Eq, PartialEq",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "PartialEq",
304 source_range: 30..30,
305 delete: 30..30,
306 insert: "PartialEq",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "PartialOrd, PartialEq",
311 source_range: 30..30,
312 delete: 30..30,
313 insert: "PartialOrd, PartialEq",
314 kind: Attribute,
315 },
316 ]
317 "###
318 ); 256 );
319 } 257 }
320 258
321 #[test] 259 #[test]
322 fn no_completion_for_incorrect_derive() { 260 fn no_completion_for_incorrect_derive() {
323 assert_debug_snapshot!( 261 check(
324 do_attr_completion( 262 r#"
325 r" 263#[derive{<|>)]
326 #[derive{<|>)] 264struct Test {}
327 struct Test {} 265"#,
328 ", 266 expect![[r#""#]],
329 ), 267 )
330 @"[]"
331 );
332 } 268 }
333 269
334 #[test] 270 #[test]
335 fn derive_with_input_completion() { 271 fn derive_with_input_completion() {
336 assert_debug_snapshot!( 272 check(
337 do_attr_completion( 273 r#"
338 r" 274#[derive(serde::Serialize, PartialEq, <|>)]
339 #[derive(serde::Serialize, PartialEq, <|>)] 275struct Test {}
340 struct Test {} 276"#,
341 ", 277 expect![[r#"
342 ), 278 at Clone
343 @r###" 279 at Copy, Clone
344 [ 280 at Debug
345 CompletionItem { 281 at Default
346 label: "Clone", 282 at Eq
347 source_range: 59..59, 283 at Hash
348 delete: 59..59, 284 at Ord, PartialOrd, Eq
349 insert: "Clone", 285 at PartialOrd
350 kind: Attribute, 286 "#]],
351 }, 287 )
352 CompletionItem {
353 label: "Copy, Clone",
354 source_range: 59..59,
355 delete: 59..59,
356 insert: "Copy, Clone",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "Debug",
361 source_range: 59..59,
362 delete: 59..59,
363 insert: "Debug",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "Default",
368 source_range: 59..59,
369 delete: 59..59,
370 insert: "Default",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "Eq",
375 source_range: 59..59,
376 delete: 59..59,
377 insert: "Eq",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "Hash",
382 source_range: 59..59,
383 delete: 59..59,
384 insert: "Hash",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "Ord, PartialOrd, Eq",
389 source_range: 59..59,
390 delete: 59..59,
391 insert: "Ord, PartialOrd, Eq",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "PartialOrd",
396 source_range: 59..59,
397 delete: 59..59,
398 insert: "PartialOrd",
399 kind: Attribute,
400 },
401 ]
402 "###
403 );
404 } 288 }
405 289
406 #[test] 290 #[test]
407 fn test_attribute_completion() { 291 fn test_attribute_completion() {
408 assert_debug_snapshot!( 292 check(
409 do_attr_completion( 293 r#"#[<|>]"#,
410 r" 294 expect![[r#"
411 #[<|>] 295 at allow(…)
412 ", 296 at cfg(…)
413 ), 297 at cfg_attr(…)
414 @r###" 298 at deny(…)
415 [ 299 at deprecated = "…"
416 CompletionItem { 300 at derive(…)
417 label: "allow", 301 at doc = "…"
418 source_range: 19..19, 302 at forbid(…)
419 delete: 19..19, 303 at ignore = "…"
420 insert: "allow(${0:lint})", 304 at inline(…)
421 kind: Attribute, 305 at link
422 }, 306 at link_name = "…"
423 CompletionItem { 307 at macro_export
424 label: "cfg", 308 at macro_use
425 source_range: 19..19, 309 at must_use = "…"
426 delete: 19..19, 310 at no_mangle
427 insert: "cfg(${0:predicate})", 311 at non_exhaustive
428 kind: Attribute, 312 at path = "…"
429 }, 313 at proc_macro
430 CompletionItem { 314 at proc_macro_attribute
431 label: "cfg_attr", 315 at proc_macro_derive(…)
432 source_range: 19..19, 316 at repr(…)
433 delete: 19..19, 317 at should_panic(…)
434 insert: "cfg_attr(${1:predicate}, ${0:attr})", 318 at target_feature = "…"
435 kind: Attribute, 319 at test
436 }, 320 at used
437 CompletionItem { 321 at warn(…)
438 label: "deny", 322 "#]],
439 source_range: 19..19, 323 )
440 delete: 19..19, 324 }
441 insert: "deny(${0:lint})", 325
442 kind: Attribute, 326 #[test]
443 }, 327 fn test_attribute_completion_inside_nested_attr() {
444 CompletionItem { 328 check(r#"#[allow(<|>)]"#, expect![[]])
445 label: "deprecated",
446 source_range: 19..19,
447 delete: 19..19,
448 insert: "deprecated = \"${0:reason}\"",
449 kind: Attribute,
450 },
451 CompletionItem {
452 label: "derive",
453 source_range: 19..19,
454 delete: 19..19,
455 insert: "derive(${0:Debug})",
456 kind: Attribute,
457 },
458 CompletionItem {
459 label: "doc",
460 source_range: 19..19,
461 delete: 19..19,
462 insert: "doc = \"${0:docs}\"",
463 kind: Attribute,
464 },
465 CompletionItem {
466 label: "forbid",
467 source_range: 19..19,
468 delete: 19..19,
469 insert: "forbid(${0:lint})",
470 kind: Attribute,
471 },
472 CompletionItem {
473 label: "ignore",
474 source_range: 19..19,
475 delete: 19..19,
476 insert: "ignore(${0:lint})",
477 kind: Attribute,
478 },
479 CompletionItem {
480 label: "inline",
481 source_range: 19..19,
482 delete: 19..19,
483 insert: "inline(${0:lint})",
484 kind: Attribute,
485 },
486 CompletionItem {
487 label: "link",
488 source_range: 19..19,
489 delete: 19..19,
490 insert: "link",
491 kind: Attribute,
492 },
493 CompletionItem {
494 label: "link_name",
495 source_range: 19..19,
496 delete: 19..19,
497 insert: "link_name = \"${0:symbol_name}\"",
498 kind: Attribute,
499 },
500 CompletionItem {
501 label: "macro_export",
502 source_range: 19..19,
503 delete: 19..19,
504 insert: "macro_export",
505 kind: Attribute,
506 },
507 CompletionItem {
508 label: "macro_use",
509 source_range: 19..19,
510 delete: 19..19,
511 insert: "macro_use",
512 kind: Attribute,
513 },
514 CompletionItem {
515 label: "must_use",
516 source_range: 19..19,
517 delete: 19..19,
518 insert: "must_use = \"${0:reason}\"",
519 kind: Attribute,
520 },
521 CompletionItem {
522 label: "no_mangle",
523 source_range: 19..19,
524 delete: 19..19,
525 insert: "no_mangle",
526 kind: Attribute,
527 },
528 CompletionItem {
529 label: "non_exhaustive",
530 source_range: 19..19,
531 delete: 19..19,
532 insert: "non_exhaustive",
533 kind: Attribute,
534 },
535 CompletionItem {
536 label: "path",
537 source_range: 19..19,
538 delete: 19..19,
539 insert: "path =\"${0:path}\"",
540 kind: Attribute,
541 },
542 CompletionItem {
543 label: "proc_macro",
544 source_range: 19..19,
545 delete: 19..19,
546 insert: "proc_macro",
547 kind: Attribute,
548 },
549 CompletionItem {
550 label: "proc_macro_attribute",
551 source_range: 19..19,
552 delete: 19..19,
553 insert: "proc_macro_attribute",
554 kind: Attribute,
555 },
556 CompletionItem {
557 label: "proc_macro_derive",
558 source_range: 19..19,
559 delete: 19..19,
560 insert: "proc_macro_derive(${0:Trait})",
561 kind: Attribute,
562 },
563 CompletionItem {
564 label: "repr",
565 source_range: 19..19,
566 delete: 19..19,
567 insert: "repr(${0:C})",
568 kind: Attribute,
569 },
570 CompletionItem {
571 label: "should_panic",
572 source_range: 19..19,
573 delete: 19..19,
574 insert: "should_panic(expected = \"${0:reason}\")",
575 kind: Attribute,
576 },
577 CompletionItem {
578 label: "target_feature",
579 source_range: 19..19,
580 delete: 19..19,
581 insert: "target_feature = \"${0:feature}\"",
582 kind: Attribute,
583 },
584 CompletionItem {
585 label: "test",
586 source_range: 19..19,
587 delete: 19..19,
588 insert: "test",
589 kind: Attribute,
590 },
591 CompletionItem {
592 label: "used",
593 source_range: 19..19,
594 delete: 19..19,
595 insert: "used",
596 kind: Attribute,
597 },
598 CompletionItem {
599 label: "warn",
600 source_range: 19..19,
601 delete: 19..19,
602 insert: "warn(${0:lint})",
603 kind: Attribute,
604 },
605 ]
606 "###
607 );
608 } 329 }
609 330
610 #[test] 331 #[test]
611 fn test_inner_attribute_completion() { 332 fn test_inner_attribute_completion() {
612 assert_debug_snapshot!( 333 check(
613 do_attr_completion( 334 r"#![<|>]",
614 r" 335 expect![[r#"
615 #![<|>] 336 at allow(…)
616 ", 337 at cfg(…)
617 ), 338 at cfg_attr(…)
618 @r###" 339 at deny(…)
619 [ 340 at deprecated = "…"
620 CompletionItem { 341 at derive(…)
621 label: "allow", 342 at doc = "…"
622 source_range: 20..20, 343 at feature(…)
623 delete: 20..20, 344 at forbid(…)
624 insert: "allow(${0:lint})", 345 at global_allocator
625 kind: Attribute, 346 at ignore = "…"
626 }, 347 at inline(…)
627 CompletionItem { 348 at link
628 label: "cfg", 349 at link_name = "…"
629 source_range: 20..20, 350 at macro_export
630 delete: 20..20, 351 at macro_use
631 insert: "cfg(${0:predicate})", 352 at must_use = "…"
632 kind: Attribute, 353 at no_mangle
633 }, 354 at no_std
634 CompletionItem { 355 at non_exhaustive
635 label: "cfg_attr", 356 at panic_handler
636 source_range: 20..20, 357 at path = "…"
637 delete: 20..20, 358 at proc_macro
638 insert: "cfg_attr(${1:predicate}, ${0:attr})", 359 at proc_macro_attribute
639 kind: Attribute, 360 at proc_macro_derive(…)
640 }, 361 at recursion_limit = …
641 CompletionItem { 362 at repr(…)
642 label: "deny", 363 at should_panic(…)
643 source_range: 20..20, 364 at target_feature = "…"
644 delete: 20..20, 365 at test
645 insert: "deny(${0:lint})", 366 at used
646 kind: Attribute, 367 at warn(…)
647 }, 368 at windows_subsystem = "…"
648 CompletionItem { 369 "#]],
649 label: "deprecated",
650 source_range: 20..20,
651 delete: 20..20,
652 insert: "deprecated = \"${0:reason}\"",
653 kind: Attribute,
654 },
655 CompletionItem {
656 label: "derive",
657 source_range: 20..20,
658 delete: 20..20,
659 insert: "derive(${0:Debug})",
660 kind: Attribute,
661 },
662 CompletionItem {
663 label: "doc",
664 source_range: 20..20,
665 delete: 20..20,
666 insert: "doc = \"${0:docs}\"",
667 kind: Attribute,
668 },
669 CompletionItem {
670 label: "feature",
671 source_range: 20..20,
672 delete: 20..20,
673 insert: "feature(${0:flag})",
674 kind: Attribute,
675 },
676 CompletionItem {
677 label: "forbid",
678 source_range: 20..20,
679 delete: 20..20,
680 insert: "forbid(${0:lint})",
681 kind: Attribute,
682 },
683 CompletionItem {
684 label: "global_allocator",
685 source_range: 20..20,
686 delete: 20..20,
687 insert: "global_allocator",
688 kind: Attribute,
689 },
690 CompletionItem {
691 label: "ignore",
692 source_range: 20..20,
693 delete: 20..20,
694 insert: "ignore(${0:lint})",
695 kind: Attribute,
696 },
697 CompletionItem {
698 label: "inline",
699 source_range: 20..20,
700 delete: 20..20,
701 insert: "inline(${0:lint})",
702 kind: Attribute,
703 },
704 CompletionItem {
705 label: "link",
706 source_range: 20..20,
707 delete: 20..20,
708 insert: "link",
709 kind: Attribute,
710 },
711 CompletionItem {
712 label: "link_name",
713 source_range: 20..20,
714 delete: 20..20,
715 insert: "link_name = \"${0:symbol_name}\"",
716 kind: Attribute,
717 },
718 CompletionItem {
719 label: "macro_export",
720 source_range: 20..20,
721 delete: 20..20,
722 insert: "macro_export",
723 kind: Attribute,
724 },
725 CompletionItem {
726 label: "macro_use",
727 source_range: 20..20,
728 delete: 20..20,
729 insert: "macro_use",
730 kind: Attribute,
731 },
732 CompletionItem {
733 label: "must_use",
734 source_range: 20..20,
735 delete: 20..20,
736 insert: "must_use = \"${0:reason}\"",
737 kind: Attribute,
738 },
739 CompletionItem {
740 label: "no_mangle",
741 source_range: 20..20,
742 delete: 20..20,
743 insert: "no_mangle",
744 kind: Attribute,
745 },
746 CompletionItem {
747 label: "no_std",
748 source_range: 20..20,
749 delete: 20..20,
750 insert: "no_std",
751 kind: Attribute,
752 },
753 CompletionItem {
754 label: "non_exhaustive",
755 source_range: 20..20,
756 delete: 20..20,
757 insert: "non_exhaustive",
758 kind: Attribute,
759 },
760 CompletionItem {
761 label: "panic_handler",
762 source_range: 20..20,
763 delete: 20..20,
764 insert: "panic_handler",
765 kind: Attribute,
766 },
767 CompletionItem {
768 label: "path",
769 source_range: 20..20,
770 delete: 20..20,
771 insert: "path =\"${0:path}\"",
772 kind: Attribute,
773 },
774 CompletionItem {
775 label: "proc_macro",
776 source_range: 20..20,
777 delete: 20..20,
778 insert: "proc_macro",
779 kind: Attribute,
780 },
781 CompletionItem {
782 label: "proc_macro_attribute",
783 source_range: 20..20,
784 delete: 20..20,
785 insert: "proc_macro_attribute",
786 kind: Attribute,
787 },
788 CompletionItem {
789 label: "proc_macro_derive",
790 source_range: 20..20,
791 delete: 20..20,
792 insert: "proc_macro_derive(${0:Trait})",
793 kind: Attribute,
794 },
795 CompletionItem {
796 label: "recursion_limit",
797 source_range: 20..20,
798 delete: 20..20,
799 insert: "recursion_limit = ${0:128}",
800 kind: Attribute,
801 },
802 CompletionItem {
803 label: "repr",
804 source_range: 20..20,
805 delete: 20..20,
806 insert: "repr(${0:C})",
807 kind: Attribute,
808 },
809 CompletionItem {
810 label: "should_panic",
811 source_range: 20..20,
812 delete: 20..20,
813 insert: "should_panic(expected = \"${0:reason}\")",
814 kind: Attribute,
815 },
816 CompletionItem {
817 label: "target_feature",
818 source_range: 20..20,
819 delete: 20..20,
820 insert: "target_feature = \"${0:feature}\"",
821 kind: Attribute,
822 },
823 CompletionItem {
824 label: "test",
825 source_range: 20..20,
826 delete: 20..20,
827 insert: "test",
828 kind: Attribute,
829 },
830 CompletionItem {
831 label: "used",
832 source_range: 20..20,
833 delete: 20..20,
834 insert: "used",
835 kind: Attribute,
836 },
837 CompletionItem {
838 label: "warn",
839 source_range: 20..20,
840 delete: 20..20,
841 insert: "warn(${0:lint})",
842 kind: Attribute,
843 },
844 CompletionItem {
845 label: "windows_subsystem",
846 source_range: 20..20,
847 delete: 20..20,
848 insert: "windows_subsystem = \"${0:subsystem}\"",
849 kind: Attribute,
850 },
851 ]
852 "###
853 ); 370 );
854 } 371 }
855} 372}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 05f825c6f..532665285 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -1,17 +1,12 @@
1//! FIXME: write short doc here 1//! Completes references after dot (fields and method calls).
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4
5use crate::{
6 completion::{
7 completion_context::CompletionContext,
8 completion_item::{CompletionKind, Completions},
9 },
10 CompletionItem,
11};
12use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark;
13 6
14/// Complete dot accesses, i.e. fields or methods (and .await syntax). 7use crate::completion::{completion_context::CompletionContext, completion_item::Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
15pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
16 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
17 Some(expr) => expr, 12 Some(expr) => expr,
@@ -23,24 +18,18 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
23 _ => return, 18 _ => return,
24 }; 19 };
25 20
26 if !ctx.is_call { 21 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else {
27 complete_fields(acc, ctx, &receiver_ty); 24 complete_fields(acc, ctx, &receiver_ty);
28 } 25 }
29 complete_methods(acc, ctx, &receiver_ty); 26 complete_methods(acc, ctx, &receiver_ty);
30
31 // Suggest .await syntax for types that implement Future trait
32 if receiver_ty.impls_future(ctx.db) {
33 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
34 .detail("expr.await")
35 .insert_text("await")
36 .add_to(acc);
37 }
38} 27}
39 28
40fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
41 for receiver in receiver.autoderef(ctx.db) { 30 for receiver in receiver.autoderef(ctx.db) {
42 for (field, ty) in receiver.fields(ctx.db) { 31 for (field, ty) in receiver.fields(ctx.db) {
43 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
44 // Skip private field. FIXME: If the definition location of the 33 // Skip private field. FIXME: If the definition location of the
45 // field is editable, we should show the completion 34 // field is editable, we should show the completion
46 continue; 35 continue;
@@ -57,10 +46,10 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
57fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
58 if let Some(krate) = ctx.krate { 47 if let Some(krate) = ctx.krate {
59 let mut seen_methods = FxHashSet::default(); 48 let mut seen_methods = FxHashSet::default();
60 let traits_in_scope = ctx.scope().traits_in_scope(); 49 let traits_in_scope = ctx.scope.traits_in_scope();
61 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { 50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
62 if func.has_self_param(ctx.db) 51 if func.has_self_param(ctx.db)
63 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
64 && seen_methods.insert(func.name(ctx.db)) 53 && seen_methods.insert(func.name(ctx.db))
65 { 54 {
66 acc.add_function(ctx, func, None); 55 acc.add_function(ctx, func, None);
@@ -72,801 +61,356 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
72 61
73#[cfg(test)] 62#[cfg(test)]
74mod tests { 63mod tests {
75 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 64 use expect::{expect, Expect};
76 use insta::assert_debug_snapshot; 65 use test_utils::mark;
66
67 use crate::completion::{test_utils::completion_list, CompletionKind};
77 68
78 fn do_ref_completion(code: &str) -> Vec<CompletionItem> { 69 fn check(ra_fixture: &str, expect: Expect) {
79 do_completion(code, CompletionKind::Reference) 70 let actual = completion_list(ra_fixture, CompletionKind::Reference);
71 expect.assert_eq(&actual);
80 } 72 }
81 73
82 #[test] 74 #[test]
83 fn test_struct_field_completion() { 75 fn test_struct_field_and_method_completion() {
84 assert_debug_snapshot!( 76 check(
85 do_ref_completion( 77 r#"
86 r" 78struct S { foo: u32 }
87 struct A { the_field: u32 } 79impl S {
88 fn foo(a: A) { 80 fn bar(&self) {}
89 a.<|> 81}
90 } 82fn foo(s: S) { s.<|> }
91 ", 83"#,
92 ), 84 expect![[r#"
93 @r###" 85 me bar() fn bar(&self)
94 [ 86 fd foo u32
95 CompletionItem { 87 "#]],
96 label: "the_field",
97 source_range: 94..94,
98 delete: 94..94,
99 insert: "the_field",
100 kind: Field,
101 detail: "u32",
102 },
103 ]
104 "###
105 ); 88 );
106 } 89 }
107 90
108 #[test] 91 #[test]
109 fn test_struct_field_completion_self() { 92 fn test_struct_field_completion_self() {
110 assert_debug_snapshot!( 93 check(
111 do_ref_completion( 94 r#"
112 r" 95struct S { the_field: (u32,) }
113 struct A { 96impl S {
114 /// This is the_field 97 fn foo(self) { self.<|> }
115 the_field: (u32,) 98}
116 } 99"#,
117 impl A { 100 expect![[r#"
118 fn foo(self) { 101 me foo() fn foo(self)
119 self.<|> 102 fd the_field (u32,)
120 } 103 "#]],
121 } 104 )
122 ",
123 ),
124 @r###"
125 [
126 CompletionItem {
127 label: "foo()",
128 source_range: 187..187,
129 delete: 187..187,
130 insert: "foo()$0",
131 kind: Method,
132 lookup: "foo",
133 detail: "fn foo(self)",
134 },
135 CompletionItem {
136 label: "the_field",
137 source_range: 187..187,
138 delete: 187..187,
139 insert: "the_field",
140 kind: Field,
141 detail: "(u32,)",
142 documentation: Documentation(
143 "This is the_field",
144 ),
145 },
146 ]
147 "###
148 );
149 } 105 }
150 106
151 #[test] 107 #[test]
152 fn test_struct_field_completion_autoderef() { 108 fn test_struct_field_completion_autoderef() {
153 assert_debug_snapshot!( 109 check(
154 do_ref_completion( 110 r#"
155 r" 111struct A { the_field: (u32, i32) }
156 struct A { the_field: (u32, i32) } 112impl A {
157 impl A { 113 fn foo(&self) { self.<|> }
158 fn foo(&self) { 114}
159 self.<|> 115"#,
160 } 116 expect![[r#"
161 } 117 me foo() fn foo(&self)
162 ", 118 fd the_field (u32, i32)
163 ), 119 "#]],
164 @r###" 120 )
165 [
166 CompletionItem {
167 label: "foo()",
168 source_range: 126..126,
169 delete: 126..126,
170 insert: "foo()$0",
171 kind: Method,
172 lookup: "foo",
173 detail: "fn foo(&self)",
174 },
175 CompletionItem {
176 label: "the_field",
177 source_range: 126..126,
178 delete: 126..126,
179 insert: "the_field",
180 kind: Field,
181 detail: "(u32, i32)",
182 },
183 ]
184 "###
185 );
186 } 121 }
187 122
188 #[test] 123 #[test]
189 fn test_no_struct_field_completion_for_method_call() { 124 fn test_no_struct_field_completion_for_method_call() {
190 assert_debug_snapshot!( 125 mark::check!(test_no_struct_field_completion_for_method_call);
191 do_ref_completion( 126 check(
192 r" 127 r#"
193 struct A { the_field: u32 } 128struct A { the_field: u32 }
194 fn foo(a: A) { 129fn foo(a: A) { a.<|>() }
195 a.<|>() 130"#,
196 } 131 expect![[""]],
197 ",
198 ),
199 @"[]"
200 ); 132 );
201 } 133 }
202 134
203 #[test] 135 #[test]
204 fn test_struct_field_visibility_private() { 136 fn test_visibility_filtering() {
205 assert_debug_snapshot!( 137 check(
206 do_ref_completion( 138 r#"
207 r" 139mod inner {
208 mod inner { 140 pub struct A {
209 struct A { 141 private_field: u32,
210 private_field: u32, 142 pub pub_field: u32,
211 pub pub_field: u32, 143 pub(crate) crate_field: u32,
212 pub(crate) crate_field: u32, 144 pub(super) super_field: u32,
213 pub(super) super_field: u32,
214 }
215 }
216 fn foo(a: inner::A) {
217 a.<|>
218 }
219 ",
220 ),
221 @r###"
222 [
223 CompletionItem {
224 label: "crate_field",
225 source_range: 313..313,
226 delete: 313..313,
227 insert: "crate_field",
228 kind: Field,
229 detail: "u32",
230 },
231 CompletionItem {
232 label: "pub_field",
233 source_range: 313..313,
234 delete: 313..313,
235 insert: "pub_field",
236 kind: Field,
237 detail: "u32",
238 },
239 CompletionItem {
240 label: "super_field",
241 source_range: 313..313,
242 delete: 313..313,
243 insert: "super_field",
244 kind: Field,
245 detail: "u32",
246 },
247 ]
248 "###
249 );
250 } 145 }
251 146}
252 #[test] 147fn foo(a: inner::A) { a.<|> }
253 fn test_union_field_completion() { 148"#,
254 assert_debug_snapshot!( 149 expect![[r#"
255 do_ref_completion( 150 fd crate_field u32
256 r" 151 fd pub_field u32
257 union Un { 152 fd super_field u32
258 field: u8, 153 "#]],
259 other: u16,
260 }
261
262 fn foo(u: Un) {
263 u.<|>
264 }
265 ",
266 ),
267 @r###"
268 [
269 CompletionItem {
270 label: "field",
271 source_range: 140..140,
272 delete: 140..140,
273 insert: "field",
274 kind: Field,
275 detail: "u8",
276 },
277 CompletionItem {
278 label: "other",
279 source_range: 140..140,
280 delete: 140..140,
281 insert: "other",
282 kind: Field,
283 detail: "u16",
284 },
285 ]
286 "###
287 ); 154 );
288 }
289 155
290 #[test] 156 check(
291 fn test_method_completion() { 157 r#"
292 assert_debug_snapshot!( 158struct A {}
293 do_ref_completion( 159mod m {
294 r" 160 impl super::A {
295 struct A {} 161 fn private_method(&self) {}
296 impl A { 162 pub(super) fn the_method(&self) {}
297 fn the_method(&self) {} 163 }
298 } 164}
299 fn foo(a: A) { 165fn foo(a: A) { a.<|> }
300 a.<|> 166"#,
301 } 167 expect![[r#"
302 ", 168 me the_method() pub(super) fn the_method(&self)
303 ), 169 "#]],
304 @r###"
305 [
306 CompletionItem {
307 label: "the_method()",
308 source_range: 144..144,
309 delete: 144..144,
310 insert: "the_method()$0",
311 kind: Method,
312 lookup: "the_method",
313 detail: "fn the_method(&self)",
314 },
315 ]
316 "###
317 ); 170 );
318 } 171 }
319 172
320 #[test] 173 #[test]
321 fn test_method_completion_only_fitting_impls() { 174 fn test_union_field_completion() {
322 assert_debug_snapshot!( 175 check(
323 do_ref_completion( 176 r#"
324 r" 177union U { field: u8, other: u16 }
325 struct A<T> {} 178fn foo(u: U) { u.<|> }
326 impl A<u32> { 179"#,
327 fn the_method(&self) {} 180 expect![[r#"
328 } 181 fd field u8
329 impl A<i32> { 182 fd other u16
330 fn the_other_method(&self) {} 183 "#]],
331 }
332 fn foo(a: A<u32>) {
333 a.<|>
334 }
335 ",
336 ),
337 @r###"
338 [
339 CompletionItem {
340 label: "the_method()",
341 source_range: 243..243,
342 delete: 243..243,
343 insert: "the_method()$0",
344 kind: Method,
345 lookup: "the_method",
346 detail: "fn the_method(&self)",
347 },
348 ]
349 "###
350 ); 184 );
351 } 185 }
352 186
353 #[test] 187 #[test]
354 fn test_method_completion_private() { 188 fn test_method_completion_only_fitting_impls() {
355 assert_debug_snapshot!( 189 check(
356 do_ref_completion( 190 r#"
357 r" 191struct A<T> {}
358 struct A {} 192impl A<u32> {
359 mod m { 193 fn the_method(&self) {}
360 impl super::A { 194}
361 fn private_method(&self) {} 195impl A<i32> {
362 pub(super) fn the_method(&self) {} 196 fn the_other_method(&self) {}
363 } 197}
364 } 198fn foo(a: A<u32>) { a.<|> }
365 fn foo(a: A) { 199"#,
366 a.<|> 200 expect![[r#"
367 } 201 me the_method() fn the_method(&self)
368 ", 202 "#]],
369 ), 203 )
370 @r###"
371 [
372 CompletionItem {
373 label: "the_method()",
374 source_range: 256..256,
375 delete: 256..256,
376 insert: "the_method()$0",
377 kind: Method,
378 lookup: "the_method",
379 detail: "pub(super) fn the_method(&self)",
380 },
381 ]
382 "###
383 );
384 } 204 }
385 205
386 #[test] 206 #[test]
387 fn test_trait_method_completion() { 207 fn test_trait_method_completion() {
388 assert_debug_snapshot!( 208 check(
389 do_ref_completion( 209 r#"
390 r" 210struct A {}
391 struct A {} 211trait Trait { fn the_method(&self); }
392 trait Trait { fn the_method(&self); } 212impl Trait for A {}
393 impl Trait for A {} 213fn foo(a: A) { a.<|> }
394 fn foo(a: A) { 214"#,
395 a.<|> 215 expect![[r#"
396 } 216 me the_method() fn the_method(&self)
397 ", 217 "#]],
398 ),
399 @r###"
400 [
401 CompletionItem {
402 label: "the_method()",
403 source_range: 151..151,
404 delete: 151..151,
405 insert: "the_method()$0",
406 kind: Method,
407 lookup: "the_method",
408 detail: "fn the_method(&self)",
409 },
410 ]
411 "###
412 ); 218 );
413 } 219 }
414 220
415 #[test] 221 #[test]
416 fn test_trait_method_completion_deduplicated() { 222 fn test_trait_method_completion_deduplicated() {
417 assert_debug_snapshot!( 223 check(
418 do_ref_completion( 224 r"
419 r" 225struct A {}
420 struct A {} 226trait Trait { fn the_method(&self); }
421 trait Trait { fn the_method(&self); } 227impl<T> Trait for T {}
422 impl<T> Trait for T {} 228fn foo(a: &A) { a.<|> }
423 fn foo(a: &A) { 229",
424 a.<|> 230 expect![[r#"
425 } 231 me the_method() fn the_method(&self)
426 ", 232 "#]],
427 ),
428 @r###"
429 [
430 CompletionItem {
431 label: "the_method()",
432 source_range: 155..155,
433 delete: 155..155,
434 insert: "the_method()$0",
435 kind: Method,
436 lookup: "the_method",
437 detail: "fn the_method(&self)",
438 },
439 ]
440 "###
441 ); 233 );
442 } 234 }
443 235
444 #[test] 236 #[test]
445 fn completes_trait_method_from_other_module() { 237 fn completes_trait_method_from_other_module() {
446 assert_debug_snapshot!( 238 check(
447 do_ref_completion(
448 r"
449 struct A {}
450 mod m {
451 pub trait Trait { fn the_method(&self); }
452 }
453 use m::Trait;
454 impl Trait for A {}
455 fn foo(a: A) {
456 a.<|>
457 }
458 ",
459 ),
460 @r###"
461 [
462 CompletionItem {
463 label: "the_method()",
464 source_range: 219..219,
465 delete: 219..219,
466 insert: "the_method()$0",
467 kind: Method,
468 lookup: "the_method",
469 detail: "fn the_method(&self)",
470 },
471 ]
472 "###
473 );
474 }
475
476 #[test]
477 fn test_no_non_self_method() {
478 assert_debug_snapshot!(
479 do_ref_completion(
480 r" 239 r"
481 struct A {} 240struct A {}
482 impl A { 241mod m {
483 fn the_method() {} 242 pub trait Trait { fn the_method(&self); }
484 } 243}
485 fn foo(a: A) { 244use m::Trait;
486 a.<|> 245impl Trait for A {}
487 } 246fn foo(a: A) { a.<|> }
488 ", 247",
489 ), 248 expect![[r#"
490 @"[]" 249 me the_method() fn the_method(&self)
250 "#]],
491 ); 251 );
492 } 252 }
493 253
494 #[test] 254 #[test]
495 fn test_method_attr_filtering() { 255 fn test_no_non_self_method() {
496 assert_debug_snapshot!( 256 check(
497 do_ref_completion( 257 r#"
498 r" 258struct A {}
499 struct A {} 259impl A {
500 impl A { 260 fn the_method() {}
501 #[inline] 261}
502 fn the_method(&self) { 262fn foo(a: A) {
503 let x = 1; 263 a.<|>
504 let y = 2; 264}
505 } 265"#,
506 } 266 expect![[""]],
507 fn foo(a: A) {
508 a.<|>
509 }
510 ",
511 ),
512 @r###"
513 [
514 CompletionItem {
515 label: "the_method()",
516 source_range: 249..249,
517 delete: 249..249,
518 insert: "the_method()$0",
519 kind: Method,
520 lookup: "the_method",
521 detail: "fn the_method(&self)",
522 },
523 ]
524 "###
525 ); 267 );
526 } 268 }
527 269
528 #[test] 270 #[test]
529 fn test_tuple_field_completion() { 271 fn test_tuple_field_completion() {
530 assert_debug_snapshot!( 272 check(
531 do_ref_completion( 273 r#"
532 r" 274fn foo() {
533 fn foo() { 275 let b = (0, 3.14);
534 let b = (0, 3.14); 276 b.<|>
535 b.<|> 277}
536 } 278"#,
537 ", 279 expect![[r#"
538 ), 280 fd 0 i32
539 @r###" 281 fd 1 f64
540 [ 282 "#]],
541 CompletionItem { 283 )
542 label: "0",
543 source_range: 75..75,
544 delete: 75..75,
545 insert: "0",
546 kind: Field,
547 detail: "i32",
548 },
549 CompletionItem {
550 label: "1",
551 source_range: 75..75,
552 delete: 75..75,
553 insert: "1",
554 kind: Field,
555 detail: "f64",
556 },
557 ]
558 "###
559 );
560 } 284 }
561 285
562 #[test] 286 #[test]
563 fn test_tuple_field_inference() { 287 fn test_tuple_field_inference() {
564 assert_debug_snapshot!( 288 check(
565 do_ref_completion( 289 r#"
566 r" 290pub struct S;
567 pub struct S; 291impl S { pub fn blah(&self) {} }
568 impl S {
569 pub fn blah(&self) {}
570 }
571 292
572 struct T(S); 293struct T(S);
573 294
574 impl T { 295impl T {
575 fn foo(&self) { 296 fn foo(&self) {
576 // FIXME: This doesn't work without the trailing `a` as `0.` is a float 297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
577 self.0.a<|> 298 self.0.a<|>
578 }
579 }
580 ",
581 ),
582 @r###"
583 [
584 CompletionItem {
585 label: "blah()",
586 source_range: 299..300,
587 delete: 299..300,
588 insert: "blah()$0",
589 kind: Method,
590 lookup: "blah",
591 detail: "pub fn blah(&self)",
592 },
593 ]
594 "###
595 );
596 } 299 }
597 300}
598 #[test] 301"#,
599 fn test_completion_works_in_consts() { 302 expect![[r#"
600 assert_debug_snapshot!( 303 me blah() pub fn blah(&self)
601 do_ref_completion( 304 "#]],
602 r"
603 struct A { the_field: u32 }
604 const X: u32 = {
605 A { the_field: 92 }.<|>
606 };
607 ",
608 ),
609 @r###"
610 [
611 CompletionItem {
612 label: "the_field",
613 source_range: 106..106,
614 delete: 106..106,
615 insert: "the_field",
616 kind: Field,
617 detail: "u32",
618 },
619 ]
620 "###
621 ); 305 );
622 } 306 }
623 307
624 #[test] 308 #[test]
625 fn test_completion_await_impls_future() { 309 fn test_completion_works_in_consts() {
626 assert_debug_snapshot!( 310 check(
627 do_completion( 311 r#"
628 r###" 312struct A { the_field: u32 }
629 //- /main.rs 313const X: u32 = {
630 use std::future::*; 314 A { the_field: 92 }.<|>
631 struct A {} 315};
632 impl Future for A {} 316"#,
633 fn foo(a: A) { 317 expect![[r#"
634 a.<|> 318 fd the_field u32
635 } 319 "#]],
636
637 //- /std/lib.rs
638 pub mod future {
639 #[lang = "future_trait"]
640 pub trait Future {}
641 }
642 "###, CompletionKind::Keyword),
643 @r###"
644 [
645 CompletionItem {
646 label: "await",
647 source_range: 74..74,
648 delete: 74..74,
649 insert: "await",
650 detail: "expr.await",
651 },
652 ]
653 "###
654 )
655 }
656
657 #[test]
658 fn test_super_super_completion() {
659 assert_debug_snapshot!(
660 do_ref_completion(
661 r"
662 mod a {
663 const A: usize = 0;
664
665 mod b {
666 const B: usize = 0;
667
668 mod c {
669 use super::super::<|>
670 }
671 }
672 }
673 ",
674 ),
675 @r###"
676 [
677 CompletionItem {
678 label: "A",
679 source_range: 217..217,
680 delete: 217..217,
681 insert: "A",
682 kind: Const,
683 },
684 CompletionItem {
685 label: "b",
686 source_range: 217..217,
687 delete: 217..217,
688 insert: "b",
689 kind: Module,
690 },
691 ]
692 "###
693 ); 320 );
694 } 321 }
695 322
696 #[test] 323 #[test]
697 fn works_in_simple_macro_1() { 324 fn works_in_simple_macro_1() {
698 assert_debug_snapshot!( 325 check(
699 do_ref_completion( 326 r#"
700 r" 327macro_rules! m { ($e:expr) => { $e } }
701 macro_rules! m { ($e:expr) => { $e } } 328struct A { the_field: u32 }
702 struct A { the_field: u32 } 329fn foo(a: A) {
703 fn foo(a: A) { 330 m!(a.x<|>)
704 m!(a.x<|>) 331}
705 } 332"#,
706 ", 333 expect![[r#"
707 ), 334 fd the_field u32
708 @r###" 335 "#]],
709 [
710 CompletionItem {
711 label: "the_field",
712 source_range: 156..157,
713 delete: 156..157,
714 insert: "the_field",
715 kind: Field,
716 detail: "u32",
717 },
718 ]
719 "###
720 );
721 }
722
723 #[test]
724 fn works_in_simple_macro_recursive() {
725 assert_debug_snapshot!(
726 do_ref_completion(
727 r"
728 macro_rules! m { ($e:expr) => { $e } }
729 struct A { the_field: u32 }
730 fn foo(a: A) {
731 m!(a.x<|>)
732 }
733 ",
734 ),
735 @r###"
736 [
737 CompletionItem {
738 label: "the_field",
739 source_range: 156..157,
740 delete: 156..157,
741 insert: "the_field",
742 kind: Field,
743 detail: "u32",
744 },
745 ]
746 "###
747 ); 336 );
748 } 337 }
749 338
750 #[test] 339 #[test]
751 fn works_in_simple_macro_2() { 340 fn works_in_simple_macro_2() {
752 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery 341 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
753 assert_debug_snapshot!( 342 check(
754 do_ref_completion( 343 r#"
755 r" 344macro_rules! m { ($e:expr) => { $e } }
756 macro_rules! m { ($e:expr) => { $e } } 345struct A { the_field: u32 }
757 struct A { the_field: u32 } 346fn foo(a: A) {
758 fn foo(a: A) { 347 m!(a.<|>)
759 m!(a.<|>) 348}
760 } 349"#,
761 ", 350 expect![[r#"
762 ), 351 fd the_field u32
763 @r###" 352 "#]],
764 [
765 CompletionItem {
766 label: "the_field",
767 source_range: 156..156,
768 delete: 156..156,
769 insert: "the_field",
770 kind: Field,
771 detail: "u32",
772 },
773 ]
774 "###
775 ); 353 );
776 } 354 }
777 355
778 #[test] 356 #[test]
779 fn works_in_simple_macro_recursive_1() { 357 fn works_in_simple_macro_recursive_1() {
780 assert_debug_snapshot!( 358 check(
781 do_ref_completion( 359 r#"
782 r" 360macro_rules! m { ($e:expr) => { $e } }
783 macro_rules! m { ($e:expr) => { $e } } 361struct A { the_field: u32 }
784 struct A { the_field: u32 } 362fn foo(a: A) {
785 fn foo(a: A) { 363 m!(m!(m!(a.x<|>)))
786 m!(m!(m!(a.x<|>))) 364}
787 } 365"#,
788 ", 366 expect![[r#"
789 ), 367 fd the_field u32
790 @r###" 368 "#]],
791 [
792 CompletionItem {
793 label: "the_field",
794 source_range: 162..163,
795 delete: 162..163,
796 insert: "the_field",
797 kind: Field,
798 detail: "u32",
799 },
800 ]
801 "###
802 ); 369 );
803 } 370 }
804 371
805 #[test] 372 #[test]
806 fn macro_expansion_resilient() { 373 fn macro_expansion_resilient() {
807 assert_debug_snapshot!( 374 check(
808 do_ref_completion( 375 r#"
809 r" 376macro_rules! dbg {
810 macro_rules! dbg { 377 () => {};
811 () => {}; 378 ($val:expr) => {
812 ($val:expr) => { 379 match $val { tmp => { tmp } }
813 match $val { tmp => { tmp } } 380 };
814 }; 381 // Trailing comma with single argument is ignored
815 // Trailing comma with single argument is ignored 382 ($val:expr,) => { $crate::dbg!($val) };
816 ($val:expr,) => { $crate::dbg!($val) }; 383 ($($val:expr),+ $(,)?) => {
817 ($($val:expr),+ $(,)?) => { 384 ($($crate::dbg!($val)),+,)
818 ($($crate::dbg!($val)),+,) 385 };
819 }; 386}
820 } 387struct A { the_field: u32 }
821 struct A { the_field: u32 } 388fn foo(a: A) {
822 fn foo(a: A) { 389 dbg!(a.<|>)
823 dbg!(a.<|>) 390}
824 } 391"#,
825 ", 392 expect![[r#"
826 ), 393 fd the_field u32
827 @r###" 394 "#]],
828 [
829 CompletionItem {
830 label: "the_field",
831 source_range: 552..552,
832 delete: 552..552,
833 insert: "the_field",
834 kind: Field,
835 detail: "u32",
836 },
837 ]
838 "###
839 ); 395 );
840 } 396 }
841 397
842 #[test] 398 #[test]
843 fn test_method_completion_3547() { 399 fn test_method_completion_issue_3547() {
844 assert_debug_snapshot!( 400 check(
845 do_ref_completion( 401 r#"
846 r" 402struct HashSet<T> {}
847 struct HashSet<T> {} 403impl<T> HashSet<T> {
848 impl<T> HashSet<T> { 404 pub fn the_method(&self) {}
849 pub fn the_method(&self) {} 405}
850 } 406fn foo() {
851 fn foo() { 407 let s: HashSet<_>;
852 let s: HashSet<_>; 408 s.<|>
853 s.<|> 409}
854 } 410"#,
855 ", 411 expect![[r#"
856 ), 412 me the_method() pub fn the_method(&self)
857 @r###" 413 "#]],
858 [
859 CompletionItem {
860 label: "the_method()",
861 source_range: 201..201,
862 delete: 201..201,
863 insert: "the_method()$0",
864 kind: Method,
865 lookup: "the_method",
866 detail: "pub fn the_method(&self)",
867 },
868 ]
869 "###
870 ); 414 );
871 } 415 }
872} 416}
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
index a661932a3..db2abb4f1 100644
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! See `complete_fn_param`.
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, ModuleItemOwner}, 4 ast::{self, ModuleItemOwner},
@@ -18,6 +18,7 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
18 } 18 }
19 19
20 let mut params = FxHashMap::default(); 20 let mut params = FxHashMap::default();
21 let me = ctx.token.ancestors().find_map(ast::FnDef::cast);
21 for node in ctx.token.parent().ancestors() { 22 for node in ctx.token.parent().ancestors() {
22 let items = match_ast! { 23 let items = match_ast! {
23 match node { 24 match node {
@@ -28,25 +29,26 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
28 }; 29 };
29 for item in items { 30 for item in items {
30 if let ast::ModuleItem::FnDef(func) = item { 31 if let ast::ModuleItem::FnDef(func) = item {
32 if Some(&func) == me.as_ref() {
33 continue;
34 }
31 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| { 35 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
32 let text = param.syntax().text().to_string(); 36 let text = param.syntax().text().to_string();
33 params.entry(text).or_insert((0, param)).0 += 1; 37 params.entry(text).or_insert(param);
34 }) 38 })
35 } 39 }
36 } 40 }
37 } 41 }
42
38 params 43 params
39 .into_iter() 44 .into_iter()
40 .filter_map(|(label, (count, param))| { 45 .filter_map(|(label, param)| {
41 let lookup = param.pat()?.syntax().text().to_string(); 46 let lookup = param.pat()?.syntax().text().to_string();
42 if count < 2 { 47 Some((label, lookup))
43 None
44 } else {
45 Some((label, lookup))
46 }
47 }) 48 })
48 .for_each(|(label, lookup)| { 49 .for_each(|(label, lookup)| {
49 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 50 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
51 .kind(crate::CompletionItemKind::Binding)
50 .lookup_by(lookup) 52 .lookup_by(lookup)
51 .add_to(acc) 53 .add_to(acc)
52 }); 54 });
@@ -54,85 +56,70 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 56
55#[cfg(test)] 57#[cfg(test)]
56mod tests { 58mod tests {
57 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 59 use expect::{expect, Expect};
58 use insta::assert_debug_snapshot;
59 60
60 fn do_magic_completion(code: &str) -> Vec<CompletionItem> { 61 use crate::completion::{test_utils::completion_list, CompletionKind};
61 do_completion(code, CompletionKind::Magic) 62
63 fn check(ra_fixture: &str, expect: Expect) {
64 let actual = completion_list(ra_fixture, CompletionKind::Magic);
65 expect.assert_eq(&actual);
62 } 66 }
63 67
64 #[test] 68 #[test]
65 fn test_param_completion_last_param() { 69 fn test_param_completion_last_param() {
66 assert_debug_snapshot!( 70 check(
67 do_magic_completion( 71 r#"
68 r" 72fn foo(file_id: FileId) {}
69 fn foo(file_id: FileId) {} 73fn bar(file_id: FileId) {}
70 fn bar(file_id: FileId) {} 74fn baz(file<|>) {}
71 fn baz(file<|>) {} 75"#,
72 ", 76 expect![[r#"
73 ), 77 bn file_id: FileId
74 @r###" 78 "#]],
75 [
76 CompletionItem {
77 label: "file_id: FileId",
78 source_range: 110..114,
79 delete: 110..114,
80 insert: "file_id: FileId",
81 lookup: "file_id",
82 },
83 ]
84 "###
85 ); 79 );
86 } 80 }
87 81
88 #[test] 82 #[test]
89 fn test_param_completion_nth_param() { 83 fn test_param_completion_nth_param() {
90 assert_debug_snapshot!( 84 check(
91 do_magic_completion( 85 r#"
92 r" 86fn foo(file_id: FileId) {}
93 fn foo(file_id: FileId) {} 87fn baz(file<|>, x: i32) {}
94 fn bar(file_id: FileId) {} 88"#,
95 fn baz(file<|>, x: i32) {} 89 expect![[r#"
96 ", 90 bn file_id: FileId
97 ), 91 "#]],
98 @r###"
99 [
100 CompletionItem {
101 label: "file_id: FileId",
102 source_range: 110..114,
103 delete: 110..114,
104 insert: "file_id: FileId",
105 lookup: "file_id",
106 },
107 ]
108 "###
109 ); 92 );
110 } 93 }
111 94
112 #[test] 95 #[test]
113 fn test_param_completion_trait_param() { 96 fn test_param_completion_trait_param() {
114 assert_debug_snapshot!( 97 check(
115 do_magic_completion( 98 r#"
116 r" 99pub(crate) trait SourceRoot {
117 pub(crate) trait SourceRoot { 100 pub fn contains(&self, file_id: FileId) -> bool;
118 pub fn contains(&self, file_id: FileId) -> bool; 101 pub fn module_map(&self) -> &ModuleMap;
119 pub fn module_map(&self) -> &ModuleMap; 102 pub fn lines(&self, file_id: FileId) -> &LineIndex;
120 pub fn lines(&self, file_id: FileId) -> &LineIndex; 103 pub fn syntax(&self, file<|>)
121 pub fn syntax(&self, file<|>) 104}
122 } 105"#,
123 ", 106 expect![[r#"
124 ), 107 bn file_id: FileId
125 @r###" 108 "#]],
126 [
127 CompletionItem {
128 label: "file_id: FileId",
129 source_range: 289..293,
130 delete: 289..293,
131 insert: "file_id: FileId",
132 lookup: "file_id",
133 },
134 ]
135 "###
136 ); 109 );
137 } 110 }
111
112 #[test]
113 fn completes_param_in_inner_function() {
114 check(
115 r#"
116fn outer(text: String) {
117 fn inner(<|>)
118}
119"#,
120 expect![[r#"
121 bn text: String
122 "#]],
123 )
124 }
138} 125}
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index fd95bc410..fcdaeef49 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,11 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ 3use ra_syntax::{ast, SyntaxKind};
4 ast::{self, LoopBodyOwner}, 4use test_utils::mark;
5 match_ast, AstNode,
6 SyntaxKind::*,
7 SyntaxToken,
8};
9 5
10use crate::completion::{ 6use crate::completion::{
11 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -16,14 +12,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
16 let source_range = ctx.source_range(); 12 let source_range = ctx.source_range();
17 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) { 13 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
18 (Some(_), None) => { 14 (Some(_), None) => {
19 CompletionItem::new(CompletionKind::Keyword, source_range, "crate") 15 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
20 .kind(CompletionItemKind::Keyword) 16 .kind(CompletionItemKind::Keyword)
21 .insert_text("crate::") 17 .insert_text("crate::")
22 .add_to(acc); 18 .add_to(acc);
23 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 19 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
24 .kind(CompletionItemKind::Keyword) 20 .kind(CompletionItemKind::Keyword)
25 .add_to(acc); 21 .add_to(acc);
26 CompletionItem::new(CompletionKind::Keyword, source_range, "super") 22 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
27 .kind(CompletionItemKind::Keyword) 23 .kind(CompletionItemKind::Keyword)
28 .insert_text("super::") 24 .insert_text("super::")
29 .add_to(acc); 25 .add_to(acc);
@@ -32,77 +28,147 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
32 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 28 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
33 .kind(CompletionItemKind::Keyword) 29 .kind(CompletionItemKind::Keyword)
34 .add_to(acc); 30 .add_to(acc);
35 CompletionItem::new(CompletionKind::Keyword, source_range, "super") 31 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
36 .kind(CompletionItemKind::Keyword) 32 .kind(CompletionItemKind::Keyword)
37 .insert_text("super::") 33 .insert_text("super::")
38 .add_to(acc); 34 .add_to(acc);
39 } 35 }
40 _ => {} 36 _ => {}
41 } 37 }
42}
43 38
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 39 // Suggest .await syntax for types that implement Future trait
45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 40 if let Some(receiver) = &ctx.dot_receiver {
46 .kind(CompletionItemKind::Keyword); 41 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
47 42 if ty.impls_future(ctx.db) {
48 match ctx.config.snippet_cap { 43 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
49 Some(cap) => res.insert_snippet(cap, snippet), 44 .kind(CompletionItemKind::Keyword)
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }), 45 .detail("expr.await")
46 .insert_text("await")
47 .add_to(acc);
48 }
49 };
51 } 50 }
52 .build()
53} 51}
54 52
55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 53pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
56 if !ctx.is_trivial_path { 54 if ctx.token.kind() == SyntaxKind::COMMENT {
55 mark::hit!(no_keyword_completion_in_comments);
57 return; 56 return;
58 } 57 }
59 58
60 let fn_def = match &ctx.function_syntax { 59 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
61 Some(it) => it, 60 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
62 None => return, 61 add_keyword(ctx, acc, "where", "where ");
63 }; 62 return;
64 acc.add(keyword(ctx, "if", "if $0 {}")); 63 }
65 acc.add(keyword(ctx, "match", "match $0 {}")); 64 if ctx.unsafe_is_prev {
66 acc.add(keyword(ctx, "while", "while $0 {}")); 65 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
67 acc.add(keyword(ctx, "loop", "loop {$0}")); 66 add_keyword(ctx, acc, "fn", "fn $0() {}")
67 }
68
69 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
70 || ctx.block_expr_parent
71 {
72 add_keyword(ctx, acc, "trait", "trait $0 {}");
73 add_keyword(ctx, acc, "impl", "impl $0 {}");
74 }
75
76 return;
77 }
78 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
79 add_keyword(ctx, acc, "fn", "fn $0() {}");
80 }
81 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
82 || ctx.block_expr_parent
83 {
84 add_keyword(ctx, acc, "use", "use ");
85 add_keyword(ctx, acc, "impl", "impl $0 {}");
86 add_keyword(ctx, acc, "trait", "trait $0 {}");
87 }
88
89 if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent {
90 add_keyword(ctx, acc, "enum", "enum $0 {}");
91 add_keyword(ctx, acc, "struct", "struct $0");
92 add_keyword(ctx, acc, "union", "union $0 {}");
93 }
94
95 if ctx.is_expr {
96 add_keyword(ctx, acc, "match", "match $0 {}");
97 add_keyword(ctx, acc, "while", "while $0 {}");
98 add_keyword(ctx, acc, "loop", "loop {$0}");
99 add_keyword(ctx, acc, "if", "if ");
100 add_keyword(ctx, acc, "if let", "if let ");
101 }
102
103 if ctx.if_is_prev || ctx.block_expr_parent {
104 add_keyword(ctx, acc, "let", "let ");
105 }
68 106
69 if ctx.after_if { 107 if ctx.after_if {
70 acc.add(keyword(ctx, "else", "else {$0}")); 108 add_keyword(ctx, acc, "else", "else {$0}");
71 acc.add(keyword(ctx, "else if", "else if $0 {}")); 109 add_keyword(ctx, acc, "else if", "else if $0 {}");
110 }
111 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
112 || ctx.block_expr_parent
113 {
114 add_keyword(ctx, acc, "mod", "mod $0 {}");
115 }
116 if ctx.bind_pat_parent || ctx.ref_pat_parent {
117 add_keyword(ctx, acc, "mut", "mut ");
118 }
119 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
120 add_keyword(ctx, acc, "const", "const ");
121 add_keyword(ctx, acc, "type", "type ");
122 }
123 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
124 || ctx.block_expr_parent
125 {
126 add_keyword(ctx, acc, "static", "static ");
127 };
128 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
129 || ctx.block_expr_parent
130 {
131 add_keyword(ctx, acc, "extern", "extern ");
132 }
133 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm {
134 add_keyword(ctx, acc, "unsafe", "unsafe ");
72 } 135 }
73 if is_in_loop_body(&ctx.token) { 136 if ctx.in_loop_body {
74 if ctx.can_be_stmt { 137 if ctx.can_be_stmt {
75 acc.add(keyword(ctx, "continue", "continue;")); 138 add_keyword(ctx, acc, "continue", "continue;");
76 acc.add(keyword(ctx, "break", "break;")); 139 add_keyword(ctx, acc, "break", "break;");
77 } else { 140 } else {
78 acc.add(keyword(ctx, "continue", "continue")); 141 add_keyword(ctx, acc, "continue", "continue");
79 acc.add(keyword(ctx, "break", "break")); 142 add_keyword(ctx, acc, "break", "break");
80 } 143 }
81 } 144 }
145 if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent {
146 add_keyword(ctx, acc, "pub", "pub ")
147 }
148
149 if !ctx.is_trivial_path {
150 return;
151 }
152 let fn_def = match &ctx.function_syntax {
153 Some(it) => it,
154 None => return,
155 };
82 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); 156 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
83} 157}
84 158
85fn is_in_loop_body(leaf: &SyntaxToken) -> bool { 159fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
86 // FIXME move this to CompletionContext and make it handle macros 160 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
87 for node in leaf.parent().ancestors() { 161 .kind(CompletionItemKind::Keyword);
88 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { 162
89 break; 163 match ctx.config.snippet_cap {
90 } 164 Some(cap) => res.insert_snippet(cap, snippet),
91 let loop_body = match_ast! { 165 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
92 match node {
93 ast::ForExpr(it) => it.loop_body(),
94 ast::WhileExpr(it) => it.loop_body(),
95 ast::LoopExpr(it) => it.loop_body(),
96 _ => None,
97 }
98 };
99 if let Some(body) = loop_body {
100 if body.syntax().text_range().contains_range(leaf.text_range()) {
101 return true;
102 }
103 }
104 } 166 }
105 false 167 .build()
168}
169
170fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
171 acc.add(keyword(ctx, kw, snippet));
106} 172}
107 173
108fn complete_return( 174fn complete_return(
@@ -121,666 +187,354 @@ fn complete_return(
121 187
122#[cfg(test)] 188#[cfg(test)]
123mod tests { 189mod tests {
124 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 190 use expect::{expect, Expect};
125 use insta::assert_debug_snapshot; 191
192 use crate::completion::{
193 test_utils::{check_edit, completion_list},
194 CompletionKind,
195 };
196 use test_utils::mark;
126 197
127 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { 198 fn check(ra_fixture: &str, expect: Expect) {
128 do_completion(code, CompletionKind::Keyword) 199 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
200 expect.assert_eq(&actual)
129 } 201 }
130 202
131 #[test] 203 #[test]
132 fn completes_keywords_in_use_stmt() { 204 fn test_keywords_in_use_stmt() {
133 assert_debug_snapshot!( 205 check(
134 do_keyword_completion( 206 r"use <|>",
135 r" 207 expect![[r#"
136 use <|> 208 kw crate::
137 ", 209 kw self
138 ), 210 kw super::
139 @r###" 211 "#]],
140 [
141 CompletionItem {
142 label: "crate",
143 source_range: 21..21,
144 delete: 21..21,
145 insert: "crate::",
146 kind: Keyword,
147 },
148 CompletionItem {
149 label: "self",
150 source_range: 21..21,
151 delete: 21..21,
152 insert: "self",
153 kind: Keyword,
154 },
155 CompletionItem {
156 label: "super",
157 source_range: 21..21,
158 delete: 21..21,
159 insert: "super::",
160 kind: Keyword,
161 },
162 ]
163 "###
164 ); 212 );
165 213
166 assert_debug_snapshot!( 214 check(
167 do_keyword_completion( 215 r"use a::<|>",
168 r" 216 expect![[r#"
169 use a::<|> 217 kw self
170 ", 218 kw super::
171 ), 219 "#]],
172 @r###"
173 [
174 CompletionItem {
175 label: "self",
176 source_range: 24..24,
177 delete: 24..24,
178 insert: "self",
179 kind: Keyword,
180 },
181 CompletionItem {
182 label: "super",
183 source_range: 24..24,
184 delete: 24..24,
185 insert: "super::",
186 kind: Keyword,
187 },
188 ]
189 "###
190 ); 220 );
191 221
192 assert_debug_snapshot!( 222 check(
193 do_keyword_completion( 223 r"use a::{b, <|>}",
194 r" 224 expect![[r#"
195 use a::{b, <|>} 225 kw self
196 ", 226 kw super::
197 ), 227 "#]],
198 @r###"
199 [
200 CompletionItem {
201 label: "self",
202 source_range: 28..28,
203 delete: 28..28,
204 insert: "self",
205 kind: Keyword,
206 },
207 CompletionItem {
208 label: "super",
209 source_range: 28..28,
210 delete: 28..28,
211 insert: "super::",
212 kind: Keyword,
213 },
214 ]
215 "###
216 ); 228 );
217 } 229 }
218 230
219 #[test] 231 #[test]
220 fn completes_various_keywords_in_function() { 232 fn test_keywords_at_source_file_level() {
221 assert_debug_snapshot!( 233 check(
222 do_keyword_completion( 234 r"m<|>",
223 r" 235 expect![[r#"
224 fn quux() { 236 kw const
225 <|> 237 kw enum
226 } 238 kw extern
227 ", 239 kw fn
228 ), 240 kw impl
229 @r###" 241 kw mod
230 [ 242 kw pub
231 CompletionItem { 243 kw static
232 label: "if", 244 kw struct
233 source_range: 49..49, 245 kw trait
234 delete: 49..49, 246 kw type
235 insert: "if $0 {}", 247 kw union
236 kind: Keyword, 248 kw unsafe
237 }, 249 kw use
238 CompletionItem { 250 "#]],
239 label: "loop",
240 source_range: 49..49,
241 delete: 49..49,
242 insert: "loop {$0}",
243 kind: Keyword,
244 },
245 CompletionItem {
246 label: "match",
247 source_range: 49..49,
248 delete: 49..49,
249 insert: "match $0 {}",
250 kind: Keyword,
251 },
252 CompletionItem {
253 label: "return",
254 source_range: 49..49,
255 delete: 49..49,
256 insert: "return;",
257 kind: Keyword,
258 },
259 CompletionItem {
260 label: "while",
261 source_range: 49..49,
262 delete: 49..49,
263 insert: "while $0 {}",
264 kind: Keyword,
265 },
266 ]
267 "###
268 ); 251 );
269 } 252 }
270 253
271 #[test] 254 #[test]
272 fn completes_else_after_if() { 255 fn test_keywords_in_function() {
273 assert_debug_snapshot!( 256 check(
274 do_keyword_completion( 257 r"fn quux() { <|> }",
275 r" 258 expect![[r#"
276 fn quux() { 259 kw const
277 if true { 260 kw extern
278 () 261 kw fn
279 } <|> 262 kw if
280 } 263 kw if let
281 ", 264 kw impl
282 ), 265 kw let
283 @r###" 266 kw loop
284 [ 267 kw match
285 CompletionItem { 268 kw mod
286 label: "else", 269 kw return
287 source_range: 108..108, 270 kw static
288 delete: 108..108, 271 kw trait
289 insert: "else {$0}", 272 kw type
290 kind: Keyword, 273 kw unsafe
291 }, 274 kw use
292 CompletionItem { 275 kw while
293 label: "else if", 276 "#]],
294 source_range: 108..108,
295 delete: 108..108,
296 insert: "else if $0 {}",
297 kind: Keyword,
298 },
299 CompletionItem {
300 label: "if",
301 source_range: 108..108,
302 delete: 108..108,
303 insert: "if $0 {}",
304 kind: Keyword,
305 },
306 CompletionItem {
307 label: "loop",
308 source_range: 108..108,
309 delete: 108..108,
310 insert: "loop {$0}",
311 kind: Keyword,
312 },
313 CompletionItem {
314 label: "match",
315 source_range: 108..108,
316 delete: 108..108,
317 insert: "match $0 {}",
318 kind: Keyword,
319 },
320 CompletionItem {
321 label: "return",
322 source_range: 108..108,
323 delete: 108..108,
324 insert: "return;",
325 kind: Keyword,
326 },
327 CompletionItem {
328 label: "while",
329 source_range: 108..108,
330 delete: 108..108,
331 insert: "while $0 {}",
332 kind: Keyword,
333 },
334 ]
335 "###
336 ); 277 );
337 } 278 }
338 279
339 #[test] 280 #[test]
340 fn test_completion_return_value() { 281 fn test_keywords_inside_block() {
341 assert_debug_snapshot!( 282 check(
342 do_keyword_completion( 283 r"fn quux() { if true { <|> } }",
343 r" 284 expect![[r#"
344 fn quux() -> i32 { 285 kw const
345 <|> 286 kw extern
346 92 287 kw fn
347 } 288 kw if
348 ", 289 kw if let
349 ), 290 kw impl
350 @r###" 291 kw let
351 [ 292 kw loop
352 CompletionItem { 293 kw match
353 label: "if", 294 kw mod
354 source_range: 56..56, 295 kw return
355 delete: 56..56, 296 kw static
356 insert: "if $0 {}", 297 kw trait
357 kind: Keyword, 298 kw type
358 }, 299 kw unsafe
359 CompletionItem { 300 kw use
360 label: "loop", 301 kw while
361 source_range: 56..56, 302 "#]],
362 delete: 56..56,
363 insert: "loop {$0}",
364 kind: Keyword,
365 },
366 CompletionItem {
367 label: "match",
368 source_range: 56..56,
369 delete: 56..56,
370 insert: "match $0 {}",
371 kind: Keyword,
372 },
373 CompletionItem {
374 label: "return",
375 source_range: 56..56,
376 delete: 56..56,
377 insert: "return $0;",
378 kind: Keyword,
379 },
380 CompletionItem {
381 label: "while",
382 source_range: 56..56,
383 delete: 56..56,
384 insert: "while $0 {}",
385 kind: Keyword,
386 },
387 ]
388 "###
389 ); 303 );
390 assert_debug_snapshot!( 304 }
391 do_keyword_completion( 305
392 r" 306 #[test]
393 fn quux() { 307 fn test_keywords_after_if() {
394 <|> 308 check(
395 92 309 r#"fn quux() { if true { () } <|> }"#,
396 } 310 expect![[r#"
397 ", 311 kw const
398 ), 312 kw else
399 @r###" 313 kw else if
400 [ 314 kw extern
401 CompletionItem { 315 kw fn
402 label: "if", 316 kw if
403 source_range: 49..49, 317 kw if let
404 delete: 49..49, 318 kw impl
405 insert: "if $0 {}", 319 kw let
406 kind: Keyword, 320 kw loop
407 }, 321 kw match
408 CompletionItem { 322 kw mod
409 label: "loop", 323 kw return
410 source_range: 49..49, 324 kw static
411 delete: 49..49, 325 kw trait
412 insert: "loop {$0}", 326 kw type
413 kind: Keyword, 327 kw unsafe
414 }, 328 kw use
415 CompletionItem { 329 kw while
416 label: "match", 330 "#]],
417 source_range: 49..49, 331 );
418 delete: 49..49, 332 check_edit(
419 insert: "match $0 {}", 333 "else",
420 kind: Keyword, 334 r#"fn quux() { if true { () } <|> }"#,
421 }, 335 r#"fn quux() { if true { () } else {$0} }"#,
422 CompletionItem {
423 label: "return",
424 source_range: 49..49,
425 delete: 49..49,
426 insert: "return;",
427 kind: Keyword,
428 },
429 CompletionItem {
430 label: "while",
431 source_range: 49..49,
432 delete: 49..49,
433 insert: "while $0 {}",
434 kind: Keyword,
435 },
436 ]
437 "###
438 ); 336 );
439 } 337 }
440 338
441 #[test] 339 #[test]
442 fn dont_add_semi_after_return_if_not_a_statement() { 340 fn test_keywords_in_match_arm() {
443 assert_debug_snapshot!( 341 check(
444 do_keyword_completion( 342 r#"
445 r" 343fn quux() -> i32 {
446 fn quux() -> i32 { 344 match () { () => <|> }
447 match () { 345}
448 () => <|> 346"#,
449 } 347 expect![[r#"
450 } 348 kw if
451 ", 349 kw if let
452 ), 350 kw loop
453 @r###" 351 kw match
454 [ 352 kw return
455 CompletionItem { 353 kw unsafe
456 label: "if", 354 kw while
457 source_range: 97..97, 355 "#]],
458 delete: 97..97,
459 insert: "if $0 {}",
460 kind: Keyword,
461 },
462 CompletionItem {
463 label: "loop",
464 source_range: 97..97,
465 delete: 97..97,
466 insert: "loop {$0}",
467 kind: Keyword,
468 },
469 CompletionItem {
470 label: "match",
471 source_range: 97..97,
472 delete: 97..97,
473 insert: "match $0 {}",
474 kind: Keyword,
475 },
476 CompletionItem {
477 label: "return",
478 source_range: 97..97,
479 delete: 97..97,
480 insert: "return $0",
481 kind: Keyword,
482 },
483 CompletionItem {
484 label: "while",
485 source_range: 97..97,
486 delete: 97..97,
487 insert: "while $0 {}",
488 kind: Keyword,
489 },
490 ]
491 "###
492 ); 356 );
493 } 357 }
494 358
495 #[test] 359 #[test]
496 fn last_return_in_block_has_semi() { 360 fn test_keywords_in_trait_def() {
497 assert_debug_snapshot!( 361 check(
498 do_keyword_completion( 362 r"trait My { <|> }",
499 r" 363 expect![[r#"
500 fn quux() -> i32 { 364 kw const
501 if condition { 365 kw fn
502 <|> 366 kw type
503 } 367 kw unsafe
504 } 368 "#]],
505 ",
506 ),
507 @r###"
508 [
509 CompletionItem {
510 label: "if",
511 source_range: 95..95,
512 delete: 95..95,
513 insert: "if $0 {}",
514 kind: Keyword,
515 },
516 CompletionItem {
517 label: "loop",
518 source_range: 95..95,
519 delete: 95..95,
520 insert: "loop {$0}",
521 kind: Keyword,
522 },
523 CompletionItem {
524 label: "match",
525 source_range: 95..95,
526 delete: 95..95,
527 insert: "match $0 {}",
528 kind: Keyword,
529 },
530 CompletionItem {
531 label: "return",
532 source_range: 95..95,
533 delete: 95..95,
534 insert: "return $0;",
535 kind: Keyword,
536 },
537 CompletionItem {
538 label: "while",
539 source_range: 95..95,
540 delete: 95..95,
541 insert: "while $0 {}",
542 kind: Keyword,
543 },
544 ]
545 "###
546 ); 369 );
547 assert_debug_snapshot!( 370 }
548 do_keyword_completion( 371
549 r" 372 #[test]
550 fn quux() -> i32 { 373 fn test_keywords_in_impl_def() {
551 if condition { 374 check(
552 <|> 375 r"impl My { <|> }",
553 } 376 expect![[r#"
554 let x = 92; 377 kw const
555 x 378 kw fn
556 } 379 kw pub
557 ", 380 kw type
558 ), 381 kw unsafe
559 @r###" 382 "#]],
560 [
561 CompletionItem {
562 label: "if",
563 source_range: 95..95,
564 delete: 95..95,
565 insert: "if $0 {}",
566 kind: Keyword,
567 },
568 CompletionItem {
569 label: "loop",
570 source_range: 95..95,
571 delete: 95..95,
572 insert: "loop {$0}",
573 kind: Keyword,
574 },
575 CompletionItem {
576 label: "match",
577 source_range: 95..95,
578 delete: 95..95,
579 insert: "match $0 {}",
580 kind: Keyword,
581 },
582 CompletionItem {
583 label: "return",
584 source_range: 95..95,
585 delete: 95..95,
586 insert: "return $0;",
587 kind: Keyword,
588 },
589 CompletionItem {
590 label: "while",
591 source_range: 95..95,
592 delete: 95..95,
593 insert: "while $0 {}",
594 kind: Keyword,
595 },
596 ]
597 "###
598 ); 383 );
599 } 384 }
600 385
601 #[test] 386 #[test]
602 fn completes_break_and_continue_in_loops() { 387 fn test_keywords_in_loop() {
603 assert_debug_snapshot!( 388 check(
604 do_keyword_completion( 389 r"fn my() { loop { <|> } }",
605 r" 390 expect![[r#"
606 fn quux() -> i32 { 391 kw break
607 loop { <|> } 392 kw const
608 } 393 kw continue
609 ", 394 kw extern
610 ), 395 kw fn
611 @r###" 396 kw if
612 [ 397 kw if let
613 CompletionItem { 398 kw impl
614 label: "break", 399 kw let
615 source_range: 63..63, 400 kw loop
616 delete: 63..63, 401 kw match
617 insert: "break;", 402 kw mod
618 kind: Keyword, 403 kw return
619 }, 404 kw static
620 CompletionItem { 405 kw trait
621 label: "continue", 406 kw type
622 source_range: 63..63, 407 kw unsafe
623 delete: 63..63, 408 kw use
624 insert: "continue;", 409 kw while
625 kind: Keyword, 410 "#]],
626 },
627 CompletionItem {
628 label: "if",
629 source_range: 63..63,
630 delete: 63..63,
631 insert: "if $0 {}",
632 kind: Keyword,
633 },
634 CompletionItem {
635 label: "loop",
636 source_range: 63..63,
637 delete: 63..63,
638 insert: "loop {$0}",
639 kind: Keyword,
640 },
641 CompletionItem {
642 label: "match",
643 source_range: 63..63,
644 delete: 63..63,
645 insert: "match $0 {}",
646 kind: Keyword,
647 },
648 CompletionItem {
649 label: "return",
650 source_range: 63..63,
651 delete: 63..63,
652 insert: "return $0;",
653 kind: Keyword,
654 },
655 CompletionItem {
656 label: "while",
657 source_range: 63..63,
658 delete: 63..63,
659 insert: "while $0 {}",
660 kind: Keyword,
661 },
662 ]
663 "###
664 ); 411 );
412 }
665 413
666 // No completion: lambda isolates control flow 414 #[test]
667 assert_debug_snapshot!( 415 fn test_keywords_after_unsafe_in_item_list() {
668 do_keyword_completion( 416 check(
669 r" 417 r"unsafe <|>",
670 fn quux() -> i32 { 418 expect![[r#"
671 loop { || { <|> } } 419 kw fn
672 } 420 kw impl
673 ", 421 kw trait
674 ), 422 "#]],
675 @r###"
676 [
677 CompletionItem {
678 label: "if",
679 source_range: 68..68,
680 delete: 68..68,
681 insert: "if $0 {}",
682 kind: Keyword,
683 },
684 CompletionItem {
685 label: "loop",
686 source_range: 68..68,
687 delete: 68..68,
688 insert: "loop {$0}",
689 kind: Keyword,
690 },
691 CompletionItem {
692 label: "match",
693 source_range: 68..68,
694 delete: 68..68,
695 insert: "match $0 {}",
696 kind: Keyword,
697 },
698 CompletionItem {
699 label: "return",
700 source_range: 68..68,
701 delete: 68..68,
702 insert: "return $0;",
703 kind: Keyword,
704 },
705 CompletionItem {
706 label: "while",
707 source_range: 68..68,
708 delete: 68..68,
709 insert: "while $0 {}",
710 kind: Keyword,
711 },
712 ]
713 "###
714 ); 423 );
715 } 424 }
716 425
717 #[test] 426 #[test]
718 fn no_semi_after_break_continue_in_expr() { 427 fn test_keywords_after_unsafe_in_block_expr() {
719 assert_debug_snapshot!( 428 check(
720 do_keyword_completion( 429 r"fn my_fn() { unsafe <|> }",
721 r" 430 expect![[r#"
722 fn f() { 431 kw fn
723 loop { 432 kw impl
724 match () { 433 kw trait
725 () => br<|> 434 "#]],
726 } 435 );
727 } 436 }
728 } 437
729 ", 438 #[test]
730 ), 439 fn test_mut_in_ref_and_in_fn_parameters_list() {
731 @r###" 440 check(
732 [ 441 r"fn my_fn(&<|>) {}",
733 CompletionItem { 442 expect![[r#"
734 label: "break", 443 kw mut
735 source_range: 122..124, 444 "#]],
736 delete: 122..124, 445 );
737 insert: "break", 446 check(
738 kind: Keyword, 447 r"fn my_fn(<|>) {}",
739 }, 448 expect![[r#"
740 CompletionItem { 449 kw mut
741 label: "continue", 450 "#]],
742 source_range: 122..124, 451 );
743 delete: 122..124, 452 check(
744 insert: "continue", 453 r"fn my_fn() { let &<|> }",
745 kind: Keyword, 454 expect![[r#"
746 }, 455 kw mut
747 CompletionItem { 456 "#]],
748 label: "if", 457 );
749 source_range: 122..124, 458 }
750 delete: 122..124, 459
751 insert: "if $0 {}", 460 #[test]
752 kind: Keyword, 461 fn test_where_keyword() {
753 }, 462 check(
754 CompletionItem { 463 r"trait A <|>",
755 label: "loop", 464 expect![[r#"
756 source_range: 122..124, 465 kw where
757 delete: 122..124, 466 "#]],
758 insert: "loop {$0}", 467 );
759 kind: Keyword, 468 check(
760 }, 469 r"impl A <|>",
761 CompletionItem { 470 expect![[r#"
762 label: "match", 471 kw where
763 source_range: 122..124, 472 "#]],
764 delete: 122..124, 473 );
765 insert: "match $0 {}", 474 }
766 kind: Keyword, 475
767 }, 476 #[test]
768 CompletionItem { 477 fn no_keyword_completion_in_comments() {
769 label: "return", 478 mark::check!(no_keyword_completion_in_comments);
770 source_range: 122..124, 479 check(
771 delete: 122..124, 480 r#"
772 insert: "return", 481fn test() {
773 kind: Keyword, 482 let x = 2; // A comment<|>
774 }, 483}
775 CompletionItem { 484"#,
776 label: "while", 485 expect![[""]],
777 source_range: 122..124, 486 );
778 delete: 122..124, 487 check(
779 insert: "while $0 {}", 488 r#"
780 kind: Keyword, 489/*
781 }, 490Some multi-line comment<|>
782 ] 491*/
783 "### 492"#,
493 expect![[""]],
494 );
495 check(
496 r#"
497/// Some doc comment
498/// let test<|> = 1
499"#,
500 expect![[""]],
501 );
502 }
503
504 #[test]
505 fn test_completion_await_impls_future() {
506 check(
507 r#"
508//- /main.rs
509use std::future::*;
510struct A {}
511impl Future for A {}
512fn foo(a: A) { a.<|> }
513
514//- /std/lib.rs
515pub mod future {
516 #[lang = "future_trait"]
517 pub trait Future {}
518}
519"#,
520 expect![[r#"
521 kw await expr.await
522 "#]],
523 )
524 }
525
526 #[test]
527 fn after_let() {
528 check(
529 r#"fn main() { let _ = <|> }"#,
530 expect![[r#"
531 kw if
532 kw if let
533 kw loop
534 kw match
535 kw return
536 kw while
537 "#]],
784 ) 538 )
785 } 539 }
786} 540}
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index d9bb5fd25..0447f0511 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions};
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level. 6 // Show only macros in top level.
7 if ctx.is_new_item { 7 if ctx.is_new_item {
8 ctx.scope().process_all_names(&mut |name, res| { 8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res { 9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac); 10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 } 11 }
@@ -15,130 +15,27 @@ pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl
15 15
16#[cfg(test)] 16#[cfg(test)]
17mod tests { 17mod tests {
18 use insta::assert_debug_snapshot; 18 use expect::{expect, Expect};
19 19
20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 20 use crate::completion::{test_utils::completion_list, CompletionKind};
21 21
22 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 22 fn check(ra_fixture: &str, expect: Expect) {
23 do_completion(code, CompletionKind::Reference) 23 let actual = completion_list(ra_fixture, CompletionKind::Reference);
24 expect.assert_eq(&actual)
24 } 25 }
25 26
26 #[test] 27 #[test]
27 fn completes_macros_as_item() { 28 fn completes_macros_as_item() {
28 assert_debug_snapshot!( 29 check(
29 do_reference_completion( 30 r#"
30 " 31macro_rules! foo { () => {} }
31 //- /main.rs 32fn foo() {}
32 macro_rules! foo { 33
33 () => {} 34<|>
34 } 35"#,
35 36 expect![[r#"
36 fn foo() {} 37 ma foo!(…) macro_rules! foo
37 38 "#]],
38 <|> 39 )
39 "
40 ),
41 @r###"
42 [
43 CompletionItem {
44 label: "foo!(…)",
45 source_range: 46..46,
46 delete: 46..46,
47 insert: "foo!($0)",
48 kind: Macro,
49 detail: "macro_rules! foo",
50 },
51 ]
52 "###
53 );
54 }
55
56 #[test]
57 fn completes_vec_macros_with_square_brackets() {
58 assert_debug_snapshot!(
59 do_reference_completion(
60 "
61 //- /main.rs
62 /// Creates a [`Vec`] containing the arguments.
63 ///
64 /// - Create a [`Vec`] containing a given list of elements:
65 ///
66 /// ```
67 /// let v = vec![1, 2, 3];
68 /// assert_eq!(v[0], 1);
69 /// assert_eq!(v[1], 2);
70 /// assert_eq!(v[2], 3);
71 /// ```
72 macro_rules! vec {
73 () => {}
74 }
75
76 fn foo() {}
77
78 <|>
79 "
80 ),
81 @r###"
82 [
83 CompletionItem {
84 label: "vec![…]",
85 source_range: 280..280,
86 delete: 280..280,
87 insert: "vec![$0]",
88 kind: Macro,
89 detail: "macro_rules! vec",
90 documentation: Documentation(
91 "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```",
92 ),
93 },
94 ]
95 "###
96 );
97 }
98
99 #[test]
100 fn completes_macros_braces_guessing() {
101 assert_debug_snapshot!(
102 do_reference_completion(
103 "
104 //- /main.rs
105 /// Foo
106 ///
107 /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.
108 /// Call as `let _=foo! { hello world };`
109 macro_rules! foo {
110 () => {}
111 }
112
113 fn main() {
114 <|>
115 }
116 "
117 ),
118 @r###"
119 [
120 CompletionItem {
121 label: "foo! {…}",
122 source_range: 163..163,
123 delete: 163..163,
124 insert: "foo! {$0}",
125 kind: Macro,
126 detail: "macro_rules! foo",
127 documentation: Documentation(
128 "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`",
129 ),
130 },
131 CompletionItem {
132 label: "main()",
133 source_range: 163..163,
134 delete: 163..163,
135 insert: "main()$0",
136 kind: Function,
137 lookup: "main",
138 detail: "fn main()",
139 },
140 ]
141 "###
142 );
143 } 40 }
144} 41}
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index fdd9e928b..aceb77cb5 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -13,7 +13,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
13 13
14 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports 15 // suggest variants + auto-imports
16 ctx.scope().process_all_names(&mut |name, res| { 16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res { 17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def { 18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..)) 19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
@@ -33,106 +33,56 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
33 33
34#[cfg(test)] 34#[cfg(test)]
35mod tests { 35mod tests {
36 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 36 use expect::{expect, Expect};
37 use insta::assert_debug_snapshot;
38 37
39 fn complete(code: &str) -> Vec<CompletionItem> { 38 use crate::completion::{test_utils::completion_list, CompletionKind};
40 do_completion(code, CompletionKind::Reference) 39
40 fn check(ra_fixture: &str, expect: Expect) {
41 let actual = completion_list(ra_fixture, CompletionKind::Reference);
42 expect.assert_eq(&actual)
41 } 43 }
42 44
43 #[test] 45 #[test]
44 fn completes_enum_variants_and_modules() { 46 fn completes_enum_variants_and_modules() {
45 let completions = complete( 47 check(
46 r" 48 r#"
47 enum E { X } 49enum E { X }
48 use self::E::X; 50use self::E::X;
49 const Z: E = E::X; 51const Z: E = E::X;
50 mod m {} 52mod m {}
51 53
52 static FOO: E = E::X; 54static FOO: E = E::X;
53 struct Bar { f: u32 } 55struct Bar { f: u32 }
54 56
55 fn foo() { 57fn foo() {
56 match E::X { 58 match E::X { <|> }
57 <|> 59}
58 } 60"#,
59 } 61 expect![[r#"
60 ", 62 st Bar
63 en E
64 ev X ()
65 ct Z
66 md m
67 "#]],
61 ); 68 );
62 assert_debug_snapshot!(completions, @r###"
63 [
64 CompletionItem {
65 label: "Bar",
66 source_range: 246..246,
67 delete: 246..246,
68 insert: "Bar",
69 kind: Struct,
70 },
71 CompletionItem {
72 label: "E",
73 source_range: 246..246,
74 delete: 246..246,
75 insert: "E",
76 kind: Enum,
77 },
78 CompletionItem {
79 label: "X",
80 source_range: 246..246,
81 delete: 246..246,
82 insert: "X",
83 kind: EnumVariant,
84 detail: "()",
85 },
86 CompletionItem {
87 label: "Z",
88 source_range: 246..246,
89 delete: 246..246,
90 insert: "Z",
91 kind: Const,
92 },
93 CompletionItem {
94 label: "m",
95 source_range: 246..246,
96 delete: 246..246,
97 insert: "m",
98 kind: Module,
99 },
100 ]
101 "###);
102 } 69 }
103 70
104 #[test] 71 #[test]
105 fn completes_in_simple_macro_call() { 72 fn completes_in_simple_macro_call() {
106 let completions = complete( 73 check(
107 r" 74 r#"
108 macro_rules! m { ($e:expr) => { $e } } 75macro_rules! m { ($e:expr) => { $e } }
109 enum E { X } 76enum E { X }
110 77
111 fn foo() { 78fn foo() {
112 m!(match E::X { 79 m!(match E::X { <|> })
113 <|> 80}
114 }) 81"#,
115 } 82 expect![[r#"
116 ", 83 en E
84 ma m!(…) macro_rules! m
85 "#]],
117 ); 86 );
118 assert_debug_snapshot!(completions, @r###"
119 [
120 CompletionItem {
121 label: "E",
122 source_range: 151..151,
123 delete: 151..151,
124 insert: "E",
125 kind: Enum,
126 },
127 CompletionItem {
128 label: "m!(…)",
129 source_range: 151..151,
130 delete: 151..151,
131 insert: "m!($0)",
132 kind: Macro,
133 detail: "macro_rules! m",
134 },
135 ]
136 "###);
137 } 87 }
138} 88}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 59b58bf98..8735b9010 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -8,14 +8,13 @@ use ra_text_edit::TextEdit;
8 8
9use crate::{ 9use crate::{
10 completion::{ 10 completion::{
11 completion_config::SnippetCap,
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
12 completion_item::{Builder, CompletionKind, Completions}, 13 completion_item::{Builder, CompletionKind, Completions},
13 }, 14 },
14 CompletionItem, 15 CompletionItem, CompletionItemKind,
15}; 16};
16 17
17use super::completion_config::SnippetCap;
18
19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
20 if !ctx.config.enable_postfix_completions { 19 if !ctx.config.enable_postfix_completions {
21 return; 20 return;
@@ -91,7 +90,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
91 &dot_receiver, 90 &dot_receiver,
92 "if", 91 "if",
93 "if expr {}", 92 "if expr {}",
94 &format!("if {} {{$0}}", receiver_text), 93 &format!("if {} {{\n $0\n}}", receiver_text),
95 ) 94 )
96 .add_to(acc); 95 .add_to(acc);
97 postfix_snippet( 96 postfix_snippet(
@@ -100,13 +99,12 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
100 &dot_receiver, 99 &dot_receiver,
101 "while", 100 "while",
102 "while expr {}", 101 "while expr {}",
103 &format!("while {} {{\n$0\n}}", receiver_text), 102 &format!("while {} {{\n $0\n}}", receiver_text),
104 ) 103 )
105 .add_to(acc); 104 .add_to(acc);
105 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
106 .add_to(acc);
106 } 107 }
107 // !&&&42 is a compiler error, ergo process it before considering the references
108 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
109 .add_to(acc);
110 108
111 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) 109 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
112 .add_to(acc); 110 .add_to(acc);
@@ -125,33 +123,35 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
125 let dot_receiver = include_references(dot_receiver); 123 let dot_receiver = include_references(dot_receiver);
126 let receiver_text = 124 let receiver_text =
127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); 125 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
126
128 match try_enum { 127 match try_enum {
129 Some(try_enum) => { 128 Some(try_enum) => match try_enum {
130 match try_enum { 129 TryEnum::Result => {
131 TryEnum::Result => { 130 postfix_snippet(
132 postfix_snippet(
133 ctx, 131 ctx,
134 cap, 132 cap,
135 &dot_receiver, 133 &dot_receiver,
136 "match", 134 "match",
137 "match expr {}", 135 "match expr {}",
138 &format!("match {} {{\n Ok(${{1:_}}) => {{$2\\}},\n Err(${{3:_}}) => {{$0\\}},\n}}", receiver_text), 136 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
139 ) 137 )
140 .add_to(acc); 138 .add_to(acc);
141 } 139 }
142 TryEnum::Option => { 140 TryEnum::Option => {
143 postfix_snippet( 141 postfix_snippet(
144 ctx, 142 ctx,
145 cap, 143 cap,
146 &dot_receiver, 144 &dot_receiver,
147 "match", 145 "match",
148 "match expr {}", 146 "match expr {}",
149 &format!("match {} {{\n Some(${{1:_}}) => {{$2\\}},\n None => {{$0\\}},\n}}", receiver_text), 147 &format!(
148 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
149 receiver_text
150 ),
150 ) 151 )
151 .add_to(acc); 152 .add_to(acc);
152 }
153 } 153 }
154 } 154 },
155 None => { 155 None => {
156 postfix_snippet( 156 postfix_snippet(
157 ctx, 157 ctx,
@@ -159,7 +159,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
159 &dot_receiver, 159 &dot_receiver,
160 "match", 160 "match",
161 "match expr {}", 161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 162 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
163 ) 163 )
164 .add_to(acc); 164 .add_to(acc);
165 } 165 }
@@ -232,536 +232,147 @@ fn postfix_snippet(
232 }; 232 };
233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
234 .detail(detail) 234 .detail(detail)
235 .kind(CompletionItemKind::Snippet)
235 .snippet_edit(cap, edit) 236 .snippet_edit(cap, edit)
236} 237}
237 238
238#[cfg(test)] 239#[cfg(test)]
239mod tests { 240mod tests {
240 use insta::assert_debug_snapshot; 241 use expect::{expect, Expect};
241 242
242 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 243 use crate::completion::{
244 test_utils::{check_edit, completion_list},
245 CompletionKind,
246 };
243 247
244 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { 248 fn check(ra_fixture: &str, expect: Expect) {
245 do_completion(code, CompletionKind::Postfix) 249 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
250 expect.assert_eq(&actual)
246 } 251 }
247 252
248 #[test] 253 #[test]
249 fn postfix_completion_works_for_trivial_path_expression() { 254 fn postfix_completion_works_for_trivial_path_expression() {
250 assert_debug_snapshot!( 255 check(
251 do_postfix_completion( 256 r#"
252 r#" 257fn main() {
253 fn main() { 258 let bar = true;
254 let bar = true; 259 bar.<|>
255 bar.<|> 260}
256 } 261"#,
257 "#, 262 expect![[r#"
258 ), 263 sn box Box::new(expr)
259 @r###" 264 sn call function(expr)
260 [ 265 sn dbg dbg!(expr)
261 CompletionItem { 266 sn if if expr {}
262 label: "box", 267 sn match match expr {}
263 source_range: 89..89, 268 sn not !expr
264 delete: 85..89, 269 sn ref &expr
265 insert: "Box::new(bar)", 270 sn refm &mut expr
266 detail: "Box::new(expr)", 271 sn while while expr {}
267 }, 272 "#]],
268 CompletionItem {
269 label: "call",
270 source_range: 89..89,
271 delete: 85..89,
272 insert: "${1}(bar)",
273 detail: "function(expr)",
274 },
275 CompletionItem {
276 label: "dbg",
277 source_range: 89..89,
278 delete: 85..89,
279 insert: "dbg!(bar)",
280 detail: "dbg!(expr)",
281 },
282 CompletionItem {
283 label: "if",
284 source_range: 89..89,
285 delete: 85..89,
286 insert: "if bar {$0}",
287 detail: "if expr {}",
288 },
289 CompletionItem {
290 label: "match",
291 source_range: 89..89,
292 delete: 85..89,
293 insert: "match bar {\n ${1:_} => {$0\\},\n}",
294 detail: "match expr {}",
295 },
296 CompletionItem {
297 label: "not",
298 source_range: 89..89,
299 delete: 85..89,
300 insert: "!bar",
301 detail: "!expr",
302 },
303 CompletionItem {
304 label: "ref",
305 source_range: 89..89,
306 delete: 85..89,
307 insert: "&bar",
308 detail: "&expr",
309 },
310 CompletionItem {
311 label: "refm",
312 source_range: 89..89,
313 delete: 85..89,
314 insert: "&mut bar",
315 detail: "&mut expr",
316 },
317 CompletionItem {
318 label: "while",
319 source_range: 89..89,
320 delete: 85..89,
321 insert: "while bar {\n$0\n}",
322 detail: "while expr {}",
323 },
324 ]
325 "###
326 ); 273 );
327 } 274 }
328 275
329 #[test] 276 #[test]
330 fn postfix_completion_works_for_option() { 277 fn postfix_type_filtering() {
331 assert_debug_snapshot!( 278 check(
332 do_postfix_completion( 279 r#"
333 r#" 280fn main() {
334 enum Option<T> { 281 let bar: u8 = 12;
335 Some(T), 282 bar.<|>
336 None, 283}
337 } 284"#,
338 285 expect![[r#"
339 fn main() { 286 sn box Box::new(expr)
340 let bar = Option::Some(true); 287 sn call function(expr)
341 bar.<|> 288 sn dbg dbg!(expr)
342 } 289 sn match match expr {}
343 "#, 290 sn ref &expr
344 ), 291 sn refm &mut expr
345 @r###" 292 "#]],
346 [ 293 )
347 CompletionItem {
348 label: "box",
349 source_range: 210..210,
350 delete: 206..210,
351 insert: "Box::new(bar)",
352 detail: "Box::new(expr)",
353 },
354 CompletionItem {
355 label: "call",
356 source_range: 210..210,
357 delete: 206..210,
358 insert: "${1}(bar)",
359 detail: "function(expr)",
360 },
361 CompletionItem {
362 label: "dbg",
363 source_range: 210..210,
364 delete: 206..210,
365 insert: "dbg!(bar)",
366 detail: "dbg!(expr)",
367 },
368 CompletionItem {
369 label: "ifl",
370 source_range: 210..210,
371 delete: 206..210,
372 insert: "if let Some($1) = bar {\n $0\n}",
373 detail: "if let Some {}",
374 },
375 CompletionItem {
376 label: "match",
377 source_range: 210..210,
378 delete: 206..210,
379 insert: "match bar {\n Some(${1:_}) => {$2\\},\n None => {$0\\},\n}",
380 detail: "match expr {}",
381 },
382 CompletionItem {
383 label: "not",
384 source_range: 210..210,
385 delete: 206..210,
386 insert: "!bar",
387 detail: "!expr",
388 },
389 CompletionItem {
390 label: "ref",
391 source_range: 210..210,
392 delete: 206..210,
393 insert: "&bar",
394 detail: "&expr",
395 },
396 CompletionItem {
397 label: "refm",
398 source_range: 210..210,
399 delete: 206..210,
400 insert: "&mut bar",
401 detail: "&mut expr",
402 },
403 CompletionItem {
404 label: "while",
405 source_range: 210..210,
406 delete: 206..210,
407 insert: "while let Some($1) = bar {\n $0\n}",
408 detail: "while let Some {}",
409 },
410 ]
411 "###
412 );
413 } 294 }
414 295
415 #[test] 296 #[test]
416 fn postfix_completion_works_for_result() { 297 fn option_iflet() {
417 assert_debug_snapshot!( 298 check_edit(
418 do_postfix_completion( 299 "ifl",
419 r#" 300 r#"
420 enum Result<T, E> { 301enum Option<T> { Some(T), None }
421 Ok(T), 302
422 Err(E), 303fn main() {
423 } 304 let bar = Option::Some(true);
305 bar.<|>
306}
307"#,
308 r#"
309enum Option<T> { Some(T), None }
424 310
425 fn main() { 311fn main() {
426 let bar = Result::Ok(true); 312 let bar = Option::Some(true);
427 bar.<|> 313 if let Some($1) = bar {
428 } 314 $0
429 "#, 315}
430 ), 316}
431 @r###" 317"#,
432 [
433 CompletionItem {
434 label: "box",
435 source_range: 211..211,
436 delete: 207..211,
437 insert: "Box::new(bar)",
438 detail: "Box::new(expr)",
439 },
440 CompletionItem {
441 label: "call",
442 source_range: 211..211,
443 delete: 207..211,
444 insert: "${1}(bar)",
445 detail: "function(expr)",
446 },
447 CompletionItem {
448 label: "dbg",
449 source_range: 211..211,
450 delete: 207..211,
451 insert: "dbg!(bar)",
452 detail: "dbg!(expr)",
453 },
454 CompletionItem {
455 label: "ifl",
456 source_range: 211..211,
457 delete: 207..211,
458 insert: "if let Ok($1) = bar {\n $0\n}",
459 detail: "if let Ok {}",
460 },
461 CompletionItem {
462 label: "match",
463 source_range: 211..211,
464 delete: 207..211,
465 insert: "match bar {\n Ok(${1:_}) => {$2\\},\n Err(${3:_}) => {$0\\},\n}",
466 detail: "match expr {}",
467 },
468 CompletionItem {
469 label: "not",
470 source_range: 211..211,
471 delete: 207..211,
472 insert: "!bar",
473 detail: "!expr",
474 },
475 CompletionItem {
476 label: "ref",
477 source_range: 211..211,
478 delete: 207..211,
479 insert: "&bar",
480 detail: "&expr",
481 },
482 CompletionItem {
483 label: "refm",
484 source_range: 211..211,
485 delete: 207..211,
486 insert: "&mut bar",
487 detail: "&mut expr",
488 },
489 CompletionItem {
490 label: "while",
491 source_range: 211..211,
492 delete: 207..211,
493 insert: "while let Ok($1) = bar {\n $0\n}",
494 detail: "while let Ok {}",
495 },
496 ]
497 "###
498 ); 318 );
499 } 319 }
500 320
501 #[test] 321 #[test]
502 fn some_postfix_completions_ignored() { 322 fn result_match() {
503 assert_debug_snapshot!( 323 check_edit(
504 do_postfix_completion( 324 "match",
505 r#" 325 r#"
506 fn main() { 326enum Result<T, E> { Ok(T), Err(E) }
507 let bar: u8 = 12; 327
508 bar.<|> 328fn main() {
509 } 329 let bar = Result::Ok(true);
510 "#, 330 bar.<|>
511 ), 331}
512 @r###" 332"#,
513 [ 333 r#"
514 CompletionItem { 334enum Result<T, E> { Ok(T), Err(E) }
515 label: "box", 335
516 source_range: 91..91, 336fn main() {
517 delete: 87..91, 337 let bar = Result::Ok(true);
518 insert: "Box::new(bar)", 338 match bar {
519 detail: "Box::new(expr)", 339 Ok(${1:_}) => {$2},
520 }, 340 Err(${3:_}) => {$0},
521 CompletionItem { 341}
522 label: "call", 342}
523 source_range: 91..91, 343"#,
524 delete: 87..91,
525 insert: "${1}(bar)",
526 detail: "function(expr)",
527 },
528 CompletionItem {
529 label: "dbg",
530 source_range: 91..91,
531 delete: 87..91,
532 insert: "dbg!(bar)",
533 detail: "dbg!(expr)",
534 },
535 CompletionItem {
536 label: "match",
537 source_range: 91..91,
538 delete: 87..91,
539 insert: "match bar {\n ${1:_} => {$0\\},\n}",
540 detail: "match expr {}",
541 },
542 CompletionItem {
543 label: "not",
544 source_range: 91..91,
545 delete: 87..91,
546 insert: "!bar",
547 detail: "!expr",
548 },
549 CompletionItem {
550 label: "ref",
551 source_range: 91..91,
552 delete: 87..91,
553 insert: "&bar",
554 detail: "&expr",
555 },
556 CompletionItem {
557 label: "refm",
558 source_range: 91..91,
559 delete: 87..91,
560 insert: "&mut bar",
561 detail: "&mut expr",
562 },
563 ]
564 "###
565 ); 344 );
566 } 345 }
567 346
568 #[test] 347 #[test]
569 fn postfix_completion_works_for_ambiguous_float_literal() { 348 fn postfix_completion_works_for_ambiguous_float_literal() {
570 assert_debug_snapshot!( 349 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
571 do_postfix_completion(
572 r#"
573 fn main() {
574 42.<|>
575 }
576 "#,
577 ),
578 @r###"
579 [
580 CompletionItem {
581 label: "box",
582 source_range: 52..52,
583 delete: 49..52,
584 insert: "Box::new(42)",
585 detail: "Box::new(expr)",
586 },
587 CompletionItem {
588 label: "call",
589 source_range: 52..52,
590 delete: 49..52,
591 insert: "${1}(42)",
592 detail: "function(expr)",
593 },
594 CompletionItem {
595 label: "dbg",
596 source_range: 52..52,
597 delete: 49..52,
598 insert: "dbg!(42)",
599 detail: "dbg!(expr)",
600 },
601 CompletionItem {
602 label: "match",
603 source_range: 52..52,
604 delete: 49..52,
605 insert: "match 42 {\n ${1:_} => {$0\\},\n}",
606 detail: "match expr {}",
607 },
608 CompletionItem {
609 label: "not",
610 source_range: 52..52,
611 delete: 49..52,
612 insert: "!42",
613 detail: "!expr",
614 },
615 CompletionItem {
616 label: "ref",
617 source_range: 52..52,
618 delete: 49..52,
619 insert: "&42",
620 detail: "&expr",
621 },
622 CompletionItem {
623 label: "refm",
624 source_range: 52..52,
625 delete: 49..52,
626 insert: "&mut 42",
627 detail: "&mut expr",
628 },
629 ]
630 "###
631 );
632 } 350 }
633 351
634 #[test] 352 #[test]
635 fn works_in_simple_macro() { 353 fn works_in_simple_macro() {
636 assert_debug_snapshot!( 354 check_edit(
637 do_postfix_completion( 355 "dbg",
638 r#" 356 r#"
639 macro_rules! m { ($e:expr) => { $e } } 357macro_rules! m { ($e:expr) => { $e } }
640 fn main() { 358fn main() {
641 let bar: u8 = 12; 359 let bar: u8 = 12;
642 m!(bar.b<|>) 360 m!(bar.d<|>)
643 } 361}
644 "#, 362"#,
645 ), 363 r#"
646 @r###" 364macro_rules! m { ($e:expr) => { $e } }
647 [ 365fn main() {
648 CompletionItem { 366 let bar: u8 = 12;
649 label: "box", 367 m!(dbg!(bar))
650 source_range: 149..150, 368}
651 delete: 145..150, 369"#,
652 insert: "Box::new(bar)",
653 detail: "Box::new(expr)",
654 },
655 CompletionItem {
656 label: "call",
657 source_range: 149..150,
658 delete: 145..150,
659 insert: "${1}(bar)",
660 detail: "function(expr)",
661 },
662 CompletionItem {
663 label: "dbg",
664 source_range: 149..150,
665 delete: 145..150,
666 insert: "dbg!(bar)",
667 detail: "dbg!(expr)",
668 },
669 CompletionItem {
670 label: "match",
671 source_range: 149..150,
672 delete: 145..150,
673 insert: "match bar {\n ${1:_} => {$0\\},\n}",
674 detail: "match expr {}",
675 },
676 CompletionItem {
677 label: "not",
678 source_range: 149..150,
679 delete: 145..150,
680 insert: "!bar",
681 detail: "!expr",
682 },
683 CompletionItem {
684 label: "ref",
685 source_range: 149..150,
686 delete: 145..150,
687 insert: "&bar",
688 detail: "&expr",
689 },
690 CompletionItem {
691 label: "refm",
692 source_range: 149..150,
693 delete: 145..150,
694 insert: "&mut bar",
695 detail: "&mut expr",
696 },
697 ]
698 "###
699 ); 370 );
700 } 371 }
701 372
702 #[test] 373 #[test]
703 fn postfix_completion_for_references() { 374 fn postfix_completion_for_references() {
704 assert_debug_snapshot!( 375 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
705 do_postfix_completion( 376 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
706 r#"
707 fn main() {
708 &&&&42.<|>
709 }
710 "#,
711 ),
712 @r###"
713 [
714 CompletionItem {
715 label: "box",
716 source_range: 56..56,
717 delete: 49..56,
718 insert: "Box::new(&&&&42)",
719 detail: "Box::new(expr)",
720 },
721 CompletionItem {
722 label: "call",
723 source_range: 56..56,
724 delete: 49..56,
725 insert: "${1}(&&&&42)",
726 detail: "function(expr)",
727 },
728 CompletionItem {
729 label: "dbg",
730 source_range: 56..56,
731 delete: 49..56,
732 insert: "dbg!(&&&&42)",
733 detail: "dbg!(expr)",
734 },
735 CompletionItem {
736 label: "match",
737 source_range: 56..56,
738 delete: 49..56,
739 insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}",
740 detail: "match expr {}",
741 },
742 CompletionItem {
743 label: "not",
744 source_range: 56..56,
745 delete: 53..56,
746 insert: "!42",
747 detail: "!expr",
748 },
749 CompletionItem {
750 label: "ref",
751 source_range: 56..56,
752 delete: 53..56,
753 insert: "&42",
754 detail: "&expr",
755 },
756 CompletionItem {
757 label: "refm",
758 source_range: 56..56,
759 delete: 53..56,
760 insert: "&mut 42",
761 detail: "&mut expr",
762 },
763 ]
764 "###
765 );
766 } 377 }
767} 378}
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index 02ac0166b..b08f5b9b4 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -17,21 +17,20 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
17 return; 17 return;
18 } 18 }
19 19
20 let scope = ctx.scope(); 20 let context_module = ctx.scope.module();
21 let context_module = scope.module();
22 21
23 let res = match scope.resolve_hir_path_qualifier(&path) { 22 let resolution = match ctx.scope.resolve_hir_path_qualifier(&path) {
24 Some(res) => res, 23 Some(res) => res,
25 None => return, 24 None => return,
26 }; 25 };
27 26
28 // Add associated types on type parameters and `Self`. 27 // Add associated types on type parameters and `Self`.
29 res.assoc_type_shorthand_candidates(ctx.db, |alias| { 28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
30 acc.add_type_alias(ctx, alias); 29 acc.add_type_alias(ctx, alias);
31 None::<()> 30 None::<()>
32 }); 31 });
33 32
34 match res { 33 match resolution {
35 PathResolution::Def(hir::ModuleDef::Module(module)) => { 34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
36 let module_scope = module.scope(ctx.db, context_module); 35 let module_scope = module.scope(ctx.db, context_module);
37 for (name, def) in module_scope { 36 for (name, def) in module_scope {
@@ -68,7 +67,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
68 67
69 let krate = ctx.krate; 68 let krate = ctx.krate;
70 if let Some(krate) = krate { 69 if let Some(krate) = krate {
71 let traits_in_scope = ctx.scope().traits_in_scope(); 70 let traits_in_scope = ctx.scope.traits_in_scope();
72 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
73 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
74 return None; 73 return None;
@@ -113,13 +112,13 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
113 } 112 }
114 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { 113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
115 if let Some(krate) = ctx.krate { 114 if let Some(krate) = ctx.krate {
116 let ty = match res { 115 let ty = match resolution {
117 PathResolution::TypeParam(param) => param.ty(ctx.db), 116 PathResolution::TypeParam(param) => param.ty(ctx.db),
118 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), 117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
119 _ => return, 118 _ => return,
120 }; 119 };
121 120
122 let traits_in_scope = ctx.scope().traits_in_scope(); 121 let traits_in_scope = ctx.scope.traits_in_scope();
123 let mut seen = FxHashSet::default(); 122 let mut seen = FxHashSet::default();
124 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
125 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
@@ -147,1229 +146,588 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
147 146
148#[cfg(test)] 147#[cfg(test)]
149mod tests { 148mod tests {
149 use expect::{expect, Expect};
150 use test_utils::mark; 150 use test_utils::mark;
151 151
152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 152 use crate::completion::{
153 use insta::assert_debug_snapshot; 153 test_utils::{check_edit, completion_list},
154 CompletionKind,
155 };
156
157 fn check(ra_fixture: &str, expect: Expect) {
158 let actual = completion_list(ra_fixture, CompletionKind::Reference);
159 expect.assert_eq(&actual);
160 }
154 161
155 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 162 fn check_builtin(ra_fixture: &str, expect: Expect) {
156 do_completion(code, CompletionKind::Reference) 163 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
164 expect.assert_eq(&actual);
157 } 165 }
158 166
159 #[test] 167 #[test]
160 fn dont_complete_current_use() { 168 fn dont_complete_current_use() {
161 mark::check!(dont_complete_current_use); 169 mark::check!(dont_complete_current_use);
162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); 170 check(r#"use self::foo<|>;"#, expect![[""]]);
163 assert!(completions.is_empty());
164 } 171 }
165 172
166 #[test] 173 #[test]
167 fn dont_complete_current_use_in_braces_with_glob() { 174 fn dont_complete_current_use_in_braces_with_glob() {
168 let completions = do_completion( 175 check(
169 r" 176 r#"
170 mod foo { pub struct S; } 177mod foo { pub struct S; }
171 use self::{foo::*, bar<|>}; 178use self::{foo::*, bar<|>};
172 ", 179"#,
173 CompletionKind::Reference, 180 expect![[r#"
181 st S
182 md foo
183 "#]],
174 ); 184 );
175 assert_eq!(completions.len(), 2);
176 } 185 }
177 186
178 #[test] 187 #[test]
179 fn dont_complete_primitive_in_use() { 188 fn dont_complete_primitive_in_use() {
180 let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType); 189 check_builtin(r#"use self::<|>;"#, expect![[""]]);
181 assert!(completions.is_empty());
182 } 190 }
183 191
184 #[test] 192 #[test]
185 fn dont_complete_primitive_in_module_scope() { 193 fn dont_complete_primitive_in_module_scope() {
186 let completions = do_completion(r"fn foo() { self::<|> }", CompletionKind::BuiltinType); 194 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]);
187 assert!(completions.is_empty());
188 } 195 }
189 196
190 #[test] 197 #[test]
191 fn completes_primitives() { 198 fn completes_primitives() {
192 let completions = 199 check_builtin(
193 do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType); 200 r#"fn main() { let _: <|> = 92; }"#,
194 assert_eq!(completions.len(), 17); 201 expect![[r#"
195 } 202 bt bool
196 203 bt char
197 #[test] 204 bt f32
198 fn completes_mod_with_docs() { 205 bt f64
199 assert_debug_snapshot!( 206 bt i128
200 do_reference_completion( 207 bt i16
201 r" 208 bt i32
202 use self::my<|>; 209 bt i64
203 210 bt i8
204 /// Some simple 211 bt isize
205 /// docs describing `mod my`. 212 bt str
206 mod my { 213 bt u128
207 struct Bar; 214 bt u16
208 } 215 bt u32
209 " 216 bt u64
210 ), 217 bt u8
211 @r###" 218 bt usize
212 [ 219 "#]],
213 CompletionItem {
214 label: "my",
215 source_range: 27..29,
216 delete: 27..29,
217 insert: "my",
218 kind: Module,
219 documentation: Documentation(
220 "Some simple\ndocs describing `mod my`.",
221 ),
222 },
223 ]
224 "###
225 ); 220 );
226 } 221 }
227 222
228 #[test] 223 #[test]
229 fn completes_mod_with_same_name_as_function() { 224 fn completes_mod_with_same_name_as_function() {
230 assert_debug_snapshot!( 225 check(
231 do_reference_completion( 226 r#"
232 r" 227use self::my::<|>;
233 use self::my::<|>; 228
234 229mod my { pub struct Bar; }
235 mod my { 230fn my() {}
236 pub struct Bar; 231"#,
237 } 232 expect![[r#"
238 233 st Bar
239 fn my() {} 234 "#]],
240 "
241 ),
242 @r###"
243 [
244 CompletionItem {
245 label: "Bar",
246 source_range: 31..31,
247 delete: 31..31,
248 insert: "Bar",
249 kind: Struct,
250 },
251 ]
252 "###
253 ); 235 );
254 } 236 }
255 237
256 #[test] 238 #[test]
257 fn path_visibility() { 239 fn filters_visibility() {
258 assert_debug_snapshot!( 240 check(
259 do_reference_completion( 241 r#"
260 r" 242use self::my::<|>;
261 use self::my::<|>; 243
262 244mod my {
263 mod my { 245 struct Bar;
264 struct Bar; 246 pub struct Foo;
265 pub struct Foo; 247 pub use Bar as PublicBar;
266 pub use Bar as PublicBar; 248}
267 } 249"#,
268 " 250 expect![[r#"
269 ), 251 st Foo
270 @r###" 252 st PublicBar
271 [ 253 "#]],
272 CompletionItem {
273 label: "Foo",
274 source_range: 31..31,
275 delete: 31..31,
276 insert: "Foo",
277 kind: Struct,
278 },
279 CompletionItem {
280 label: "PublicBar",
281 source_range: 31..31,
282 delete: 31..31,
283 insert: "PublicBar",
284 kind: Struct,
285 },
286 ]
287 "###
288 ); 254 );
289 } 255 }
290 256
291 #[test] 257 #[test]
292 fn completes_use_item_starting_with_self() { 258 fn completes_use_item_starting_with_self() {
293 assert_debug_snapshot!( 259 check(
294 do_reference_completion( 260 r#"
295 r" 261use self::m::<|>;
296 use self::m::<|>;
297 262
298 mod m { 263mod m { pub struct Bar; }
299 pub struct Bar; 264"#,
300 } 265 expect![[r#"
301 " 266 st Bar
302 ), 267 "#]],
303 @r###"
304 [
305 CompletionItem {
306 label: "Bar",
307 source_range: 30..30,
308 delete: 30..30,
309 insert: "Bar",
310 kind: Struct,
311 },
312 ]
313 "###
314 ); 268 );
315 } 269 }
316 270
317 #[test] 271 #[test]
318 fn completes_use_item_starting_with_crate() { 272 fn completes_use_item_starting_with_crate() {
319 assert_debug_snapshot!( 273 check(
320 do_reference_completion( 274 r#"
321 " 275//- /lib.rs
322 //- /lib.rs 276mod foo;
323 mod foo; 277struct Spam;
324 struct Spam; 278//- /foo.rs
325 //- /foo.rs 279use crate::Sp<|>
326 use crate::Sp<|> 280"#,
327 " 281 expect![[r#"
328 ), 282 st Spam
329 @r###" 283 md foo
330 [ 284 "#]],
331 CompletionItem {
332 label: "Spam",
333 source_range: 11..13,
334 delete: 11..13,
335 insert: "Spam",
336 kind: Struct,
337 },
338 CompletionItem {
339 label: "foo",
340 source_range: 11..13,
341 delete: 11..13,
342 insert: "foo",
343 kind: Module,
344 },
345 ]
346 "###
347 ); 285 );
348 } 286 }
349 287
350 #[test] 288 #[test]
351 fn completes_nested_use_tree() { 289 fn completes_nested_use_tree() {
352 assert_debug_snapshot!( 290 check(
353 do_reference_completion( 291 r#"
354 " 292//- /lib.rs
355 //- /lib.rs 293mod foo;
356 mod foo; 294struct Spam;
357 struct Spam; 295//- /foo.rs
358 //- /foo.rs 296use crate::{Sp<|>};
359 use crate::{Sp<|>}; 297"#,
360 " 298 expect![[r#"
361 ), 299 st Spam
362 @r###" 300 md foo
363 [ 301 "#]],
364 CompletionItem {
365 label: "Spam",
366 source_range: 12..14,
367 delete: 12..14,
368 insert: "Spam",
369 kind: Struct,
370 },
371 CompletionItem {
372 label: "foo",
373 source_range: 12..14,
374 delete: 12..14,
375 insert: "foo",
376 kind: Module,
377 },
378 ]
379 "###
380 ); 302 );
381 } 303 }
382 304
383 #[test] 305 #[test]
384 fn completes_deeply_nested_use_tree() { 306 fn completes_deeply_nested_use_tree() {
385 assert_debug_snapshot!( 307 check(
386 do_reference_completion( 308 r#"
387 " 309//- /lib.rs
388 //- /lib.rs 310mod foo;
389 mod foo; 311pub mod bar {
390 pub mod bar { 312 pub mod baz {
391 pub mod baz { 313 pub struct Spam;
392 pub struct Spam;
393 }
394 }
395 //- /foo.rs
396 use crate::{bar::{baz::Sp<|>}};
397 "
398 ),
399 @r###"
400 [
401 CompletionItem {
402 label: "Spam",
403 source_range: 23..25,
404 delete: 23..25,
405 insert: "Spam",
406 kind: Struct,
407 },
408 ]
409 "###
410 );
411 }
412
413 #[test]
414 fn completes_enum_variant() {
415 assert_debug_snapshot!(
416 do_reference_completion(
417 "
418 //- /lib.rs
419 /// An enum
420 enum E {
421 /// Foo Variant
422 Foo,
423 /// Bar Variant with i32
424 Bar(i32)
425 }
426 fn foo() { let _ = E::<|> }
427 "
428 ),
429 @r###"
430 [
431 CompletionItem {
432 label: "Bar(…)",
433 source_range: 116..116,
434 delete: 116..116,
435 insert: "Bar($0)",
436 kind: EnumVariant,
437 lookup: "Bar",
438 detail: "(i32)",
439 documentation: Documentation(
440 "Bar Variant with i32",
441 ),
442 trigger_call_info: true,
443 },
444 CompletionItem {
445 label: "Foo",
446 source_range: 116..116,
447 delete: 116..116,
448 insert: "Foo",
449 kind: EnumVariant,
450 detail: "()",
451 documentation: Documentation(
452 "Foo Variant",
453 ),
454 },
455 ]
456 "###
457 );
458 }
459
460 #[test]
461 fn completes_enum_variant_with_details() {
462 assert_debug_snapshot!(
463 do_reference_completion(
464 "
465 //- /lib.rs
466 struct S { field: u32 }
467 /// An enum
468 enum E {
469 /// Foo Variant (empty)
470 Foo,
471 /// Bar Variant with i32 and u32
472 Bar(i32, u32),
473 ///
474 S(S),
475 }
476 fn foo() { let _ = E::<|> }
477 "
478 ),
479 @r###"
480 [
481 CompletionItem {
482 label: "Bar(…)",
483 source_range: 180..180,
484 delete: 180..180,
485 insert: "Bar($0)",
486 kind: EnumVariant,
487 lookup: "Bar",
488 detail: "(i32, u32)",
489 documentation: Documentation(
490 "Bar Variant with i32 and u32",
491 ),
492 trigger_call_info: true,
493 },
494 CompletionItem {
495 label: "Foo",
496 source_range: 180..180,
497 delete: 180..180,
498 insert: "Foo",
499 kind: EnumVariant,
500 detail: "()",
501 documentation: Documentation(
502 "Foo Variant (empty)",
503 ),
504 },
505 CompletionItem {
506 label: "S(…)",
507 source_range: 180..180,
508 delete: 180..180,
509 insert: "S($0)",
510 kind: EnumVariant,
511 lookup: "S",
512 detail: "(S)",
513 documentation: Documentation(
514 "",
515 ),
516 trigger_call_info: true,
517 },
518 ]
519 "###
520 );
521 } 314 }
522 315}
523 #[test] 316//- /foo.rs
524 fn completes_struct_associated_method() { 317use crate::{bar::{baz::Sp<|>}};
525 assert_debug_snapshot!( 318"#,
526 do_reference_completion( 319 expect![[r#"
527 " 320 st Spam
528 //- /lib.rs 321 "#]],
529 /// A Struct
530 struct S;
531
532 impl S {
533 /// An associated method
534 fn m() { }
535 }
536
537 fn foo() { let _ = S::<|> }
538 "
539 ),
540 @r###"
541 [
542 CompletionItem {
543 label: "m()",
544 source_range: 100..100,
545 delete: 100..100,
546 insert: "m()$0",
547 kind: Function,
548 lookup: "m",
549 detail: "fn m()",
550 documentation: Documentation(
551 "An associated method",
552 ),
553 },
554 ]
555 "###
556 ); 322 );
557 } 323 }
558 324
559 #[test] 325 #[test]
560 fn completes_struct_associated_method_with_self() { 326 fn completes_enum_variant() {
561 assert_debug_snapshot!( 327 check(
562 do_reference_completion( 328 r#"
563 " 329enum E { Foo, Bar(i32) }
564 //- /lib.rs 330fn foo() { let _ = E::<|> }
565 /// A Struct 331"#,
566 struct S; 332 expect![[r#"
567 333 ev Bar(…) (i32)
568 impl S { 334 ev Foo ()
569 /// An associated method 335 "#]],
570 fn m(&self) { }
571 }
572
573 fn foo() { let _ = S::<|> }
574 "
575 ),
576 @r###"
577 [
578 CompletionItem {
579 label: "m()",
580 source_range: 105..105,
581 delete: 105..105,
582 insert: "m()$0",
583 kind: Method,
584 lookup: "m",
585 detail: "fn m(&self)",
586 documentation: Documentation(
587 "An associated method",
588 ),
589 },
590 ]
591 "###
592 ); 336 );
593 } 337 }
594 338
595 #[test] 339 #[test]
596 fn completes_struct_associated_const() { 340 fn completes_struct_associated_items() {
597 assert_debug_snapshot!( 341 check(
598 do_reference_completion( 342 r#"
599 " 343//- /lib.rs
600 //- /lib.rs 344struct S;
601 /// A Struct 345
602 struct S; 346impl S {
603 347 fn a() {}
604 impl S { 348 fn b(&self) {}
605 /// An associated const 349 const C: i32 = 42;
606 const C: i32 = 42; 350 type T = i32;
607 } 351}
608 352
609 fn foo() { let _ = S::<|> } 353fn foo() { let _ = S::<|> }
610 " 354"#,
611 ), 355 expect![[r#"
612 @r###" 356 ct C const C: i32 = 42;
613 [ 357 ta T type T = i32;
614 CompletionItem { 358 fn a() fn a()
615 label: "C", 359 me b() fn b(&self)
616 source_range: 107..107, 360 "#]],
617 delete: 107..107,
618 insert: "C",
619 kind: Const,
620 detail: "const C: i32 = 42;",
621 documentation: Documentation(
622 "An associated const",
623 ),
624 },
625 ]
626 "###
627 ); 361 );
628 } 362 }
629 363
630 #[test] 364 #[test]
631 fn completes_struct_associated_type() { 365 fn associated_item_visibility() {
632 assert_debug_snapshot!( 366 check(
633 do_reference_completion( 367 r#"
634 " 368struct S;
635 //- /lib.rs
636 /// A Struct
637 struct S;
638
639 impl S {
640 /// An associated type
641 type T = i32;
642 }
643 369
644 fn foo() { let _ = S::<|> } 370mod m {
645 " 371 impl super::S {
646 ), 372 pub(super) fn public_method() { }
647 @r###" 373 fn private_method() { }
648 [ 374 pub(super) type PublicType = u32;
649 CompletionItem { 375 type PrivateType = u32;
650 label: "T", 376 pub(super) const PUBLIC_CONST: u32 = 1;
651 source_range: 101..101, 377 const PRIVATE_CONST: u32 = 1;
652 delete: 101..101,
653 insert: "T",
654 kind: TypeAlias,
655 detail: "type T = i32;",
656 documentation: Documentation(
657 "An associated type",
658 ),
659 },
660 ]
661 "###
662 );
663 } 378 }
379}
664 380
665 #[test] 381fn foo() { let _ = S::<|> }
666 fn associated_item_visibility() { 382"#,
667 assert_debug_snapshot!( 383 expect![[r#"
668 do_reference_completion( 384 ct PUBLIC_CONST pub(super) const PUBLIC_CONST: u32 = 1;
669 " 385 ta PublicType pub(super) type PublicType = u32;
670 //- /lib.rs 386 fn public_method() pub(super) fn public_method()
671 struct S; 387 "#]],
672
673 mod m {
674 impl super::S {
675 pub(super) fn public_method() { }
676 fn private_method() { }
677 pub(super) type PublicType = u32;
678 type PrivateType = u32;
679 pub(super) const PUBLIC_CONST: u32 = 1;
680 const PRIVATE_CONST: u32 = 1;
681 }
682 }
683
684 fn foo() { let _ = S::<|> }
685 "
686 ),
687 @r###"
688 [
689 CompletionItem {
690 label: "PUBLIC_CONST",
691 source_range: 302..302,
692 delete: 302..302,
693 insert: "PUBLIC_CONST",
694 kind: Const,
695 detail: "pub(super) const PUBLIC_CONST: u32 = 1;",
696 },
697 CompletionItem {
698 label: "PublicType",
699 source_range: 302..302,
700 delete: 302..302,
701 insert: "PublicType",
702 kind: TypeAlias,
703 detail: "pub(super) type PublicType = u32;",
704 },
705 CompletionItem {
706 label: "public_method()",
707 source_range: 302..302,
708 delete: 302..302,
709 insert: "public_method()$0",
710 kind: Function,
711 lookup: "public_method",
712 detail: "pub(super) fn public_method()",
713 },
714 ]
715 "###
716 ); 388 );
717 } 389 }
718 390
719 #[test] 391 #[test]
720 fn completes_enum_associated_method() { 392 fn completes_enum_associated_method() {
721 assert_debug_snapshot!( 393 check(
722 do_reference_completion( 394 r#"
723 " 395enum E {};
724 //- /lib.rs 396impl E { fn m() { } }
725 /// An enum 397
726 enum S {}; 398fn foo() { let _ = E::<|> }
727 399 "#,
728 impl S { 400 expect![[r#"
729 /// An associated method 401 fn m() fn m()
730 fn m() { } 402 "#]],
731 }
732
733 fn foo() { let _ = S::<|> }
734 "
735 ),
736 @r###"
737 [
738 CompletionItem {
739 label: "m()",
740 source_range: 100..100,
741 delete: 100..100,
742 insert: "m()$0",
743 kind: Function,
744 lookup: "m",
745 detail: "fn m()",
746 documentation: Documentation(
747 "An associated method",
748 ),
749 },
750 ]
751 "###
752 ); 403 );
753 } 404 }
754 405
755 #[test] 406 #[test]
756 fn completes_union_associated_method() { 407 fn completes_union_associated_method() {
757 assert_debug_snapshot!( 408 check(
758 do_reference_completion( 409 r#"
759 " 410union U {};
760 //- /lib.rs 411impl U { fn m() { } }
761 /// A union 412
762 union U {}; 413fn foo() { let _ = U::<|> }
763 414"#,
764 impl U { 415 expect![[r#"
765 /// An associated method 416 fn m() fn m()
766 fn m() { } 417 "#]],
767 }
768
769 fn foo() { let _ = U::<|> }
770 "
771 ),
772 @r###"
773 [
774 CompletionItem {
775 label: "m()",
776 source_range: 101..101,
777 delete: 101..101,
778 insert: "m()$0",
779 kind: Function,
780 lookup: "m",
781 detail: "fn m()",
782 documentation: Documentation(
783 "An associated method",
784 ),
785 },
786 ]
787 "###
788 ); 418 );
789 } 419 }
790 420
791 #[test] 421 #[test]
792 fn completes_use_paths_across_crates() { 422 fn completes_use_paths_across_crates() {
793 assert_debug_snapshot!( 423 check(
794 do_reference_completion( 424 r#"
795 " 425//- /main.rs
796 //- /main.rs 426use foo::<|>;
797 use foo::<|>; 427
798 428//- /foo/lib.rs
799 //- /foo/lib.rs 429pub mod bar { pub struct S; }
800 pub mod bar { 430"#,
801 pub struct S; 431 expect![[r#"
802 } 432 md bar
803 " 433 "#]],
804 ),
805 @r###"
806 [
807 CompletionItem {
808 label: "bar",
809 source_range: 9..9,
810 delete: 9..9,
811 insert: "bar",
812 kind: Module,
813 },
814 ]
815 "###
816 ); 434 );
817 } 435 }
818 436
819 #[test] 437 #[test]
820 fn completes_trait_associated_method_1() { 438 fn completes_trait_associated_method_1() {
821 assert_debug_snapshot!( 439 check(
822 do_reference_completion( 440 r#"
823 " 441trait Trait { fn m(); }
824 //- /lib.rs
825 trait Trait {
826 /// A trait method
827 fn m();
828 }
829 442
830 fn foo() { let _ = Trait::<|> } 443fn foo() { let _ = Trait::<|> }
831 " 444"#,
832 ), 445 expect![[r#"
833 @r###" 446 fn m() fn m()
834 [ 447 "#]],
835 CompletionItem {
836 label: "m()",
837 source_range: 73..73,
838 delete: 73..73,
839 insert: "m()$0",
840 kind: Function,
841 lookup: "m",
842 detail: "fn m()",
843 documentation: Documentation(
844 "A trait method",
845 ),
846 },
847 ]
848 "###
849 ); 448 );
850 } 449 }
851 450
852 #[test] 451 #[test]
853 fn completes_trait_associated_method_2() { 452 fn completes_trait_associated_method_2() {
854 assert_debug_snapshot!( 453 check(
855 do_reference_completion( 454 r#"
856 " 455trait Trait { fn m(); }
857 //- /lib.rs 456
858 trait Trait { 457struct S;
859 /// A trait method 458impl Trait for S {}
860 fn m();
861 }
862 459
863 struct S; 460fn foo() { let _ = S::<|> }
864 impl Trait for S {} 461"#,
865 462 expect![[r#"
866 fn foo() { let _ = S::<|> } 463 fn m() fn m()
867 " 464 "#]],
868 ),
869 @r###"
870 [
871 CompletionItem {
872 label: "m()",
873 source_range: 99..99,
874 delete: 99..99,
875 insert: "m()$0",
876 kind: Function,
877 lookup: "m",
878 detail: "fn m()",
879 documentation: Documentation(
880 "A trait method",
881 ),
882 },
883 ]
884 "###
885 ); 465 );
886 } 466 }
887 467
888 #[test] 468 #[test]
889 fn completes_trait_associated_method_3() { 469 fn completes_trait_associated_method_3() {
890 assert_debug_snapshot!( 470 check(
891 do_reference_completion( 471 r#"
892 " 472trait Trait { fn m(); }
893 //- /lib.rs
894 trait Trait {
895 /// A trait method
896 fn m();
897 }
898 473
899 struct S; 474struct S;
900 impl Trait for S {} 475impl Trait for S {}
901 476
902 fn foo() { let _ = <S as Trait>::<|> } 477fn foo() { let _ = <S as Trait>::<|> }
903 " 478"#,
904 ), 479 expect![[r#"
905 @r###" 480 fn m() fn m()
906 [ 481 "#]],
907 CompletionItem {
908 label: "m()",
909 source_range: 110..110,
910 delete: 110..110,
911 insert: "m()$0",
912 kind: Function,
913 lookup: "m",
914 detail: "fn m()",
915 documentation: Documentation(
916 "A trait method",
917 ),
918 },
919 ]
920 "###
921 ); 482 );
922 } 483 }
923 484
924 #[test] 485 #[test]
925 fn completes_ty_param_assoc_ty() { 486 fn completes_ty_param_assoc_ty() {
926 assert_debug_snapshot!( 487 check(
927 do_reference_completion( 488 r#"
928 " 489trait Super {
929 //- /lib.rs 490 type Ty;
930 trait Super { 491 const CONST: u8;
931 type Ty; 492 fn func() {}
932 const CONST: u8; 493 fn method(&self) {}
933 fn func() {} 494}
934 fn method(&self) {}
935 }
936 495
937 trait Sub: Super { 496trait Sub: Super {
938 type SubTy; 497 type SubTy;
939 const C2: (); 498 const C2: ();
940 fn subfunc() {} 499 fn subfunc() {}
941 fn submethod(&self) {} 500 fn submethod(&self) {}
942 } 501}
943 502
944 fn foo<T: Sub>() { 503fn foo<T: Sub>() { T::<|> }
945 T::<|> 504"#,
946 } 505 expect![[r#"
947 " 506 ct C2 const C2: ();
948 ), 507 ct CONST const CONST: u8;
949 @r###" 508 ta SubTy type SubTy;
950 [ 509 ta Ty type Ty;
951 CompletionItem { 510 fn func() fn func()
952 label: "C2", 511 me method() fn method(&self)
953 source_range: 219..219, 512 fn subfunc() fn subfunc()
954 delete: 219..219, 513 me submethod() fn submethod(&self)
955 insert: "C2", 514 "#]],
956 kind: Const,
957 detail: "const C2: ();",
958 },
959 CompletionItem {
960 label: "CONST",
961 source_range: 219..219,
962 delete: 219..219,
963 insert: "CONST",
964 kind: Const,
965 detail: "const CONST: u8;",
966 },
967 CompletionItem {
968 label: "SubTy",
969 source_range: 219..219,
970 delete: 219..219,
971 insert: "SubTy",
972 kind: TypeAlias,
973 detail: "type SubTy;",
974 },
975 CompletionItem {
976 label: "Ty",
977 source_range: 219..219,
978 delete: 219..219,
979 insert: "Ty",
980 kind: TypeAlias,
981 detail: "type Ty;",
982 },
983 CompletionItem {
984 label: "func()",
985 source_range: 219..219,
986 delete: 219..219,
987 insert: "func()$0",
988 kind: Function,
989 lookup: "func",
990 detail: "fn func()",
991 },
992 CompletionItem {
993 label: "method()",
994 source_range: 219..219,
995 delete: 219..219,
996 insert: "method()$0",
997 kind: Method,
998 lookup: "method",
999 detail: "fn method(&self)",
1000 },
1001 CompletionItem {
1002 label: "subfunc()",
1003 source_range: 219..219,
1004 delete: 219..219,
1005 insert: "subfunc()$0",
1006 kind: Function,
1007 lookup: "subfunc",
1008 detail: "fn subfunc()",
1009 },
1010 CompletionItem {
1011 label: "submethod()",
1012 source_range: 219..219,
1013 delete: 219..219,
1014 insert: "submethod()$0",
1015 kind: Method,
1016 lookup: "submethod",
1017 detail: "fn submethod(&self)",
1018 },
1019 ]
1020 "###
1021 ); 515 );
1022 } 516 }
1023 517
1024 #[test] 518 #[test]
1025 fn completes_self_param_assoc_ty() { 519 fn completes_self_param_assoc_ty() {
1026 assert_debug_snapshot!( 520 check(
1027 do_reference_completion( 521 r#"
1028 " 522trait Super {
1029 //- /lib.rs 523 type Ty;
1030 trait Super { 524 const CONST: u8 = 0;
1031 type Ty; 525 fn func() {}
1032 const CONST: u8 = 0; 526 fn method(&self) {}
1033 fn func() {} 527}
1034 fn method(&self) {}
1035 }
1036 528
1037 trait Sub: Super { 529trait Sub: Super {
1038 type SubTy; 530 type SubTy;
1039 const C2: () = (); 531 const C2: () = ();
1040 fn subfunc() {} 532 fn subfunc() {}
1041 fn submethod(&self) {} 533 fn submethod(&self) {}
1042 } 534}
1043 535
1044 struct Wrap<T>(T); 536struct Wrap<T>(T);
1045 impl<T> Super for Wrap<T> {} 537impl<T> Super for Wrap<T> {}
1046 impl<T> Sub for Wrap<T> { 538impl<T> Sub for Wrap<T> {
1047 fn subfunc() { 539 fn subfunc() {
1048 // Should be able to assume `Self: Sub + Super` 540 // Should be able to assume `Self: Sub + Super`
1049 Self::<|> 541 Self::<|>
1050 } 542 }
1051 } 543}
1052 " 544"#,
1053 ), 545 expect![[r#"
1054 @r###" 546 ct C2 const C2: () = ();
1055 [ 547 ct CONST const CONST: u8 = 0;
1056 CompletionItem { 548 ta SubTy type SubTy;
1057 label: "C2", 549 ta Ty type Ty;
1058 source_range: 365..365, 550 fn func() fn func()
1059 delete: 365..365, 551 me method() fn method(&self)
1060 insert: "C2", 552 fn subfunc() fn subfunc()
1061 kind: Const, 553 me submethod() fn submethod(&self)
1062 detail: "const C2: () = ();", 554 "#]],
1063 },
1064 CompletionItem {
1065 label: "CONST",
1066 source_range: 365..365,
1067 delete: 365..365,
1068 insert: "CONST",
1069 kind: Const,
1070 detail: "const CONST: u8 = 0;",
1071 },
1072 CompletionItem {
1073 label: "SubTy",
1074 source_range: 365..365,
1075 delete: 365..365,
1076 insert: "SubTy",
1077 kind: TypeAlias,
1078 detail: "type SubTy;",
1079 },
1080 CompletionItem {
1081 label: "Ty",
1082 source_range: 365..365,
1083 delete: 365..365,
1084 insert: "Ty",
1085 kind: TypeAlias,
1086 detail: "type Ty;",
1087 },
1088 CompletionItem {
1089 label: "func()",
1090 source_range: 365..365,
1091 delete: 365..365,
1092 insert: "func()$0",
1093 kind: Function,
1094 lookup: "func",
1095 detail: "fn func()",
1096 },
1097 CompletionItem {
1098 label: "method()",
1099 source_range: 365..365,
1100 delete: 365..365,
1101 insert: "method()$0",
1102 kind: Method,
1103 lookup: "method",
1104 detail: "fn method(&self)",
1105 },
1106 CompletionItem {
1107 label: "subfunc()",
1108 source_range: 365..365,
1109 delete: 365..365,
1110 insert: "subfunc()$0",
1111 kind: Function,
1112 lookup: "subfunc",
1113 detail: "fn subfunc()",
1114 },
1115 CompletionItem {
1116 label: "submethod()",
1117 source_range: 365..365,
1118 delete: 365..365,
1119 insert: "submethod()$0",
1120 kind: Method,
1121 lookup: "submethod",
1122 detail: "fn submethod(&self)",
1123 },
1124 ]
1125 "###
1126 ); 555 );
1127 } 556 }
1128 557
1129 #[test] 558 #[test]
1130 fn completes_type_alias() { 559 fn completes_type_alias() {
1131 assert_debug_snapshot!( 560 check(
1132 do_reference_completion( 561 r#"
1133 " 562struct S;
1134 struct S; 563impl S { fn foo() {} }
1135 impl S { fn foo() {} } 564type T = S;
1136 type T = S; 565impl T { fn bar() {} }
1137 impl T { fn bar() {} } 566
1138 567fn main() { T::<|>; }
1139 fn main() { 568"#,
1140 T::<|>; 569 expect![[r#"
1141 } 570 fn bar() fn bar()
1142 " 571 fn foo() fn foo()
1143 ), 572 "#]],
1144 @r###"
1145 [
1146 CompletionItem {
1147 label: "bar()",
1148 source_range: 185..185,
1149 delete: 185..185,
1150 insert: "bar()$0",
1151 kind: Function,
1152 lookup: "bar",
1153 detail: "fn bar()",
1154 },
1155 CompletionItem {
1156 label: "foo()",
1157 source_range: 185..185,
1158 delete: 185..185,
1159 insert: "foo()$0",
1160 kind: Function,
1161 lookup: "foo",
1162 detail: "fn foo()",
1163 },
1164 ]
1165 "###
1166 ); 573 );
1167 } 574 }
1168 575
1169 #[test] 576 #[test]
1170 fn completes_qualified_macros() { 577 fn completes_qualified_macros() {
1171 assert_debug_snapshot!( 578 check(
1172 do_reference_completion( 579 r#"
1173 " 580#[macro_export]
1174 #[macro_export] 581macro_rules! foo { () => {} }
1175 macro_rules! foo { 582
1176 () => {} 583fn main() { let _ = crate::<|> }
1177 } 584 "#,
585 expect![[r##"
586 ma foo!(…) #[macro_export]
587 macro_rules! foo
588 fn main() fn main()
589 "##]],
590 );
591 }
1178 592
1179 fn main() { 593 #[test]
1180 let _ = crate::<|> 594 fn test_super_super_completion() {
1181 } 595 check(
1182 " 596 r#"
1183 ), 597mod a {
1184 @r###" 598 const A: usize = 0;
1185 [ 599 mod b {
1186 CompletionItem { 600 const B: usize = 0;
1187 label: "foo!(…)", 601 mod c { use super::super::<|> }
1188 source_range: 179..179, 602 }
1189 delete: 179..179, 603}
1190 insert: "foo!($0)", 604"#,
1191 kind: Macro, 605 expect![[r#"
1192 detail: "#[macro_export]\nmacro_rules! foo", 606 ct A
1193 }, 607 md b
1194 CompletionItem { 608 "#]],
1195 label: "main()",
1196 source_range: 179..179,
1197 delete: 179..179,
1198 insert: "main()$0",
1199 kind: Function,
1200 lookup: "main",
1201 detail: "fn main()",
1202 },
1203 ]
1204 "###
1205 ); 609 );
1206 } 610 }
1207 611
1208 #[test] 612 #[test]
1209 fn completes_reexported_items_under_correct_name() { 613 fn completes_reexported_items_under_correct_name() {
1210 assert_debug_snapshot!( 614 check(
1211 do_reference_completion( 615 r#"
1212 r" 616fn foo() { self::m::<|> }
1213 fn foo() {
1214 self::m::<|>
1215 }
1216 617
1217 mod m { 618mod m {
1218 pub use super::p::wrong_fn as right_fn; 619 pub use super::p::wrong_fn as right_fn;
1219 pub use super::p::WRONG_CONST as RIGHT_CONST; 620 pub use super::p::WRONG_CONST as RIGHT_CONST;
1220 pub use super::p::WrongType as RightType; 621 pub use super::p::WrongType as RightType;
1221 } 622}
1222 mod p { 623mod p {
1223 fn wrong_fn() {} 624 fn wrong_fn() {}
1224 const WRONG_CONST: u32 = 1; 625 const WRONG_CONST: u32 = 1;
1225 struct WrongType {}; 626 struct WrongType {};
1226 } 627}
1227 " 628"#,
1228 ), 629 expect![[r#"
1229 @r###" 630 ct RIGHT_CONST
1230 [ 631 st RightType
1231 CompletionItem { 632 fn right_fn() fn wrong_fn()
1232 label: "RIGHT_CONST", 633 "#]],
1233 source_range: 57..57, 634 );
1234 delete: 57..57, 635
1235 insert: "RIGHT_CONST", 636 check_edit(
1236 kind: Const, 637 "RightType",
1237 }, 638 r#"
1238 CompletionItem { 639fn foo() { self::m::<|> }
1239 label: "RightType", 640
1240 source_range: 57..57, 641mod m {
1241 delete: 57..57, 642 pub use super::p::wrong_fn as right_fn;
1242 insert: "RightType", 643 pub use super::p::WRONG_CONST as RIGHT_CONST;
1243 kind: Struct, 644 pub use super::p::WrongType as RightType;
1244 }, 645}
1245 CompletionItem { 646mod p {
1246 label: "right_fn()", 647 fn wrong_fn() {}
1247 source_range: 57..57, 648 const WRONG_CONST: u32 = 1;
1248 delete: 57..57, 649 struct WrongType {};
1249 insert: "right_fn()$0", 650}
1250 kind: Function, 651"#,
1251 lookup: "right_fn", 652 r#"
1252 detail: "fn wrong_fn()", 653fn foo() { self::m::RightType }
1253 }, 654
1254 ] 655mod m {
1255 "### 656 pub use super::p::wrong_fn as right_fn;
657 pub use super::p::WRONG_CONST as RIGHT_CONST;
658 pub use super::p::WrongType as RightType;
659}
660mod p {
661 fn wrong_fn() {}
662 const WRONG_CONST: u32 = 1;
663 struct WrongType {};
664}
665"#,
1256 ); 666 );
1257 } 667 }
1258 668
1259 #[test] 669 #[test]
1260 fn completes_in_simple_macro_call() { 670 fn completes_in_simple_macro_call() {
1261 let completions = do_reference_completion( 671 check(
1262 r#" 672 r#"
1263 macro_rules! m { ($e:expr) => { $e } } 673macro_rules! m { ($e:expr) => { $e } }
1264 fn main() { m!(self::f<|>); } 674fn main() { m!(self::f<|>); }
1265 fn foo() {} 675fn foo() {}
1266 "#, 676"#,
677 expect![[r#"
678 fn foo() fn foo()
679 fn main() fn main()
680 "#]],
1267 ); 681 );
1268 assert_debug_snapshot!(completions, @r###"
1269 [
1270 CompletionItem {
1271 label: "foo()",
1272 source_range: 93..94,
1273 delete: 93..94,
1274 insert: "foo()$0",
1275 kind: Function,
1276 lookup: "foo",
1277 detail: "fn foo()",
1278 },
1279 CompletionItem {
1280 label: "main()",
1281 source_range: 93..94,
1282 delete: 93..94,
1283 insert: "main()$0",
1284 kind: Function,
1285 lookup: "main",
1286 detail: "fn main()",
1287 },
1288 ]
1289 "###);
1290 } 682 }
1291 683
1292 #[test] 684 #[test]
1293 fn function_mod_share_name() { 685 fn function_mod_share_name() {
1294 assert_debug_snapshot!( 686 check(
1295 do_reference_completion( 687 r#"
1296 r" 688fn foo() { self::m::<|> }
1297 fn foo() {
1298 self::m::<|>
1299 }
1300 689
1301 mod m { 690mod m {
1302 pub mod z {} 691 pub mod z {}
1303 pub fn z() {} 692 pub fn z() {}
1304 } 693}
1305 ", 694"#,
1306 ), 695 expect![[r#"
1307 @r###" 696 md z
1308 [ 697 fn z() pub fn z()
1309 CompletionItem { 698 "#]],
1310 label: "z",
1311 source_range: 57..57,
1312 delete: 57..57,
1313 insert: "z",
1314 kind: Module,
1315 },
1316 CompletionItem {
1317 label: "z()",
1318 source_range: 57..57,
1319 delete: 57..57,
1320 insert: "z()$0",
1321 kind: Function,
1322 lookup: "z",
1323 detail: "pub fn z()",
1324 },
1325 ]
1326 "###
1327 ); 699 );
1328 } 700 }
1329 701
1330 #[test] 702 #[test]
1331 fn completes_hashmap_new() { 703 fn completes_hashmap_new() {
1332 assert_debug_snapshot!( 704 check(
1333 do_reference_completion( 705 r#"
1334 r" 706struct RandomState;
1335 struct RandomState; 707struct HashMap<K, V, S = RandomState> {}
1336 struct HashMap<K, V, S = RandomState> {} 708
1337 709impl<K, V> HashMap<K, V, RandomState> {
1338 impl<K, V> HashMap<K, V, RandomState> { 710 pub fn new() -> HashMap<K, V, RandomState> { }
1339 pub fn new() -> HashMap<K, V, RandomState> { } 711}
1340 } 712fn foo() {
1341 fn foo() { 713 HashMap::<|>
1342 HashMap::<|> 714}
1343 } 715"#,
1344 " 716 expect![[r#"
1345 ), 717 fn new() pub fn new() -> HashMap<K, V, RandomState>
1346 @r###" 718 "#]],
1347 [
1348 CompletionItem {
1349 label: "new()",
1350 source_range: 292..292,
1351 delete: 292..292,
1352 insert: "new()$0",
1353 kind: Function,
1354 lookup: "new",
1355 detail: "pub fn new() -> HashMap<K, V, RandomState>",
1356 },
1357 ]
1358 "###
1359 ); 719 );
1360 } 720 }
1361 721
1362 #[test] 722 #[test]
1363 fn dont_complete_attr() { 723 fn dont_complete_attr() {
1364 assert_debug_snapshot!( 724 check(
1365 do_reference_completion( 725 r#"
1366 r" 726mod foo { pub struct Foo; }
1367 mod foo { pub struct Foo; } 727#[foo::<|>]
1368 #[foo::<|>] 728fn f() {}
1369 fn f() {} 729"#,
1370 " 730 expect![[""]],
1371 ), 731 );
1372 @r###"[]"###
1373 )
1374 } 732 }
1375} 733}
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
index b7ab654c5..74b94594d 100644
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -18,389 +18,209 @@ pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
18 18
19#[cfg(test)] 19#[cfg(test)]
20mod tests { 20mod tests {
21 mod record_pat_tests { 21 use expect::{expect, Expect};
22 use insta::assert_debug_snapshot;
23 22
24 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 23 use crate::completion::{test_utils::completion_list, CompletionKind};
25 24
26 fn complete(code: &str) -> Vec<CompletionItem> { 25 fn check(ra_fixture: &str, expect: Expect) {
27 do_completion(code, CompletionKind::Reference) 26 let actual = completion_list(ra_fixture, CompletionKind::Reference);
28 } 27 expect.assert_eq(&actual);
29 28 }
30 #[test]
31 fn test_record_pattern_field() {
32 let completions = complete(
33 r"
34 struct S { foo: u32 }
35
36 fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40 }
41 ",
42 );
43 assert_debug_snapshot!(completions, @r###"
44 [
45 CompletionItem {
46 label: "foo",
47 source_range: 117..118,
48 delete: 117..118,
49 insert: "foo",
50 kind: Field,
51 detail: "u32",
52 },
53 ]
54 "###);
55 }
56
57 #[test]
58 fn test_record_pattern_enum_variant() {
59 let completions = complete(
60 r"
61 enum E {
62 S { foo: u32, bar: () }
63 }
64
65 fn process(e: E) {
66 match e {
67 E::S { <|> } => (),
68 }
69 }
70 ",
71 );
72 assert_debug_snapshot!(completions, @r###"
73 [
74 CompletionItem {
75 label: "bar",
76 source_range: 161..161,
77 delete: 161..161,
78 insert: "bar",
79 kind: Field,
80 detail: "()",
81 },
82 CompletionItem {
83 label: "foo",
84 source_range: 161..161,
85 delete: 161..161,
86 insert: "foo",
87 kind: Field,
88 detail: "u32",
89 },
90 ]
91 "###);
92 }
93 29
94 #[test] 30 #[test]
95 fn test_record_pattern_field_in_simple_macro() { 31 fn test_record_pattern_field() {
96 let completions = complete( 32 check(
97 r" 33 r#"
98 macro_rules! m { ($e:expr) => { $e } } 34struct S { foo: u32 }
99 struct S { foo: u32 }
100 35
101 fn process(f: S) { 36fn process(f: S) {
102 m!(match f { 37 match f {
103 S { f<|>: 92 } => (), 38 S { f<|>: 92 } => (),
104 }) 39 }
105 } 40}
106 ", 41"#,
107 ); 42 expect![[r#"
108 assert_debug_snapshot!(completions, @r###" 43 fd foo u32
109 [ 44 "#]],
110 CompletionItem { 45 );
111 label: "foo", 46 }
112 source_range: 171..172,
113 delete: 171..172,
114 insert: "foo",
115 kind: Field,
116 detail: "u32",
117 },
118 ]
119 "###);
120 }
121 47
122 #[test] 48 #[test]
123 fn only_missing_fields_are_completed_in_destruct_pats() { 49 fn test_record_pattern_enum_variant() {
124 let completions = complete( 50 check(
125 r" 51 r#"
126 struct S { 52enum E { S { foo: u32, bar: () } }
127 foo1: u32,
128 foo2: u32,
129 bar: u32,
130 baz: u32,
131 }
132 53
133 fn main() { 54fn process(e: E) {
134 let s = S { 55 match e {
135 foo1: 1, 56 E::S { <|> } => (),
136 foo2: 2, 57 }
137 bar: 3, 58}
138 baz: 4, 59"#,
139 }; 60 expect![[r#"
140 if let S { foo1, foo2: a, <|> } = s {} 61 fd bar ()
141 } 62 fd foo u32
142 ", 63 "#]],
143 ); 64 );
144 assert_debug_snapshot!(completions, @r###"
145 [
146 CompletionItem {
147 label: "bar",
148 source_range: 372..372,
149 delete: 372..372,
150 insert: "bar",
151 kind: Field,
152 detail: "u32",
153 },
154 CompletionItem {
155 label: "baz",
156 source_range: 372..372,
157 delete: 372..372,
158 insert: "baz",
159 kind: Field,
160 detail: "u32",
161 },
162 ]
163 "###);
164 }
165 } 65 }
166 66
167 mod record_lit_tests { 67 #[test]
168 use insta::assert_debug_snapshot; 68 fn test_record_pattern_field_in_simple_macro() {
169 69 check(
170 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 70 r"
71macro_rules! m { ($e:expr) => { $e } }
72struct S { foo: u32 }
73
74fn process(f: S) {
75 m!(match f {
76 S { f<|>: 92 } => (),
77 })
78}
79",
80 expect![[r#"
81 fd foo u32
82 "#]],
83 );
84 }
171 85
172 fn complete(code: &str) -> Vec<CompletionItem> { 86 #[test]
173 do_completion(code, CompletionKind::Reference) 87 fn only_missing_fields_are_completed_in_destruct_pats() {
174 } 88 check(
89 r#"
90struct S {
91 foo1: u32, foo2: u32,
92 bar: u32, baz: u32,
93}
175 94
176 #[test] 95fn main() {
177 fn test_record_literal_deprecated_field() { 96 let s = S {
178 let completions = complete( 97 foo1: 1, foo2: 2,
179 r" 98 bar: 3, baz: 4,
180 struct A { 99 };
181 #[deprecated] 100 if let S { foo1, foo2: a, <|> } = s {}
182 the_field: u32, 101}
183 } 102"#,
184 fn foo() { 103 expect![[r#"
185 A { the<|> } 104 fd bar u32
186 } 105 fd baz u32
187 ", 106 "#]],
188 ); 107 );
189 assert_debug_snapshot!(completions, @r###" 108 }
190 [
191 CompletionItem {
192 label: "the_field",
193 source_range: 142..145,
194 delete: 142..145,
195 insert: "the_field",
196 kind: Field,
197 detail: "u32",
198 deprecated: true,
199 },
200 ]
201 "###);
202 }
203 109
204 #[test] 110 #[test]
205 fn test_record_literal_field() { 111 fn test_record_literal_field() {
206 let completions = complete( 112 check(
207 r" 113 r#"
208 struct A { the_field: u32 } 114struct A { the_field: u32 }
209 fn foo() { 115fn foo() {
210 A { the<|> } 116 A { the<|> }
211 } 117}
212 ", 118"#,
213 ); 119 expect![[r#"
214 assert_debug_snapshot!(completions, @r###" 120 fd the_field u32
215 [ 121 "#]],
216 CompletionItem { 122 );
217 label: "the_field", 123 }
218 source_range: 83..86,
219 delete: 83..86,
220 insert: "the_field",
221 kind: Field,
222 detail: "u32",
223 },
224 ]
225 "###);
226 }
227 124
228 #[test] 125 #[test]
229 fn test_record_literal_enum_variant() { 126 fn test_record_literal_enum_variant() {
230 let completions = complete( 127 check(
231 r" 128 r#"
232 enum E { 129enum E { A { a: u32 } }
233 A { a: u32 } 130fn foo() {
234 } 131 let _ = E::A { <|> }
235 fn foo() { 132}
236 let _ = E::A { <|> } 133"#,
237 } 134 expect![[r#"
238 ", 135 fd a u32
239 ); 136 "#]],
240 assert_debug_snapshot!(completions, @r###" 137 );
241 [ 138 }
242 CompletionItem {
243 label: "a",
244 source_range: 119..119,
245 delete: 119..119,
246 insert: "a",
247 kind: Field,
248 detail: "u32",
249 },
250 ]
251 "###);
252 }
253 139
254 #[test] 140 #[test]
255 fn test_record_literal_two_structs() { 141 fn test_record_literal_two_structs() {
256 let completions = complete( 142 check(
257 r" 143 r#"
258 struct A { a: u32 } 144struct A { a: u32 }
259 struct B { b: u32 } 145struct B { b: u32 }
260 146
261 fn foo() { 147fn foo() {
262 let _: A = B { <|> } 148 let _: A = B { <|> }
263 } 149}
264 ", 150"#,
265 ); 151 expect![[r#"
266 assert_debug_snapshot!(completions, @r###" 152 fd b u32
267 [ 153 "#]],
268 CompletionItem { 154 );
269 label: "b", 155 }
270 source_range: 119..119,
271 delete: 119..119,
272 insert: "b",
273 kind: Field,
274 detail: "u32",
275 },
276 ]
277 "###);
278 }
279 156
280 #[test] 157 #[test]
281 fn test_record_literal_generic_struct() { 158 fn test_record_literal_generic_struct() {
282 let completions = complete( 159 check(
283 r" 160 r#"
284 struct A<T> { a: T } 161struct A<T> { a: T }
285 162
286 fn foo() { 163fn foo() {
287 let _: A<u32> = A { <|> } 164 let _: A<u32> = A { <|> }
288 } 165}
289 ", 166"#,
290 ); 167 expect![[r#"
291 assert_debug_snapshot!(completions, @r###" 168 fd a u32
292 [ 169 "#]],
293 CompletionItem { 170 );
294 label: "a", 171 }
295 source_range: 93..93,
296 delete: 93..93,
297 insert: "a",
298 kind: Field,
299 detail: "u32",
300 },
301 ]
302 "###);
303 }
304 172
305 #[test] 173 #[test]
306 fn test_record_literal_field_in_simple_macro() { 174 fn test_record_literal_field_in_simple_macro() {
307 let completions = complete( 175 check(
308 r" 176 r#"
309 macro_rules! m { ($e:expr) => { $e } } 177macro_rules! m { ($e:expr) => { $e } }
310 struct A { the_field: u32 } 178struct A { the_field: u32 }
311 fn foo() { 179fn foo() {
312 m!(A { the<|> }) 180 m!(A { the<|> })
313 } 181}
314 ", 182"#,
315 ); 183 expect![[r#"
316 assert_debug_snapshot!(completions, @r###" 184 fd the_field u32
317 [ 185 "#]],
318 CompletionItem { 186 );
319 label: "the_field", 187 }
320 source_range: 137..140,
321 delete: 137..140,
322 insert: "the_field",
323 kind: Field,
324 detail: "u32",
325 },
326 ]
327 "###);
328 }
329 188
330 #[test] 189 #[test]
331 fn only_missing_fields_are_completed() { 190 fn only_missing_fields_are_completed() {
332 let completions = complete( 191 check(
333 r" 192 r#"
334 struct S { 193struct S {
335 foo1: u32, 194 foo1: u32, foo2: u32,
336 foo2: u32, 195 bar: u32, baz: u32,
337 bar: u32, 196}
338 baz: u32,
339 }
340 197
341 fn main() { 198fn main() {
342 let foo1 = 1; 199 let foo1 = 1;
343 let s = S { 200 let s = S { foo1, foo2: 5, <|> }
344 foo1, 201}
345 foo2: 5, 202"#,
346 <|> 203 expect![[r#"
347 } 204 fd bar u32
348 } 205 fd baz u32
349 ", 206 "#]],
350 ); 207 );
351 assert_debug_snapshot!(completions, @r###" 208 }
352 [
353 CompletionItem {
354 label: "bar",
355 source_range: 302..302,
356 delete: 302..302,
357 insert: "bar",
358 kind: Field,
359 detail: "u32",
360 },
361 CompletionItem {
362 label: "baz",
363 source_range: 302..302,
364 delete: 302..302,
365 insert: "baz",
366 kind: Field,
367 detail: "u32",
368 },
369 ]
370 "###);
371 }
372 209
373 #[test] 210 #[test]
374 fn completes_functional_update() { 211 fn completes_functional_update() {
375 let completions = complete( 212 check(
376 r" 213 r#"
377 struct S { 214struct S { foo1: u32, foo2: u32 }
378 foo1: u32,
379 foo2: u32,
380 }
381 215
382 fn main() { 216fn main() {
383 let foo1 = 1; 217 let foo1 = 1;
384 let s = S { 218 let s = S { foo1, <|> .. loop {} }
385 foo1, 219}
386 <|> 220"#,
387 .. loop {} 221 expect![[r#"
388 } 222 fd foo2 u32
389 } 223 "#]],
390 ", 224 );
391 );
392 assert_debug_snapshot!(completions, @r###"
393 [
394 CompletionItem {
395 label: "foo2",
396 source_range: 221..221,
397 delete: 221..221,
398 insert: "foo2",
399 kind: Field,
400 detail: "u32",
401 },
402 ]
403 "###);
404 }
405 } 225 }
406} 226}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index 0568d9ccf..28d8f7876 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -70,95 +70,47 @@ fn ${1:feature}() {
70 70
71#[cfg(test)] 71#[cfg(test)]
72mod tests { 72mod tests {
73 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 73 use expect::{expect, Expect};
74 use insta::assert_debug_snapshot;
75 74
76 fn do_snippet_completion(code: &str) -> Vec<CompletionItem> { 75 use crate::completion::{test_utils::completion_list, CompletionKind};
77 do_completion(code, CompletionKind::Snippet) 76
77 fn check(ra_fixture: &str, expect: Expect) {
78 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
79 expect.assert_eq(&actual)
78 } 80 }
79 81
80 #[test] 82 #[test]
81 fn completes_snippets_in_expressions() { 83 fn completes_snippets_in_expressions() {
82 assert_debug_snapshot!( 84 check(
83 do_snippet_completion(r"fn foo(x: i32) { <|> }"), 85 r#"fn foo(x: i32) { <|> }"#,
84 @r###" 86 expect![[r#"
85 [ 87 sn pd
86 CompletionItem { 88 sn ppd
87 label: "pd", 89 "#]],
88 source_range: 17..17, 90 );
89 delete: 17..17,
90 insert: "eprintln!(\"$0 = {:?}\", $0);",
91 kind: Snippet,
92 },
93 CompletionItem {
94 label: "ppd",
95 source_range: 17..17,
96 delete: 17..17,
97 insert: "eprintln!(\"$0 = {:#?}\", $0);",
98 kind: Snippet,
99 },
100 ]
101 "###
102 );
103 } 91 }
104 92
105 #[test] 93 #[test]
106 fn should_not_complete_snippets_in_path() { 94 fn should_not_complete_snippets_in_path() {
107 assert_debug_snapshot!( 95 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
108 do_snippet_completion(r"fn foo(x: i32) { ::foo<|> }"), 96 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
109 @"[]"
110 );
111 assert_debug_snapshot!(
112 do_snippet_completion(r"fn foo(x: i32) { ::<|> }"),
113 @"[]"
114 );
115 } 97 }
116 98
117 #[test] 99 #[test]
118 fn completes_snippets_in_items() { 100 fn completes_snippets_in_items() {
119 assert_debug_snapshot!( 101 check(
120 do_snippet_completion( 102 r#"
121 r" 103#[cfg(test)]
122 #[cfg(test)] 104mod tests {
123 mod tests { 105 <|>
124 <|> 106}
125 } 107"#,
126 " 108 expect![[r#"
127 ), 109 sn Test function
128 @r###" 110 sn Test module
129 [ 111 sn macro_rules
130 CompletionItem { 112 sn pub(crate)
131 label: "Test function", 113 "#]],
132 source_range: 78..78, 114 )
133 delete: 78..78,
134 insert: "#[test]\nfn ${1:feature}() {\n $0\n}",
135 kind: Snippet,
136 lookup: "tfn",
137 },
138 CompletionItem {
139 label: "Test module",
140 source_range: 78..78,
141 delete: 78..78,
142 insert: "#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn ${1:test_name}() {\n $0\n }\n}",
143 kind: Snippet,
144 lookup: "tmod",
145 },
146 CompletionItem {
147 label: "macro_rules",
148 source_range: 78..78,
149 delete: 78..78,
150 insert: "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}",
151 kind: Snippet,
152 },
153 CompletionItem {
154 label: "pub(crate)",
155 source_range: 78..78,
156 delete: 78..78,
157 insert: "pub(crate) $0",
158 kind: Snippet,
159 },
160 ]
161 "###
162 );
163 } 115 }
164} 116}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 21c9316e6..a610fd6d1 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -227,330 +227,264 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
227 227
228#[cfg(test)] 228#[cfg(test)]
229mod tests { 229mod tests {
230 use insta::assert_debug_snapshot; 230 use expect::{expect, Expect};
231 231
232 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 232 use crate::completion::{
233 test_utils::{check_edit, completion_list},
234 CompletionKind,
235 };
233 236
234 fn complete(code: &str) -> Vec<CompletionItem> { 237 fn check(ra_fixture: &str, expect: Expect) {
235 do_completion(code, CompletionKind::Magic) 238 let actual = completion_list(ra_fixture, CompletionKind::Magic);
239 expect.assert_eq(&actual)
236 } 240 }
237 241
238 #[test] 242 #[test]
239 fn name_ref_function_type_const() { 243 fn name_ref_function_type_const() {
240 let completions = complete( 244 check(
241 r" 245 r#"
242 trait Test { 246trait Test {
243 type TestType; 247 type TestType;
244 const TEST_CONST: u16; 248 const TEST_CONST: u16;
245 fn test(); 249 fn test();
246 } 250}
247 251struct T;
248 struct T1;
249 252
250 impl Test for T1 { 253impl Test for T {
251 t<|> 254 t<|>
252 } 255}
253 ", 256"#,
257 expect![["
258ct const TEST_CONST: u16 = \n\
259fn fn test()
260ta type TestType = \n\
261 "]],
254 ); 262 );
255 assert_debug_snapshot!(completions, @r###"
256 [
257 CompletionItem {
258 label: "const TEST_CONST: u16 = ",
259 source_range: 209..210,
260 delete: 209..210,
261 insert: "const TEST_CONST: u16 = ",
262 kind: Const,
263 lookup: "TEST_CONST",
264 },
265 CompletionItem {
266 label: "fn test()",
267 source_range: 209..210,
268 delete: 209..210,
269 insert: "fn test() {\n $0\n}",
270 kind: Function,
271 lookup: "test",
272 },
273 CompletionItem {
274 label: "type TestType = ",
275 source_range: 209..210,
276 delete: 209..210,
277 insert: "type TestType = ",
278 kind: TypeAlias,
279 lookup: "TestType",
280 },
281 ]
282 "###);
283 } 263 }
284 264
285 #[test] 265 #[test]
286 fn no_nested_fn_completions() { 266 fn no_nested_fn_completions() {
287 let completions = complete( 267 check(
288 r" 268 r"
289 trait Test { 269trait Test {
290 fn test(); 270 fn test();
291 fn test2(); 271 fn test2();
292 } 272}
293 273struct T;
294 struct T1;
295 274
296 impl Test for T1 { 275impl Test for T {
297 fn test() { 276 fn test() {
298 t<|> 277 t<|>
299 } 278 }
300 } 279}
301 ", 280",
281 expect![[""]],
302 ); 282 );
303 assert_debug_snapshot!(completions, @r###"[]"###);
304 } 283 }
305 284
306 #[test] 285 #[test]
307 fn name_ref_single_function() { 286 fn name_ref_single_function() {
308 let completions = complete( 287 check_edit(
309 r" 288 "test",
310 trait Test { 289 r#"
311 fn test(); 290trait Test {
312 } 291 fn test();
292}
293struct T;
313 294
314 struct T1; 295impl Test for T {
296 t<|>
297}
298"#,
299 r#"
300trait Test {
301 fn test();
302}
303struct T;
315 304
316 impl Test for T1 { 305impl Test for T {
317 t<|> 306 fn test() {
318 } 307 $0
319 ", 308}
309}
310"#,
320 ); 311 );
321 assert_debug_snapshot!(completions, @r###"
322 [
323 CompletionItem {
324 label: "fn test()",
325 source_range: 139..140,
326 delete: 139..140,
327 insert: "fn test() {\n $0\n}",
328 kind: Function,
329 lookup: "test",
330 },
331 ]
332 "###);
333 } 312 }
334 313
335 #[test] 314 #[test]
336 fn single_function() { 315 fn single_function() {
337 let completions = complete( 316 check_edit(
338 r" 317 "test",
339 trait Test { 318 r#"
340 fn foo(); 319trait Test {
341 } 320 fn test();
321}
322struct T;
342 323
343 struct T1; 324impl Test for T {
325 fn t<|>
326}
327"#,
328 r#"
329trait Test {
330 fn test();
331}
332struct T;
344 333
345 impl Test for T1 { 334impl Test for T {
346 fn f<|> 335 fn test() {
347 } 336 $0
348 ", 337}
338}
339"#,
349 ); 340 );
350 assert_debug_snapshot!(completions, @r###"
351 [
352 CompletionItem {
353 label: "fn foo()",
354 source_range: 141..142,
355 delete: 138..142,
356 insert: "fn foo() {\n $0\n}",
357 kind: Function,
358 lookup: "foo",
359 },
360 ]
361 "###);
362 } 341 }
363 342
364 #[test] 343 #[test]
365 fn hide_implemented_fn() { 344 fn hide_implemented_fn() {
366 let completions = complete( 345 check(
367 r" 346 r#"
368 trait Test { 347trait Test {
369 fn foo(); 348 fn foo();
370 fn foo_bar(); 349 fn foo_bar();
371 } 350}
372 351struct T;
373 struct T1;
374
375 impl Test for T1 {
376 fn foo() {}
377
378 fn f<|>
379 }
380 ",
381 );
382 assert_debug_snapshot!(completions, @r###"
383 [
384 CompletionItem {
385 label: "fn foo_bar()",
386 source_range: 200..201,
387 delete: 197..201,
388 insert: "fn foo_bar() {\n $0\n}",
389 kind: Function,
390 lookup: "foo_bar",
391 },
392 ]
393 "###);
394 }
395
396 #[test]
397 fn completes_only_on_top_level() {
398 let completions = complete(
399 r"
400 trait Test {
401 fn foo();
402
403 fn foo_bar();
404 }
405
406 struct T1;
407 352
408 impl Test for T1 { 353impl Test for T {
409 fn foo() { 354 fn foo() {}
410 <|> 355 fn f<|>
411 } 356}
412 } 357"#,
413 ", 358 expect![[r#"
359 fn fn foo_bar()
360 "#]],
414 ); 361 );
415 assert_debug_snapshot!(completions, @r###"[]"###);
416 } 362 }
417 363
418 #[test] 364 #[test]
419 fn generic_fn() { 365 fn generic_fn() {
420 let completions = complete( 366 check_edit(
421 r" 367 "foo",
422 trait Test { 368 r#"
423 fn foo<T>(); 369trait Test {
424 } 370 fn foo<T>();
371}
372struct T;
425 373
426 struct T1; 374impl Test for T {
375 fn f<|>
376}
377"#,
378 r#"
379trait Test {
380 fn foo<T>();
381}
382struct T;
427 383
428 impl Test for T1 { 384impl Test for T {
429 fn f<|> 385 fn foo<T>() {
430 } 386 $0
431 ", 387}
388}
389"#,
432 ); 390 );
433 assert_debug_snapshot!(completions, @r###" 391 check_edit(
434 [ 392 "foo",
435 CompletionItem { 393 r#"
436 label: "fn foo()", 394trait Test {
437 source_range: 144..145, 395 fn foo<T>() where T: Into<String>;
438 delete: 141..145, 396}
439 insert: "fn foo<T>() {\n $0\n}", 397struct T;
440 kind: Function,
441 lookup: "foo",
442 },
443 ]
444 "###);
445 }
446
447 #[test]
448 fn generic_constrait_fn() {
449 let completions = complete(
450 r"
451 trait Test {
452 fn foo<T>() where T: Into<String>;
453 }
454 398
455 struct T1; 399impl Test for T {
400 fn f<|>
401}
402"#,
403 r#"
404trait Test {
405 fn foo<T>() where T: Into<String>;
406}
407struct T;
456 408
457 impl Test for T1 { 409impl Test for T {
458 fn f<|> 410 fn foo<T>()
459 } 411where T: Into<String> {
460 ", 412 $0
413}
414}
415"#,
461 ); 416 );
462 assert_debug_snapshot!(completions, @r###"
463 [
464 CompletionItem {
465 label: "fn foo()",
466 source_range: 166..167,
467 delete: 163..167,
468 insert: "fn foo<T>()\nwhere T: Into<String> {\n $0\n}",
469 kind: Function,
470 lookup: "foo",
471 },
472 ]
473 "###);
474 } 417 }
475 418
476 #[test] 419 #[test]
477 fn associated_type() { 420 fn associated_type() {
478 let completions = complete( 421 check_edit(
479 r" 422 "SomeType",
480 trait Test { 423 r#"
481 type SomeType; 424trait Test {
482 } 425 type SomeType;
426}
483 427
484 impl Test for () { 428impl Test for () {
485 type S<|> 429 type S<|>
486 } 430}
487 ", 431"#,
432 "
433trait Test {
434 type SomeType;
435}
436
437impl Test for () {
438 type SomeType = \n\
439}
440",
488 ); 441 );
489 assert_debug_snapshot!(completions, @r###"
490 [
491 CompletionItem {
492 label: "type SomeType = ",
493 source_range: 124..125,
494 delete: 119..125,
495 insert: "type SomeType = ",
496 kind: TypeAlias,
497 lookup: "SomeType",
498 },
499 ]
500 "###);
501 } 442 }
502 443
503 #[test] 444 #[test]
504 fn associated_const() { 445 fn associated_const() {
505 let completions = complete( 446 check_edit(
506 r" 447 "SOME_CONST",
507 trait Test { 448 r#"
508 const SOME_CONST: u16; 449trait Test {
509 } 450 const SOME_CONST: u16;
451}
510 452
511 impl Test for () { 453impl Test for () {
512 const S<|> 454 const S<|>
513 } 455}
514 ", 456"#,
457 "
458trait Test {
459 const SOME_CONST: u16;
460}
461
462impl Test for () {
463 const SOME_CONST: u16 = \n\
464}
465",
515 ); 466 );
516 assert_debug_snapshot!(completions, @r###"
517 [
518 CompletionItem {
519 label: "const SOME_CONST: u16 = ",
520 source_range: 133..134,
521 delete: 127..134,
522 insert: "const SOME_CONST: u16 = ",
523 kind: Const,
524 lookup: "SOME_CONST",
525 },
526 ]
527 "###);
528 }
529 467
530 #[test] 468 check_edit(
531 fn associated_const_with_default() { 469 "SOME_CONST",
532 let completions = complete( 470 r#"
533 r" 471trait Test {
534 trait Test { 472 const SOME_CONST: u16 = 92;
535 const SOME_CONST: u16 = 42; 473}
536 }
537 474
538 impl Test for () { 475impl Test for () {
539 const S<|> 476 const S<|>
540 } 477}
541 ", 478"#,
479 "
480trait Test {
481 const SOME_CONST: u16 = 92;
482}
483
484impl Test for () {
485 const SOME_CONST: u16 = \n\
486}
487",
542 ); 488 );
543 assert_debug_snapshot!(completions, @r###"
544 [
545 CompletionItem {
546 label: "const SOME_CONST: u16 = ",
547 source_range: 138..139,
548 delete: 132..139,
549 insert: "const SOME_CONST: u16 = ",
550 kind: Const,
551 lookup: "SOME_CONST",
552 },
553 ]
554 "###);
555 } 489 }
556} 490}
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 68032c37e..bd9551f35 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,11 +1,10 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::{Adt, ModuleDef, ScopeDef, Type};
4use ra_syntax::AstNode;
4use test_utils::mark; 5use test_utils::mark;
5 6
6use crate::completion::{CompletionContext, Completions}; 7use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode;
9 8
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -26,7 +25,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
26 return; 25 return;
27 } 26 }
28 27
29 ctx.scope().process_all_names(&mut |name, res| { 28 ctx.scope.process_all_names(&mut |name, res| {
30 if ctx.use_item_syntax.is_some() { 29 if ctx.use_item_syntax.is_some() {
31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
32 if name_ref.syntax().text() == name.to_string().as_str() { 31 if name_ref.syntax().text() == name.to_string().as_str() {
@@ -43,7 +42,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
43 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
44 let variants = enum_data.variants(ctx.db); 43 let variants = enum_data.variants(ctx.db);
45 44
46 let module = if let Some(module) = ctx.scope().module() { 45 let module = if let Some(module) = ctx.scope.module() {
47 // Compute path from the completion site if available. 46 // Compute path from the completion site if available.
48 module 47 module
49 } else { 48 } else {
@@ -65,1361 +64,595 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
65 64
66#[cfg(test)] 65#[cfg(test)]
67mod tests { 66mod tests {
68 use insta::assert_debug_snapshot; 67 use expect::{expect, Expect};
69 use test_utils::mark; 68 use test_utils::mark;
70 69
71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 70 use crate::completion::{
71 test_utils::{check_edit, completion_list},
72 CompletionKind,
73 };
72 74
73 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 75 fn check(ra_fixture: &str, expect: Expect) {
74 do_completion(ra_fixture, CompletionKind::Reference) 76 let actual = completion_list(ra_fixture, CompletionKind::Reference);
77 expect.assert_eq(&actual)
75 } 78 }
76 79
77 #[test] 80 #[test]
78 fn self_fulfilling_completion() { 81 fn self_fulfilling_completion() {
79 mark::check!(self_fulfilling_completion); 82 mark::check!(self_fulfilling_completion);
80 assert_debug_snapshot!( 83 check(
81 do_reference_completion( 84 r#"
82 r#" 85use foo<|>
83 use foo<|> 86use std::collections;
84 use std::collections; 87"#,
85 "#, 88 expect![[r#"
86 ), 89 ?? collections
87 @r###" 90 "#]],
88 [
89 CompletionItem {
90 label: "collections",
91 source_range: 21..24,
92 delete: 21..24,
93 insert: "collections",
94 },
95 ]
96 "###
97 ); 91 );
98 } 92 }
99 93
100 #[test] 94 #[test]
101 fn bind_pat_and_path_ignore_at() { 95 fn bind_pat_and_path_ignore_at() {
102 assert_debug_snapshot!( 96 check(
103 do_reference_completion( 97 r#"
104 r" 98enum Enum { A, B }
105 enum Enum { 99fn quux(x: Option<Enum>) {
106 A, 100 match x {
107 B, 101 None => (),
108 } 102 Some(en<|> @ Enum::A) => (),
109 fn quux(x: Option<Enum>) { 103 }
110 match x { 104}
111 None => (), 105"#,
112 Some(en<|> @ Enum::A) => (), 106 expect![[""]],
113 }
114 }
115 "
116 ),
117 @"[]"
118 ); 107 );
119 } 108 }
120 109
121 #[test] 110 #[test]
122 fn bind_pat_and_path_ignore_ref() { 111 fn bind_pat_and_path_ignore_ref() {
123 assert_debug_snapshot!( 112 check(
124 do_reference_completion( 113 r#"
125 r" 114enum Enum { A, B }
126 enum Enum { 115fn quux(x: Option<Enum>) {
127 A, 116 match x {
128 B, 117 None => (),
129 } 118 Some(ref en<|>) => (),
130 fn quux(x: Option<Enum>) { 119 }
131 match x { 120}
132 None => (), 121"#,
133 Some(ref en<|>) => (), 122 expect![[""]],
134 }
135 }
136 "
137 ),
138 @r###"[]"###
139 ); 123 );
140 } 124 }
141 125
142 #[test] 126 #[test]
143 fn bind_pat_and_path() { 127 fn bind_pat_and_path() {
144 assert_debug_snapshot!( 128 check(
145 do_reference_completion( 129 r#"
146 r" 130enum Enum { A, B }
147 enum Enum { 131fn quux(x: Option<Enum>) {
148 A, 132 match x {
149 B, 133 None => (),
150 } 134 Some(En<|>) => (),
151 fn quux(x: Option<Enum>) { 135 }
152 match x { 136}
153 None => (), 137"#,
154 Some(En<|>) => (), 138 expect![[r#"
155 } 139 en Enum
156 } 140 "#]],
157 "
158 ),
159 @r###"
160 [
161 CompletionItem {
162 label: "Enum",
163 source_range: 231..233,
164 delete: 231..233,
165 insert: "Enum",
166 kind: Enum,
167 },
168 ]
169 "###
170 ); 141 );
171 } 142 }
172 143
173 #[test] 144 #[test]
174 fn completes_bindings_from_let() { 145 fn completes_bindings_from_let() {
175 assert_debug_snapshot!( 146 check(
176 do_reference_completion( 147 r#"
177 r" 148fn quux(x: i32) {
178 fn quux(x: i32) { 149 let y = 92;
179 let y = 92; 150 1 + <|>;
180 1 + <|>; 151 let z = ();
181 let z = (); 152}
182 } 153"#,
183 " 154 expect![[r#"
184 ), 155 fn quux(…) fn quux(x: i32)
185 @r###" 156 bn x i32
186 [ 157 bn y i32
187 CompletionItem { 158 "#]],
188 label: "quux(…)",
189 source_range: 91..91,
190 delete: 91..91,
191 insert: "quux(${1:x})$0",
192 kind: Function,
193 lookup: "quux",
194 detail: "fn quux(x: i32)",
195 trigger_call_info: true,
196 },
197 CompletionItem {
198 label: "x",
199 source_range: 91..91,
200 delete: 91..91,
201 insert: "x",
202 kind: Binding,
203 detail: "i32",
204 },
205 CompletionItem {
206 label: "y",
207 source_range: 91..91,
208 delete: 91..91,
209 insert: "y",
210 kind: Binding,
211 detail: "i32",
212 },
213 ]
214 "###
215 ); 159 );
216 } 160 }
217 161
218 #[test] 162 #[test]
219 fn completes_bindings_from_if_let() { 163 fn completes_bindings_from_if_let() {
220 assert_debug_snapshot!( 164 check(
221 do_reference_completion( 165 r#"
222 r" 166fn quux() {
223 fn quux() { 167 if let Some(x) = foo() {
224 if let Some(x) = foo() { 168 let y = 92;
225 let y = 92; 169 };
226 }; 170 if let Some(a) = bar() {
227 if let Some(a) = bar() { 171 let b = 62;
228 let b = 62; 172 1 + <|>
229 1 + <|> 173 }
230 } 174}
231 } 175"#,
232 " 176 expect![[r#"
233 ), 177 bn a
234 @r###" 178 bn b i32
235 [ 179 fn quux() fn quux()
236 CompletionItem { 180 "#]],
237 label: "a",
238 source_range: 242..242,
239 delete: 242..242,
240 insert: "a",
241 kind: Binding,
242 },
243 CompletionItem {
244 label: "b",
245 source_range: 242..242,
246 delete: 242..242,
247 insert: "b",
248 kind: Binding,
249 detail: "i32",
250 },
251 CompletionItem {
252 label: "quux()",
253 source_range: 242..242,
254 delete: 242..242,
255 insert: "quux()$0",
256 kind: Function,
257 lookup: "quux",
258 detail: "fn quux()",
259 },
260 ]
261 "###
262 ); 181 );
263 } 182 }
264 183
265 #[test] 184 #[test]
266 fn completes_bindings_from_for() { 185 fn completes_bindings_from_for() {
267 assert_debug_snapshot!( 186 check(
268 do_reference_completion( 187 r#"
269 r" 188fn quux() {
270 fn quux() { 189 for x in &[1, 2, 3] { <|> }
271 for x in &[1, 2, 3] { 190}
272 <|> 191"#,
273 } 192 expect![[r#"
274 } 193 fn quux() fn quux()
275 " 194 bn x
276 ), 195 "#]],
277 @r###"
278 [
279 CompletionItem {
280 label: "quux()",
281 source_range: 95..95,
282 delete: 95..95,
283 insert: "quux()$0",
284 kind: Function,
285 lookup: "quux",
286 detail: "fn quux()",
287 },
288 CompletionItem {
289 label: "x",
290 source_range: 95..95,
291 delete: 95..95,
292 insert: "x",
293 kind: Binding,
294 },
295 ]
296 "###
297 ); 196 );
298 } 197 }
299 198
300 #[test] 199 #[test]
301 fn completes_bindings_from_for_with_in_prefix() { 200 fn completes_if_prefix_is_keyword() {
302 mark::check!(completes_bindings_from_for_with_in_prefix); 201 mark::check!(completes_if_prefix_is_keyword);
303 assert_debug_snapshot!( 202 check_edit(
304 do_reference_completion( 203 "wherewolf",
305 r" 204 r#"
306 fn test() { 205fn main() {
307 for index in &[1, 2, 3] { 206 let wherewolf = 92;
308 let t = in<|> 207 drop(where<|>)
309 } 208}
310 } 209"#,
311 " 210 r#"
312 ), 211fn main() {
313 @r###" 212 let wherewolf = 92;
314 [ 213 drop(wherewolf)
315 CompletionItem { 214}
316 label: "index", 215"#,
317 source_range: 107..107, 216 )
318 delete: 107..107,
319 insert: "index",
320 kind: Binding,
321 },
322 CompletionItem {
323 label: "test()",
324 source_range: 107..107,
325 delete: 107..107,
326 insert: "test()$0",
327 kind: Function,
328 lookup: "test",
329 detail: "fn test()",
330 },
331 ]
332 "###
333 );
334 } 217 }
335 218
336 #[test] 219 #[test]
337 fn completes_generic_params() { 220 fn completes_generic_params() {
338 assert_debug_snapshot!( 221 check(
339 do_reference_completion( 222 r#"fn quux<T>() { <|> }"#,
340 r" 223 expect![[r#"
341 fn quux<T>() { 224 tp T
342 <|> 225 fn quux() fn quux<T>()
343 } 226 "#]],
344 "
345 ),
346 @r###"
347 [
348 CompletionItem {
349 label: "T",
350 source_range: 52..52,
351 delete: 52..52,
352 insert: "T",
353 kind: TypeParam,
354 },
355 CompletionItem {
356 label: "quux()",
357 source_range: 52..52,
358 delete: 52..52,
359 insert: "quux()$0",
360 kind: Function,
361 lookup: "quux",
362 detail: "fn quux<T>()",
363 },
364 ]
365 "###
366 ); 227 );
367 } 228 }
368 229
369 #[test] 230 #[test]
370 fn completes_generic_params_in_struct() { 231 fn completes_generic_params_in_struct() {
371 assert_debug_snapshot!( 232 check(
372 do_reference_completion( 233 r#"struct S<T> { x: <|>}"#,
373 r" 234 expect![[r#"
374 struct X<T> { 235 st S<…>
375 x: <|> 236 tp Self
376 } 237 tp T
377 " 238 "#]],
378 ),
379 @r###"
380 [
381 CompletionItem {
382 label: "Self",
383 source_range: 54..54,
384 delete: 54..54,
385 insert: "Self",
386 kind: TypeParam,
387 },
388 CompletionItem {
389 label: "T",
390 source_range: 54..54,
391 delete: 54..54,
392 insert: "T",
393 kind: TypeParam,
394 },
395 CompletionItem {
396 label: "X<…>",
397 source_range: 54..54,
398 delete: 54..54,
399 insert: "X<$0>",
400 kind: Struct,
401 lookup: "X",
402 },
403 ]
404 "###
405 ); 239 );
406 } 240 }
407 241
408 #[test] 242 #[test]
409 fn completes_self_in_enum() { 243 fn completes_self_in_enum() {
410 assert_debug_snapshot!( 244 check(
411 do_reference_completion( 245 r#"enum X { Y(<|>) }"#,
412 r" 246 expect![[r#"
413 enum X { 247 tp Self
414 Y(<|>) 248 en X
415 } 249 "#]],
416 "
417 ),
418 @r###"
419 [
420 CompletionItem {
421 label: "Self",
422 source_range: 48..48,
423 delete: 48..48,
424 insert: "Self",
425 kind: TypeParam,
426 },
427 CompletionItem {
428 label: "X",
429 source_range: 48..48,
430 delete: 48..48,
431 insert: "X",
432 kind: Enum,
433 },
434 ]
435 "###
436 ); 250 );
437 } 251 }
438 252
439 #[test] 253 #[test]
440 fn completes_module_items() { 254 fn completes_module_items() {
441 assert_debug_snapshot!( 255 check(
442 do_reference_completion( 256 r#"
443 r" 257struct S;
444 struct Foo; 258enum E {}
445 enum Baz {} 259fn quux() { <|> }
446 fn quux() { 260"#,
447 <|> 261 expect![[r#"
448 } 262 en E
449 " 263 st S
450 ), 264 fn quux() fn quux()
451 @r###" 265 "#]],
452 [ 266 );
453 CompletionItem {
454 label: "Baz",
455 source_range: 105..105,
456 delete: 105..105,
457 insert: "Baz",
458 kind: Enum,
459 },
460 CompletionItem {
461 label: "Foo",
462 source_range: 105..105,
463 delete: 105..105,
464 insert: "Foo",
465 kind: Struct,
466 },
467 CompletionItem {
468 label: "quux()",
469 source_range: 105..105,
470 delete: 105..105,
471 insert: "quux()$0",
472 kind: Function,
473 lookup: "quux",
474 detail: "fn quux()",
475 },
476 ]
477 "###
478 );
479 } 267 }
480 268
481 #[test] 269 #[test]
482 fn completes_extern_prelude() { 270 fn completes_extern_prelude() {
483 assert_debug_snapshot!( 271 check(
484 do_reference_completion( 272 r#"
485 r" 273//- /lib.rs
486 //- /lib.rs 274use <|>;
487 use <|>; 275
488 276//- /other_crate/lib.rs
489 //- /other_crate/lib.rs 277// nothing here
490 // nothing here 278"#,
491 " 279 expect![[r#"
492 ), 280 md other_crate
493 @r###" 281 "#]],
494 [
495 CompletionItem {
496 label: "other_crate",
497 source_range: 4..4,
498 delete: 4..4,
499 insert: "other_crate",
500 kind: Module,
501 },
502 ]
503 "###
504 ); 282 );
505 } 283 }
506 284
507 #[test] 285 #[test]
508 fn completes_module_items_in_nested_modules() { 286 fn completes_module_items_in_nested_modules() {
509 assert_debug_snapshot!( 287 check(
510 do_reference_completion( 288 r#"
511 r" 289struct Foo;
512 struct Foo; 290mod m {
513 mod m { 291 struct Bar;
514 struct Bar; 292 fn quux() { <|> }
515 fn quux() { <|> } 293}
516 } 294"#,
517 " 295 expect![[r#"
518 ), 296 st Bar
519 @r###" 297 fn quux() fn quux()
520 [ 298 "#]],
521 CompletionItem {
522 label: "Bar",
523 source_range: 117..117,
524 delete: 117..117,
525 insert: "Bar",
526 kind: Struct,
527 },
528 CompletionItem {
529 label: "quux()",
530 source_range: 117..117,
531 delete: 117..117,
532 insert: "quux()$0",
533 kind: Function,
534 lookup: "quux",
535 detail: "fn quux()",
536 },
537 ]
538 "###
539 ); 299 );
540 } 300 }
541 301
542 #[test] 302 #[test]
543 fn completes_return_type() { 303 fn completes_return_type() {
544 assert_debug_snapshot!( 304 check(
545 do_reference_completion( 305 r#"
546 r" 306struct Foo;
547 struct Foo; 307fn x() -> <|>
548 fn x() -> <|> 308"#,
549 " 309 expect![[r#"
550 ), 310 st Foo
551 @r###" 311 fn x() fn x()
552 [ 312 "#]],
553 CompletionItem {
554 label: "Foo",
555 source_range: 55..55,
556 delete: 55..55,
557 insert: "Foo",
558 kind: Struct,
559 },
560 CompletionItem {
561 label: "x()",
562 source_range: 55..55,
563 delete: 55..55,
564 insert: "x()$0",
565 kind: Function,
566 lookup: "x",
567 detail: "fn x()",
568 },
569 ]
570 "###
571 ); 313 );
572 } 314 }
573 315
574 #[test] 316 #[test]
575 fn dont_show_both_completions_for_shadowing() { 317 fn dont_show_both_completions_for_shadowing() {
576 assert_debug_snapshot!( 318 check(
577 do_reference_completion( 319 r#"
578 r" 320fn foo() {
579 fn foo() { 321 let bar = 92;
580 let bar = 92; 322 {
581 { 323 let bar = 62;
582 let bar = 62; 324 drop(<|>)
583 <|> 325 }
584 } 326}
585 } 327"#,
586 " 328 // FIXME: should be only one bar here
587 ), 329 expect![[r#"
588 @r###" 330 bn bar i32
589 [ 331 bn bar i32
590 CompletionItem { 332 fn foo() fn foo()
591 label: "bar", 333 "#]],
592 source_range: 146..146,
593 delete: 146..146,
594 insert: "bar",
595 kind: Binding,
596 detail: "i32",
597 },
598 CompletionItem {
599 label: "foo()",
600 source_range: 146..146,
601 delete: 146..146,
602 insert: "foo()$0",
603 kind: Function,
604 lookup: "foo",
605 detail: "fn foo()",
606 },
607 ]
608 "###
609 ); 334 );
610 } 335 }
611 336
612 #[test] 337 #[test]
613 fn completes_self_in_methods() { 338 fn completes_self_in_methods() {
614 assert_debug_snapshot!( 339 check(
615 do_reference_completion(r"impl S { fn foo(&self) { <|> } }"), 340 r#"impl S { fn foo(&self) { <|> } }"#,
616 @r###" 341 expect![[r#"
617 [ 342 tp Self
618 CompletionItem { 343 bn self &{unknown}
619 label: "Self", 344 "#]],
620 source_range: 25..25,
621 delete: 25..25,
622 insert: "Self",
623 kind: TypeParam,
624 },
625 CompletionItem {
626 label: "self",
627 source_range: 25..25,
628 delete: 25..25,
629 insert: "self",
630 kind: Binding,
631 detail: "&{unknown}",
632 },
633 ]
634 "###
635 ); 345 );
636 } 346 }
637 347
638 #[test] 348 #[test]
639 fn completes_prelude() { 349 fn completes_prelude() {
640 assert_debug_snapshot!( 350 check(
641 do_reference_completion( 351 r#"
642 " 352//- /main.rs
643 //- /main.rs 353fn foo() { let x: <|> }
644 fn foo() { let x: <|> } 354
645 355//- /std/lib.rs
646 //- /std/lib.rs 356#[prelude_import]
647 #[prelude_import] 357use prelude::*;
648 use prelude::*; 358
649 359mod prelude { struct Option; }
650 mod prelude { 360"#,
651 struct Option; 361 expect![[r#"
652 } 362 st Option
653 " 363 fn foo() fn foo()
654 ), 364 md std
655 @r###" 365 "#]],
656 [
657 CompletionItem {
658 label: "Option",
659 source_range: 18..18,
660 delete: 18..18,
661 insert: "Option",
662 kind: Struct,
663 },
664 CompletionItem {
665 label: "foo()",
666 source_range: 18..18,
667 delete: 18..18,
668 insert: "foo()$0",
669 kind: Function,
670 lookup: "foo",
671 detail: "fn foo()",
672 },
673 CompletionItem {
674 label: "std",
675 source_range: 18..18,
676 delete: 18..18,
677 insert: "std",
678 kind: Module,
679 },
680 ]
681 "###
682 ); 366 );
683 } 367 }
684 368
685 #[test] 369 #[test]
686 fn completes_std_prelude_if_core_is_defined() { 370 fn completes_std_prelude_if_core_is_defined() {
687 assert_debug_snapshot!( 371 check(
688 do_reference_completion( 372 r#"
689 " 373//- /main.rs
690 //- /main.rs 374fn foo() { let x: <|> }
691 fn foo() { let x: <|> } 375
692 376//- /core/lib.rs
693 //- /core/lib.rs 377#[prelude_import]
694 #[prelude_import] 378use prelude::*;
695 use prelude::*; 379
696 380mod prelude { struct Option; }
697 mod prelude { 381
698 struct Option; 382//- /std/lib.rs
699 } 383#[prelude_import]
700 384use prelude::*;
701 //- /std/lib.rs 385
702 #[prelude_import] 386mod prelude { struct String; }
703 use prelude::*; 387"#,
704 388 expect![[r#"
705 mod prelude { 389 st String
706 struct String; 390 md core
707 } 391 fn foo() fn foo()
708 " 392 md std
709 ), 393 "#]],
710 @r###"
711 [
712 CompletionItem {
713 label: "String",
714 source_range: 18..18,
715 delete: 18..18,
716 insert: "String",
717 kind: Struct,
718 },
719 CompletionItem {
720 label: "core",
721 source_range: 18..18,
722 delete: 18..18,
723 insert: "core",
724 kind: Module,
725 },
726 CompletionItem {
727 label: "foo()",
728 source_range: 18..18,
729 delete: 18..18,
730 insert: "foo()$0",
731 kind: Function,
732 lookup: "foo",
733 detail: "fn foo()",
734 },
735 CompletionItem {
736 label: "std",
737 source_range: 18..18,
738 delete: 18..18,
739 insert: "std",
740 kind: Module,
741 },
742 ]
743 "###
744 ); 394 );
745 } 395 }
746 396
747 #[test] 397 #[test]
748 fn completes_macros_as_value() { 398 fn completes_macros_as_value() {
749 assert_debug_snapshot!( 399 check(
750 do_reference_completion( 400 r#"
751 " 401macro_rules! foo { () => {} }
752 //- /main.rs
753 macro_rules! foo {
754 () => {}
755 }
756 402
757 #[macro_use] 403#[macro_use]
758 mod m1 { 404mod m1 {
759 macro_rules! bar { 405 macro_rules! bar { () => {} }
760 () => {} 406}
761 }
762 }
763 407
764 mod m2 { 408mod m2 {
765 macro_rules! nope { 409 macro_rules! nope { () => {} }
766 () => {}
767 }
768 410
769 #[macro_export] 411 #[macro_export]
770 macro_rules! baz { 412 macro_rules! baz { () => {} }
771 () => {} 413}
772 }
773 }
774 414
775 fn main() { 415fn main() { let v = <|> }
776 let v = <|> 416"#,
777 } 417 expect![[r##"
778 " 418 ma bar!(…) macro_rules! bar
779 ), 419 ma baz!(…) #[macro_export]
780 @r###" 420 macro_rules! baz
781 [ 421 ma foo!(…) macro_rules! foo
782 CompletionItem { 422 md m1
783 label: "bar!(…)", 423 md m2
784 source_range: 252..252, 424 fn main() fn main()
785 delete: 252..252, 425 "##]],
786 insert: "bar!($0)",
787 kind: Macro,
788 detail: "macro_rules! bar",
789 },
790 CompletionItem {
791 label: "baz!(…)",
792 source_range: 252..252,
793 delete: 252..252,
794 insert: "baz!($0)",
795 kind: Macro,
796 detail: "#[macro_export]\nmacro_rules! baz",
797 },
798 CompletionItem {
799 label: "foo!(…)",
800 source_range: 252..252,
801 delete: 252..252,
802 insert: "foo!($0)",
803 kind: Macro,
804 detail: "macro_rules! foo",
805 },
806 CompletionItem {
807 label: "m1",
808 source_range: 252..252,
809 delete: 252..252,
810 insert: "m1",
811 kind: Module,
812 },
813 CompletionItem {
814 label: "m2",
815 source_range: 252..252,
816 delete: 252..252,
817 insert: "m2",
818 kind: Module,
819 },
820 CompletionItem {
821 label: "main()",
822 source_range: 252..252,
823 delete: 252..252,
824 insert: "main()$0",
825 kind: Function,
826 lookup: "main",
827 detail: "fn main()",
828 },
829 ]
830 "###
831 ); 426 );
832 } 427 }
833 428
834 #[test] 429 #[test]
835 fn completes_both_macro_and_value() { 430 fn completes_both_macro_and_value() {
836 assert_debug_snapshot!( 431 check(
837 do_reference_completion( 432 r#"
838 " 433macro_rules! foo { () => {} }
839 //- /main.rs 434fn foo() { <|> }
840 macro_rules! foo { 435"#,
841 () => {} 436 expect![[r#"
842 } 437 ma foo!(…) macro_rules! foo
843 438 fn foo() fn foo()
844 fn foo() { 439 "#]],
845 <|>
846 }
847 "
848 ),
849 @r###"
850 [
851 CompletionItem {
852 label: "foo!(…)",
853 source_range: 49..49,
854 delete: 49..49,
855 insert: "foo!($0)",
856 kind: Macro,
857 detail: "macro_rules! foo",
858 },
859 CompletionItem {
860 label: "foo()",
861 source_range: 49..49,
862 delete: 49..49,
863 insert: "foo()$0",
864 kind: Function,
865 lookup: "foo",
866 detail: "fn foo()",
867 },
868 ]
869 "###
870 ); 440 );
871 } 441 }
872 442
873 #[test] 443 #[test]
874 fn completes_macros_as_type() { 444 fn completes_macros_as_type() {
875 assert_debug_snapshot!( 445 check(
876 do_reference_completion( 446 r#"
877 " 447macro_rules! foo { () => {} }
878 //- /main.rs 448fn main() { let x: <|> }
879 macro_rules! foo { 449"#,
880 () => {} 450 expect![[r#"
881 } 451 ma foo!(…) macro_rules! foo
882 452 fn main() fn main()
883 fn main() { 453 "#]],
884 let x: <|>
885 }
886 "
887 ),
888 @r###"
889 [
890 CompletionItem {
891 label: "foo!(…)",
892 source_range: 57..57,
893 delete: 57..57,
894 insert: "foo!($0)",
895 kind: Macro,
896 detail: "macro_rules! foo",
897 },
898 CompletionItem {
899 label: "main()",
900 source_range: 57..57,
901 delete: 57..57,
902 insert: "main()$0",
903 kind: Function,
904 lookup: "main",
905 detail: "fn main()",
906 },
907 ]
908 "###
909 ); 454 );
910 } 455 }
911 456
912 #[test] 457 #[test]
913 fn completes_macros_as_stmt() { 458 fn completes_macros_as_stmt() {
914 assert_debug_snapshot!( 459 check(
915 do_reference_completion( 460 r#"
916 " 461macro_rules! foo { () => {} }
917 //- /main.rs 462fn main() { <|> }
918 macro_rules! foo { 463"#,
919 () => {} 464 expect![[r#"
920 } 465 ma foo!(…) macro_rules! foo
921 466 fn main() fn main()
922 fn main() { 467 "#]],
923 <|>
924 }
925 "
926 ),
927 @r###"
928 [
929 CompletionItem {
930 label: "foo!(…)",
931 source_range: 50..50,
932 delete: 50..50,
933 insert: "foo!($0)",
934 kind: Macro,
935 detail: "macro_rules! foo",
936 },
937 CompletionItem {
938 label: "main()",
939 source_range: 50..50,
940 delete: 50..50,
941 insert: "main()$0",
942 kind: Function,
943 lookup: "main",
944 detail: "fn main()",
945 },
946 ]
947 "###
948 ); 468 );
949 } 469 }
950 470
951 #[test] 471 #[test]
952 fn completes_local_item() { 472 fn completes_local_item() {
953 assert_debug_snapshot!( 473 check(
954 do_reference_completion( 474 r#"
955 " 475fn main() {
956 //- /main.rs 476 return f<|>;
957 fn main() { 477 fn frobnicate() {}
958 return f<|>; 478}
959 fn frobnicate() {} 479"#,
960 } 480 expect![[r#"
961 " 481 fn frobnicate() fn frobnicate()
962 ), 482 fn main() fn main()
963 @r###" 483 "#]],
964 [ 484 );
965 CompletionItem {
966 label: "frobnicate()",
967 source_range: 23..24,
968 delete: 23..24,
969 insert: "frobnicate()$0",
970 kind: Function,
971 lookup: "frobnicate",
972 detail: "fn frobnicate()",
973 },
974 CompletionItem {
975 label: "main()",
976 source_range: 23..24,
977 delete: 23..24,
978 insert: "main()$0",
979 kind: Function,
980 lookup: "main",
981 detail: "fn main()",
982 },
983 ]
984 "###
985 )
986 } 485 }
987 486
988 #[test] 487 #[test]
989 fn completes_in_simple_macro_1() { 488 fn completes_in_simple_macro_1() {
990 assert_debug_snapshot!( 489 check(
991 do_reference_completion( 490 r#"
992 r" 491macro_rules! m { ($e:expr) => { $e } }
993 macro_rules! m { ($e:expr) => { $e } } 492fn quux(x: i32) {
994 fn quux(x: i32) { 493 let y = 92;
995 let y = 92; 494 m!(<|>);
996 m!(<|>); 495}
997 } 496"#,
998 " 497 expect![[r#"
999 ), 498 ma m!(…) macro_rules! m
1000 @r###" 499 fn quux(…) fn quux(x: i32)
1001 [ 500 bn x i32
1002 CompletionItem { 501 bn y i32
1003 label: "m!(…)", 502 "#]],
1004 source_range: 145..145,
1005 delete: 145..145,
1006 insert: "m!($0)",
1007 kind: Macro,
1008 detail: "macro_rules! m",
1009 },
1010 CompletionItem {
1011 label: "quux(…)",
1012 source_range: 145..145,
1013 delete: 145..145,
1014 insert: "quux(${1:x})$0",
1015 kind: Function,
1016 lookup: "quux",
1017 detail: "fn quux(x: i32)",
1018 trigger_call_info: true,
1019 },
1020 CompletionItem {
1021 label: "x",
1022 source_range: 145..145,
1023 delete: 145..145,
1024 insert: "x",
1025 kind: Binding,
1026 detail: "i32",
1027 },
1028 CompletionItem {
1029 label: "y",
1030 source_range: 145..145,
1031 delete: 145..145,
1032 insert: "y",
1033 kind: Binding,
1034 detail: "i32",
1035 },
1036 ]
1037 "###
1038 ); 503 );
1039 } 504 }
1040 505
1041 #[test] 506 #[test]
1042 fn completes_in_simple_macro_2() { 507 fn completes_in_simple_macro_2() {
1043 assert_debug_snapshot!( 508 check(
1044 do_reference_completion( 509 r"
1045 r" 510macro_rules! m { ($e:expr) => { $e } }
1046 macro_rules! m { ($e:expr) => { $e } } 511fn quux(x: i32) {
1047 fn quux(x: i32) { 512 let y = 92;
1048 let y = 92; 513 m!(x<|>);
1049 m!(x<|>); 514}
1050 } 515",
1051 " 516 expect![[r#"
1052 ), 517 ma m!(…) macro_rules! m
1053 @r###" 518 fn quux(…) fn quux(x: i32)
1054 [ 519 bn x i32
1055 CompletionItem { 520 bn y i32
1056 label: "m!(…)", 521 "#]],
1057 source_range: 145..146,
1058 delete: 145..146,
1059 insert: "m!($0)",
1060 kind: Macro,
1061 detail: "macro_rules! m",
1062 },
1063 CompletionItem {
1064 label: "quux(…)",
1065 source_range: 145..146,
1066 delete: 145..146,
1067 insert: "quux(${1:x})$0",
1068 kind: Function,
1069 lookup: "quux",
1070 detail: "fn quux(x: i32)",
1071 trigger_call_info: true,
1072 },
1073 CompletionItem {
1074 label: "x",
1075 source_range: 145..146,
1076 delete: 145..146,
1077 insert: "x",
1078 kind: Binding,
1079 detail: "i32",
1080 },
1081 CompletionItem {
1082 label: "y",
1083 source_range: 145..146,
1084 delete: 145..146,
1085 insert: "y",
1086 kind: Binding,
1087 detail: "i32",
1088 },
1089 ]
1090 "###
1091 ); 522 );
1092 } 523 }
1093 524
1094 #[test] 525 #[test]
1095 fn completes_in_simple_macro_without_closing_parens() { 526 fn completes_in_simple_macro_without_closing_parens() {
1096 assert_debug_snapshot!( 527 check(
1097 do_reference_completion( 528 r#"
1098 r" 529macro_rules! m { ($e:expr) => { $e } }
1099 macro_rules! m { ($e:expr) => { $e } } 530fn quux(x: i32) {
1100 fn quux(x: i32) { 531 let y = 92;
1101 let y = 92; 532 m!(x<|>
1102 m!(x<|> 533}
1103 } 534"#,
1104 " 535 expect![[r#"
1105 ), 536 ma m!(…) macro_rules! m
1106 @r###" 537 fn quux(…) fn quux(x: i32)
1107 [ 538 bn x i32
1108 CompletionItem { 539 bn y i32
1109 label: "m!(…)", 540 "#]],
1110 source_range: 145..146,
1111 delete: 145..146,
1112 insert: "m!($0)",
1113 kind: Macro,
1114 detail: "macro_rules! m",
1115 },
1116 CompletionItem {
1117 label: "quux(…)",
1118 source_range: 145..146,
1119 delete: 145..146,
1120 insert: "quux(${1:x})$0",
1121 kind: Function,
1122 lookup: "quux",
1123 detail: "fn quux(x: i32)",
1124 trigger_call_info: true,
1125 },
1126 CompletionItem {
1127 label: "x",
1128 source_range: 145..146,
1129 delete: 145..146,
1130 insert: "x",
1131 kind: Binding,
1132 detail: "i32",
1133 },
1134 CompletionItem {
1135 label: "y",
1136 source_range: 145..146,
1137 delete: 145..146,
1138 insert: "y",
1139 kind: Binding,
1140 detail: "i32",
1141 },
1142 ]
1143 "###
1144 ); 541 );
1145 } 542 }
1146 543
1147 #[test] 544 #[test]
1148 fn completes_unresolved_uses() { 545 fn completes_unresolved_uses() {
1149 assert_debug_snapshot!( 546 check(
1150 do_reference_completion( 547 r#"
1151 r" 548use spam::Quux;
1152 use spam::Quux; 549
1153 550fn main() { <|> }
1154 fn main() { 551"#,
1155 <|> 552 expect![[r#"
1156 } 553 ?? Quux
1157 " 554 fn main() fn main()
1158 ), 555 "#]],
1159 @r###"
1160 [
1161 CompletionItem {
1162 label: "Quux",
1163 source_range: 82..82,
1164 delete: 82..82,
1165 insert: "Quux",
1166 },
1167 CompletionItem {
1168 label: "main()",
1169 source_range: 82..82,
1170 delete: 82..82,
1171 insert: "main()$0",
1172 kind: Function,
1173 lookup: "main",
1174 detail: "fn main()",
1175 },
1176 ]
1177 "###
1178 ); 556 );
1179 } 557 }
1180 #[test] 558 #[test]
1181 fn completes_enum_variant_matcharm() { 559 fn completes_enum_variant_matcharm() {
1182 assert_debug_snapshot!( 560 check(
1183 do_reference_completion( 561 r#"
1184 r" 562enum Foo { Bar, Baz, Quux }
1185 enum Foo {
1186 Bar,
1187 Baz,
1188 Quux
1189 }
1190
1191 fn main() {
1192 let foo = Foo::Quux;
1193 563
1194 match foo { 564fn main() {
1195 Qu<|> 565 let foo = Foo::Quux;
1196 } 566 match foo { Qu<|> }
1197 } 567}
1198 " 568"#,
1199 ), 569 expect![[r#"
1200 @r###" 570 en Foo
1201 [ 571 ev Foo::Bar ()
1202 CompletionItem { 572 ev Foo::Baz ()
1203 label: "Foo", 573 ev Foo::Quux ()
1204 source_range: 248..250, 574 "#]],
1205 delete: 248..250,
1206 insert: "Foo",
1207 kind: Enum,
1208 },
1209 CompletionItem {
1210 label: "Foo::Bar",
1211 source_range: 248..250,
1212 delete: 248..250,
1213 insert: "Foo::Bar",
1214 kind: EnumVariant,
1215 lookup: "Bar",
1216 detail: "()",
1217 },
1218 CompletionItem {
1219 label: "Foo::Baz",
1220 source_range: 248..250,
1221 delete: 248..250,
1222 insert: "Foo::Baz",
1223 kind: EnumVariant,
1224 lookup: "Baz",
1225 detail: "()",
1226 },
1227 CompletionItem {
1228 label: "Foo::Quux",
1229 source_range: 248..250,
1230 delete: 248..250,
1231 insert: "Foo::Quux",
1232 kind: EnumVariant,
1233 lookup: "Quux",
1234 detail: "()",
1235 },
1236 ]
1237 "###
1238 ) 575 )
1239 } 576 }
1240 577
1241 #[test] 578 #[test]
1242 fn completes_enum_variant_iflet() { 579 fn completes_enum_variant_iflet() {
1243 assert_debug_snapshot!( 580 check(
1244 do_reference_completion( 581 r#"
1245 r" 582enum Foo { Bar, Baz, Quux }
1246 enum Foo {
1247 Bar,
1248 Baz,
1249 Quux
1250 }
1251 583
1252 fn main() { 584fn main() {
1253 let foo = Foo::Quux; 585 let foo = Foo::Quux;
1254 586 if let Qu<|> = foo { }
1255 if let Qu<|> = foo { 587}
1256 588"#,
1257 } 589 expect![[r#"
1258 } 590 en Foo
1259 " 591 ev Foo::Bar ()
1260 ), 592 ev Foo::Baz ()
1261 @r###" 593 ev Foo::Quux ()
1262 [ 594 "#]],
1263 CompletionItem {
1264 label: "Foo",
1265 source_range: 219..221,
1266 delete: 219..221,
1267 insert: "Foo",
1268 kind: Enum,
1269 },
1270 CompletionItem {
1271 label: "Foo::Bar",
1272 source_range: 219..221,
1273 delete: 219..221,
1274 insert: "Foo::Bar",
1275 kind: EnumVariant,
1276 lookup: "Bar",
1277 detail: "()",
1278 },
1279 CompletionItem {
1280 label: "Foo::Baz",
1281 source_range: 219..221,
1282 delete: 219..221,
1283 insert: "Foo::Baz",
1284 kind: EnumVariant,
1285 lookup: "Baz",
1286 detail: "()",
1287 },
1288 CompletionItem {
1289 label: "Foo::Quux",
1290 source_range: 219..221,
1291 delete: 219..221,
1292 insert: "Foo::Quux",
1293 kind: EnumVariant,
1294 lookup: "Quux",
1295 detail: "()",
1296 },
1297 ]
1298 "###
1299 ) 595 )
1300 } 596 }
1301 597
1302 #[test] 598 #[test]
1303 fn completes_enum_variant_basic_expr() { 599 fn completes_enum_variant_basic_expr() {
1304 assert_debug_snapshot!( 600 check(
1305 do_reference_completion( 601 r#"
1306 r" 602enum Foo { Bar, Baz, Quux }
1307 enum Foo { 603fn main() { let foo: Foo = Q<|> }
1308 Bar, 604"#,
1309 Baz, 605 expect![[r#"
1310 Quux 606 en Foo
1311 } 607 ev Foo::Bar ()
1312 608 ev Foo::Baz ()
1313 fn main() { 609 ev Foo::Quux ()
1314 let foo: Foo = Q<|> 610 fn main() fn main()
1315 } 611 "#]],
1316 "
1317 ),
1318 @r###"
1319 [
1320 CompletionItem {
1321 label: "Foo",
1322 source_range: 185..186,
1323 delete: 185..186,
1324 insert: "Foo",
1325 kind: Enum,
1326 },
1327 CompletionItem {
1328 label: "Foo::Bar",
1329 source_range: 185..186,
1330 delete: 185..186,
1331 insert: "Foo::Bar",
1332 kind: EnumVariant,
1333 lookup: "Bar",
1334 detail: "()",
1335 },
1336 CompletionItem {
1337 label: "Foo::Baz",
1338 source_range: 185..186,
1339 delete: 185..186,
1340 insert: "Foo::Baz",
1341 kind: EnumVariant,
1342 lookup: "Baz",
1343 detail: "()",
1344 },
1345 CompletionItem {
1346 label: "Foo::Quux",
1347 source_range: 185..186,
1348 delete: 185..186,
1349 insert: "Foo::Quux",
1350 kind: EnumVariant,
1351 lookup: "Quux",
1352 detail: "()",
1353 },
1354 CompletionItem {
1355 label: "main()",
1356 source_range: 185..186,
1357 delete: 185..186,
1358 insert: "main()$0",
1359 kind: Function,
1360 lookup: "main",
1361 detail: "fn main()",
1362 },
1363 ]
1364 "###
1365 ) 612 )
1366 } 613 }
1367 614
1368 #[test] 615 #[test]
1369 fn completes_enum_variant_from_module() { 616 fn completes_enum_variant_from_module() {
1370 assert_debug_snapshot!( 617 check(
1371 do_reference_completion( 618 r#"
1372 r" 619mod m { pub enum E { V } }
1373 mod m { pub enum E { V } } 620fn f() -> m::E { V<|> }
1374 621"#,
1375 fn f() -> m::E { 622 expect![[r#"
1376 V<|> 623 fn f() fn f() -> m::E
1377 } 624 md m
1378 " 625 ev m::E::V ()
1379 ), 626 "#]],
1380 @r###"
1381 [
1382 CompletionItem {
1383 label: "f()",
1384 source_range: 98..99,
1385 delete: 98..99,
1386 insert: "f()$0",
1387 kind: Function,
1388 lookup: "f",
1389 detail: "fn f() -> m::E",
1390 },
1391 CompletionItem {
1392 label: "m",
1393 source_range: 98..99,
1394 delete: 98..99,
1395 insert: "m",
1396 kind: Module,
1397 },
1398 CompletionItem {
1399 label: "m::E::V",
1400 source_range: 98..99,
1401 delete: 98..99,
1402 insert: "m::E::V",
1403 kind: EnumVariant,
1404 lookup: "V",
1405 detail: "()",
1406 },
1407 ]
1408 "###
1409 ) 627 )
1410 } 628 }
1411 629
1412 #[test] 630 #[test]
1413 fn dont_complete_attr() { 631 fn dont_complete_attr() {
1414 assert_debug_snapshot!( 632 check(
1415 do_reference_completion( 633 r#"
1416 r" 634struct Foo;
1417 struct Foo; 635#[<|>]
1418 #[<|>] 636fn f() {}
1419 fn f() {} 637"#,
1420 " 638 expect![[""]],
1421 ), 639 )
1422 @r###"[]"### 640 }
641
642 #[test]
643 fn completes_type_or_trait_in_impl_block() {
644 check(
645 r#"
646trait MyTrait {}
647struct MyStruct {}
648
649impl My<|>
650"#,
651 expect![[r#"
652 st MyStruct
653 tt MyTrait
654 tp Self
655 "#]],
1423 ) 656 )
1424 } 657 }
1425} 658}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index c4646b727..9e82d6854 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,12 +5,17 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, 8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::Indel; 12use ra_text_edit::Indel;
13 13
14use super::patterns::{
15 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
16 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
17 has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
18};
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 19use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use test_utils::mark; 20use test_utils::mark;
16 21
@@ -19,6 +24,7 @@ use test_utils::mark;
19#[derive(Debug)] 24#[derive(Debug)]
20pub(crate) struct CompletionContext<'a> { 25pub(crate) struct CompletionContext<'a> {
21 pub(super) sema: Semantics<'a, RootDatabase>, 26 pub(super) sema: Semantics<'a, RootDatabase>,
27 pub(super) scope: SemanticsScope<'a>,
22 pub(super) db: &'a RootDatabase, 28 pub(super) db: &'a RootDatabase,
23 pub(super) config: &'a CompletionConfig, 29 pub(super) config: &'a CompletionConfig,
24 pub(super) offset: TextSize, 30 pub(super) offset: TextSize,
@@ -48,6 +54,8 @@ pub(crate) struct CompletionContext<'a> {
48 pub(super) after_if: bool, 54 pub(super) after_if: bool,
49 /// `true` if we are a statement or a last expr in the block. 55 /// `true` if we are a statement or a last expr in the block.
50 pub(super) can_be_stmt: bool, 56 pub(super) can_be_stmt: bool,
57 /// `true` if we expect an expression at the cursor position.
58 pub(super) is_expr: bool,
51 /// Something is typed at the "top" level, in module or impl/trait. 59 /// Something is typed at the "top" level, in module or impl/trait.
52 pub(super) is_new_item: bool, 60 pub(super) is_new_item: bool,
53 /// The receiver if this is a field or method access, i.e. writing something.<|> 61 /// The receiver if this is a field or method access, i.e. writing something.<|>
@@ -55,11 +63,25 @@ pub(crate) struct CompletionContext<'a> {
55 pub(super) dot_receiver_is_ambiguous_float_literal: bool, 63 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
56 /// If this is a call (method or function) in particular, i.e. the () are already there. 64 /// If this is a call (method or function) in particular, i.e. the () are already there.
57 pub(super) is_call: bool, 65 pub(super) is_call: bool,
66 /// Like `is_call`, but for tuple patterns.
67 pub(super) is_pattern_call: bool,
58 /// If this is a macro call, i.e. the () are already there. 68 /// If this is a macro call, i.e. the () are already there.
59 pub(super) is_macro_call: bool, 69 pub(super) is_macro_call: bool,
60 pub(super) is_path_type: bool, 70 pub(super) is_path_type: bool,
61 pub(super) has_type_args: bool, 71 pub(super) has_type_args: bool,
62 pub(super) attribute_under_caret: Option<ast::Attr>, 72 pub(super) attribute_under_caret: Option<ast::Attr>,
73 pub(super) unsafe_is_prev: bool,
74 pub(super) if_is_prev: bool,
75 pub(super) block_expr_parent: bool,
76 pub(super) bind_pat_parent: bool,
77 pub(super) ref_pat_parent: bool,
78 pub(super) in_loop_body: bool,
79 pub(super) has_trait_parent: bool,
80 pub(super) has_impl_parent: bool,
81 pub(super) trait_as_prev_sibling: bool,
82 pub(super) impl_as_prev_sibling: bool,
83 pub(super) is_match_arm: bool,
84 pub(super) has_item_list_or_source_file_parent: bool,
63} 85}
64 86
65impl<'a> CompletionContext<'a> { 87impl<'a> CompletionContext<'a> {
@@ -87,8 +109,10 @@ impl<'a> CompletionContext<'a> {
87 let original_token = 109 let original_token =
88 original_file.syntax().token_at_offset(position.offset).left_biased()?; 110 original_file.syntax().token_at_offset(position.offset).left_biased()?;
89 let token = sema.descend_into_macros(original_token.clone()); 111 let token = sema.descend_into_macros(original_token.clone());
112 let scope = sema.scope_at_offset(&token.parent(), position.offset);
90 let mut ctx = CompletionContext { 113 let mut ctx = CompletionContext {
91 sema, 114 sema,
115 scope,
92 db, 116 db,
93 config, 117 config,
94 original_token, 118 original_token,
@@ -110,14 +134,28 @@ impl<'a> CompletionContext<'a> {
110 path_prefix: None, 134 path_prefix: None,
111 after_if: false, 135 after_if: false,
112 can_be_stmt: false, 136 can_be_stmt: false,
137 is_expr: false,
113 is_new_item: false, 138 is_new_item: false,
114 dot_receiver: None, 139 dot_receiver: None,
115 is_call: false, 140 is_call: false,
141 is_pattern_call: false,
116 is_macro_call: false, 142 is_macro_call: false,
117 is_path_type: false, 143 is_path_type: false,
118 has_type_args: false, 144 has_type_args: false,
119 dot_receiver_is_ambiguous_float_literal: false, 145 dot_receiver_is_ambiguous_float_literal: false,
120 attribute_under_caret: None, 146 attribute_under_caret: None,
147 unsafe_is_prev: false,
148 in_loop_body: false,
149 ref_pat_parent: false,
150 bind_pat_parent: false,
151 block_expr_parent: false,
152 has_trait_parent: false,
153 has_impl_parent: false,
154 trait_as_prev_sibling: false,
155 impl_as_prev_sibling: false,
156 if_is_prev: false,
157 is_match_arm: false,
158 has_item_list_or_source_file_parent: false,
121 }; 159 };
122 160
123 let mut original_file = original_file.syntax().clone(); 161 let mut original_file = original_file.syntax().clone();
@@ -159,7 +197,7 @@ impl<'a> CompletionContext<'a> {
159 break; 197 break;
160 } 198 }
161 } 199 }
162 200 ctx.fill_keyword_patterns(&hypothetical_file, offset);
163 ctx.fill(&original_file, hypothetical_file, offset); 201 ctx.fill(&original_file, hypothetical_file, offset);
164 Some(ctx) 202 Some(ctx)
165 } 203 }
@@ -167,25 +205,30 @@ impl<'a> CompletionContext<'a> {
167 // The range of the identifier that is being completed. 205 // The range of the identifier that is being completed.
168 pub(crate) fn source_range(&self) -> TextRange { 206 pub(crate) fn source_range(&self) -> TextRange {
169 // check kind of macro-expanded token, but use range of original token 207 // check kind of macro-expanded token, but use range of original token
170 match self.token.kind() { 208 if self.token.kind() == IDENT || self.token.kind().is_keyword() {
171 // workaroud when completion is triggered by trigger characters. 209 mark::hit!(completes_if_prefix_is_keyword);
172 IDENT => self.original_token.text_range(), 210 self.original_token.text_range()
173 _ => { 211 } else {
174 // If we haven't characters between keyword and our cursor we take the keyword start range to edit 212 TextRange::empty(self.offset)
175 if self.token.kind().is_keyword()
176 && self.offset == self.original_token.text_range().end()
177 {
178 mark::hit!(completes_bindings_from_for_with_in_prefix);
179 TextRange::empty(self.original_token.text_range().start())
180 } else {
181 TextRange::empty(self.offset)
182 }
183 }
184 } 213 }
185 } 214 }
186 215
187 pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { 216 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
188 self.sema.scope_at_offset(&self.token.parent(), self.offset) 217 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
218 let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
219 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
220 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
221 self.if_is_prev = if_is_prev(syntax_element.clone());
222 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
223 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
224 self.in_loop_body = is_in_loop_body(syntax_element.clone());
225 self.has_trait_parent = has_trait_parent(syntax_element.clone());
226 self.has_impl_parent = has_impl_parent(syntax_element.clone());
227 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
228 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
229 self.is_match_arm = is_match_arm(syntax_element.clone());
230 self.has_item_list_or_source_file_parent =
231 has_item_list_or_source_file_parent(syntax_element.clone());
189 } 232 }
190 233
191 fn fill( 234 fn fill(
@@ -330,10 +373,13 @@ impl<'a> CompletionContext<'a> {
330 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 373 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
331 .is_some(); 374 .is_some();
332 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 375 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
376 self.is_pattern_call =
377 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
333 378
334 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 379 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
335 self.has_type_args = segment.type_arg_list().is_some(); 380 self.has_type_args = segment.type_arg_list().is_some();
336 381
382 #[allow(deprecated)]
337 if let Some(path) = hir::Path::from_ast(path.clone()) { 383 if let Some(path) = hir::Path::from_ast(path.clone()) {
338 if let Some(path_prefix) = path.qualifier() { 384 if let Some(path_prefix) = path.qualifier() {
339 self.path_prefix = Some(path_prefix); 385 self.path_prefix = Some(path_prefix);
@@ -364,6 +410,7 @@ impl<'a> CompletionContext<'a> {
364 None 410 None
365 }) 411 })
366 .unwrap_or(false); 412 .unwrap_or(false);
413 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
367 414
368 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { 415 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
369 if let Some(if_expr) = 416 if let Some(if_expr) =
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index cfb7c1e38..7bdda316c 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -58,7 +58,7 @@ pub struct CompletionItem {
58 score: Option<CompletionScore>, 58 score: Option<CompletionScore>,
59} 59}
60 60
61// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem { 62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
@@ -95,7 +95,7 @@ impl fmt::Debug for CompletionItem {
95 } 95 }
96} 96}
97 97
98#[derive(Debug, Clone, Copy)] 98#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
99pub enum CompletionScore { 99pub enum CompletionScore {
100 /// If only type match 100 /// If only type match
101 TypeMatch, 101 TypeMatch,
@@ -123,6 +123,34 @@ pub enum CompletionItemKind {
123 TypeParam, 123 TypeParam,
124 Macro, 124 Macro,
125 Attribute, 125 Attribute,
126 UnresolvedReference,
127}
128
129impl CompletionItemKind {
130 #[cfg(test)]
131 pub(crate) fn tag(&self) -> &'static str {
132 match self {
133 CompletionItemKind::Attribute => "at",
134 CompletionItemKind::Binding => "bn",
135 CompletionItemKind::BuiltinType => "bt",
136 CompletionItemKind::Const => "ct",
137 CompletionItemKind::Enum => "en",
138 CompletionItemKind::EnumVariant => "ev",
139 CompletionItemKind::Field => "fd",
140 CompletionItemKind::Function => "fn",
141 CompletionItemKind::Keyword => "kw",
142 CompletionItemKind::Macro => "ma",
143 CompletionItemKind::Method => "me",
144 CompletionItemKind::Module => "md",
145 CompletionItemKind::Snippet => "sn",
146 CompletionItemKind::Static => "sc",
147 CompletionItemKind::Struct => "st",
148 CompletionItemKind::Trait => "tt",
149 CompletionItemKind::TypeAlias => "ta",
150 CompletionItemKind::TypeParam => "tp",
151 CompletionItemKind::UnresolvedReference => "??",
152 }
153 }
126} 154}
127 155
128#[derive(Debug, PartialEq, Eq, Copy, Clone)] 156#[derive(Debug, PartialEq, Eq, Copy, Clone)]
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
new file mode 100644
index 000000000..b2fe13280
--- /dev/null
+++ b/crates/ra_ide/src/completion/patterns.rs
@@ -0,0 +1,194 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use ra_syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::completion::test_utils::check_pattern_is_applicable;
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT_DEF)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL_DEF)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
39 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
40}
41#[test]
42fn test_has_block_expr_parent() {
43 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
44}
45
46pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
47 element.ancestors().find(|it| it.kind() == BIND_PAT).is_some()
48}
49#[test]
50fn test_has_bind_pat_parent() {
51 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
52 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
53}
54
55pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
56 not_same_range_ancestor(element)
57 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
58 .is_some()
59}
60#[test]
61fn test_has_ref_parent() {
62 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
63 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
64}
65
66pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
67 let ancestor = not_same_range_ancestor(element);
68 if !ancestor.is_some() {
69 return true;
70 }
71 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
72}
73#[test]
74fn test_has_item_list_or_source_file_parent() {
75 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
76 check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent);
77}
78
79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
80 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
81 && previous_sibling_or_ancestor_sibling(element)
82 .and_then(|it| it.into_token())
83 .filter(|it| it.kind() == FAT_ARROW)
84 .is_some()
85}
86#[test]
87fn test_is_match_arm() {
88 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
89}
90
91pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
92 element
93 .into_token()
94 .and_then(|it| previous_non_trivia_token(it))
95 .filter(|it| it.kind() == UNSAFE_KW)
96 .is_some()
97}
98#[test]
99fn test_unsafe_is_prev() {
100 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
101}
102
103pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
104 element
105 .into_token()
106 .and_then(|it| previous_non_trivia_token(it))
107 .filter(|it| it.kind() == IF_KW)
108 .is_some()
109}
110#[test]
111fn test_if_is_prev() {
112 check_pattern_is_applicable(r"if l<|>", if_is_prev);
113}
114
115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some()
117}
118#[test]
119fn test_has_trait_as_prev_sibling() {
120 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
121}
122
123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some()
125}
126#[test]
127fn test_has_impl_as_prev_sibling() {
128 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
129}
130
131pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
132 let leaf = match element {
133 NodeOrToken::Node(node) => node,
134 NodeOrToken::Token(token) => token.parent(),
135 };
136 for node in leaf.ancestors() {
137 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
138 break;
139 }
140 let loop_body = match_ast! {
141 match node {
142 ast::ForExpr(it) => it.loop_body(),
143 ast::WhileExpr(it) => it.loop_body(),
144 ast::LoopExpr(it) => it.loop_body(),
145 _ => None,
146 }
147 };
148 if let Some(body) = loop_body {
149 if body.syntax().text_range().contains_range(leaf.text_range()) {
150 return true;
151 }
152 }
153 }
154 false
155}
156
157fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
158 element
159 .ancestors()
160 .take_while(|it| it.text_range() == element.text_range())
161 .last()
162 .and_then(|it| it.parent())
163}
164
165fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
166 let mut token = token.prev_token();
167 while let Some(inner) = token.clone() {
168 if !inner.kind().is_trivia() {
169 return Some(inner);
170 } else {
171 token = inner.prev_token();
172 }
173 }
174 None
175}
176
177fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
178 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
179 if let Some(sibling) = token_sibling {
180 Some(sibling)
181 } else {
182 // if not trying to find first ancestor which has such a sibling
183 let node = match element {
184 NodeOrToken::Node(node) => node,
185 NodeOrToken::Token(token) => token.parent(),
186 };
187 let range = node.text_range();
188 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
189 let prev_sibling_node = top_node.ancestors().find(|it| {
190 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
191 })?;
192 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
193 }
194}
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 61565c84f..64349dcb8 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,4 +1,5 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
2 3
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 4use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
@@ -78,11 +79,10 @@ impl Completions {
78 return self.add_macro(ctx, Some(local_name), *mac); 79 return self.add_macro(ctx, Some(local_name), *mac);
79 } 80 }
80 ScopeDef::Unknown => { 81 ScopeDef::Unknown => {
81 return self.add(CompletionItem::new( 82 return self.add(
82 CompletionKind::Reference, 83 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
83 ctx.source_range(), 84 .kind(CompletionItemKind::UnresolvedReference),
84 local_name, 85 );
85 ));
86 } 86 }
87 }; 87 };
88 88
@@ -173,6 +173,7 @@ impl Completions {
173 builder 173 builder
174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket)) 174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
175 .label(format!("{}!{}…{}", name, bra, ket)) 175 .label(format!("{}!{}…{}", name, bra, ket))
176 .lookup_by(format!("{}!", name))
176 } 177 }
177 None if needs_bang => builder.insert_text(format!("{}!", name)), 178 None if needs_bang => builder.insert_text(format!("{}!", name)),
178 _ => { 179 _ => {
@@ -314,6 +315,7 @@ impl Completions {
314 } 315 }
315 316
316 if variant_kind == StructKind::Tuple { 317 if variant_kind == StructKind::Tuple {
318 mark::hit!(inserts_parens_for_tuple_enums);
317 let params = Params::Anonymous(variant.fields(ctx.db).len()); 319 let params = Params::Anonymous(variant.fields(ctx.db).len());
318 res = res.add_call_parens(ctx, qualified_name, params) 320 res = res.add_call_parens(ctx, qualified_name, params)
319 } 321 }
@@ -330,14 +332,14 @@ pub(crate) fn compute_score(
330 // FIXME: this should not fall back to string equality. 332 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string(); 333 let ty = &ty.display(ctx.db).to_string();
332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 334 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
333 mark::hit!(test_struct_field_completion_in_record_lit); 335 mark::hit!(record_field_type_match);
334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 336 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
335 ( 337 (
336 struct_field.name(ctx.db).to_string(), 338 struct_field.name(ctx.db).to_string(),
337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), 339 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
338 ) 340 )
339 } else if let Some(active_parameter) = &ctx.active_parameter { 341 } else if let Some(active_parameter) = &ctx.active_parameter {
340 mark::hit!(test_struct_field_completion_in_func_call); 342 mark::hit!(active_param_type_match);
341 (active_parameter.name.clone(), active_parameter.ty.clone()) 343 (active_parameter.name.clone(), active_parameter.ty.clone())
342 } else { 344 } else {
343 return None; 345 return None;
@@ -382,13 +384,22 @@ impl Builder {
382 if !ctx.config.add_call_parenthesis { 384 if !ctx.config.add_call_parenthesis {
383 return self; 385 return self;
384 } 386 }
385 if ctx.use_item_syntax.is_some() || ctx.is_call { 387 if ctx.use_item_syntax.is_some() {
388 mark::hit!(no_parens_in_use_item);
389 return self;
390 }
391 if ctx.is_pattern_call {
392 mark::hit!(dont_duplicate_pattern_parens);
393 return self;
394 }
395 if ctx.is_call {
386 return self; 396 return self;
387 } 397 }
388 398
389 // Don't add parentheses if the expected type is some function reference. 399 // Don't add parentheses if the expected type is some function reference.
390 if let Some(ty) = &ctx.expected_type { 400 if let Some(ty) = &ctx.expected_type {
391 if ty.is_fn() { 401 if ty.is_fn() {
402 mark::hit!(no_call_parens_if_fn_ptr_needed);
392 return self; 403 return self;
393 } 404 }
394 } 405 }
@@ -413,7 +424,10 @@ impl Builder {
413 .sep_by(", "); 424 .sep_by(", ");
414 format!("{}({})$0", name, function_params_snippet) 425 format!("{}({})$0", name, function_params_snippet)
415 } 426 }
416 _ => format!("{}($0)", name), 427 _ => {
428 mark::hit!(suppress_arg_snippets);
429 format!("{}($0)", name)
430 }
417 }; 431 };
418 432
419 (snippet, format!("{}(…)", name)) 433 (snippet, format!("{}(…)", name))
@@ -456,1061 +470,750 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
456 470
457#[cfg(test)] 471#[cfg(test)]
458mod tests { 472mod tests {
459 use insta::assert_debug_snapshot; 473 use std::cmp::Reverse;
474
475 use expect::{expect, Expect};
460 use test_utils::mark; 476 use test_utils::mark;
461 477
462 use crate::completion::{ 478 use crate::{
463 test_utils::{do_completion, do_completion_with_options}, 479 completion::{
464 CompletionConfig, CompletionItem, CompletionKind, 480 test_utils::{
481 check_edit, check_edit_with_config, do_completion, get_all_completion_items,
482 },
483 CompletionConfig, CompletionKind,
484 },
485 CompletionScore,
465 }; 486 };
466 487
467 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 488 fn check(ra_fixture: &str, expect: Expect) {
468 do_completion(ra_fixture, CompletionKind::Reference) 489 let actual = do_completion(ra_fixture, CompletionKind::Reference);
490 expect.assert_debug_eq(&actual);
469 } 491 }
470 492
471 fn do_reference_completion_with_options( 493 fn check_scores(ra_fixture: &str, expect: Expect) {
472 ra_fixture: &str, 494 fn display_score(score: Option<CompletionScore>) -> &'static str {
473 options: CompletionConfig, 495 match score {
474 ) -> Vec<CompletionItem> { 496 Some(CompletionScore::TypeMatch) => "[type]",
475 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 497 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
498 None => "[]".into(),
499 }
500 }
501
502 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
503 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
504 let actual = completions
505 .into_iter()
506 .filter(|it| it.completion_kind == CompletionKind::Reference)
507 .map(|it| {
508 let tag = it.kind().unwrap().tag();
509 let score = display_score(it.score());
510 format!("{} {} {}\n", tag, it.label(), score)
511 })
512 .collect::<String>();
513 expect.assert_eq(&actual);
476 } 514 }
477 515
478 #[test] 516 #[test]
479 fn enum_detail_includes_names_for_record() { 517 fn enum_detail_includes_record_fields() {
480 assert_debug_snapshot!( 518 check(
481 do_reference_completion(
482 r#" 519 r#"
483 enum Foo { 520enum Foo { Foo { x: i32, y: i32 } }
484 Foo {x: i32, y: i32} 521
485 } 522fn main() { Foo::Fo<|> }
486 523"#,
487 fn main() { Foo::Fo<|> } 524 expect![[r#"
488 "#, 525 [
489 ), 526 CompletionItem {
490 @r###" 527 label: "Foo",
491 [ 528 source_range: 54..56,
492 CompletionItem { 529 delete: 54..56,
493 label: "Foo", 530 insert: "Foo",
494 source_range: 121..123, 531 kind: EnumVariant,
495 delete: 121..123, 532 detail: "{ x: i32, y: i32 }",
496 insert: "Foo", 533 },
497 kind: EnumVariant, 534 ]
498 detail: "{ x: i32, y: i32 }", 535 "#]],
499 },
500 ]"###
501 ); 536 );
502 } 537 }
503 538
504 #[test] 539 #[test]
505 fn enum_detail_doesnt_include_names_for_tuple() { 540 fn enum_detail_doesnt_include_tuple_fields() {
506 assert_debug_snapshot!( 541 check(
507 do_reference_completion(
508 r#" 542 r#"
509 enum Foo { 543enum Foo { Foo (i32, i32) }
510 Foo (i32, i32) 544
511 } 545fn main() { Foo::Fo<|> }
512 546"#,
513 fn main() { Foo::Fo<|> } 547 expect![[r#"
514 "#, 548 [
515 ), 549 CompletionItem {
516 @r###" 550 label: "Foo(…)",
517 [ 551 source_range: 46..48,
518 CompletionItem { 552 delete: 46..48,
519 label: "Foo(…)", 553 insert: "Foo($0)",
520 source_range: 115..117, 554 kind: EnumVariant,
521 delete: 115..117, 555 lookup: "Foo",
522 insert: "Foo($0)", 556 detail: "(i32, i32)",
523 kind: EnumVariant, 557 trigger_call_info: true,
524 lookup: "Foo", 558 },
525 detail: "(i32, i32)", 559 ]
526 trigger_call_info: true, 560 "#]],
527 },
528 ]"###
529 ); 561 );
530 } 562 }
531 563
532 #[test] 564 #[test]
533 fn enum_detail_just_parentheses_for_unit() { 565 fn enum_detail_just_parentheses_for_unit() {
534 assert_debug_snapshot!( 566 check(
535 do_reference_completion(
536 r#" 567 r#"
537 enum Foo { 568enum Foo { Foo }
538 Foo 569
539 } 570fn main() { Foo::Fo<|> }
540 571"#,
541 fn main() { Foo::Fo<|> } 572 expect![[r#"
542 "#, 573 [
543 ), 574 CompletionItem {
544 @r###" 575 label: "Foo",
545 [ 576 source_range: 35..37,
546 CompletionItem { 577 delete: 35..37,
547 label: "Foo", 578 insert: "Foo",
548 source_range: 104..106, 579 kind: EnumVariant,
549 delete: 104..106, 580 detail: "()",
550 insert: "Foo", 581 },
551 kind: EnumVariant, 582 ]
552 detail: "()", 583 "#]],
553 },
554 ]"###
555 ); 584 );
556 } 585 }
557 586
558 #[test] 587 #[test]
559 fn sets_deprecated_flag_in_completion_items() { 588 fn sets_deprecated_flag_in_completion_items() {
560 assert_debug_snapshot!( 589 check(
561 do_reference_completion( 590 r#"
562 r#" 591#[deprecated]
563 #[deprecated] 592fn something_deprecated() {}
564 fn something_deprecated() {} 593#[deprecated(since = "1.0.0")]
565 594fn something_else_deprecated() {}
566 #[deprecated(since = "1.0.0")] 595
567 fn something_else_deprecated() {} 596fn main() { som<|> }
568 597"#,
569 fn main() { som<|> } 598 expect![[r#"
570 "#, 599 [
571 ), 600 CompletionItem {
572 @r###" 601 label: "main()",
573 [ 602 source_range: 121..124,
574 CompletionItem { 603 delete: 121..124,
575 label: "main()", 604 insert: "main()$0",
576 source_range: 203..206, 605 kind: Function,
577 delete: 203..206, 606 lookup: "main",
578 insert: "main()$0", 607 detail: "fn main()",
579 kind: Function, 608 },
580 lookup: "main", 609 CompletionItem {
581 detail: "fn main()", 610 label: "something_deprecated()",
582 }, 611 source_range: 121..124,
583 CompletionItem { 612 delete: 121..124,
584 label: "something_deprecated()", 613 insert: "something_deprecated()$0",
585 source_range: 203..206, 614 kind: Function,
586 delete: 203..206, 615 lookup: "something_deprecated",
587 insert: "something_deprecated()$0", 616 detail: "fn something_deprecated()",
588 kind: Function, 617 deprecated: true,
589 lookup: "something_deprecated", 618 },
590 detail: "fn something_deprecated()", 619 CompletionItem {
591 deprecated: true, 620 label: "something_else_deprecated()",
592 }, 621 source_range: 121..124,
593 CompletionItem { 622 delete: 121..124,
594 label: "something_else_deprecated()", 623 insert: "something_else_deprecated()$0",
595 source_range: 203..206, 624 kind: Function,
596 delete: 203..206, 625 lookup: "something_else_deprecated",
597 insert: "something_else_deprecated()$0", 626 detail: "fn something_else_deprecated()",
598 kind: Function, 627 deprecated: true,
599 lookup: "something_else_deprecated", 628 },
600 detail: "fn something_else_deprecated()", 629 ]
601 deprecated: true, 630 "#]],
602 }, 631 );
603 ] 632
604 "### 633 check(
634 r#"
635struct A { #[deprecated] the_field: u32 }
636fn foo() { A { the<|> } }
637"#,
638 expect![[r#"
639 [
640 CompletionItem {
641 label: "the_field",
642 source_range: 57..60,
643 delete: 57..60,
644 insert: "the_field",
645 kind: Field,
646 detail: "u32",
647 deprecated: true,
648 },
649 ]
650 "#]],
605 ); 651 );
606 } 652 }
607 653
608 #[test] 654 #[test]
655 fn renders_docs() {
656 check(
657 r#"
658struct S {
659 /// Field docs
660 foo:
661}
662impl S {
663 /// Method docs
664 fn bar(self) { self.<|> }
665}"#,
666 expect![[r#"
667 [
668 CompletionItem {
669 label: "bar()",
670 source_range: 94..94,
671 delete: 94..94,
672 insert: "bar()$0",
673 kind: Method,
674 lookup: "bar",
675 detail: "fn bar(self)",
676 documentation: Documentation(
677 "Method docs",
678 ),
679 },
680 CompletionItem {
681 label: "foo",
682 source_range: 94..94,
683 delete: 94..94,
684 insert: "foo",
685 kind: Field,
686 detail: "{unknown}",
687 documentation: Documentation(
688 "Field docs",
689 ),
690 },
691 ]
692 "#]],
693 );
694
695 check(
696 r#"
697use self::my<|>;
698
699/// mod docs
700mod my { }
701
702/// enum docs
703enum E {
704 /// variant docs
705 V
706}
707use self::E::*;
708"#,
709 expect![[r#"
710 [
711 CompletionItem {
712 label: "E",
713 source_range: 10..12,
714 delete: 10..12,
715 insert: "E",
716 kind: Enum,
717 documentation: Documentation(
718 "enum docs",
719 ),
720 },
721 CompletionItem {
722 label: "V",
723 source_range: 10..12,
724 delete: 10..12,
725 insert: "V",
726 kind: EnumVariant,
727 detail: "()",
728 documentation: Documentation(
729 "variant docs",
730 ),
731 },
732 CompletionItem {
733 label: "my",
734 source_range: 10..12,
735 delete: 10..12,
736 insert: "my",
737 kind: Module,
738 documentation: Documentation(
739 "mod docs",
740 ),
741 },
742 ]
743 "#]],
744 )
745 }
746
747 #[test]
748 fn dont_render_attrs() {
749 check(
750 r#"
751struct S;
752impl S {
753 #[inline]
754 fn the_method(&self) { }
755}
756fn foo(s: S) { s.<|> }
757"#,
758 expect![[r#"
759 [
760 CompletionItem {
761 label: "the_method()",
762 source_range: 81..81,
763 delete: 81..81,
764 insert: "the_method()$0",
765 kind: Method,
766 lookup: "the_method",
767 detail: "fn the_method(&self)",
768 },
769 ]
770 "#]],
771 )
772 }
773
774 #[test]
609 fn inserts_parens_for_function_calls() { 775 fn inserts_parens_for_function_calls() {
610 mark::check!(inserts_parens_for_function_calls); 776 mark::check!(inserts_parens_for_function_calls);
611 assert_debug_snapshot!( 777 check_edit(
612 do_reference_completion( 778 "no_args",
613 r" 779 r#"
614 fn no_args() {} 780fn no_args() {}
615 fn main() { no_<|> } 781fn main() { no_<|> }
616 " 782"#,
617 ), 783 r#"
618 @r###" 784fn no_args() {}
619 [ 785fn main() { no_args()$0 }
620 CompletionItem { 786"#,
621 label: "main()",
622 source_range: 61..64,
623 delete: 61..64,
624 insert: "main()$0",
625 kind: Function,
626 lookup: "main",
627 detail: "fn main()",
628 },
629 CompletionItem {
630 label: "no_args()",
631 source_range: 61..64,
632 delete: 61..64,
633 insert: "no_args()$0",
634 kind: Function,
635 lookup: "no_args",
636 detail: "fn no_args()",
637 },
638 ]
639 "###
640 );
641 assert_debug_snapshot!(
642 do_reference_completion(
643 r"
644 fn with_args(x: i32, y: String) {}
645 fn main() { with_<|> }
646 "
647 ),
648 @r###"
649 [
650 CompletionItem {
651 label: "main()",
652 source_range: 80..85,
653 delete: 80..85,
654 insert: "main()$0",
655 kind: Function,
656 lookup: "main",
657 detail: "fn main()",
658 },
659 CompletionItem {
660 label: "with_args(…)",
661 source_range: 80..85,
662 delete: 80..85,
663 insert: "with_args(${1:x}, ${2:y})$0",
664 kind: Function,
665 lookup: "with_args",
666 detail: "fn with_args(x: i32, y: String)",
667 trigger_call_info: true,
668 },
669 ]
670 "###
671 ); 787 );
672 assert_debug_snapshot!( 788
673 do_reference_completion( 789 check_edit(
674 r" 790 "with_args",
675 fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {} 791 r#"
676 fn main() { with_<|> } 792fn with_args(x: i32, y: String) {}
677 " 793fn main() { with_<|> }
678 ), 794"#,
679 @r###" 795 r#"
680 [ 796fn with_args(x: i32, y: String) {}
681 CompletionItem { 797fn main() { with_args(${1:x}, ${2:y})$0 }
682 label: "main()", 798"#,
683 source_range: 110..115,
684 delete: 110..115,
685 insert: "main()$0",
686 kind: Function,
687 lookup: "main",
688 detail: "fn main()",
689 },
690 CompletionItem {
691 label: "with_ignored_args(…)",
692 source_range: 110..115,
693 delete: 110..115,
694 insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0",
695 kind: Function,
696 lookup: "with_ignored_args",
697 detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)",
698 trigger_call_info: true,
699 },
700 ]
701 "###
702 ); 799 );
703 assert_debug_snapshot!( 800
704 do_reference_completion( 801 check_edit(
705 r" 802 "foo",
706 struct S {} 803 r#"
707 impl S { 804struct S;
708 fn foo(&self) {} 805impl S {
709 } 806 fn foo(&self) {}
710 fn bar(s: &S) { 807}
711 s.f<|> 808fn bar(s: &S) { s.f<|> }
712 } 809"#,
713 " 810 r#"
714 ), 811struct S;
715 @r###" 812impl S {
716 [ 813 fn foo(&self) {}
717 CompletionItem { 814}
718 label: "foo()", 815fn bar(s: &S) { s.foo()$0 }
719 source_range: 163..164, 816"#,
720 delete: 163..164,
721 insert: "foo()$0",
722 kind: Method,
723 lookup: "foo",
724 detail: "fn foo(&self)",
725 },
726 ]
727 "###
728 ); 817 );
729 assert_debug_snapshot!( 818
730 do_reference_completion( 819 check_edit(
731 r" 820 "foo",
732 struct S {} 821 r#"
733 impl S { 822struct S {}
734 fn foo_ignored_args(&self, _a: bool, b: i32) {} 823impl S {
735 } 824 fn foo(&self, x: i32) {}
736 fn bar(s: &S) { 825}
737 s.f<|> 826fn bar(s: &S) {
738 } 827 s.f<|>
739 " 828}
740 ), 829"#,
741 @r###" 830 r#"
742 [ 831struct S {}
743 CompletionItem { 832impl S {
744 label: "foo_ignored_args(…)", 833 fn foo(&self, x: i32) {}
745 source_range: 194..195, 834}
746 delete: 194..195, 835fn bar(s: &S) {
747 insert: "foo_ignored_args(${1:a}, ${2:b})$0", 836 s.foo(${1:x})$0
748 kind: Method, 837}
749 lookup: "foo_ignored_args", 838"#,
750 detail: "fn foo_ignored_args(&self, _a: bool, b: i32)",
751 trigger_call_info: true,
752 },
753 ]
754 "###
755 ); 839 );
756 } 840 }
757 841
758 #[test] 842 #[test]
759 fn inserts_parens_for_tuple_enums() { 843 fn suppress_arg_snippets() {
760 assert_debug_snapshot!( 844 mark::check!(suppress_arg_snippets);
761 do_reference_completion( 845 check_edit_with_config(
762 r" 846 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
763 enum Option<T> { Some(T), None } 847 "with_args",
764 use Option::*; 848 r#"
765 fn main() -> Option<i32> { 849fn with_args(x: i32, y: String) {}
766 Som<|> 850fn main() { with_<|> }
767 } 851"#,
768 " 852 r#"
769 ), 853fn with_args(x: i32, y: String) {}
770 @r###" 854fn main() { with_args($0) }
771 [ 855"#,
772 CompletionItem {
773 label: "None",
774 source_range: 144..147,
775 delete: 144..147,
776 insert: "None",
777 kind: EnumVariant,
778 detail: "()",
779 },
780 CompletionItem {
781 label: "Option",
782 source_range: 144..147,
783 delete: 144..147,
784 insert: "Option",
785 kind: Enum,
786 },
787 CompletionItem {
788 label: "Some(…)",
789 source_range: 144..147,
790 delete: 144..147,
791 insert: "Some($0)",
792 kind: EnumVariant,
793 lookup: "Some",
794 detail: "(T)",
795 trigger_call_info: true,
796 },
797 CompletionItem {
798 label: "main()",
799 source_range: 144..147,
800 delete: 144..147,
801 insert: "main()$0",
802 kind: Function,
803 lookup: "main",
804 detail: "fn main() -> Option<i32>",
805 },
806 ]
807 "###
808 );
809 assert_debug_snapshot!(
810 do_reference_completion(
811 r"
812 enum Option<T> { Some(T), None }
813 use Option::*;
814 fn main(value: Option<i32>) {
815 match value {
816 Som<|>
817 }
818 }
819 "
820 ),
821 @r###"
822 [
823 CompletionItem {
824 label: "None",
825 source_range: 185..188,
826 delete: 185..188,
827 insert: "None",
828 kind: EnumVariant,
829 detail: "()",
830 },
831 CompletionItem {
832 label: "Option",
833 source_range: 185..188,
834 delete: 185..188,
835 insert: "Option",
836 kind: Enum,
837 },
838 CompletionItem {
839 label: "Some(…)",
840 source_range: 185..188,
841 delete: 185..188,
842 insert: "Some($0)",
843 kind: EnumVariant,
844 lookup: "Some",
845 detail: "(T)",
846 trigger_call_info: true,
847 },
848 ]
849 "###
850 ); 856 );
851 } 857 }
852 858
853 #[test] 859 #[test]
854 fn no_call_parens_if_fn_ptr_needed() { 860 fn strips_underscores_from_args() {
855 assert_debug_snapshot!( 861 check_edit(
856 do_reference_completion( 862 "foo",
857 r" 863 r#"
858 fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {} 864fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
859 865fn main() { f<|> }
860 struct ManualVtable { 866"#,
861 method: fn(u8, u8, u8, u8, u8), 867 r#"
862 } 868fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
869fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
870"#,
871 );
872 }
863 873
864 fn main() -> ManualVtable { 874 #[test]
865 ManualVtable { 875 fn inserts_parens_for_tuple_enums() {
866 method: some<|> 876 mark::check!(inserts_parens_for_tuple_enums);
867 } 877 check_edit(
868 } 878 "Some",
869 " 879 r#"
870 ), 880enum Option<T> { Some(T), None }
871 @r###" 881use Option::*;
872 [ 882fn main() -> Option<i32> {
873 CompletionItem { 883 Som<|>
874 label: "ManualVtable", 884}
875 source_range: 295..299, 885"#,
876 delete: 295..299, 886 r#"
877 insert: "ManualVtable", 887enum Option<T> { Some(T), None }
878 kind: Struct, 888use Option::*;
879 }, 889fn main() -> Option<i32> {
880 CompletionItem { 890 Some($0)
881 label: "main", 891}
882 source_range: 295..299, 892"#,
883 delete: 295..299, 893 );
884 insert: "main", 894 check_edit(
885 kind: Function, 895 "Some",
886 detail: "fn main() -> ManualVtable", 896 r#"
887 }, 897enum Option<T> { Some(T), None }
888 CompletionItem { 898use Option::*;
889 label: "somefn", 899fn main(value: Option<i32>) {
890 source_range: 295..299, 900 match value {
891 delete: 295..299, 901 Som<|>
892 insert: "somefn", 902 }
893 kind: Function, 903}
894 detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)", 904"#,
895 }, 905 r#"
896 ] 906enum Option<T> { Some(T), None }
897 "### 907use Option::*;
908fn main(value: Option<i32>) {
909 match value {
910 Some($0)
911 }
912}
913"#,
898 ); 914 );
899 } 915 }
900 916
901 #[test] 917 #[test]
902 fn arg_snippets_for_method_call() { 918 fn dont_duplicate_pattern_parens() {
903 assert_debug_snapshot!( 919 mark::check!(dont_duplicate_pattern_parens);
904 do_reference_completion( 920 check_edit(
905 r" 921 "Var",
906 struct S {} 922 r#"
907 impl S { 923enum E { Var(i32) }
908 fn foo(&self, x: i32) {} 924fn main() {
909 } 925 match E::Var(92) {
910 fn bar(s: &S) { 926 E::<|>(92) => (),
911 s.f<|> 927 }
912 } 928}
913 " 929"#,
914 ), 930 r#"
915 @r###" 931enum E { Var(i32) }
916 [ 932fn main() {
917 CompletionItem { 933 match E::Var(92) {
918 label: "foo(…)", 934 E::Var(92) => (),
919 source_range: 171..172, 935 }
920 delete: 171..172, 936}
921 insert: "foo(${1:x})$0", 937"#,
922 kind: Method, 938 );
923 lookup: "foo",
924 detail: "fn foo(&self, x: i32)",
925 trigger_call_info: true,
926 },
927 ]
928 "###
929 )
930 } 939 }
931 940
932 #[test] 941 #[test]
933 fn no_arg_snippets_for_method_call() { 942 fn no_call_parens_if_fn_ptr_needed() {
934 assert_debug_snapshot!( 943 mark::check!(no_call_parens_if_fn_ptr_needed);
935 do_reference_completion_with_options( 944 check_edit(
936 r" 945 "foo",
937 struct S {} 946 r#"
938 impl S { 947fn foo(foo: u8, bar: u8) {}
939 fn foo(&self, x: i32) {} 948struct ManualVtable { f: fn(u8, u8) }
940 } 949
941 fn bar(s: &S) { 950fn main() -> ManualVtable {
942 s.f<|> 951 ManualVtable { f: f<|> }
943 } 952}
944 ", 953"#,
945 CompletionConfig { 954 r#"
946 add_call_argument_snippets: false, 955fn foo(foo: u8, bar: u8) {}
947 .. Default::default() 956struct ManualVtable { f: fn(u8, u8) }
948 } 957
949 ), 958fn main() -> ManualVtable {
950 @r###" 959 ManualVtable { f: foo }
951 [ 960}
952 CompletionItem { 961"#,
953 label: "foo(…)", 962 );
954 source_range: 171..172,
955 delete: 171..172,
956 insert: "foo($0)",
957 kind: Method,
958 lookup: "foo",
959 detail: "fn foo(&self, x: i32)",
960 trigger_call_info: true,
961 },
962 ]
963 "###
964 )
965 } 963 }
966 964
967 #[test] 965 #[test]
968 fn dont_render_function_parens_in_use_item() { 966 fn no_parens_in_use_item() {
969 assert_debug_snapshot!( 967 mark::check!(no_parens_in_use_item);
970 do_reference_completion( 968 check_edit(
971 " 969 "foo",
972 //- /lib.rs 970 r#"
973 mod m { pub fn foo() {} } 971mod m { pub fn foo() {} }
974 use crate::m::f<|>; 972use crate::m::f<|>;
975 " 973"#,
976 ), 974 r#"
977 @r###" 975mod m { pub fn foo() {} }
978 [ 976use crate::m::foo;
979 CompletionItem { 977"#,
980 label: "foo",
981 source_range: 40..41,
982 delete: 40..41,
983 insert: "foo",
984 kind: Function,
985 detail: "pub fn foo()",
986 },
987 ]
988 "###
989 ); 978 );
990 } 979 }
991 980
992 #[test] 981 #[test]
993 fn dont_render_function_parens_if_already_call() { 982 fn no_parens_in_call() {
994 assert_debug_snapshot!( 983 check_edit(
995 do_reference_completion( 984 "foo",
996 " 985 r#"
997 //- /lib.rs 986fn foo(x: i32) {}
998 fn frobnicate() {} 987fn main() { f<|>(); }
999 fn main() { 988"#,
1000 frob<|>(); 989 r#"
1001 } 990fn foo(x: i32) {}
1002 " 991fn main() { foo(); }
1003 ), 992"#,
1004 @r###"
1005 [
1006 CompletionItem {
1007 label: "frobnicate",
1008 source_range: 35..39,
1009 delete: 35..39,
1010 insert: "frobnicate",
1011 kind: Function,
1012 detail: "fn frobnicate()",
1013 },
1014 CompletionItem {
1015 label: "main",
1016 source_range: 35..39,
1017 delete: 35..39,
1018 insert: "main",
1019 kind: Function,
1020 detail: "fn main()",
1021 },
1022 ]
1023 "###
1024 ); 993 );
1025 assert_debug_snapshot!( 994 check_edit(
1026 do_reference_completion( 995 "foo",
1027 " 996 r#"
1028 //- /lib.rs 997struct Foo;
1029 struct Foo {} 998impl Foo { fn foo(&self){} }
1030 impl Foo { fn new() -> Foo {} } 999fn f(foo: &Foo) { foo.f<|>(); }
1031 fn main() { 1000"#,
1032 Foo::ne<|>(); 1001 r#"
1033 } 1002struct Foo;
1034 " 1003impl Foo { fn foo(&self){} }
1035 ), 1004fn f(foo: &Foo) { foo.foo(); }
1036 @r###" 1005"#,
1037 [
1038 CompletionItem {
1039 label: "new",
1040 source_range: 67..69,
1041 delete: 67..69,
1042 insert: "new",
1043 kind: Function,
1044 detail: "fn new() -> Foo",
1045 },
1046 ]
1047 "###
1048 ); 1006 );
1049 } 1007 }
1050 1008
1051 #[test] 1009 #[test]
1052 fn inserts_angle_brackets_for_generics() { 1010 fn inserts_angle_brackets_for_generics() {
1053 mark::check!(inserts_angle_brackets_for_generics); 1011 mark::check!(inserts_angle_brackets_for_generics);
1054 assert_debug_snapshot!( 1012 check_edit(
1055 do_reference_completion( 1013 "Vec",
1056 r" 1014 r#"
1057 struct Vec<T> {} 1015struct Vec<T> {}
1058 fn foo(xs: Ve<|>) 1016fn foo(xs: Ve<|>)
1059 " 1017"#,
1060 ), 1018 r#"
1061 @r###" 1019struct Vec<T> {}
1062 [ 1020fn foo(xs: Vec<$0>)
1063 CompletionItem { 1021"#,
1064 label: "Vec<…>",
1065 source_range: 61..63,
1066 delete: 61..63,
1067 insert: "Vec<$0>",
1068 kind: Struct,
1069 lookup: "Vec",
1070 },
1071 CompletionItem {
1072 label: "foo(…)",
1073 source_range: 61..63,
1074 delete: 61..63,
1075 insert: "foo(${1:xs})$0",
1076 kind: Function,
1077 lookup: "foo",
1078 detail: "fn foo(xs: Ve)",
1079 trigger_call_info: true,
1080 },
1081 ]
1082 "###
1083 ); 1022 );
1084 assert_debug_snapshot!( 1023 check_edit(
1085 do_reference_completion( 1024 "Vec",
1086 r" 1025 r#"
1087 type Vec<T> = (T,); 1026type Vec<T> = (T,);
1088 fn foo(xs: Ve<|>) 1027fn foo(xs: Ve<|>)
1089 " 1028"#,
1090 ), 1029 r#"
1091 @r###" 1030type Vec<T> = (T,);
1092 [ 1031fn foo(xs: Vec<$0>)
1093 CompletionItem { 1032"#,
1094 label: "Vec<…>",
1095 source_range: 64..66,
1096 delete: 64..66,
1097 insert: "Vec<$0>",
1098 kind: TypeAlias,
1099 lookup: "Vec",
1100 },
1101 CompletionItem {
1102 label: "foo(…)",
1103 source_range: 64..66,
1104 delete: 64..66,
1105 insert: "foo(${1:xs})$0",
1106 kind: Function,
1107 lookup: "foo",
1108 detail: "fn foo(xs: Ve)",
1109 trigger_call_info: true,
1110 },
1111 ]
1112 "###
1113 ); 1033 );
1114 assert_debug_snapshot!( 1034 check_edit(
1115 do_reference_completion( 1035 "Vec",
1116 r" 1036 r#"
1117 struct Vec<T = i128> {} 1037struct Vec<T = i128> {}
1118 fn foo(xs: Ve<|>) 1038fn foo(xs: Ve<|>)
1119 " 1039"#,
1120 ), 1040 r#"
1121 @r###" 1041struct Vec<T = i128> {}
1122 [ 1042fn foo(xs: Vec)
1123 CompletionItem { 1043"#,
1124 label: "Vec",
1125 source_range: 68..70,
1126 delete: 68..70,
1127 insert: "Vec",
1128 kind: Struct,
1129 },
1130 CompletionItem {
1131 label: "foo(…)",
1132 source_range: 68..70,
1133 delete: 68..70,
1134 insert: "foo(${1:xs})$0",
1135 kind: Function,
1136 lookup: "foo",
1137 detail: "fn foo(xs: Ve)",
1138 trigger_call_info: true,
1139 },
1140 ]
1141 "###
1142 ); 1044 );
1143 assert_debug_snapshot!( 1045 check_edit(
1144 do_reference_completion( 1046 "Vec",
1145 r" 1047 r#"
1146 struct Vec<T> {} 1048struct Vec<T> {}
1147 fn foo(xs: Ve<|><i128>) 1049fn foo(xs: Ve<|><i128>)
1148 " 1050"#,
1149 ), 1051 r#"
1150 @r###" 1052struct Vec<T> {}
1151 [ 1053fn foo(xs: Vec<i128>)
1152 CompletionItem { 1054"#,
1153 label: "Vec",
1154 source_range: 61..63,
1155 delete: 61..63,
1156 insert: "Vec",
1157 kind: Struct,
1158 },
1159 CompletionItem {
1160 label: "foo(…)",
1161 source_range: 61..63,
1162 delete: 61..63,
1163 insert: "foo(${1:xs})$0",
1164 kind: Function,
1165 lookup: "foo",
1166 detail: "fn foo(xs: Ve<i128>)",
1167 trigger_call_info: true,
1168 },
1169 ]
1170 "###
1171 ); 1055 );
1172 } 1056 }
1173 1057
1174 #[test] 1058 #[test]
1175 fn dont_insert_macro_call_parens_unncessary() { 1059 fn dont_insert_macro_call_parens_unncessary() {
1176 mark::check!(dont_insert_macro_call_parens_unncessary); 1060 mark::check!(dont_insert_macro_call_parens_unncessary);
1177 assert_debug_snapshot!( 1061 check_edit(
1178 do_reference_completion( 1062 "frobnicate!",
1179 r" 1063 r#"
1180 //- /main.rs 1064//- /main.rs
1181 use foo::<|>; 1065use foo::<|>;
1182 1066//- /foo/lib.rs
1183 //- /foo/lib.rs 1067#[macro_export]
1184 #[macro_export] 1068macro_rules frobnicate { () => () }
1185 macro_rules frobnicate { 1069"#,
1186 () => () 1070 r#"
1187 } 1071use foo::frobnicate;
1188 " 1072"#,
1189 ),
1190 @r###"
1191 [
1192 CompletionItem {
1193 label: "frobnicate!",
1194 source_range: 9..9,
1195 delete: 9..9,
1196 insert: "frobnicate",
1197 kind: Macro,
1198 detail: "#[macro_export]\nmacro_rules! frobnicate",
1199 },
1200 ]
1201 "###
1202 ); 1073 );
1203 1074
1204 assert_debug_snapshot!( 1075 check_edit(
1205 do_reference_completion( 1076 "frobnicate!",
1206 r" 1077 r#"
1207 //- /main.rs 1078macro_rules frobnicate { () => () }
1208 macro_rules frobnicate { 1079fn main() { frob<|>!(); }
1209 () => () 1080"#,
1210 } 1081 r#"
1211 fn main() { 1082macro_rules frobnicate { () => () }
1212 frob<|>!(); 1083fn main() { frobnicate!(); }
1213 } 1084"#,
1214 "
1215 ),
1216 @r###"
1217 [
1218 CompletionItem {
1219 label: "frobnicate!",
1220 source_range: 56..60,
1221 delete: 56..60,
1222 insert: "frobnicate",
1223 kind: Macro,
1224 detail: "macro_rules! frobnicate",
1225 },
1226 CompletionItem {
1227 label: "main()",
1228 source_range: 56..60,
1229 delete: 56..60,
1230 insert: "main()$0",
1231 kind: Function,
1232 lookup: "main",
1233 detail: "fn main()",
1234 },
1235 ]
1236 "###
1237 ); 1085 );
1238 } 1086 }
1239 1087
1240 #[test] 1088 #[test]
1241 fn test_struct_field_completion_in_func_call() { 1089 fn active_param_score() {
1242 mark::check!(test_struct_field_completion_in_func_call); 1090 mark::check!(active_param_type_match);
1243 assert_debug_snapshot!( 1091 check_scores(
1244 do_reference_completion( 1092 r#"
1245 r" 1093struct S { foo: i64, bar: u32, baz: u32 }
1246 struct A { another_field: i64, the_field: u32, my_string: String } 1094fn test(bar: u32) { }
1247 fn test(my_param: u32) -> u32 { my_param } 1095fn foo(s: S) { test(s.<|>) }
1248 fn foo(a: A) { 1096"#,
1249 test(a.<|>) 1097 expect![[r#"
1250 } 1098 fd bar [type+name]
1251 ", 1099 fd baz [type]
1252 ), 1100 fd foo []
1253 @r###" 1101 "#]],
1254 [
1255 CompletionItem {
1256 label: "another_field",
1257 source_range: 201..201,
1258 delete: 201..201,
1259 insert: "another_field",
1260 kind: Field,
1261 detail: "i64",
1262 },
1263 CompletionItem {
1264 label: "my_string",
1265 source_range: 201..201,
1266 delete: 201..201,
1267 insert: "my_string",
1268 kind: Field,
1269 detail: "{unknown}",
1270 },
1271 CompletionItem {
1272 label: "the_field",
1273 source_range: 201..201,
1274 delete: 201..201,
1275 insert: "the_field",
1276 kind: Field,
1277 detail: "u32",
1278 score: TypeMatch,
1279 },
1280 ]
1281 "###
1282 ); 1102 );
1283 } 1103 }
1284 1104
1285 #[test] 1105 #[test]
1286 fn test_struct_field_completion_in_func_call_with_type_and_name() { 1106 fn record_field_scores() {
1287 assert_debug_snapshot!( 1107 mark::check!(record_field_type_match);
1288 do_reference_completion( 1108 check_scores(
1289 r" 1109 r#"
1290 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1110struct A { foo: i64, bar: u32, baz: u32 }
1291 fn test(the_field: u32) -> u32 { the_field } 1111struct B { x: (), y: f32, bar: u32 }
1292 fn foo(a: A) { 1112fn foo(a: A) { B { bar: a.<|> }; }
1293 test(a.<|>) 1113"#,
1294 } 1114 expect![[r#"
1295 ", 1115 fd bar [type+name]
1296 ), 1116 fd baz [type]
1297 @r###" 1117 fd foo []
1298 [ 1118 "#]],
1299 CompletionItem { 1119 )
1300 label: "another_field",
1301 source_range: 208..208,
1302 delete: 208..208,
1303 insert: "another_field",
1304 kind: Field,
1305 detail: "i64",
1306 },
1307 CompletionItem {
1308 label: "another_good_type",
1309 source_range: 208..208,
1310 delete: 208..208,
1311 insert: "another_good_type",
1312 kind: Field,
1313 detail: "u32",
1314 score: TypeMatch,
1315 },
1316 CompletionItem {
1317 label: "the_field",
1318 source_range: 208..208,
1319 delete: 208..208,
1320 insert: "the_field",
1321 kind: Field,
1322 detail: "u32",
1323 score: TypeAndNameMatch,
1324 },
1325 ]
1326 "###
1327 );
1328 } 1120 }
1329 1121
1330 #[test] 1122 #[test]
1331 fn test_struct_field_completion_in_record_lit() { 1123 fn record_field_and_call_scores() {
1332 mark::check!(test_struct_field_completion_in_record_lit); 1124 check_scores(
1333 assert_debug_snapshot!( 1125 r#"
1334 do_reference_completion( 1126struct A { foo: i64, bar: u32, baz: u32 }
1335 r" 1127struct B { x: (), y: f32, bar: u32 }
1336 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1128fn f(foo: i64) { }
1337 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1129fn foo(a: A) { B { bar: f(a.<|>) }; }
1338 fn foo(a: A) { 1130"#,
1339 let b = B { 1131 expect![[r#"
1340 the_field: a.<|> 1132 fd foo [type+name]
1341 }; 1133 fd bar []
1342 } 1134 fd baz []
1343 ", 1135 "#]],
1344 ),
1345 @r###"
1346 [
1347 CompletionItem {
1348 label: "another_field",
1349 source_range: 270..270,
1350 delete: 270..270,
1351 insert: "another_field",
1352 kind: Field,
1353 detail: "i64",
1354 },
1355 CompletionItem {
1356 label: "another_good_type",
1357 source_range: 270..270,
1358 delete: 270..270,
1359 insert: "another_good_type",
1360 kind: Field,
1361 detail: "u32",
1362 score: TypeMatch,
1363 },
1364 CompletionItem {
1365 label: "the_field",
1366 source_range: 270..270,
1367 delete: 270..270,
1368 insert: "the_field",
1369 kind: Field,
1370 detail: "u32",
1371 score: TypeAndNameMatch,
1372 },
1373 ]
1374 "###
1375 ); 1136 );
1376 } 1137 check_scores(
1377 1138 r#"
1378 #[test] 1139struct A { foo: i64, bar: u32, baz: u32 }
1379 fn test_struct_field_completion_in_record_lit_and_fn_call() { 1140struct B { x: (), y: f32, bar: u32 }
1380 assert_debug_snapshot!( 1141fn f(foo: i64) { }
1381 do_reference_completion( 1142fn foo(a: A) { f(B { bar: a.<|> }); }
1382 r" 1143"#,
1383 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1144 expect![[r#"
1384 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1145 fd bar [type+name]
1385 fn test(the_field: i64) -> i64 { the_field } 1146 fd baz [type]
1386 fn foo(a: A) { 1147 fd foo []
1387 let b = B { 1148 "#]],
1388 the_field: test(a.<|>)
1389 };
1390 }
1391 ",
1392 ),
1393 @r###"
1394 [
1395 CompletionItem {
1396 label: "another_field",
1397 source_range: 336..336,
1398 delete: 336..336,
1399 insert: "another_field",
1400 kind: Field,
1401 detail: "i64",
1402 score: TypeMatch,
1403 },
1404 CompletionItem {
1405 label: "another_good_type",
1406 source_range: 336..336,
1407 delete: 336..336,
1408 insert: "another_good_type",
1409 kind: Field,
1410 detail: "u32",
1411 },
1412 CompletionItem {
1413 label: "the_field",
1414 source_range: 336..336,
1415 delete: 336..336,
1416 insert: "the_field",
1417 kind: Field,
1418 detail: "u32",
1419 },
1420 ]
1421 "###
1422 ); 1149 );
1423 } 1150 }
1424 1151
1425 #[test] 1152 #[test]
1426 fn test_struct_field_completion_in_fn_call_and_record_lit() { 1153 fn prioritize_exact_ref_match() {
1427 assert_debug_snapshot!( 1154 check_scores(
1428 do_reference_completion( 1155 r#"
1429 r" 1156struct WorldSnapshot { _f: () };
1430 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1157fn go(world: &WorldSnapshot) { go(w<|>) }
1431 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1158"#,
1432 fn test(the_field: i64) -> i64 { the_field } 1159 expect![[r#"
1433 fn foo(a: A) { 1160 bn world [type+name]
1434 test(B { 1161 st WorldSnapshot []
1435 the_field: a.<|> 1162 fn go(…) []
1436 }); 1163 "#]],
1437 }
1438 ",
1439 ),
1440 @r###"
1441 [
1442 CompletionItem {
1443 label: "another_field",
1444 source_range: 328..328,
1445 delete: 328..328,
1446 insert: "another_field",
1447 kind: Field,
1448 detail: "i64",
1449 },
1450 CompletionItem {
1451 label: "another_good_type",
1452 source_range: 328..328,
1453 delete: 328..328,
1454 insert: "another_good_type",
1455 kind: Field,
1456 detail: "u32",
1457 score: TypeMatch,
1458 },
1459 CompletionItem {
1460 label: "the_field",
1461 source_range: 328..328,
1462 delete: 328..328,
1463 insert: "the_field",
1464 kind: Field,
1465 detail: "u32",
1466 score: TypeAndNameMatch,
1467 },
1468 ]
1469 "###
1470 ); 1164 );
1471 } 1165 }
1472 1166
1473 #[test] 1167 #[test]
1474 fn prioritize_exact_ref_match() { 1168 fn guesses_macro_braces() {
1475 assert_debug_snapshot!( 1169 check_edit(
1476 do_reference_completion( 1170 "vec!",
1477 r" 1171 r#"
1478 struct WorldSnapshot { _f: () }; 1172/// Creates a [`Vec`] containing the arguments.
1479 fn go(world: &WorldSnapshot) { 1173///
1480 go(w<|>) 1174/// ```
1481 } 1175/// let v = vec![1, 2, 3];
1482 ", 1176/// assert_eq!(v[0], 1);
1483 ), 1177/// assert_eq!(v[1], 2);
1484 @r###" 1178/// assert_eq!(v[2], 3);
1485 [ 1179/// ```
1486 CompletionItem { 1180macro_rules! vec { () => {} }
1487 label: "WorldSnapshot", 1181
1488 source_range: 132..133, 1182fn fn main() { v<|> }
1489 delete: 132..133, 1183"#,
1490 insert: "WorldSnapshot", 1184 r#"
1491 kind: Struct, 1185/// Creates a [`Vec`] containing the arguments.
1492 }, 1186///
1493 CompletionItem { 1187/// ```
1494 label: "go(…)", 1188/// let v = vec![1, 2, 3];
1495 source_range: 132..133, 1189/// assert_eq!(v[0], 1);
1496 delete: 132..133, 1190/// assert_eq!(v[1], 2);
1497 insert: "go(${1:world})$0", 1191/// assert_eq!(v[2], 3);
1498 kind: Function, 1192/// ```
1499 lookup: "go", 1193macro_rules! vec { () => {} }
1500 detail: "fn go(world: &WorldSnapshot)", 1194
1501 trigger_call_info: true, 1195fn fn main() { vec![$0] }
1502 }, 1196"#,
1503 CompletionItem {
1504 label: "world",
1505 source_range: 132..133,
1506 delete: 132..133,
1507 insert: "world",
1508 kind: Binding,
1509 detail: "&WorldSnapshot",
1510 score: TypeAndNameMatch,
1511 },
1512 ]
1513 "###
1514 ); 1197 );
1198
1199 check_edit(
1200 "foo!",
1201 r#"
1202/// Foo
1203///
1204/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1205/// call as `let _=foo! { hello world };`
1206macro_rules! foo { () => {} }
1207fn main() { <|> }
1208"#,
1209 r#"
1210/// Foo
1211///
1212/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1213/// call as `let _=foo! { hello world };`
1214macro_rules! foo { () => {} }
1215fn main() { foo! {$0} }
1216"#,
1217 )
1515 } 1218 }
1516} 1219}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index bf22452a2..919177745 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,29 +1,114 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics;
4use itertools::Itertools;
5use ra_syntax::{AstNode, NodeOrToken, SyntaxElement};
6use stdx::{format_to, trim_indent};
7use test_utils::assert_eq_text;
8
3use crate::{ 9use crate::{
4 completion::{completion_item::CompletionKind, CompletionConfig}, 10 completion::{completion_item::CompletionKind, CompletionConfig},
5 mock_analysis::{analysis_and_position, single_file_with_position}, 11 mock_analysis::analysis_and_position,
6 CompletionItem, 12 CompletionItem,
7}; 13};
8 14
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionConfig::default()) 16 do_completion_with_config(CompletionConfig::default(), code, kind)
11} 17}
12 18
13pub(crate) fn do_completion_with_options( 19pub(crate) fn do_completion_with_config(
20 config: CompletionConfig,
14 code: &str, 21 code: &str,
15 kind: CompletionKind, 22 kind: CompletionKind,
16 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
19 analysis_and_position(code) 25 .into_iter()
20 } else { 26 .filter(|c| c.completion_kind == kind)
21 single_file_with_position(code) 27 .collect();
22 }; 28 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
23 let completions = analysis.completions(options, position).unwrap().unwrap(); 29 kind_completions
24 let completion_items: Vec<CompletionItem> = completions.into(); 30}
25 let mut kind_completions: Vec<CompletionItem> = 31
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
33 completion_list_with_config(CompletionConfig::default(), code, kind)
34}
35
36pub(crate) fn completion_list_with_config(
37 config: CompletionConfig,
38 code: &str,
39 kind: CompletionKind,
40) -> String {
41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
42 .into_iter()
43 .filter(|c| c.completion_kind == kind)
44 .collect();
27 kind_completions.sort_by_key(|c| c.label().to_owned()); 45 kind_completions.sort_by_key(|c| c.label().to_owned());
46 let label_width = kind_completions
47 .iter()
48 .map(|it| monospace_width(it.label()))
49 .max()
50 .unwrap_or_default()
51 .min(16);
28 kind_completions 52 kind_completions
53 .into_iter()
54 .map(|it| {
55 let tag = it.kind().unwrap().tag();
56 let var_name = format!("{} {}", tag, it.label());
57 let mut buf = var_name;
58 if let Some(detail) = it.detail() {
59 let width = label_width.saturating_sub(monospace_width(it.label()));
60 format_to!(buf, "{:width$} {}", "", detail, width = width);
61 }
62 format_to!(buf, "\n");
63 buf
64 })
65 .collect()
66}
67
68fn monospace_width(s: &str) -> usize {
69 s.chars().count()
70}
71
72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
73 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after)
74}
75
76pub(crate) fn check_edit_with_config(
77 config: CompletionConfig,
78 what: &str,
79 ra_fixture_before: &str,
80 ra_fixture_after: &str,
81) {
82 let ra_fixture_after = trim_indent(ra_fixture_after);
83 let (analysis, position) = analysis_and_position(ra_fixture_before);
84 let completions: Vec<CompletionItem> =
85 analysis.completions(&config, position).unwrap().unwrap().into();
86 let (completion,) = completions
87 .iter()
88 .filter(|it| it.lookup() == what)
89 .collect_tuple()
90 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
91 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
92 completion.text_edit().apply(&mut actual);
93 assert_eq_text!(&ra_fixture_after, &actual)
94}
95
96pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
97 let (analysis, pos) = analysis_and_position(code);
98 analysis
99 .with_db(|db| {
100 let sema = Semantics::new(db);
101 let original_file = sema.parse(pos.file_id);
102 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
103 assert!(check(NodeOrToken::Token(token)));
104 })
105 .unwrap();
106}
107
108pub(crate) fn get_all_completion_items(
109 config: CompletionConfig,
110 code: &str,
111) -> Vec<CompletionItem> {
112 let (analysis, position) = analysis_and_position(code);
113 analysis.completions(&config, position).unwrap().unwrap().into()
29} 114}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 15dc50cf1..fe75f4b2c 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -8,20 +8,20 @@ use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, 10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink},
11 Semantics, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
14use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; 14use ra_db::SourceDatabase;
15use ra_ide_db::RootDatabase; 15use ra_ide_db::RootDatabase;
16use ra_prof::profile; 16use ra_prof::profile;
17use ra_syntax::{ 17use ra_syntax::{
18 algo, 18 algo,
19 ast::{self, make, AstNode}, 19 ast::{self, edit::IndentLevel, make, AstNode},
20 SyntaxNode, TextRange, T, 20 SyntaxNode, TextRange, T,
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -35,7 +35,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
35 let parse = db.parse(file_id); 35 let parse = db.parse(file_id);
36 let mut res = Vec::new(); 36 let mut res = Vec::new();
37 37
38 res.extend(parse.errors().iter().map(|err| Diagnostic { 38 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
39 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
39 range: err.range(), 40 range: err.range(),
40 message: format!("Syntax Error: {}", err), 41 message: format!("Syntax Error: {}", err),
41 severity: Severity::Error, 42 severity: Severity::Error,
@@ -57,14 +58,10 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
57 }) 58 })
58 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 59 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
59 let original_file = d.source().file_id.original_file(db); 60 let original_file = d.source().file_id.original_file(db);
60 let source_root = db.file_source_root(original_file); 61 let fix = Fix::new(
61 let path = db 62 "Create module",
62 .file_relative_path(original_file) 63 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(),
63 .parent() 64 );
64 .unwrap_or_else(|| RelativePath::new(""))
65 .join(&d.candidate);
66 let fix =
67 Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into());
68 res.borrow_mut().push(Diagnostic { 65 res.borrow_mut().push(Diagnostic {
69 range: sema.diagnostics_range(d).range, 66 range: sema.diagnostics_range(d).range,
70 message: d.message(), 67 message: d.message(),
@@ -103,19 +100,11 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
103 fix, 100 fix,
104 }) 101 })
105 }) 102 })
106 .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
107 res.borrow_mut().push(Diagnostic {
108 range: sema.diagnostics_range(d).range,
109 message: d.message(),
110 severity: Severity::Error,
111 fix: None,
112 })
113 })
114 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 103 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
115 let node = d.ast(db); 104 let node = d.ast(db);
116 let replacement = format!("Ok({})", node.syntax()); 105 let replacement = format!("Ok({})", node.syntax());
117 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 106 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
118 let source_change = SourceChange::source_file_edit_from(file_id, edit); 107 let source_change = SourceFileEdit { file_id, edit }.into();
119 let fix = Fix::new("Wrap with ok", source_change); 108 let fix = Fix::new("Wrap with ok", source_change);
120 res.borrow_mut().push(Diagnostic { 109 res.borrow_mut().push(Diagnostic {
121 range: sema.diagnostics_range(d).range, 110 range: sema.diagnostics_range(d).range,
@@ -123,7 +112,16 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
123 severity: Severity::Error, 112 severity: Severity::Error,
124 fix: Some(fix), 113 fix: Some(fix),
125 }) 114 })
115 })
116 .on::<hir::diagnostics::NoSuchField, _>(|d| {
117 res.borrow_mut().push(Diagnostic {
118 range: sema.diagnostics_range(d).range,
119 message: d.message(),
120 severity: Severity::Error,
121 fix: missing_struct_field_fix(&sema, file_id, d),
122 })
126 }); 123 });
124
127 if let Some(m) = sema.to_module_def(file_id) { 125 if let Some(m) = sema.to_module_def(file_id) {
128 m.diagnostics(db, &mut sink); 126 m.diagnostics(db, &mut sink);
129 }; 127 };
@@ -131,6 +129,80 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
131 res.into_inner() 129 res.into_inner()
132} 130}
133 131
132fn missing_struct_field_fix(
133 sema: &Semantics<RootDatabase>,
134 usage_file_id: FileId,
135 d: &hir::diagnostics::NoSuchField,
136) -> Option<Fix> {
137 let record_expr = sema.ast(d);
138
139 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?;
140 let def_id = sema.resolve_variant(record_lit)?;
141 let module;
142 let def_file_id;
143 let record_fields = match VariantDef::from(def_id) {
144 VariantDef::Struct(s) => {
145 module = s.module(sema.db);
146 let source = s.source(sema.db);
147 def_file_id = source.file_id;
148 let fields = source.value.field_def_list()?;
149 record_field_def_list(fields)?
150 }
151 VariantDef::Union(u) => {
152 module = u.module(sema.db);
153 let source = u.source(sema.db);
154 def_file_id = source.file_id;
155 source.value.record_field_def_list()?
156 }
157 VariantDef::EnumVariant(e) => {
158 module = e.module(sema.db);
159 let source = e.source(sema.db);
160 def_file_id = source.file_id;
161 let fields = source.value.field_def_list()?;
162 record_field_def_list(fields)?
163 }
164 };
165 let def_file_id = def_file_id.original_file(sema.db);
166
167 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
168 if new_field_type.is_unknown() {
169 return None;
170 }
171 let new_field = make::record_field_def(
172 record_expr.field_name()?,
173 make::type_ref(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
174 );
175
176 let last_field = record_fields.fields().last()?;
177 let last_field_syntax = last_field.syntax();
178 let indent = IndentLevel::from_node(last_field_syntax);
179
180 let mut new_field = new_field.to_string();
181 if usage_file_id != def_file_id {
182 new_field = format!("pub(crate) {}", new_field);
183 }
184 new_field = format!("\n{}{}", indent, new_field);
185
186 let needs_comma = !last_field_syntax.to_string().ends_with(",");
187 if needs_comma {
188 new_field = format!(",{}", new_field);
189 }
190
191 let source_change = SourceFileEdit {
192 file_id: def_file_id,
193 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
194 };
195 let fix = Fix::new("Create field", source_change.into());
196 return Some(fix);
197
198 fn record_field_def_list(field_def_list: ast::FieldDefList) -> Option<ast::RecordFieldDefList> {
199 match field_def_list {
200 ast::FieldDefList::RecordFieldDefList(it) => Some(it),
201 ast::FieldDefList::TupleFieldDefList(_) => None,
202 }
203 }
204}
205
134fn check_unnecessary_braces_in_use_statement( 206fn check_unnecessary_braces_in_use_statement(
135 acc: &mut Vec<Diagnostic>, 207 acc: &mut Vec<Diagnostic>,
136 file_id: FileId, 208 file_id: FileId,
@@ -187,7 +259,8 @@ fn check_struct_shorthand_initialization(
187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { 259 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
188 let field_name = name_ref.syntax().text().to_string(); 260 let field_name = name_ref.syntax().text().to_string();
189 let field_expr = expr.syntax().text().to_string(); 261 let field_expr = expr.syntax().text().to_string();
190 if field_name == field_expr { 262 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
263 if field_name == field_expr && !field_name_is_tup_index {
191 let mut edit_builder = TextEditBuilder::default(); 264 let mut edit_builder = TextEditBuilder::default();
192 edit_builder.delete(record_field.syntax().text_range()); 265 edit_builder.delete(record_field.syntax().text_range());
193 edit_builder.insert(record_field.syntax().text_range().start(), field_name); 266 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
@@ -210,51 +283,21 @@ fn check_struct_shorthand_initialization(
210 283
211#[cfg(test)] 284#[cfg(test)]
212mod tests { 285mod tests {
213 use insta::assert_debug_snapshot; 286 use stdx::trim_indent;
214 use ra_syntax::SourceFile;
215 use stdx::SepBy;
216 use test_utils::assert_eq_text; 287 use test_utils::assert_eq_text;
217 288
218 use crate::mock_analysis::{analysis_and_position, single_file}; 289 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
219 290 use expect::{expect, Expect};
220 use super::*;
221
222 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
223
224 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
225 let parse = SourceFile::parse(code);
226 let mut diagnostics = Vec::new();
227 for node in parse.tree().syntax().descendants() {
228 func(&mut diagnostics, FileId(0), &node);
229 }
230 assert!(diagnostics.is_empty());
231 }
232
233 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
234 let parse = SourceFile::parse(before);
235 let mut diagnostics = Vec::new();
236 for node in parse.tree().syntax().descendants() {
237 func(&mut diagnostics, FileId(0), &node);
238 }
239 let diagnostic =
240 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
241 let mut fix = diagnostic.fix.unwrap();
242 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
243 let actual = {
244 let mut actual = before.to_string();
245 edit.apply(&mut actual);
246 actual
247 };
248 assert_eq_text!(after, &actual);
249 }
250 291
251 /// Takes a multi-file input fixture with annotated cursor positions, 292 /// Takes a multi-file input fixture with annotated cursor positions,
252 /// and checks that: 293 /// and checks that:
253 /// * a diagnostic is produced 294 /// * a diagnostic is produced
254 /// * this diagnostic touches the input cursor position 295 /// * this diagnostic touches the input cursor position
255 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 296 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
256 fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) { 297 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
257 let (analysis, file_position) = analysis_and_position(fixture); 298 let after = trim_indent(ra_fixture_after);
299
300 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
258 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 301 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
259 let mut fix = diagnostic.fix.unwrap(); 302 let mut fix = diagnostic.fix.unwrap();
260 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 303 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
@@ -265,21 +308,6 @@ mod tests {
265 actual 308 actual
266 }; 309 };
267 310
268 // Strip indent and empty lines from `after`, to match the behaviour of
269 // `parse_fixture` called from `analysis_and_position`.
270 let margin = fixture
271 .lines()
272 .filter(|it| it.trim_start().starts_with("//-"))
273 .map(|it| it.len() - it.trim_start().len())
274 .next()
275 .expect("empty fixture");
276 let after = after
277 .lines()
278 .filter_map(|line| if line.len() > margin { Some(&line[margin..]) } else { None })
279 .sep_by("\n")
280 .suffix("\n")
281 .to_string();
282
283 assert_eq_text!(&after, &actual); 311 assert_eq_text!(&after, &actual);
284 assert!( 312 assert!(
285 diagnostic.range.start() <= file_position.offset 313 diagnostic.range.start() <= file_position.offset
@@ -290,508 +318,470 @@ mod tests {
290 ); 318 );
291 } 319 }
292 320
293 fn check_apply_diagnostic_fix(before: &str, after: &str) { 321 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
294 let (analysis, file_id) = single_file(before); 322 /// which has a fix that can apply to other files.
295 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 323 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
324 let ra_fixture_after = &trim_indent(ra_fixture_after);
325 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
326 let current_file_id = file_pos.file_id;
327 let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap();
296 let mut fix = diagnostic.fix.unwrap(); 328 let mut fix = diagnostic.fix.unwrap();
297 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 329 let edit = fix.source_change.source_file_edits.pop().unwrap();
330 let changed_file_id = edit.file_id;
331 let before = analysis.file_text(changed_file_id).unwrap();
298 let actual = { 332 let actual = {
299 let mut actual = before.to_string(); 333 let mut actual = before.to_string();
300 edit.apply(&mut actual); 334 edit.edit.apply(&mut actual);
301 actual 335 actual
302 }; 336 };
303 assert_eq_text!(after, &actual); 337 assert_eq_text!(ra_fixture_after, &actual);
304 } 338 }
305 339
306 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 340 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
307 /// apply to the file containing the cursor. 341 /// apply to the file containing the cursor.
308 fn check_no_diagnostic_for_target_file(fixture: &str) { 342 fn check_no_diagnostics(ra_fixture: &str) {
309 let (analysis, file_position) = analysis_and_position(fixture); 343 let mock = MockAnalysis::with_files(ra_fixture);
310 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 344 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
311 assert_eq!(diagnostics.len(), 0); 345 let analysis = mock.analysis();
346 let diagnostics = files
347 .into_iter()
348 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap())
349 .collect::<Vec<_>>();
350 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
312 } 351 }
313 352
314 fn check_no_diagnostic(content: &str) { 353 fn check_expect(ra_fixture: &str, expect: Expect) {
315 let (analysis, file_id) = single_file(content); 354 let (analysis, file_id) = single_file(ra_fixture);
316 let diagnostics = analysis.diagnostics(file_id).unwrap(); 355 let diagnostics = analysis.diagnostics(file_id).unwrap();
317 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 356 expect.assert_debug_eq(&diagnostics)
318 } 357 }
319 358
320 #[test] 359 #[test]
321 fn test_wrap_return_type() { 360 fn test_wrap_return_type() {
322 let before = r#" 361 check_fix(
323 //- /main.rs 362 r#"
324 use std::{string::String, result::Result::{self, Ok, Err}}; 363//- /main.rs
364use core::result::Result::{self, Ok, Err};
325 365
326 fn div(x: i32, y: i32) -> Result<i32, String> { 366fn div(x: i32, y: i32) -> Result<i32, ()> {
327 if y == 0 { 367 if y == 0 {
328 return Err("div by zero".into()); 368 return Err(());
329 } 369 }
330 x / y<|> 370 x / y<|>
331 } 371}
372//- /core/lib.rs
373pub mod result {
374 pub enum Result<T, E> { Ok(T), Err(E) }
375}
376"#,
377 r#"
378use core::result::Result::{self, Ok, Err};
332 379
333 //- /std/lib.rs 380fn div(x: i32, y: i32) -> Result<i32, ()> {
334 pub mod string { 381 if y == 0 {
335 pub struct String { } 382 return Err(());
336 } 383 }
337 pub mod result { 384 Ok(x / y)
338 pub enum Result<T, E> { Ok(T), Err(E) } 385}
339 } 386"#,
340 "#; 387 );
341 let after = r#"
342 use std::{string::String, result::Result::{self, Ok, Err}};
343
344 fn div(x: i32, y: i32) -> Result<i32, String> {
345 if y == 0 {
346 return Err("div by zero".into());
347 }
348 Ok(x / y)
349 }
350 "#;
351 check_apply_diagnostic_fix_from_position(before, after);
352 } 388 }
353 389
354 #[test] 390 #[test]
355 fn test_wrap_return_type_handles_generic_functions() { 391 fn test_wrap_return_type_handles_generic_functions() {
356 let before = r#" 392 check_fix(
357 //- /main.rs 393 r#"
358 use std::result::Result::{self, Ok, Err}; 394//- /main.rs
395use core::result::Result::{self, Ok, Err};
359 396
360 fn div<T>(x: T) -> Result<T, i32> { 397fn div<T>(x: T) -> Result<T, i32> {
361 if x == 0 { 398 if x == 0 {
362 return Err(7); 399 return Err(7);
363 } 400 }
364 <|>x 401 <|>x
365 } 402}
403//- /core/lib.rs
404pub mod result {
405 pub enum Result<T, E> { Ok(T), Err(E) }
406}
407"#,
408 r#"
409use core::result::Result::{self, Ok, Err};
366 410
367 //- /std/lib.rs 411fn div<T>(x: T) -> Result<T, i32> {
368 pub mod result { 412 if x == 0 {
369 pub enum Result<T, E> { Ok(T), Err(E) } 413 return Err(7);
370 } 414 }
371 "#; 415 Ok(x)
372 let after = r#" 416}
373 use std::result::Result::{self, Ok, Err}; 417"#,
374 418 );
375 fn div<T>(x: T) -> Result<T, i32> {
376 if x == 0 {
377 return Err(7);
378 }
379 Ok(x)
380 }
381 "#;
382 check_apply_diagnostic_fix_from_position(before, after);
383 } 419 }
384 420
385 #[test] 421 #[test]
386 fn test_wrap_return_type_handles_type_aliases() { 422 fn test_wrap_return_type_handles_type_aliases() {
387 let before = r#" 423 check_fix(
388 //- /main.rs 424 r#"
389 use std::{string::String, result::Result::{self, Ok, Err}}; 425//- /main.rs
426use core::result::Result::{self, Ok, Err};
390 427
391 type MyResult<T> = Result<T, String>; 428type MyResult<T> = Result<T, ()>;
392 429
393 fn div(x: i32, y: i32) -> MyResult<i32> { 430fn div(x: i32, y: i32) -> MyResult<i32> {
394 if y == 0 { 431 if y == 0 {
395 return Err("div by zero".into()); 432 return Err(());
396 } 433 }
397 x <|>/ y 434 x <|>/ y
398 } 435}
436//- /core/lib.rs
437pub mod result {
438 pub enum Result<T, E> { Ok(T), Err(E) }
439}
440"#,
441 r#"
442use core::result::Result::{self, Ok, Err};
399 443
400 //- /std/lib.rs 444type MyResult<T> = Result<T, ()>;
401 pub mod string { 445
402 pub struct String { } 446fn div(x: i32, y: i32) -> MyResult<i32> {
403 } 447 if y == 0 {
404 pub mod result { 448 return Err(());
405 pub enum Result<T, E> { Ok(T), Err(E) } 449 }
406 } 450 Ok(x / y)
407 "#; 451}
408 let after = r#" 452"#,
409 use std::{string::String, result::Result::{self, Ok, Err}}; 453 );
410
411 type MyResult<T> = Result<T, String>;
412 fn div(x: i32, y: i32) -> MyResult<i32> {
413 if y == 0 {
414 return Err("div by zero".into());
415 }
416 Ok(x / y)
417 }
418 "#;
419 check_apply_diagnostic_fix_from_position(before, after);
420 } 454 }
421 455
422 #[test] 456 #[test]
423 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 457 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
424 let content = r#" 458 check_no_diagnostics(
425 //- /main.rs 459 r#"
426 use std::{string::String, result::Result::{self, Ok, Err}}; 460//- /main.rs
461use core::result::Result::{self, Ok, Err};
427 462
428 fn foo() -> Result<String, i32> { 463fn foo() -> Result<(), i32> { 0 }
429 0<|>
430 }
431 464
432 //- /std/lib.rs 465//- /core/lib.rs
433 pub mod string { 466pub mod result {
434 pub struct String { } 467 pub enum Result<T, E> { Ok(T), Err(E) }
435 } 468}
436 pub mod result { 469"#,
437 pub enum Result<T, E> { Ok(T), Err(E) } 470 );
438 }
439 "#;
440 check_no_diagnostic_for_target_file(content);
441 } 471 }
442 472
443 #[test] 473 #[test]
444 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 474 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
445 let content = r#" 475 check_no_diagnostics(
446 //- /main.rs 476 r#"
447 use std::{string::String, result::Result::{self, Ok, Err}}; 477//- /main.rs
478use core::result::Result::{self, Ok, Err};
448 479
449 enum SomeOtherEnum { 480enum SomeOtherEnum { Ok(i32), Err(String) }
450 Ok(i32),
451 Err(String),
452 }
453 481
454 fn foo() -> SomeOtherEnum { 482fn foo() -> SomeOtherEnum { 0 }
455 0<|>
456 }
457 483
458 //- /std/lib.rs 484//- /core/lib.rs
459 pub mod string { 485pub mod result {
460 pub struct String { } 486 pub enum Result<T, E> { Ok(T), Err(E) }
461 } 487}
462 pub mod result { 488"#,
463 pub enum Result<T, E> { Ok(T), Err(E) } 489 );
464 }
465 "#;
466 check_no_diagnostic_for_target_file(content);
467 } 490 }
468 491
469 #[test] 492 #[test]
470 fn test_fill_struct_fields_empty() { 493 fn test_fill_struct_fields_empty() {
471 let before = r" 494 check_fix(
472 struct TestStruct { 495 r#"
473 one: i32, 496struct TestStruct { one: i32, two: i64 }
474 two: i64,
475 }
476 497
477 fn test_fn() { 498fn test_fn() {
478 let s = TestStruct{}; 499 let s = TestStruct {<|>};
479 } 500}
480 "; 501"#,
481 let after = r" 502 r#"
482 struct TestStruct { 503struct TestStruct { one: i32, two: i64 }
483 one: i32,
484 two: i64,
485 }
486 504
487 fn test_fn() { 505fn test_fn() {
488 let s = TestStruct{ one: (), two: ()}; 506 let s = TestStruct { one: (), two: ()};
489 } 507}
490 "; 508"#,
491 check_apply_diagnostic_fix(before, after); 509 );
492 } 510 }
493 511
494 #[test] 512 #[test]
495 fn test_fill_struct_fields_self() { 513 fn test_fill_struct_fields_self() {
496 let before = r" 514 check_fix(
497 struct TestStruct { 515 r#"
498 one: i32, 516struct TestStruct { one: i32 }
499 }
500 517
501 impl TestStruct { 518impl TestStruct {
502 fn test_fn() { 519 fn test_fn() { let s = Self {<|>}; }
503 let s = Self {}; 520}
504 } 521"#,
505 } 522 r#"
506 "; 523struct TestStruct { one: i32 }
507 let after = r"
508 struct TestStruct {
509 one: i32,
510 }
511 524
512 impl TestStruct { 525impl TestStruct {
513 fn test_fn() { 526 fn test_fn() { let s = Self { one: ()}; }
514 let s = Self { one: ()}; 527}
515 } 528"#,
516 } 529 );
517 ";
518 check_apply_diagnostic_fix(before, after);
519 } 530 }
520 531
521 #[test] 532 #[test]
522 fn test_fill_struct_fields_enum() { 533 fn test_fill_struct_fields_enum() {
523 let before = r" 534 check_fix(
524 enum Expr { 535 r#"
525 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 536enum Expr {
526 } 537 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
527 538}
528 impl Expr {
529 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
530 Expr::Bin { <|> }
531 }
532 }
533
534 ";
535 let after = r"
536 enum Expr {
537 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
538 }
539 539
540 impl Expr { 540impl Expr {
541 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 541 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
542 Expr::Bin { lhs: (), rhs: () <|> } 542 Expr::Bin {<|> }
543 } 543 }
544 } 544}
545"#,
546 r#"
547enum Expr {
548 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
549}
545 550
546 "; 551impl Expr {
547 check_apply_diagnostic_fix(before, after); 552 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
553 Expr::Bin { lhs: (), rhs: () }
554 }
555}
556"#,
557 );
548 } 558 }
549 559
550 #[test] 560 #[test]
551 fn test_fill_struct_fields_partial() { 561 fn test_fill_struct_fields_partial() {
552 let before = r" 562 check_fix(
553 struct TestStruct { 563 r#"
554 one: i32, 564struct TestStruct { one: i32, two: i64 }
555 two: i64,
556 }
557 565
558 fn test_fn() { 566fn test_fn() {
559 let s = TestStruct{ two: 2 }; 567 let s = TestStruct{ two: 2<|> };
560 } 568}
561 "; 569"#,
562 let after = r" 570 r"
563 struct TestStruct { 571struct TestStruct { one: i32, two: i64 }
564 one: i32,
565 two: i64,
566 }
567 572
568 fn test_fn() { 573fn test_fn() {
569 let s = TestStruct{ two: 2, one: () }; 574 let s = TestStruct{ two: 2, one: () };
570 } 575}
571 "; 576",
572 check_apply_diagnostic_fix(before, after); 577 );
573 } 578 }
574 579
575 #[test] 580 #[test]
576 fn test_fill_struct_fields_no_diagnostic() { 581 fn test_fill_struct_fields_no_diagnostic() {
577 let content = r" 582 check_no_diagnostics(
578 struct TestStruct { 583 r"
579 one: i32, 584 struct TestStruct { one: i32, two: i64 }
580 two: i64,
581 }
582 585
583 fn test_fn() { 586 fn test_fn() {
584 let one = 1; 587 let one = 1;
585 let s = TestStruct{ one, two: 2 }; 588 let s = TestStruct{ one, two: 2 };
586 } 589 }
587 "; 590 ",
588 591 );
589 check_no_diagnostic(content);
590 } 592 }
591 593
592 #[test] 594 #[test]
593 fn test_fill_struct_fields_no_diagnostic_on_spread() { 595 fn test_fill_struct_fields_no_diagnostic_on_spread() {
594 let content = r" 596 check_no_diagnostics(
595 struct TestStruct { 597 r"
596 one: i32, 598 struct TestStruct { one: i32, two: i64 }
597 two: i64,
598 }
599 599
600 fn test_fn() { 600 fn test_fn() {
601 let one = 1; 601 let one = 1;
602 let s = TestStruct{ ..a }; 602 let s = TestStruct{ ..a };
603 } 603 }
604 "; 604 ",
605 605 );
606 check_no_diagnostic(content);
607 } 606 }
608 607
609 #[test] 608 #[test]
610 fn test_unresolved_module_diagnostic() { 609 fn test_unresolved_module_diagnostic() {
611 let (analysis, file_id) = single_file("mod foo;"); 610 check_expect(
612 let diagnostics = analysis.diagnostics(file_id).unwrap(); 611 r#"mod foo;"#,
613 assert_debug_snapshot!(diagnostics, @r###" 612 expect![[r#"
614 [ 613 [
615 Diagnostic { 614 Diagnostic {
616 message: "unresolved module", 615 message: "unresolved module",
617 range: 0..8, 616 range: 0..8,
618 severity: Error, 617 severity: Error,
619 fix: Some( 618 fix: Some(
620 Fix { 619 Fix {
621 label: "Create module", 620 label: "Create module",
622 source_change: SourceChange { 621 source_change: SourceChange {
623 source_file_edits: [], 622 source_file_edits: [],
624 file_system_edits: [ 623 file_system_edits: [
625 CreateFile { 624 CreateFile {
626 source_root: SourceRootId( 625 anchor: FileId(
627 0, 626 1,
628 ), 627 ),
629 path: "foo.rs", 628 dst: "foo.rs",
629 },
630 ],
631 is_snippet: false,
630 }, 632 },
631 ], 633 },
632 is_snippet: false, 634 ),
633 },
634 }, 635 },
635 ), 636 ]
636 }, 637 "#]],
637 ] 638 );
638 "###);
639 } 639 }
640 640
641 #[test] 641 #[test]
642 fn range_mapping_out_of_macros() { 642 fn range_mapping_out_of_macros() {
643 let (analysis, file_id) = single_file( 643 // FIXME: this is very wrong, but somewhat tricky to fix.
644 r" 644 check_fix(
645 fn some() {} 645 r#"
646 fn items() {} 646fn some() {}
647 fn here() {} 647fn items() {}
648fn here() {}
648 649
649 macro_rules! id { 650macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
650 ($($tt:tt)*) => { $($tt)*};
651 }
652 651
653 fn main() { 652fn main() {
654 let _x = id![Foo { a: 42 }]; 653 let _x = id![Foo { a: <|>42 }];
655 } 654}
656 655
657 pub struct Foo { 656pub struct Foo { pub a: i32, pub b: i32 }
658 pub a: i32, 657"#,
659 pub b: i32, 658 r#"
660 } 659fn {a:42, b: ()} {}
661 ", 660fn items() {}
661fn here() {}
662
663macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
664
665fn main() {
666 let _x = id![Foo { a: 42 }];
667}
668
669pub struct Foo { pub a: i32, pub b: i32 }
670"#,
662 ); 671 );
663 let diagnostics = analysis.diagnostics(file_id).unwrap();
664 assert_debug_snapshot!(diagnostics, @r###"
665 [
666 Diagnostic {
667 message: "Missing structure fields:\n- b\n",
668 range: 224..233,
669 severity: Error,
670 fix: Some(
671 Fix {
672 label: "Fill struct fields",
673 source_change: SourceChange {
674 source_file_edits: [
675 SourceFileEdit {
676 file_id: FileId(
677 1,
678 ),
679 edit: TextEdit {
680 indels: [
681 Indel {
682 insert: "{a:42, b: ()}",
683 delete: 3..9,
684 },
685 ],
686 },
687 },
688 ],
689 file_system_edits: [],
690 is_snippet: false,
691 },
692 },
693 ),
694 },
695 ]
696 "###);
697 } 672 }
698 673
699 #[test] 674 #[test]
700 fn test_check_unnecessary_braces_in_use_statement() { 675 fn test_check_unnecessary_braces_in_use_statement() {
701 check_not_applicable( 676 check_no_diagnostics(
702 " 677 r#"
703 use a; 678use a;
704 use a::{c, d::e}; 679use a::{c, d::e};
705 ", 680"#,
706 check_unnecessary_braces_in_use_statement,
707 );
708 check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
709 check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
710 check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
711 check_apply(
712 "use a::{c, d::{e}};",
713 "use a::{c, d::e};",
714 check_unnecessary_braces_in_use_statement,
715 ); 681 );
682 check_fix(r#"use {<|>b};"#, r#"use b;"#);
683 check_fix(r#"use {b<|>};"#, r#"use b;"#);
684 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
685 check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
686 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
716 } 687 }
717 688
718 #[test] 689 #[test]
719 fn test_check_struct_shorthand_initialization() { 690 fn test_check_struct_shorthand_initialization() {
720 check_not_applicable( 691 check_no_diagnostics(
721 r#" 692 r#"
722 struct A { 693struct A { a: &'static str }
723 a: &'static str 694fn main() { A { a: "hello" } }
724 } 695"#,
725
726 fn main() {
727 A {
728 a: "hello"
729 }
730 }
731 "#,
732 check_struct_shorthand_initialization,
733 ); 696 );
734 697 check_no_diagnostics(
735 check_apply(
736 r#" 698 r#"
737struct A { 699struct A(usize);
738 a: &'static str 700fn main() { A { 0: 0 } }
739} 701"#,
702 );
740 703
704 check_fix(
705 r#"
706struct A { a: &'static str }
741fn main() { 707fn main() {
742 let a = "haha"; 708 let a = "haha";
743 A { 709 A { a<|>: a }
744 a: a
745 }
746} 710}
747 "#, 711"#,
748 r#" 712 r#"
749struct A { 713struct A { a: &'static str }
750 a: &'static str
751}
752
753fn main() { 714fn main() {
754 let a = "haha"; 715 let a = "haha";
755 A { 716 A { a }
756 a
757 }
758} 717}
759 "#, 718"#,
760 check_struct_shorthand_initialization,
761 ); 719 );
762 720
763 check_apply( 721 check_fix(
764 r#" 722 r#"
765struct A { 723struct A { a: &'static str, b: &'static str }
766 a: &'static str,
767 b: &'static str
768}
769
770fn main() { 724fn main() {
771 let a = "haha"; 725 let a = "haha";
772 let b = "bb"; 726 let b = "bb";
773 A { 727 A { a<|>: a, b }
774 a: a,
775 b
776 }
777} 728}
778 "#, 729"#,
779 r#" 730 r#"
780struct A { 731struct A { a: &'static str, b: &'static str }
781 a: &'static str,
782 b: &'static str
783}
784
785fn main() { 732fn main() {
786 let a = "haha"; 733 let a = "haha";
787 let b = "bb"; 734 let b = "bb";
788 A { 735 A { a, b }
789 a,
790 b
791 }
792} 736}
793 "#, 737"#,
794 check_struct_shorthand_initialization,
795 ); 738 );
796 } 739 }
740
741 #[test]
742 fn test_add_field_from_usage() {
743 check_fix(
744 r"
745fn main() {
746 Foo { bar: 3, baz<|>: false};
747}
748struct Foo {
749 bar: i32
750}
751",
752 r"
753fn main() {
754 Foo { bar: 3, baz: false};
755}
756struct Foo {
757 bar: i32,
758 baz: bool
759}
760",
761 )
762 }
763
764 #[test]
765 fn test_add_field_in_other_file_from_usage() {
766 check_apply_diagnostic_fix_in_other_file(
767 r"
768 //- /main.rs
769 mod foo;
770
771 fn main() {
772 <|>foo::Foo { bar: 3, baz: false};
773 }
774 //- /foo.rs
775 struct Foo {
776 bar: i32
777 }
778 ",
779 r"
780 struct Foo {
781 bar: i32,
782 pub(crate) baz: bool
783 }
784 ",
785 )
786 }
797} 787}
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 827c094e7..70d2a2dd1 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -6,13 +6,10 @@ mod navigation_target;
6mod structure; 6mod structure;
7mod short_label; 7mod short_label;
8 8
9use std::fmt::Display;
10
11use ra_syntax::{ 9use ra_syntax::{
12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 10 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
13 SyntaxKind::{ATTR, COMMENT}, 11 SyntaxKind::{ATTR, COMMENT},
14}; 12};
15use stdx::format_to;
16 13
17pub use function_signature::FunctionSignature; 14pub use function_signature::FunctionSignature;
18pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
@@ -69,29 +66,3 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
69 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; 66 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
70 format!("{}macro_rules! {}", vis, name) 67 format!("{}macro_rules! {}", vis, name)
71} 68}
72
73pub(crate) fn rust_code_markup(code: &impl Display) -> String {
74 rust_code_markup_with_doc(code, None, None)
75}
76
77pub(crate) fn rust_code_markup_with_doc(
78 code: &impl Display,
79 doc: Option<&str>,
80 mod_path: Option<&str>,
81) -> String {
82 let mut buf = String::new();
83
84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() {
86 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
87 }
88 }
89 format_to!(buf, "```rust\n{}\n```", code);
90
91 if let Some(doc) = doc {
92 format_to!(buf, "\n___");
93 format_to!(buf, "\n\n{}", doc);
94 }
95
96 buf
97}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 9572debd8..1d39544d3 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -10,7 +10,7 @@ use std::{
10use hir::{Docs, Documentation, HasSource, HirDisplay}; 10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase; 11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::SepBy; 13use stdx::{split_delim, SepBy};
14 14
15use crate::display::{generic_parameters, where_predicates}; 15use crate::display::{generic_parameters, where_predicates};
16 16
@@ -61,15 +61,11 @@ pub struct FunctionQualifier {
61} 61}
62 62
63impl FunctionSignature { 63impl FunctionSignature {
64 pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
65 self.doc = doc;
66 self
67 }
68
69 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self { 64 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
70 let doc = function.docs(db);
71 let ast_node = function.source(db).value; 65 let ast_node = function.source(db).value;
72 FunctionSignature::from(&ast_node).with_doc_opt(doc) 66 let mut res = FunctionSignature::from(&ast_node);
67 res.doc = function.docs(db);
68 res
73 } 69 }
74 70
75 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> { 71 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
@@ -93,24 +89,21 @@ impl FunctionSignature {
93 params.push(raw_param); 89 params.push(raw_param);
94 } 90 }
95 91
96 Some( 92 Some(FunctionSignature {
97 FunctionSignature { 93 kind: CallableKind::StructConstructor,
98 kind: CallableKind::StructConstructor, 94 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
99 visibility: node.visibility().map(|n| n.syntax().text().to_string()), 95 // Do we need `const`?
100 // Do we need `const`? 96 qualifier: Default::default(),
101 qualifier: Default::default(), 97 name: node.name().map(|n| n.text().to_string()),
102 name: node.name().map(|n| n.text().to_string()), 98 ret_type: node.name().map(|n| n.text().to_string()),
103 ret_type: node.name().map(|n| n.text().to_string()), 99 parameters: params,
104 parameters: params, 100 parameter_names: vec![],
105 parameter_names: vec![], 101 parameter_types,
106 parameter_types, 102 generic_parameters: generic_parameters(&node),
107 generic_parameters: generic_parameters(&node), 103 where_predicates: where_predicates(&node),
108 where_predicates: where_predicates(&node), 104 doc: st.docs(db),
109 doc: None, 105 has_self_param: false,
110 has_self_param: false, 106 })
111 }
112 .with_doc_opt(st.docs(db)),
113 )
114 } 107 }
115 108
116 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> { 109 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
@@ -140,24 +133,21 @@ impl FunctionSignature {
140 params.push(format!("{}: {}", name, ty.display(db))); 133 params.push(format!("{}: {}", name, ty.display(db)));
141 } 134 }
142 135
143 Some( 136 Some(FunctionSignature {
144 FunctionSignature { 137 kind: CallableKind::VariantConstructor,
145 kind: CallableKind::VariantConstructor, 138 visibility: None,
146 visibility: None, 139 // Do we need `const`?
147 // Do we need `const`? 140 qualifier: Default::default(),
148 qualifier: Default::default(), 141 name: Some(name),
149 name: Some(name), 142 ret_type: None,
150 ret_type: None, 143 parameters: params,
151 parameters: params, 144 parameter_names: vec![],
152 parameter_names: vec![], 145 parameter_types,
153 parameter_types, 146 generic_parameters: vec![],
154 generic_parameters: vec![], 147 where_predicates: vec![],
155 where_predicates: vec![], 148 doc: variant.docs(db),
156 doc: None, 149 has_self_param: false,
157 has_self_param: false, 150 })
158 }
159 .with_doc_opt(variant.docs(db)),
160 )
161 } 151 }
162 152
163 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> { 153 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
@@ -165,23 +155,20 @@ impl FunctionSignature {
165 155
166 let params = vec![]; 156 let params = vec![];
167 157
168 Some( 158 Some(FunctionSignature {
169 FunctionSignature { 159 kind: CallableKind::Macro,
170 kind: CallableKind::Macro, 160 visibility: None,
171 visibility: None, 161 qualifier: Default::default(),
172 qualifier: Default::default(), 162 name: node.name().map(|n| n.text().to_string()),
173 name: node.name().map(|n| n.text().to_string()), 163 ret_type: None,
174 ret_type: None, 164 parameters: params,
175 parameters: params, 165 parameter_names: vec![],
176 parameter_names: vec![], 166 parameter_types: vec![],
177 parameter_types: vec![], 167 generic_parameters: vec![],
178 generic_parameters: vec![], 168 where_predicates: vec![],
179 where_predicates: vec![], 169 doc: macro_def.docs(db),
180 doc: None, 170 has_self_param: false,
181 has_self_param: false, 171 })
182 }
183 .with_doc_opt(macro_def.docs(db)),
184 )
185 } 172 }
186} 173}
187 174
@@ -207,7 +194,16 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
207 res.push(raw_param); 194 res.push(raw_param);
208 } 195 }
209 196
210 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 197 // macro-generated functions are missing whitespace
198 fn fmt_param(param: ast::Param) -> String {
199 let text = param.syntax().text().to_string();
200 match split_delim(&text, ':') {
201 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
202 _ => text,
203 }
204 }
205
206 res.extend(param_list.params().map(fmt_param));
211 res_types.extend(param_list.params().map(|param| { 207 res_types.extend(param_list.params().map(|param| {
212 let param_text = param.syntax().text().to_string(); 208 let param_text = param.syntax().text().to_string();
213 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { 209 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5da28edd2..8bf2428ed 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 TextRange, 11 TextRange,
12}; 12};
13 13
14use crate::{FileRange, FileSymbol}; 14use crate::FileSymbol;
15 15
16use super::short_label::ShortLabel; 16use super::short_label::ShortLabel;
17 17
@@ -47,6 +47,19 @@ impl NavigationTarget {
47 pub fn range(&self) -> TextRange { 47 pub fn range(&self) -> TextRange {
48 self.focus_range.unwrap_or(self.full_range) 48 self.focus_range.unwrap_or(self.full_range)
49 } 49 }
50 /// A "most interesting" range withing the `full_range`.
51 ///
52 /// Typically, `full_range` is the whole syntax node,
53 /// including doc comments, and `focus_range` is the range of the identifier.
54 pub fn focus_range(&self) -> Option<TextRange> {
55 self.focus_range
56 }
57 pub fn full_range(&self) -> TextRange {
58 self.full_range
59 }
60 pub fn file_id(&self) -> FileId {
61 self.file_id
62 }
50 63
51 pub fn name(&self) -> &SmolStr { 64 pub fn name(&self) -> &SmolStr {
52 &self.name 65 &self.name
@@ -60,18 +73,6 @@ impl NavigationTarget {
60 self.kind 73 self.kind
61 } 74 }
62 75
63 pub fn file_id(&self) -> FileId {
64 self.file_id
65 }
66
67 pub fn file_range(&self) -> FileRange {
68 FileRange { file_id: self.file_id, range: self.full_range }
69 }
70
71 pub fn full_range(&self) -> TextRange {
72 self.full_range
73 }
74
75 pub fn docs(&self) -> Option<&str> { 76 pub fn docs(&self) -> Option<&str> {
76 self.docs.as_deref() 77 self.docs.as_deref()
77 } 78 }
@@ -80,27 +81,20 @@ impl NavigationTarget {
80 self.description.as_deref() 81 self.description.as_deref()
81 } 82 }
82 83
83 /// A "most interesting" range withing the `full_range`.
84 ///
85 /// Typically, `full_range` is the whole syntax node,
86 /// including doc comments, and `focus_range` is the range of the identifier.
87 pub fn focus_range(&self) -> Option<TextRange> {
88 self.focus_range
89 }
90
91 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 84 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 85 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 86 if let Some(src) = module.declaration_source(db) {
94 let frange = original_range(db, src.as_ref().map(|it| it.syntax())); 87 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
95 return NavigationTarget::from_syntax( 88 let mut res = NavigationTarget::from_syntax(
96 frange.file_id, 89 frange.file_id,
97 name, 90 name,
98 None, 91 None,
99 frange.range, 92 frange.range,
100 src.value.syntax().kind(), 93 src.value.syntax().kind(),
101 src.value.doc_comment_text(),
102 src.value.short_label(),
103 ); 94 );
95 res.docs = src.value.doc_comment_text();
96 res.description = src.value.short_label();
97 return res;
104 } 98 }
105 module.to_nav(db) 99 module.to_nav(db)
106 } 100 }
@@ -130,14 +124,12 @@ impl NavigationTarget {
130 } 124 }
131 125
132 /// Allows `NavigationTarget` to be created from a `NameOwner` 126 /// Allows `NavigationTarget` to be created from a `NameOwner`
133 fn from_named( 127 pub(crate) fn from_named(
134 db: &RootDatabase, 128 db: &RootDatabase,
135 node: InFile<&dyn ast::NameOwner>, 129 node: InFile<&dyn ast::NameOwner>,
136 docs: Option<String>,
137 description: Option<String>,
138 ) -> NavigationTarget { 130 ) -> NavigationTarget {
139 //FIXME: use `_` instead of empty string 131 let name =
140 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); 132 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
141 let focus_range = 133 let focus_range =
142 node.value.name().map(|it| original_range(db, node.with_value(it.syntax())).range); 134 node.value.name().map(|it| original_range(db, node.with_value(it.syntax())).range);
143 let frange = original_range(db, node.map(|it| it.syntax())); 135 let frange = original_range(db, node.map(|it| it.syntax()));
@@ -148,8 +140,25 @@ impl NavigationTarget {
148 focus_range, 140 focus_range,
149 frange.range, 141 frange.range,
150 node.value.syntax().kind(), 142 node.value.syntax().kind(),
151 docs, 143 )
152 description, 144 }
145
146 /// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner`
147 pub(crate) fn from_doc_commented(
148 db: &RootDatabase,
149 named: InFile<&dyn ast::NameOwner>,
150 node: InFile<&dyn ast::DocCommentsOwner>,
151 ) -> NavigationTarget {
152 let name =
153 named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
154 let frange = original_range(db, node.map(|it| it.syntax()));
155
156 NavigationTarget::from_syntax(
157 frange.file_id,
158 name,
159 None,
160 frange.range,
161 node.value.syntax().kind(),
153 ) 162 )
154 } 163 }
155 164
@@ -159,8 +168,6 @@ impl NavigationTarget {
159 focus_range: Option<TextRange>, 168 focus_range: Option<TextRange>,
160 full_range: TextRange, 169 full_range: TextRange,
161 kind: SyntaxKind, 170 kind: SyntaxKind,
162 docs: Option<String>,
163 description: Option<String>,
164 ) -> NavigationTarget { 171 ) -> NavigationTarget {
165 NavigationTarget { 172 NavigationTarget {
166 file_id, 173 file_id,
@@ -169,8 +176,8 @@ impl NavigationTarget {
169 full_range, 176 full_range,
170 focus_range, 177 focus_range,
171 container_name: None, 178 container_name: None,
172 description, 179 description: None,
173 docs, 180 docs: None,
174 } 181 }
175 } 182 }
176} 183}
@@ -238,12 +245,11 @@ where
238{ 245{
239 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 246 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
240 let src = self.source(db); 247 let src = self.source(db);
241 NavigationTarget::from_named( 248 let mut res =
242 db, 249 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
243 src.as_ref().map(|it| it as &dyn ast::NameOwner), 250 res.docs = src.value.doc_comment_text();
244 src.value.doc_comment_text(), 251 res.description = src.value.short_label();
245 src.value.short_label(), 252 res
246 )
247 } 253 }
248} 254}
249 255
@@ -258,35 +264,31 @@ impl ToNav for hir::Module {
258 } 264 }
259 }; 265 };
260 let frange = original_range(db, src.with_value(syntax)); 266 let frange = original_range(db, src.with_value(syntax));
261 NavigationTarget::from_syntax( 267 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
262 frange.file_id,
263 name,
264 focus,
265 frange.range,
266 syntax.kind(),
267 None,
268 None,
269 )
270 } 268 }
271} 269}
272 270
273impl ToNav for hir::ImplDef { 271impl ToNav for hir::ImplDef {
274 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 272 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
275 let src = self.source(db); 273 let src = self.source(db);
276 let frange = if let Some(item) = self.is_builtin_derive(db) { 274 let derive_attr = self.is_builtin_derive(db);
275 let frange = if let Some(item) = &derive_attr {
277 original_range(db, item.syntax()) 276 original_range(db, item.syntax())
278 } else { 277 } else {
279 original_range(db, src.as_ref().map(|it| it.syntax())) 278 original_range(db, src.as_ref().map(|it| it.syntax()))
280 }; 279 };
280 let focus_range = if derive_attr.is_some() {
281 None
282 } else {
283 src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
284 };
281 285
282 NavigationTarget::from_syntax( 286 NavigationTarget::from_syntax(
283 frange.file_id, 287 frange.file_id,
284 "impl".into(), 288 "impl".into(),
285 None, 289 focus_range,
286 frange.range, 290 frange.range,
287 src.value.syntax().kind(), 291 src.value.syntax().kind(),
288 None,
289 None,
290 ) 292 )
291 } 293 }
292} 294}
@@ -296,12 +298,12 @@ impl ToNav for hir::Field {
296 let src = self.source(db); 298 let src = self.source(db);
297 299
298 match &src.value { 300 match &src.value {
299 FieldSource::Named(it) => NavigationTarget::from_named( 301 FieldSource::Named(it) => {
300 db, 302 let mut res = NavigationTarget::from_named(db, src.with_value(it));
301 src.with_value(it), 303 res.docs = it.doc_comment_text();
302 it.doc_comment_text(), 304 res.description = it.short_label();
303 it.short_label(), 305 res
304 ), 306 }
305 FieldSource::Pos(it) => { 307 FieldSource::Pos(it) => {
306 let frange = original_range(db, src.with_value(it.syntax())); 308 let frange = original_range(db, src.with_value(it.syntax()));
307 NavigationTarget::from_syntax( 309 NavigationTarget::from_syntax(
@@ -310,8 +312,6 @@ impl ToNav for hir::Field {
310 None, 312 None,
311 frange.range, 313 frange.range,
312 it.syntax().kind(), 314 it.syntax().kind(),
313 None,
314 None,
315 ) 315 )
316 } 316 }
317 } 317 }
@@ -322,12 +322,10 @@ impl ToNav for hir::MacroDef {
322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
323 let src = self.source(db); 323 let src = self.source(db);
324 log::debug!("nav target {:#?}", src.value.syntax()); 324 log::debug!("nav target {:#?}", src.value.syntax());
325 NavigationTarget::from_named( 325 let mut res =
326 db, 326 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
327 src.as_ref().map(|it| it as &dyn ast::NameOwner), 327 res.docs = src.value.doc_comment_text();
328 src.value.doc_comment_text(), 328 res
329 None,
330 )
331 } 329 }
332} 330}
333 331
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index aad5a8e4d..1f6a3febf 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -127,6 +127,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
127 decl_with_detail(it, Some(detail)) 127 decl_with_detail(it, Some(detail))
128 }, 128 },
129 ast::StructDef(it) => decl(it), 129 ast::StructDef(it) => decl(it),
130 ast::UnionDef(it) => decl(it),
130 ast::EnumDef(it) => decl(it), 131 ast::EnumDef(it) => decl(it),
131 ast::EnumVariant(it) => decl(it), 132 ast::EnumVariant(it) => decl(it),
132 ast::TraitDef(it) => decl(it), 133 ast::TraitDef(it) => decl(it),
@@ -173,12 +174,19 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
173 174
174#[cfg(test)] 175#[cfg(test)]
175mod tests { 176mod tests {
177 use expect::{expect, Expect};
178
176 use super::*; 179 use super::*;
177 use insta::assert_debug_snapshot; 180
181 fn check(ra_fixture: &str, expect: Expect) {
182 let file = SourceFile::parse(ra_fixture).ok().unwrap();
183 let structure = file_structure(&file);
184 expect.assert_debug_eq(&structure)
185 }
178 186
179 #[test] 187 #[test]
180 fn test_file_structure() { 188 fn test_file_structure() {
181 let file = SourceFile::parse( 189 check(
182 r#" 190 r#"
183struct Foo { 191struct Foo {
184 x: i32 192 x: i32
@@ -223,216 +231,211 @@ fn obsolete() {}
223#[deprecated(note = "for awhile")] 231#[deprecated(note = "for awhile")]
224fn very_obsolete() {} 232fn very_obsolete() {}
225"#, 233"#,
226 ) 234 expect![[r#"
227 .ok() 235 [
228 .unwrap(); 236 StructureNode {
229 let structure = file_structure(&file); 237 parent: None,
230 assert_debug_snapshot!(structure, 238 label: "Foo",
231 @r###" 239 navigation_range: 8..11,
232 [ 240 node_range: 1..26,
233 StructureNode { 241 kind: STRUCT_DEF,
234 parent: None, 242 detail: None,
235 label: "Foo", 243 deprecated: false,
236 navigation_range: 8..11, 244 },
237 node_range: 1..26, 245 StructureNode {
238 kind: STRUCT_DEF, 246 parent: Some(
239 detail: None, 247 0,
240 deprecated: false, 248 ),
241 }, 249 label: "x",
242 StructureNode { 250 navigation_range: 18..19,
243 parent: Some( 251 node_range: 18..24,
244 0, 252 kind: RECORD_FIELD_DEF,
245 ), 253 detail: Some(
246 label: "x", 254 "i32",
247 navigation_range: 18..19, 255 ),
248 node_range: 18..24, 256 deprecated: false,
249 kind: RECORD_FIELD_DEF, 257 },
250 detail: Some( 258 StructureNode {
251 "i32", 259 parent: None,
252 ), 260 label: "m",
253 deprecated: false, 261 navigation_range: 32..33,
254 }, 262 node_range: 28..158,
255 StructureNode { 263 kind: MODULE,
256 parent: None, 264 detail: None,
257 label: "m", 265 deprecated: false,
258 navigation_range: 32..33, 266 },
259 node_range: 28..158, 267 StructureNode {
260 kind: MODULE, 268 parent: Some(
261 detail: None, 269 2,
262 deprecated: false, 270 ),
263 }, 271 label: "bar1",
264 StructureNode { 272 navigation_range: 43..47,
265 parent: Some( 273 node_range: 40..52,
266 2, 274 kind: FN_DEF,
267 ), 275 detail: Some(
268 label: "bar1", 276 "fn()",
269 navigation_range: 43..47, 277 ),
270 node_range: 40..52, 278 deprecated: false,
271 kind: FN_DEF, 279 },
272 detail: Some( 280 StructureNode {
273 "fn()", 281 parent: Some(
274 ), 282 2,
275 deprecated: false, 283 ),
276 }, 284 label: "bar2",
277 StructureNode { 285 navigation_range: 60..64,
278 parent: Some( 286 node_range: 57..81,
279 2, 287 kind: FN_DEF,
280 ), 288 detail: Some(
281 label: "bar2", 289 "fn<T>(t: T) -> T",
282 navigation_range: 60..64, 290 ),
283 node_range: 57..81, 291 deprecated: false,
284 kind: FN_DEF, 292 },
285 detail: Some( 293 StructureNode {
286 "fn<T>(t: T) -> T", 294 parent: Some(
287 ), 295 2,
288 deprecated: false, 296 ),
289 }, 297 label: "bar3",
290 StructureNode { 298 navigation_range: 89..93,
291 parent: Some( 299 node_range: 86..156,
292 2, 300 kind: FN_DEF,
293 ), 301 detail: Some(
294 label: "bar3", 302 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
295 navigation_range: 89..93, 303 ),
296 node_range: 86..156, 304 deprecated: false,
297 kind: FN_DEF, 305 },
298 detail: Some( 306 StructureNode {
299 "fn<A, B>(a: A, b: B) -> Vec< u32 >", 307 parent: None,
300 ), 308 label: "E",
301 deprecated: false, 309 navigation_range: 165..166,
302 }, 310 node_range: 160..180,
303 StructureNode { 311 kind: ENUM_DEF,
304 parent: None, 312 detail: None,
305 label: "E", 313 deprecated: false,
306 navigation_range: 165..166, 314 },
307 node_range: 160..180, 315 StructureNode {
308 kind: ENUM_DEF, 316 parent: Some(
309 detail: None, 317 6,
310 deprecated: false, 318 ),
311 }, 319 label: "X",
312 StructureNode { 320 navigation_range: 169..170,
313 parent: Some( 321 node_range: 169..170,
314 6, 322 kind: ENUM_VARIANT,
315 ), 323 detail: None,
316 label: "X", 324 deprecated: false,
317 navigation_range: 169..170, 325 },
318 node_range: 169..170, 326 StructureNode {
319 kind: ENUM_VARIANT, 327 parent: Some(
320 detail: None, 328 6,
321 deprecated: false, 329 ),
322 }, 330 label: "Y",
323 StructureNode { 331 navigation_range: 172..173,
324 parent: Some( 332 node_range: 172..178,
325 6, 333 kind: ENUM_VARIANT,
326 ), 334 detail: None,
327 label: "Y", 335 deprecated: false,
328 navigation_range: 172..173, 336 },
329 node_range: 172..178, 337 StructureNode {
330 kind: ENUM_VARIANT, 338 parent: None,
331 detail: None, 339 label: "T",
332 deprecated: false, 340 navigation_range: 186..187,
333 }, 341 node_range: 181..193,
334 StructureNode { 342 kind: TYPE_ALIAS_DEF,
335 parent: None, 343 detail: Some(
336 label: "T", 344 "()",
337 navigation_range: 186..187, 345 ),
338 node_range: 181..193, 346 deprecated: false,
339 kind: TYPE_ALIAS_DEF, 347 },
340 detail: Some( 348 StructureNode {
341 "()", 349 parent: None,
342 ), 350 label: "S",
343 deprecated: false, 351 navigation_range: 201..202,
344 }, 352 node_range: 194..213,
345 StructureNode { 353 kind: STATIC_DEF,
346 parent: None, 354 detail: Some(
347 label: "S", 355 "i32",
348 navigation_range: 201..202, 356 ),
349 node_range: 194..213, 357 deprecated: false,
350 kind: STATIC_DEF, 358 },
351 detail: Some( 359 StructureNode {
352 "i32", 360 parent: None,
353 ), 361 label: "C",
354 deprecated: false, 362 navigation_range: 220..221,
355 }, 363 node_range: 214..232,
356 StructureNode { 364 kind: CONST_DEF,
357 parent: None, 365 detail: Some(
358 label: "C", 366 "i32",
359 navigation_range: 220..221, 367 ),
360 node_range: 214..232, 368 deprecated: false,
361 kind: CONST_DEF, 369 },
362 detail: Some( 370 StructureNode {
363 "i32", 371 parent: None,
364 ), 372 label: "impl E",
365 deprecated: false, 373 navigation_range: 239..240,
366 }, 374 node_range: 234..243,
367 StructureNode { 375 kind: IMPL_DEF,
368 parent: None, 376 detail: None,
369 label: "impl E", 377 deprecated: false,
370 navigation_range: 239..240, 378 },
371 node_range: 234..243, 379 StructureNode {
372 kind: IMPL_DEF, 380 parent: None,
373 detail: None, 381 label: "impl fmt::Debug for E",
374 deprecated: false, 382 navigation_range: 265..266,
375 }, 383 node_range: 245..269,
376 StructureNode { 384 kind: IMPL_DEF,
377 parent: None, 385 detail: None,
378 label: "impl fmt::Debug for E", 386 deprecated: false,
379 navigation_range: 265..266, 387 },
380 node_range: 245..269, 388 StructureNode {
381 kind: IMPL_DEF, 389 parent: None,
382 detail: None, 390 label: "mc",
383 deprecated: false, 391 navigation_range: 284..286,
384 }, 392 node_range: 271..303,
385 StructureNode { 393 kind: MACRO_CALL,
386 parent: None, 394 detail: None,
387 label: "mc", 395 deprecated: false,
388 navigation_range: 284..286, 396 },
389 node_range: 271..303, 397 StructureNode {
390 kind: MACRO_CALL, 398 parent: None,
391 detail: None, 399 label: "mcexp",
392 deprecated: false, 400 navigation_range: 334..339,
393 }, 401 node_range: 305..356,
394 StructureNode { 402 kind: MACRO_CALL,
395 parent: None, 403 detail: None,
396 label: "mcexp", 404 deprecated: false,
397 navigation_range: 334..339, 405 },
398 node_range: 305..356, 406 StructureNode {
399 kind: MACRO_CALL, 407 parent: None,
400 detail: None, 408 label: "mcexp",
401 deprecated: false, 409 navigation_range: 387..392,
402 }, 410 node_range: 358..409,
403 StructureNode { 411 kind: MACRO_CALL,
404 parent: None, 412 detail: None,
405 label: "mcexp", 413 deprecated: false,
406 navigation_range: 387..392, 414 },
407 node_range: 358..409, 415 StructureNode {
408 kind: MACRO_CALL, 416 parent: None,
409 detail: None, 417 label: "obsolete",
410 deprecated: false, 418 navigation_range: 428..436,
411 }, 419 node_range: 411..441,
412 StructureNode { 420 kind: FN_DEF,
413 parent: None, 421 detail: Some(
414 label: "obsolete", 422 "fn()",
415 navigation_range: 428..436, 423 ),
416 node_range: 411..441, 424 deprecated: true,
417 kind: FN_DEF, 425 },
418 detail: Some( 426 StructureNode {
419 "fn()", 427 parent: None,
420 ), 428 label: "very_obsolete",
421 deprecated: true, 429 navigation_range: 481..494,
422 }, 430 node_range: 443..499,
423 StructureNode { 431 kind: FN_DEF,
424 parent: None, 432 detail: Some(
425 label: "very_obsolete", 433 "fn()",
426 navigation_range: 481..494, 434 ),
427 node_range: 443..499, 435 deprecated: true,
428 kind: FN_DEF, 436 },
429 detail: Some( 437 ]
430 "fn()", 438 "#]],
431 ), 439 );
432 deprecated: true,
433 },
434 ]
435 "###
436 );
437 } 440 }
438} 441}
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index 54a47aac0..043515f54 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -2,7 +2,9 @@ use hir::Semantics;
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo::{find_node_at_offset, SyntaxRewriter}, 4 algo::{find_node_at_offset, SyntaxRewriter},
5 ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, 5 ast, AstNode, NodeOrToken, SyntaxKind,
6 SyntaxKind::*,
7 SyntaxNode, WalkEvent, T,
6}; 8};
7 9
8use crate::FilePosition; 10use crate::FilePosition;
@@ -65,8 +67,6 @@ fn expand_macro_recur(
65// FIXME: It would also be cool to share logic here and in the mbe tests, 67// FIXME: It would also be cool to share logic here and in the mbe tests,
66// which are pretty unreadable at the moment. 68// which are pretty unreadable at the moment.
67fn insert_whitespaces(syn: SyntaxNode) -> String { 69fn insert_whitespaces(syn: SyntaxNode) -> String {
68 use SyntaxKind::*;
69
70 let mut res = String::new(); 70 let mut res = String::new();
71 let mut token_iter = syn 71 let mut token_iter = syn
72 .preorder_with_tokens() 72 .preorder_with_tokens()
@@ -120,175 +120,164 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
120 120
121#[cfg(test)] 121#[cfg(test)]
122mod tests { 122mod tests {
123 use insta::assert_snapshot; 123 use expect::{expect, Expect};
124 124
125 use crate::mock_analysis::analysis_and_position; 125 use crate::mock_analysis::analysis_and_position;
126 126
127 use super::*; 127 fn check(ra_fixture: &str, expect: Expect) {
128 128 let (analysis, pos) = analysis_and_position(ra_fixture);
129 fn check_expand_macro(fixture: &str) -> ExpandedMacro { 129 let expansion = analysis.expand_macro(pos).unwrap().unwrap();
130 let (analysis, pos) = analysis_and_position(fixture); 130 let actual = format!("{}\n{}", expansion.name, expansion.expansion);
131 analysis.expand_macro(pos).unwrap().unwrap() 131 expect.assert_eq(&actual);
132 } 132 }
133 133
134 #[test] 134 #[test]
135 fn macro_expand_recursive_expansion() { 135 fn macro_expand_recursive_expansion() {
136 let res = check_expand_macro( 136 check(
137 r#" 137 r#"
138 //- /lib.rs 138macro_rules! bar {
139 macro_rules! bar { 139 () => { fn b() {} }
140 () => { fn b() {} } 140}
141 } 141macro_rules! foo {
142 macro_rules! foo { 142 () => { bar!(); }
143 () => { bar!(); } 143}
144 } 144macro_rules! baz {
145 macro_rules! baz { 145 () => { foo!(); }
146 () => { foo!(); } 146}
147 } 147f<|>oo!();
148 f<|>oo!(); 148"#,
149 "#, 149 expect![[r#"
150 foo
151 fn b(){}
152 "#]],
150 ); 153 );
151
152 assert_eq!(res.name, "foo");
153 assert_snapshot!(res.expansion, @r###"
154fn b(){}
155"###);
156 } 154 }
157 155
158 #[test] 156 #[test]
159 fn macro_expand_multiple_lines() { 157 fn macro_expand_multiple_lines() {
160 let res = check_expand_macro( 158 check(
161 r#" 159 r#"
162 //- /lib.rs 160macro_rules! foo {
163 macro_rules! foo { 161 () => {
164 () => { 162 fn some_thing() -> u32 {
165 fn some_thing() -> u32 { 163 let a = 0;
166 let a = 0; 164 a + 10
167 a + 10
168 }
169 }
170 } 165 }
171 f<|>oo!(); 166 }
167}
168f<|>oo!();
172 "#, 169 "#,
170 expect![[r#"
171 foo
172 fn some_thing() -> u32 {
173 let a = 0;
174 a+10
175 }"#]],
173 ); 176 );
174
175 assert_eq!(res.name, "foo");
176 assert_snapshot!(res.expansion, @r###"
177fn some_thing() -> u32 {
178 let a = 0;
179 a+10
180}
181"###);
182 } 177 }
183 178
184 #[test] 179 #[test]
185 fn macro_expand_match_ast() { 180 fn macro_expand_match_ast() {
186 let res = check_expand_macro( 181 check(
187 r#" 182 r#"
188 //- /lib.rs 183macro_rules! match_ast {
189 macro_rules! match_ast { 184 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
190 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 185 (match ($node:expr) {
186 $( ast::$ast:ident($it:ident) => $res:block, )*
187 _ => $catch_all:expr $(,)?
188 }) => {{
189 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
190 { $catch_all }
191 }};
192}
191 193
192 (match ($node:expr) { 194fn main() {
193 $( ast::$ast:ident($it:ident) => $res:block, )* 195 mat<|>ch_ast! {
194 _ => $catch_all:expr $(,)? 196 match container {
195 }) => {{ 197 ast::TraitDef(it) => {},
196 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* 198 ast::ImplDef(it) => {},
197 { $catch_all } 199 _ => { continue },
198 }};
199 } 200 }
200
201 fn main() {
202 mat<|>ch_ast! {
203 match container {
204 ast::TraitDef(it) => {},
205 ast::ImplDef(it) => {},
206 _ => { continue },
207 }
208 }
209 }
210 "#,
211 );
212
213 assert_eq!(res.name, "match_ast");
214 assert_snapshot!(res.expansion, @r###"
215{
216 if let Some(it) = ast::TraitDef::cast(container.clone()){}
217 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
218 else {
219 {
220 continue
221 } 201 }
222 }
223} 202}
224"###); 203"#,
204 expect![[r#"
205 match_ast
206 {
207 if let Some(it) = ast::TraitDef::cast(container.clone()){}
208 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
209 else {
210 {
211 continue
212 }
213 }
214 }"#]],
215 );
225 } 216 }
226 217
227 #[test] 218 #[test]
228 fn macro_expand_match_ast_inside_let_statement() { 219 fn macro_expand_match_ast_inside_let_statement() {
229 let res = check_expand_macro( 220 check(
230 r#" 221 r#"
231 //- /lib.rs 222macro_rules! match_ast {
232 macro_rules! match_ast { 223 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
233 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 224 (match ($node:expr) {}) => {{}};
234 (match ($node:expr) {}) => {{}}; 225}
235 }
236 226
237 fn main() { 227fn main() {
238 let p = f(|it| { 228 let p = f(|it| {
239 let res = mat<|>ch_ast! { match c {}}; 229 let res = mat<|>ch_ast! { match c {}};
240 Some(res) 230 Some(res)
241 })?; 231 })?;
242 } 232}
243 "#, 233"#,
234 expect![[r#"
235 match_ast
236 {}
237 "#]],
244 ); 238 );
245
246 assert_eq!(res.name, "match_ast");
247 assert_snapshot!(res.expansion, @r###"{}"###);
248 } 239 }
249 240
250 #[test] 241 #[test]
251 fn macro_expand_inner_macro_fail_to_expand() { 242 fn macro_expand_inner_macro_fail_to_expand() {
252 let res = check_expand_macro( 243 check(
253 r#" 244 r#"
254 //- /lib.rs 245macro_rules! bar {
255 macro_rules! bar { 246 (BAD) => {};
256 (BAD) => {}; 247}
257 } 248macro_rules! foo {
258 macro_rules! foo { 249 () => {bar!()};
259 () => {bar!()}; 250}
260 }
261 251
262 fn main() { 252fn main() {
263 let res = fo<|>o!(); 253 let res = fo<|>o!();
264 } 254}
265 "#, 255"#,
256 expect![[r#"
257 foo
258 "#]],
266 ); 259 );
267
268 assert_eq!(res.name, "foo");
269 assert_snapshot!(res.expansion, @r###""###);
270 } 260 }
271 261
272 #[test] 262 #[test]
273 fn macro_expand_with_dollar_crate() { 263 fn macro_expand_with_dollar_crate() {
274 let res = check_expand_macro( 264 check(
275 r#" 265 r#"
276 //- /lib.rs 266#[macro_export]
277 #[macro_export] 267macro_rules! bar {
278 macro_rules! bar { 268 () => {0};
279 () => {0}; 269}
280 } 270macro_rules! foo {
281 macro_rules! foo { 271 () => {$crate::bar!()};
282 () => {$crate::bar!()}; 272}
283 }
284 273
285 fn main() { 274fn main() {
286 let res = fo<|>o!(); 275 let res = fo<|>o!();
287 } 276}
288 "#, 277"#,
278 expect![[r#"
279 foo
280 0 "#]],
289 ); 281 );
290
291 assert_eq!(res.name, "foo");
292 assert_snapshot!(res.expansion, @r###"0"###);
293 } 282 }
294} 283}
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index a4bc93cdb..8a6b3ea99 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -315,17 +315,15 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment {
315 315
316#[cfg(test)] 316#[cfg(test)]
317mod tests { 317mod tests {
318 use test_utils::extract_offset; 318 use crate::mock_analysis::analysis_and_position;
319
320 use crate::mock_analysis::single_file;
321 319
322 use super::*; 320 use super::*;
323 321
324 fn do_check(before: &str, afters: &[&str]) { 322 fn do_check(before: &str, afters: &[&str]) {
325 let (cursor, before) = extract_offset(before); 323 let (analysis, position) = analysis_and_position(&before);
326 let (analysis, file_id) = single_file(&before); 324 let before = analysis.file_text(position.file_id).unwrap();
327 let range = TextRange::empty(cursor); 325 let range = TextRange::empty(position.offset);
328 let mut frange = FileRange { file_id, range }; 326 let mut frange = FileRange { file_id: position.file_id, range };
329 327
330 for &after in afters { 328 for &after in afters {
331 frange.range = analysis.extend_selection(frange).unwrap(); 329 frange.range = analysis.extend_selection(frange).unwrap();
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs
index 8657377de..e7ec9953f 100644
--- a/crates/ra_ide/src/folding_ranges.rs
+++ b/crates/ra_ide/src/folding_ranges.rs
@@ -15,6 +15,7 @@ pub enum FoldKind {
15 Imports, 15 Imports,
16 Mods, 16 Mods,
17 Block, 17 Block,
18 ArgList,
18} 19}
19 20
20#[derive(Debug)] 21#[derive(Debug)]
@@ -83,8 +84,10 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
83 match kind { 84 match kind {
84 COMMENT => Some(FoldKind::Comment), 85 COMMENT => Some(FoldKind::Comment),
85 USE_ITEM => Some(FoldKind::Imports), 86 USE_ITEM => Some(FoldKind::Imports),
87 ARG_LIST => Some(FoldKind::ArgList),
86 RECORD_FIELD_DEF_LIST 88 RECORD_FIELD_DEF_LIST
87 | RECORD_FIELD_PAT_LIST 89 | RECORD_FIELD_PAT_LIST
90 | RECORD_FIELD_LIST
88 | ITEM_LIST 91 | ITEM_LIST
89 | EXTERN_ITEM_LIST 92 | EXTERN_ITEM_LIST
90 | USE_TREE_LIST 93 | USE_TREE_LIST
@@ -196,89 +199,85 @@ fn contiguous_range_for_comment(
196 199
197#[cfg(test)] 200#[cfg(test)]
198mod tests { 201mod tests {
202 use test_utils::extract_tags;
203
199 use super::*; 204 use super::*;
200 use test_utils::extract_ranges;
201 205
202 fn do_check(text: &str, fold_kinds: &[FoldKind]) { 206 fn check(ra_fixture: &str) {
203 let (ranges, text) = extract_ranges(text, "fold"); 207 let (ranges, text) = extract_tags(ra_fixture, "fold");
208
204 let parse = SourceFile::parse(&text); 209 let parse = SourceFile::parse(&text);
205 let folds = folding_ranges(&parse.tree()); 210 let folds = folding_ranges(&parse.tree());
206
207 assert_eq!( 211 assert_eq!(
208 folds.len(), 212 folds.len(),
209 ranges.len(), 213 ranges.len(),
210 "The amount of folds is different than the expected amount" 214 "The amount of folds is different than the expected amount"
211 ); 215 );
212 assert_eq!( 216
213 folds.len(), 217 for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
214 fold_kinds.len(),
215 "The amount of fold kinds is different than the expected amount"
216 );
217 for ((fold, range), fold_kind) in
218 folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
219 {
220 assert_eq!(fold.range.start(), range.start()); 218 assert_eq!(fold.range.start(), range.start());
221 assert_eq!(fold.range.end(), range.end()); 219 assert_eq!(fold.range.end(), range.end());
222 assert_eq!(&fold.kind, fold_kind); 220
221 let kind = match fold.kind {
222 FoldKind::Comment => "comment",
223 FoldKind::Imports => "imports",
224 FoldKind::Mods => "mods",
225 FoldKind::Block => "block",
226 FoldKind::ArgList => "arglist",
227 };
228 assert_eq!(kind, &attr.unwrap());
223 } 229 }
224 } 230 }
225 231
226 #[test] 232 #[test]
227 fn test_fold_comments() { 233 fn test_fold_comments() {
228 let text = r#" 234 check(
229<fold>// Hello 235 r#"
236<fold comment>// Hello
230// this is a multiline 237// this is a multiline
231// comment 238// comment
232//</fold> 239//</fold>
233 240
234// But this is not 241// But this is not
235 242
236fn main() <fold>{ 243fn main() <fold block>{
237 <fold>// We should 244 <fold comment>// We should
238 // also 245 // also
239 // fold 246 // fold
240 // this one.</fold> 247 // this one.</fold>
241 <fold>//! But this one is different 248 <fold comment>//! But this one is different
242 //! because it has another flavor</fold> 249 //! because it has another flavor</fold>
243 <fold>/* As does this 250 <fold comment>/* As does this
244 multiline comment */</fold> 251 multiline comment */</fold>
245}</fold>"#; 252}</fold>"#,
246 253 );
247 let fold_kinds = &[
248 FoldKind::Comment,
249 FoldKind::Block,
250 FoldKind::Comment,
251 FoldKind::Comment,
252 FoldKind::Comment,
253 ];
254 do_check(text, fold_kinds);
255 } 254 }
256 255
257 #[test] 256 #[test]
258 fn test_fold_imports() { 257 fn test_fold_imports() {
259 let text = r#" 258 check(
260<fold>use std::<fold>{ 259 r#"
260<fold imports>use std::<fold block>{
261 str, 261 str,
262 vec, 262 vec,
263 io as iop 263 io as iop
264}</fold>;</fold> 264}</fold>;</fold>
265 265
266fn main() <fold>{ 266fn main() <fold block>{
267}</fold>"#; 267}</fold>"#,
268 268 );
269 let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
270 do_check(text, folds);
271 } 269 }
272 270
273 #[test] 271 #[test]
274 fn test_fold_mods() { 272 fn test_fold_mods() {
275 let text = r#" 273 check(
274 r#"
276 275
277pub mod foo; 276pub mod foo;
278<fold>mod after_pub; 277<fold mods>mod after_pub;
279mod after_pub_next;</fold> 278mod after_pub_next;</fold>
280 279
281<fold>mod before_pub; 280<fold mods>mod before_pub;
282mod before_pub_next;</fold> 281mod before_pub_next;</fold>
283pub mod bar; 282pub mod bar;
284 283
@@ -286,90 +285,105 @@ mod not_folding_single;
286pub mod foobar; 285pub mod foobar;
287pub not_folding_single_next; 286pub not_folding_single_next;
288 287
289<fold>#[cfg(test)] 288<fold mods>#[cfg(test)]
290mod with_attribute; 289mod with_attribute;
291mod with_attribute_next;</fold> 290mod with_attribute_next;</fold>
292 291
293fn main() <fold>{ 292fn main() <fold block>{
294}</fold>"#; 293}</fold>"#,
295 294 );
296 let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
297 do_check(text, folds);
298 } 295 }
299 296
300 #[test] 297 #[test]
301 fn test_fold_import_groups() { 298 fn test_fold_import_groups() {
302 let text = r#" 299 check(
303<fold>use std::str; 300 r#"
301<fold imports>use std::str;
304use std::vec; 302use std::vec;
305use std::io as iop;</fold> 303use std::io as iop;</fold>
306 304
307<fold>use std::mem; 305<fold imports>use std::mem;
308use std::f64;</fold> 306use std::f64;</fold>
309 307
310use std::collections::HashMap; 308use std::collections::HashMap;
311// Some random comment 309// Some random comment
312use std::collections::VecDeque; 310use std::collections::VecDeque;
313 311
314fn main() <fold>{ 312fn main() <fold block>{
315}</fold>"#; 313}</fold>"#,
316 314 );
317 let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
318 do_check(text, folds);
319 } 315 }
320 316
321 #[test] 317 #[test]
322 fn test_fold_import_and_groups() { 318 fn test_fold_import_and_groups() {
323 let text = r#" 319 check(
324<fold>use std::str; 320 r#"
321<fold imports>use std::str;
325use std::vec; 322use std::vec;
326use std::io as iop;</fold> 323use std::io as iop;</fold>
327 324
328<fold>use std::mem; 325<fold imports>use std::mem;
329use std::f64;</fold> 326use std::f64;</fold>
330 327
331<fold>use std::collections::<fold>{ 328<fold imports>use std::collections::<fold block>{
332 HashMap, 329 HashMap,
333 VecDeque, 330 VecDeque,
334}</fold>;</fold> 331}</fold>;</fold>
335// Some random comment 332// Some random comment
336 333
337fn main() <fold>{ 334fn main() <fold block>{
338}</fold>"#; 335}</fold>"#,
339 336 );
340 let folds = &[
341 FoldKind::Imports,
342 FoldKind::Imports,
343 FoldKind::Imports,
344 FoldKind::Block,
345 FoldKind::Block,
346 ];
347 do_check(text, folds);
348 } 337 }
349 338
350 #[test] 339 #[test]
351 fn test_folds_macros() { 340 fn test_folds_macros() {
352 let text = r#" 341 check(
353macro_rules! foo <fold>{ 342 r#"
343macro_rules! foo <fold block>{
354 ($($tt:tt)*) => { $($tt)* } 344 ($($tt:tt)*) => { $($tt)* }
355}</fold> 345}</fold>
356"#; 346"#,
357 347 );
358 let folds = &[FoldKind::Block];
359 do_check(text, folds);
360 } 348 }
361 349
362 #[test] 350 #[test]
363 fn test_fold_match_arms() { 351 fn test_fold_match_arms() {
364 let text = r#" 352 check(
365fn main() <fold>{ 353 r#"
366 match 0 <fold>{ 354fn main() <fold block>{
355 match 0 <fold block>{
367 0 => 0, 356 0 => 0,
368 _ => 1, 357 _ => 1,
369 }</fold> 358 }</fold>
370}</fold>"#; 359}</fold>
360"#,
361 );
362 }
363
364 #[test]
365 fn fold_big_calls() {
366 check(
367 r#"
368fn main() <fold block>{
369 frobnicate<fold arglist>(
370 1,
371 2,
372 3,
373 )</fold>
374}</fold>
375"#,
376 )
377 }
371 378
372 let folds = &[FoldKind::Block, FoldKind::Block]; 379 #[test]
373 do_check(text, folds); 380 fn fold_record_literals() {
381 check(
382 r#"
383const _: S = S <fold block>{
384
385}</fold>;
386"#,
387 )
374 } 388 }
375} 389}
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index a6c86e99c..c30b20611 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,13 +1,13 @@
1use hir::Semantics; 1use hir::Semantics;
2use ra_ide_db::{ 2use ra_ide_db::{
3 defs::{classify_name, classify_name_ref}, 3 defs::{classify_name, classify_name_ref, NameClass},
4 symbol_index, RootDatabase, 4 symbol_index, RootDatabase,
5}; 5};
6use ra_syntax::{ 6use ra_syntax::{
7 ast::{self}, 7 ast::{self},
8 match_ast, AstNode, 8 match_ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset, 10 SyntaxToken, TokenAtOffset, T,
11}; 11};
12 12
13use crate::{ 13use crate::{
@@ -32,14 +32,18 @@ pub(crate) fn goto_definition(
32 let file = sema.parse(position.file_id).syntax().clone(); 32 let file = sema.parse(position.file_id).syntax().clone();
33 let original_token = pick_best(file.token_at_offset(position.offset))?; 33 let original_token = pick_best(file.token_at_offset(position.offset))?;
34 let token = sema.descend_into_macros(original_token.clone()); 34 let token = sema.descend_into_macros(original_token.clone());
35 let parent = token.parent();
35 36
36 let nav_targets = match_ast! { 37 let nav_targets = match_ast! {
37 match (token.parent()) { 38 match parent {
38 ast::NameRef(name_ref) => { 39 ast::NameRef(name_ref) => {
39 reference_definition(&sema, &name_ref).to_vec() 40 reference_definition(&sema, &name_ref).to_vec()
40 }, 41 },
41 ast::Name(name) => { 42 ast::Name(name) => {
42 let def = classify_name(&sema, &name)?.definition(); 43 let def = match classify_name(&sema, &name)? {
44 NameClass::Definition(def) | NameClass::ConstReference(def) => def,
45 NameClass::FieldShorthand { local: _, field } => field,
46 };
43 let nav = def.try_to_nav(sema.db)?; 47 let nav = def.try_to_nav(sema.db)?;
44 vec![nav] 48 vec![nav]
45 }, 49 },
@@ -54,7 +58,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
54 return tokens.max_by_key(priority); 58 return tokens.max_by_key(priority);
55 fn priority(n: &SyntaxToken) -> usize { 59 fn priority(n: &SyntaxToken) -> usize {
56 match n.kind() { 60 match n.kind() {
57 IDENT | INT_NUMBER => 2, 61 IDENT | INT_NUMBER | T![self] => 2,
58 kind if kind.is_trivia() => 0, 62 kind if kind.is_trivia() => 0,
59 _ => 1, 63 _ => 1,
60 } 64 }
@@ -100,790 +104,784 @@ pub(crate) fn reference_definition(
100 104
101#[cfg(test)] 105#[cfg(test)]
102mod tests { 106mod tests {
103 use test_utils::assert_eq_text; 107 use ra_db::FileRange;
104 108 use ra_syntax::{TextRange, TextSize};
105 use crate::mock_analysis::analysis_and_position; 109
106 110 use crate::mock_analysis::MockAnalysis;
107 fn check_goto(ra_fixture: &str, expected: &str, expected_range: &str) { 111
108 let (analysis, pos) = analysis_and_position(ra_fixture); 112 fn check(ra_fixture: &str) {
113 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
114 let (mut expected, data) = mock.annotation();
115 let analysis = mock.analysis();
116 match data.as_str() {
117 "" => (),
118 "file" => {
119 expected.range =
120 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
121 }
122 data => panic!("bad data: {}", data),
123 }
109 124
110 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 125 let mut navs =
126 analysis.goto_definition(position).unwrap().expect("no definition found").info;
111 if navs.len() == 0 { 127 if navs.len() == 0 {
112 panic!("unresolved reference") 128 panic!("unresolved reference")
113 } 129 }
114 assert_eq!(navs.len(), 1); 130 assert_eq!(navs.len(), 1);
115 131
116 let nav = navs.pop().unwrap(); 132 let nav = navs.pop().unwrap();
117 let file_text = analysis.file_text(nav.file_id()).unwrap(); 133 assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() });
118
119 let mut actual = file_text[nav.full_range()].to_string();
120 if let Some(focus) = nav.focus_range() {
121 actual += "|";
122 actual += &file_text[focus];
123 }
124
125 if !expected_range.contains("...") {
126 test_utils::assert_eq_text!(&actual, expected_range);
127 } else {
128 let mut parts = expected_range.split("...");
129 let prefix = parts.next().unwrap();
130 let suffix = parts.next().unwrap();
131 assert!(
132 actual.starts_with(prefix) && actual.ends_with(suffix),
133 "\nExpected: {}\n Actual: {}\n",
134 expected_range,
135 actual
136 );
137 }
138
139 nav.assert_match(expected);
140 } 134 }
141 135
142 #[test] 136 #[test]
143 fn goto_def_in_items() { 137 fn goto_def_in_items() {
144 check_goto( 138 check(
145 " 139 r#"
146 //- /lib.rs 140struct Foo;
147 struct Foo; 141 //^^^
148 enum E { X(Foo<|>) } 142enum E { X(Foo<|>) }
149 ", 143"#,
150 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
151 "struct Foo;|Foo",
152 ); 144 );
153 } 145 }
154 146
155 #[test] 147 #[test]
156 fn goto_def_at_start_of_item() { 148 fn goto_def_at_start_of_item() {
157 check_goto( 149 check(
158 " 150 r#"
159 //- /lib.rs 151struct Foo;
160 struct Foo; 152 //^^^
161 enum E { X(<|>Foo) } 153enum E { X(<|>Foo) }
162 ", 154"#,
163 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
164 "struct Foo;|Foo",
165 ); 155 );
166 } 156 }
167 157
168 #[test] 158 #[test]
169 fn goto_definition_resolves_correct_name() { 159 fn goto_definition_resolves_correct_name() {
170 check_goto( 160 check(
171 " 161 r#"
172 //- /lib.rs 162//- /lib.rs
173 use a::Foo; 163use a::Foo;
174 mod a; 164mod a;
175 mod b; 165mod b;
176 enum E { X(Foo<|>) } 166enum E { X(Foo<|>) }
177
178 //- /a.rs
179 struct Foo;
180 167
181 //- /b.rs 168//- /a.rs
182 struct Foo; 169struct Foo;
183 ", 170 //^^^
184 "Foo STRUCT_DEF FileId(2) 0..11 7..10", 171//- /b.rs
185 "struct Foo;|Foo", 172struct Foo;
173"#,
186 ); 174 );
187 } 175 }
188 176
189 #[test] 177 #[test]
190 fn goto_def_for_module_declaration() { 178 fn goto_def_for_module_declaration() {
191 check_goto( 179 check(
192 " 180 r#"
193 //- /lib.rs 181//- /lib.rs
194 mod <|>foo; 182mod <|>foo;
195 183
196 //- /foo.rs 184//- /foo.rs
197 // empty 185// empty
198 ", 186//^ file
199 "foo SOURCE_FILE FileId(2) 0..10", 187"#,
200 "// empty\n\n",
201 ); 188 );
202 189
203 check_goto( 190 check(
204 " 191 r#"
205 //- /lib.rs 192//- /lib.rs
206 mod <|>foo; 193mod <|>foo;
207 194
208 //- /foo/mod.rs 195//- /foo/mod.rs
209 // empty 196// empty
210 ", 197//^ file
211 "foo SOURCE_FILE FileId(2) 0..10", 198"#,
212 "// empty\n\n",
213 ); 199 );
214 } 200 }
215 201
216 #[test] 202 #[test]
217 fn goto_def_for_macros() { 203 fn goto_def_for_macros() {
218 check_goto( 204 check(
219 " 205 r#"
220 //- /lib.rs 206macro_rules! foo { () => { () } }
221 macro_rules! foo { () => { () } } 207 //^^^
222 208fn bar() {
223 fn bar() { 209 <|>foo!();
224 <|>foo!(); 210}
225 } 211"#,
226 ",
227 "foo MACRO_CALL FileId(1) 0..33 13..16",
228 "macro_rules! foo { () => { () } }|foo",
229 ); 212 );
230 } 213 }
231 214
232 #[test] 215 #[test]
233 fn goto_def_for_macros_from_other_crates() { 216 fn goto_def_for_macros_from_other_crates() {
234 check_goto( 217 check(
235 " 218 r#"
236 //- /lib.rs 219//- /lib.rs
237 use foo::foo; 220use foo::foo;
238 fn bar() { 221fn bar() {
239 <|>foo!(); 222 <|>foo!();
240 } 223}
241 224
242 //- /foo/lib.rs 225//- /foo/lib.rs
243 #[macro_export] 226#[macro_export]
244 macro_rules! foo { () => { () } } 227macro_rules! foo { () => { () } }
245 ", 228 //^^^
246 "foo MACRO_CALL FileId(2) 0..49 29..32", 229"#,
247 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
248 ); 230 );
249 } 231 }
250 232
251 #[test] 233 #[test]
252 fn goto_def_for_use_alias() { 234 fn goto_def_for_macros_in_use_tree() {
253 check_goto( 235 check(
254 " 236 r#"
255 //- /lib.rs 237//- /lib.rs
256 use foo as bar<|>; 238use foo::foo<|>;
257
258 239
259 //- /foo/lib.rs 240//- /foo/lib.rs
260 #[macro_export] 241#[macro_export]
261 macro_rules! foo { () => { () } }", 242macro_rules! foo { () => { () } }
262 "SOURCE_FILE FileId(2) 0..50", 243 //^^^
263 "#[macro_export]\nmacro_rules! foo { () => { () } }\n", 244"#,
264 ); 245 );
265 } 246 }
266 247
267 #[test] 248 #[test]
268 fn goto_def_for_use_alias_foo_macro() { 249 fn goto_def_for_macro_defined_fn_with_arg() {
269 check_goto( 250 check(
270 " 251 r#"
271 //- /lib.rs 252//- /lib.rs
272 use foo::foo as bar<|>; 253macro_rules! define_fn {
254 ($name:ident) => (fn $name() {})
255}
256
257define_fn!(foo);
258 //^^^
273 259
274 //- /foo/lib.rs 260fn bar() {
275 #[macro_export] 261 <|>foo();
276 macro_rules! foo { () => { () } } 262}
277 ", 263"#,
278 "foo MACRO_CALL FileId(2) 0..49 29..32",
279 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
280 ); 264 );
281 } 265 }
282 266
283 #[test] 267 #[test]
284 fn goto_def_for_macros_in_use_tree() { 268 fn goto_def_for_macro_defined_fn_no_arg() {
285 check_goto( 269 check(
286 " 270 r#"
287 //- /lib.rs 271//- /lib.rs
288 use foo::foo<|>; 272macro_rules! define_fn {
273 () => (fn foo() {})
274}
275
276 define_fn!();
277//^^^^^^^^^^^^^
289 278
290 //- /foo/lib.rs 279fn bar() {
291 #[macro_export] 280 <|>foo();
292 macro_rules! foo { () => { () } } 281}
293 ", 282"#,
294 "foo MACRO_CALL FileId(2) 0..49 29..32",
295 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
296 ); 283 );
297 } 284 }
298 285
299 #[test] 286 #[test]
300 fn goto_def_for_macro_defined_fn_with_arg() { 287 fn goto_definition_works_for_macro_inside_pattern() {
301 check_goto( 288 check(
302 " 289 r#"
303 //- /lib.rs 290//- /lib.rs
304 macro_rules! define_fn { 291macro_rules! foo {() => {0}}
305 ($name:ident) => (fn $name() {}) 292 //^^^
306 }
307
308 define_fn!(foo);
309 293
310 fn bar() { 294fn bar() {
311 <|>foo(); 295 match (0,1) {
312 } 296 (<|>foo!(), _) => {}
313 ", 297 }
314 "foo FN_DEF FileId(1) 64..80 75..78", 298}
315 "define_fn!(foo);|foo", 299"#,
316 ); 300 );
317 } 301 }
318 302
319 #[test] 303 #[test]
320 fn goto_def_for_macro_defined_fn_no_arg() { 304 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
321 check_goto( 305 check(
322 " 306 r#"
323 //- /lib.rs 307//- /lib.rs
324 macro_rules! define_fn { 308macro_rules! foo {() => {0}}
325 () => (fn foo() {}) 309 //^^^
326 } 310fn bar() {
327 311 match 0 {
328 define_fn!(); 312 <|>foo!() => {}
329 313 }
330 fn bar() { 314}
331 <|>foo(); 315"#,
332 }
333 ",
334 "foo FN_DEF FileId(1) 51..64 51..64",
335 "define_fn!();|define_fn!();",
336 ); 316 );
337 } 317 }
338 318
339 #[test] 319 #[test]
340 fn goto_definition_works_for_macro_inside_pattern() { 320 fn goto_def_for_use_alias() {
341 check_goto( 321 check(
342 " 322 r#"
343 //- /lib.rs 323//- /lib.rs
344 macro_rules! foo {() => {0}} 324use foo as bar<|>;
345 325
346 fn bar() { 326//- /foo/lib.rs
347 match (0,1) { 327// empty
348 (<|>foo!(), _) => {} 328//^ file
349 } 329"#,
350 }
351 ",
352 "foo MACRO_CALL FileId(1) 0..28 13..16",
353 "macro_rules! foo {() => {0}}|foo",
354 ); 330 );
355 } 331 }
356 332
357 #[test] 333 #[test]
358 fn goto_definition_works_for_macro_inside_match_arm_lhs() { 334 fn goto_def_for_use_alias_foo_macro() {
359 check_goto( 335 check(
360 " 336 r#"
361 //- /lib.rs 337//- /lib.rs
362 macro_rules! foo {() => {0}} 338use foo::foo as bar<|>;
363 339
364 fn bar() { 340//- /foo/lib.rs
365 match 0 { 341#[macro_export]
366 <|>foo!() => {} 342macro_rules! foo { () => { () } }
367 } 343 //^^^
368 } 344"#,
369 ",
370 "foo MACRO_CALL FileId(1) 0..28 13..16",
371 "macro_rules! foo {() => {0}}|foo",
372 ); 345 );
373 } 346 }
374 347
375 #[test] 348 #[test]
376 fn goto_def_for_methods() { 349 fn goto_def_for_methods() {
377 check_goto( 350 check(
378 " 351 r#"
379 //- /lib.rs 352//- /lib.rs
380 struct Foo; 353struct Foo;
381 impl Foo { 354impl Foo {
382 fn frobnicate(&self) { } 355 fn frobnicate(&self) { }
383 } 356 //^^^^^^^^^^
357}
384 358
385 fn bar(foo: &Foo) { 359fn bar(foo: &Foo) {
386 foo.frobnicate<|>(); 360 foo.frobnicate<|>();
387 } 361}
388 ", 362"#,
389 "frobnicate FN_DEF FileId(1) 27..51 30..40",
390 "fn frobnicate(&self) { }|frobnicate",
391 ); 363 );
392 } 364 }
393 365
394 #[test] 366 #[test]
395 fn goto_def_for_fields() { 367 fn goto_def_for_fields() {
396 check_goto( 368 check(
397 r" 369 r#"
398 //- /lib.rs 370struct Foo {
399 struct Foo { 371 spam: u32,
400 spam: u32, 372} //^^^^
401 }
402 373
403 fn bar(foo: &Foo) { 374fn bar(foo: &Foo) {
404 foo.spam<|>; 375 foo.spam<|>;
405 } 376}
406 ", 377"#,
407 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
408 "spam: u32|spam",
409 ); 378 );
410 } 379 }
411 380
412 #[test] 381 #[test]
413 fn goto_def_for_record_fields() { 382 fn goto_def_for_record_fields() {
414 check_goto( 383 check(
415 r" 384 r#"
416 //- /lib.rs 385//- /lib.rs
417 struct Foo { 386struct Foo {
418 spam: u32, 387 spam: u32,
419 } 388} //^^^^
420 389
421 fn bar() -> Foo { 390fn bar() -> Foo {
422 Foo { 391 Foo {
423 spam<|>: 0, 392 spam<|>: 0,
424 } 393 }
425 } 394}
426 ", 395"#,
427 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
428 "spam: u32|spam",
429 ); 396 );
430 } 397 }
431 398
432 #[test] 399 #[test]
433 fn goto_def_for_record_pat_fields() { 400 fn goto_def_for_record_pat_fields() {
434 check_goto( 401 check(
435 r" 402 r#"
436 //- /lib.rs 403//- /lib.rs
437 struct Foo { 404struct Foo {
438 spam: u32, 405 spam: u32,
439 } 406} //^^^^
440 407
441 fn bar(foo: Foo) -> Foo { 408fn bar(foo: Foo) -> Foo {
442 let Foo { spam<|>: _, } = foo 409 let Foo { spam<|>: _, } = foo
443 } 410}
444 ", 411"#,
445 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
446 "spam: u32|spam",
447 ); 412 );
448 } 413 }
449 414
450 #[test] 415 #[test]
451 fn goto_def_for_record_fields_macros() { 416 fn goto_def_for_record_fields_macros() {
452 check_goto( 417 check(
453 r" 418 r"
454 //- /lib.rs 419macro_rules! m { () => { 92 };}
455 macro_rules! m { () => { 92 };} 420struct Foo { spam: u32 }
456 struct Foo { spam: u32 } 421 //^^^^
457 422
458 fn bar() -> Foo { 423fn bar() -> Foo {
459 Foo { spam<|>: m!() } 424 Foo { spam<|>: m!() }
460 } 425}
461 ", 426",
462 "spam RECORD_FIELD_DEF FileId(1) 45..54 45..49",
463 "spam: u32|spam",
464 ); 427 );
465 } 428 }
466 429
467 #[test] 430 #[test]
468 fn goto_for_tuple_fields() { 431 fn goto_for_tuple_fields() {
469 check_goto( 432 check(
470 " 433 r#"
471 //- /lib.rs 434struct Foo(u32);
472 struct Foo(u32); 435 //^^^
473 436
474 fn bar() { 437fn bar() {
475 let foo = Foo(0); 438 let foo = Foo(0);
476 foo.<|>0; 439 foo.<|>0;
477 } 440}
478 ", 441"#,
479 "TUPLE_FIELD_DEF FileId(1) 11..14",
480 "u32",
481 ); 442 );
482 } 443 }
483 444
484 #[test] 445 #[test]
485 fn goto_def_for_ufcs_inherent_methods() { 446 fn goto_def_for_ufcs_inherent_methods() {
486 check_goto( 447 check(
487 " 448 r#"
488 //- /lib.rs 449struct Foo;
489 struct Foo; 450impl Foo {
490 impl Foo { 451 fn frobnicate() { }
491 fn frobnicate() { } 452} //^^^^^^^^^^
492 }
493 453
494 fn bar(foo: &Foo) { 454fn bar(foo: &Foo) {
495 Foo::frobnicate<|>(); 455 Foo::frobnicate<|>();
496 } 456}
497 ", 457"#,
498 "frobnicate FN_DEF FileId(1) 27..46 30..40",
499 "fn frobnicate() { }|frobnicate",
500 ); 458 );
501 } 459 }
502 460
503 #[test] 461 #[test]
504 fn goto_def_for_ufcs_trait_methods_through_traits() { 462 fn goto_def_for_ufcs_trait_methods_through_traits() {
505 check_goto( 463 check(
506 " 464 r#"
507 //- /lib.rs 465trait Foo {
508 trait Foo { 466 fn frobnicate();
509 fn frobnicate(); 467} //^^^^^^^^^^
510 }
511 468
512 fn bar() { 469fn bar() {
513 Foo::frobnicate<|>(); 470 Foo::frobnicate<|>();
514 } 471}
515 ", 472"#,
516 "frobnicate FN_DEF FileId(1) 16..32 19..29",
517 "fn frobnicate();|frobnicate",
518 ); 473 );
519 } 474 }
520 475
521 #[test] 476 #[test]
522 fn goto_def_for_ufcs_trait_methods_through_self() { 477 fn goto_def_for_ufcs_trait_methods_through_self() {
523 check_goto( 478 check(
524 " 479 r#"
525 //- /lib.rs 480struct Foo;
526 struct Foo; 481trait Trait {
527 trait Trait { 482 fn frobnicate();
528 fn frobnicate(); 483} //^^^^^^^^^^
529 } 484impl Trait for Foo {}
530 impl Trait for Foo {} 485
531 486fn bar() {
532 fn bar() { 487 Foo::frobnicate<|>();
533 Foo::frobnicate<|>(); 488}
534 } 489"#,
535 ",
536 "frobnicate FN_DEF FileId(1) 30..46 33..43",
537 "fn frobnicate();|frobnicate",
538 ); 490 );
539 } 491 }
540 492
541 #[test] 493 #[test]
542 fn goto_definition_on_self() { 494 fn goto_definition_on_self() {
543 check_goto( 495 check(
544 " 496 r#"
545 //- /lib.rs 497struct Foo;
546 struct Foo; 498impl Foo {
547 impl Foo { 499 //^^^
548 pub fn new() -> Self { 500 pub fn new() -> Self {
549 Self<|> {} 501 Self<|> {}
550 } 502 }
551 } 503}
552 ", 504"#,
553 "impl IMPL_DEF FileId(1) 12..73", 505 );
554 "impl Foo {...}", 506 check(
555 ); 507 r#"
556 508struct Foo;
557 check_goto( 509impl Foo {
558 " 510 //^^^
559 //- /lib.rs 511 pub fn new() -> Self<|> {
560 struct Foo; 512 Self {}
561 impl Foo { 513 }
562 pub fn new() -> Self<|> { 514}
563 Self {} 515"#,
564 } 516 );
565 } 517
566 ", 518 check(
567 "impl IMPL_DEF FileId(1) 12..73", 519 r#"
568 "impl Foo {...}", 520enum Foo { A }
569 ); 521impl Foo {
570 522 //^^^
571 check_goto( 523 pub fn new() -> Self<|> {
572 " 524 Foo::A
573 //- /lib.rs 525 }
574 enum Foo { A } 526}
575 impl Foo { 527"#,
576 pub fn new() -> Self<|> { 528 );
577 Foo::A 529
578 } 530 check(
579 } 531 r#"
580 ", 532enum Foo { A }
581 "impl IMPL_DEF FileId(1) 15..75", 533impl Foo {
582 "impl Foo {...}", 534 //^^^
583 ); 535 pub fn thing(a: &Self<|>) {
584 536 }
585 check_goto( 537}
586 " 538"#,
587 //- /lib.rs
588 enum Foo { A }
589 impl Foo {
590 pub fn thing(a: &Self<|>) {
591 }
592 }
593 ",
594 "impl IMPL_DEF FileId(1) 15..62",
595 "impl Foo {...}",
596 ); 539 );
597 } 540 }
598 541
599 #[test] 542 #[test]
600 fn goto_definition_on_self_in_trait_impl() { 543 fn goto_definition_on_self_in_trait_impl() {
601 check_goto( 544 check(
602 " 545 r#"
603 //- /lib.rs 546struct Foo;
604 struct Foo; 547trait Make {
605 trait Make { 548 fn new() -> Self;
606 fn new() -> Self; 549}
607 } 550impl Make for Foo {
608 impl Make for Foo { 551 //^^^
609 fn new() -> Self { 552 fn new() -> Self {
610 Self<|> {} 553 Self<|> {}
611 } 554 }
612 } 555}
613 ", 556"#,
614 "impl IMPL_DEF FileId(1) 49..115",
615 "impl Make for Foo {...}",
616 ); 557 );
617 558
618 check_goto( 559 check(
619 " 560 r#"
620 //- /lib.rs 561struct Foo;
621 struct Foo; 562trait Make {
622 trait Make { 563 fn new() -> Self;
623 fn new() -> Self; 564}
624 } 565impl Make for Foo {
625 impl Make for Foo { 566 //^^^
626 fn new() -> Self<|> { 567 fn new() -> Self<|> {
627 Self {} 568 Self {}
628 } 569 }
629 } 570}
630 ", 571"#,
631 "impl IMPL_DEF FileId(1) 49..115",
632 "impl Make for Foo {...}",
633 ); 572 );
634 } 573 }
635 574
636 #[test] 575 #[test]
637 fn goto_def_when_used_on_definition_name_itself() { 576 fn goto_def_when_used_on_definition_name_itself() {
638 check_goto( 577 check(
639 " 578 r#"
640 //- /lib.rs 579struct Foo<|> { value: u32 }
641 struct Foo<|> { value: u32 } 580 //^^^
642 ", 581 "#,
643 "Foo STRUCT_DEF FileId(1) 0..25 7..10",
644 "struct Foo { value: u32 }|Foo",
645 ); 582 );
646 583
647 check_goto( 584 check(
648 r#" 585 r#"
649 //- /lib.rs 586struct Foo {
650 struct Foo { 587 field<|>: string,
651 field<|>: string, 588} //^^^^^
652 } 589"#,
653 "#,
654 "field RECORD_FIELD_DEF FileId(1) 17..30 17..22",
655 "field: string|field",
656 ); 590 );
657 591
658 check_goto( 592 check(
659 " 593 r#"
660 //- /lib.rs 594fn foo_test<|>() { }
661 fn foo_test<|>() { } 595 //^^^^^^^^
662 ", 596"#,
663 "foo_test FN_DEF FileId(1) 0..17 3..11",
664 "fn foo_test() { }|foo_test",
665 ); 597 );
666 598
667 check_goto( 599 check(
668 " 600 r#"
669 //- /lib.rs 601enum Foo<|> { Variant }
670 enum Foo<|> { 602 //^^^
671 Variant, 603"#,
672 }
673 ",
674 "Foo ENUM_DEF FileId(1) 0..25 5..8",
675 "enum Foo {...}|Foo",
676 );
677
678 check_goto(
679 "
680 //- /lib.rs
681 enum Foo {
682 Variant1,
683 Variant2<|>,
684 Variant3,
685 }
686 ",
687 "Variant2 ENUM_VARIANT FileId(1) 29..37 29..37",
688 "Variant2|Variant2",
689 ); 604 );
690 605
691 check_goto( 606 check(
692 r#" 607 r#"
693 //- /lib.rs 608enum Foo {
694 static INNER<|>: &str = ""; 609 Variant1,
695 "#, 610 Variant2<|>,
696 "INNER STATIC_DEF FileId(1) 0..24 7..12", 611 //^^^^^^^^
697 "static INNER: &str = \"\";|INNER", 612 Variant3,
613}
614"#,
698 ); 615 );
699 616
700 check_goto( 617 check(
701 r#" 618 r#"
702 //- /lib.rs 619static INNER<|>: &str = "";
703 const INNER<|>: &str = ""; 620 //^^^^^
704 "#, 621"#,
705 "INNER CONST_DEF FileId(1) 0..23 6..11",
706 "const INNER: &str = \"\";|INNER",
707 ); 622 );
708 623
709 check_goto( 624 check(
710 r#" 625 r#"
711 //- /lib.rs 626const INNER<|>: &str = "";
712 type Thing<|> = Option<()>; 627 //^^^^^
713 "#, 628"#,
714 "Thing TYPE_ALIAS_DEF FileId(1) 0..24 5..10",
715 "type Thing = Option<()>;|Thing",
716 ); 629 );
717 630
718 check_goto( 631 check(
719 r#" 632 r#"
720 //- /lib.rs 633type Thing<|> = Option<()>;
721 trait Foo<|> { } 634 //^^^^^
722 "#, 635"#,
723 "Foo TRAIT_DEF FileId(1) 0..13 6..9",
724 "trait Foo { }|Foo",
725 ); 636 );
726 637
727 check_goto( 638 check(
728 r#" 639 r#"
729 //- /lib.rs 640trait Foo<|> { }
730 mod bar<|> { } 641 //^^^
731 "#, 642"#,
732 "bar MODULE FileId(1) 0..11 4..7", 643 );
733 "mod bar { }|bar", 644
645 check(
646 r#"
647mod bar<|> { }
648 //^^^
649"#,
734 ); 650 );
735 } 651 }
736 652
737 #[test] 653 #[test]
738 fn goto_from_macro() { 654 fn goto_from_macro() {
739 check_goto( 655 check(
740 " 656 r#"
741 //- /lib.rs 657macro_rules! id {
742 macro_rules! id { 658 ($($tt:tt)*) => { $($tt)* }
743 ($($tt:tt)*) => { $($tt)* } 659}
744 } 660fn foo() {}
745 fn foo() {} 661 //^^^
746 id! { 662id! {
747 fn bar() { 663 fn bar() {
748 fo<|>o(); 664 fo<|>o();
749 } 665 }
750 } 666}
751 mod confuse_index { fn foo(); } 667mod confuse_index { fn foo(); }
752 ", 668"#,
753 "foo FN_DEF FileId(1) 52..63 55..58",
754 "fn foo() {}|foo",
755 ); 669 );
756 } 670 }
757 671
758 #[test] 672 #[test]
759 fn goto_through_format() { 673 fn goto_through_format() {
760 check_goto( 674 check(
761 " 675 r#"
762 //- /lib.rs 676#[macro_export]
763 #[macro_export] 677macro_rules! format {
764 macro_rules! format { 678 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
765 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) 679}
766 } 680#[rustc_builtin_macro]
767 #[rustc_builtin_macro] 681#[macro_export]
768 #[macro_export] 682macro_rules! format_args {
769 macro_rules! format_args { 683 ($fmt:expr) => ({ /* compiler built-in */ });
770 ($fmt:expr) => ({ /* compiler built-in */ }); 684 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
771 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) 685}
772 } 686pub mod __export {
773 pub mod __export { 687 pub use crate::format_args;
774 pub use crate::format_args; 688 fn foo() {} // for index confusion
775 fn foo() {} // for index confusion 689}
776 } 690fn foo() -> i8 {}
777 fn foo() -> i8 {} 691 //^^^
778 fn test() { 692fn test() {
779 format!(\"{}\", fo<|>o()) 693 format!("{}", fo<|>o())
780 } 694}
781 ", 695"#,
782 "foo FN_DEF FileId(1) 398..415 401..404",
783 "fn foo() -> i8 {}|foo",
784 ); 696 );
785 } 697 }
786 698
787 #[test] 699 #[test]
788 fn goto_for_type_param() { 700 fn goto_for_type_param() {
789 check_goto( 701 check(
790 r#" 702 r#"
791 //- /lib.rs 703struct Foo<T: Clone> { t: <|>T }
792 struct Foo<T: Clone> { 704 //^
793 t: <|>T, 705"#,
794 }
795 "#,
796 "T TYPE_PARAM FileId(1) 11..19 11..12",
797 "T: Clone|T",
798 ); 706 );
799 } 707 }
800 708
801 #[test] 709 #[test]
802 fn goto_within_macro() { 710 fn goto_within_macro() {
803 check_goto( 711 check(
804 " 712 r#"
805 //- /lib.rs 713macro_rules! id {
806 macro_rules! id { 714 ($($tt:tt)*) => ($($tt)*)
807 ($($tt:tt)*) => ($($tt)*) 715}
808 }
809 716
810 fn foo() { 717fn foo() {
811 let x = 1; 718 let x = 1;
812 id!({ 719 //^
813 let y = <|>x; 720 id!({
814 let z = y; 721 let y = <|>x;
815 }); 722 let z = y;
816 } 723 });
817 ", 724}
818 "x BIND_PAT FileId(1) 69..70", 725"#,
819 "x",
820 ); 726 );
821 727
822 check_goto( 728 check(
823 " 729 r#"
824 //- /lib.rs 730macro_rules! id {
825 macro_rules! id { 731 ($($tt:tt)*) => ($($tt)*)
826 ($($tt:tt)*) => ($($tt)*) 732}
827 }
828 733
829 fn foo() { 734fn foo() {
830 let x = 1; 735 let x = 1;
831 id!({ 736 id!({
832 let y = x; 737 let y = x;
833 let z = <|>y; 738 //^
834 }); 739 let z = <|>y;
835 } 740 });
836 ", 741}
837 "y BIND_PAT FileId(1) 98..99", 742"#,
838 "y",
839 ); 743 );
840 } 744 }
841 745
842 #[test] 746 #[test]
843 fn goto_def_in_local_fn() { 747 fn goto_def_in_local_fn() {
844 check_goto( 748 check(
845 " 749 r#"
846 //- /lib.rs 750fn main() {
847 fn main() { 751 fn foo() {
848 fn foo() { 752 let x = 92;
849 let x = 92; 753 //^
850 <|>x; 754 <|>x;
851 } 755 }
852 } 756}
853 ", 757"#,
854 "x BIND_PAT FileId(1) 39..40",
855 "x",
856 ); 758 );
857 } 759 }
858 760
859 #[test] 761 #[test]
860 fn goto_def_in_local_macro() { 762 fn goto_def_in_local_macro() {
861 check_goto( 763 check(
862 r" 764 r#"
863 //- /lib.rs 765fn bar() {
864 fn bar() { 766 macro_rules! foo { () => { () } }
865 macro_rules! foo { () => { () } } 767 //^^^
866 <|>foo!(); 768 <|>foo!();
867 } 769}
868 ", 770"#,
869 "foo MACRO_CALL FileId(1) 15..48 28..31",
870 "macro_rules! foo { () => { () } }|foo",
871 ); 771 );
872 } 772 }
873 773
874 #[test] 774 #[test]
875 fn goto_def_for_field_init_shorthand() { 775 fn goto_def_for_field_init_shorthand() {
876 check_goto( 776 check(
877 " 777 r#"
878 //- /lib.rs 778struct Foo { x: i32 }
879 struct Foo { x: i32 } 779fn main() {
880 fn main() { 780 let x = 92;
881 let x = 92; 781 //^
882 Foo { x<|> }; 782 Foo { x<|> };
883 } 783}
884 ", 784"#,
885 "x BIND_PAT FileId(1) 42..43",
886 "x",
887 ) 785 )
888 } 786 }
787
788 #[test]
789 fn goto_def_for_enum_variant_field() {
790 check(
791 r#"
792enum Foo {
793 Bar { x: i32 }
794} //^
795fn baz(foo: Foo) {
796 match foo {
797 Foo::Bar { x<|> } => x
798 };
799}
800"#,
801 );
802 }
803
804 #[test]
805 fn goto_def_for_enum_variant_self_pattern_const() {
806 check(
807 r#"
808enum Foo { Bar }
809 //^^^
810impl Foo {
811 fn baz(self) {
812 match self { Self::Bar<|> => {} }
813 }
814}
815"#,
816 );
817 }
818
819 #[test]
820 fn goto_def_for_enum_variant_self_pattern_record() {
821 check(
822 r#"
823enum Foo { Bar { val: i32 } }
824 //^^^
825impl Foo {
826 fn baz(self) -> i32 {
827 match self { Self::Bar<|> { val } => {} }
828 }
829}
830"#,
831 );
832 }
833
834 #[test]
835 fn goto_def_for_enum_variant_self_expr_const() {
836 check(
837 r#"
838enum Foo { Bar }
839 //^^^
840impl Foo {
841 fn baz(self) { Self::Bar<|>; }
842}
843"#,
844 );
845 }
846
847 #[test]
848 fn goto_def_for_enum_variant_self_expr_record() {
849 check(
850 r#"
851enum Foo { Bar { val: i32 } }
852 //^^^
853impl Foo {
854 fn baz(self) { Self::Bar<|> {val: 4}; }
855}
856"#,
857 );
858 }
859
860 #[test]
861 fn goto_def_for_type_alias_generic_parameter() {
862 check(
863 r#"
864type Alias<T> = T<|>;
865 //^
866"#,
867 )
868 }
869
870 #[test]
871 fn goto_def_for_macro_container() {
872 check(
873 r#"
874//- /lib.rs
875foo::module<|>::mac!();
876
877//- /foo/lib.rs
878pub mod module {
879 //^^^^^^
880 #[macro_export]
881 macro_rules! _mac { () => { () } }
882 pub use crate::_mac as mac;
883}
884"#,
885 );
886 }
889} 887}
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 0cec0657e..9acc960fc 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -74,135 +74,156 @@ fn impls_for_trait(
74 74
75#[cfg(test)] 75#[cfg(test)]
76mod tests { 76mod tests {
77 use crate::mock_analysis::analysis_and_position; 77 use ra_db::FileRange;
78 78
79 fn check_goto(fixture: &str, expected: &[&str]) { 79 use crate::mock_analysis::MockAnalysis;
80 let (analysis, pos) = analysis_and_position(fixture);
81 80
82 let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; 81 fn check(ra_fixture: &str) {
83 assert_eq!(navs.len(), expected.len()); 82 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
84 navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); 83 let annotations = mock.annotations();
85 navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); 84 let analysis = mock.analysis();
85
86 let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
87
88 let key = |frange: &FileRange| (frange.file_id, frange.range.start());
89
90 let mut expected = annotations
91 .into_iter()
92 .map(|(range, data)| {
93 assert!(data.is_empty());
94 range
95 })
96 .collect::<Vec<_>>();
97 expected.sort_by_key(key);
98
99 let mut actual = navs
100 .into_iter()
101 .map(|nav| FileRange { file_id: nav.file_id(), range: nav.range() })
102 .collect::<Vec<_>>();
103 actual.sort_by_key(key);
104
105 assert_eq!(expected, actual);
86 } 106 }
87 107
88 #[test] 108 #[test]
89 fn goto_implementation_works() { 109 fn goto_implementation_works() {
90 check_goto( 110 check(
91 " 111 r#"
92 //- /lib.rs 112struct Foo<|>;
93 struct Foo<|>; 113impl Foo {}
94 impl Foo {} 114 //^^^
95 ", 115"#,
96 &["impl IMPL_DEF FileId(1) 12..23"],
97 ); 116 );
98 } 117 }
99 118
100 #[test] 119 #[test]
101 fn goto_implementation_works_multiple_blocks() { 120 fn goto_implementation_works_multiple_blocks() {
102 check_goto( 121 check(
103 " 122 r#"
104 //- /lib.rs 123struct Foo<|>;
105 struct Foo<|>; 124impl Foo {}
106 impl Foo {} 125 //^^^
107 impl Foo {} 126impl Foo {}
108 ", 127 //^^^
109 &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], 128"#,
110 ); 129 );
111 } 130 }
112 131
113 #[test] 132 #[test]
114 fn goto_implementation_works_multiple_mods() { 133 fn goto_implementation_works_multiple_mods() {
115 check_goto( 134 check(
116 " 135 r#"
117 //- /lib.rs 136struct Foo<|>;
118 struct Foo<|>; 137mod a {
119 mod a { 138 impl super::Foo {}
120 impl super::Foo {} 139 //^^^^^^^^^^
121 } 140}
122 mod b { 141mod b {
123 impl super::Foo {} 142 impl super::Foo {}
124 } 143 //^^^^^^^^^^
125 ", 144}
126 &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], 145"#,
127 ); 146 );
128 } 147 }
129 148
130 #[test] 149 #[test]
131 fn goto_implementation_works_multiple_files() { 150 fn goto_implementation_works_multiple_files() {
132 check_goto( 151 check(
133 " 152 r#"
134 //- /lib.rs 153//- /lib.rs
135 struct Foo<|>; 154struct Foo<|>;
136 mod a; 155mod a;
137 mod b; 156mod b;
138 //- /a.rs 157//- /a.rs
139 impl crate::Foo {} 158impl crate::Foo {}
140 //- /b.rs 159 //^^^^^^^^^^
141 impl crate::Foo {} 160//- /b.rs
142 ", 161impl crate::Foo {}
143 &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], 162 //^^^^^^^^^^
163"#,
144 ); 164 );
145 } 165 }
146 166
147 #[test] 167 #[test]
148 fn goto_implementation_for_trait() { 168 fn goto_implementation_for_trait() {
149 check_goto( 169 check(
150 " 170 r#"
151 //- /lib.rs 171trait T<|> {}
152 trait T<|> {} 172struct Foo;
153 struct Foo; 173impl T for Foo {}
154 impl T for Foo {} 174 //^^^
155 ", 175"#,
156 &["impl IMPL_DEF FileId(1) 23..40"],
157 ); 176 );
158 } 177 }
159 178
160 #[test] 179 #[test]
161 fn goto_implementation_for_trait_multiple_files() { 180 fn goto_implementation_for_trait_multiple_files() {
162 check_goto( 181 check(
163 " 182 r#"
164 //- /lib.rs 183//- /lib.rs
165 trait T<|> {}; 184trait T<|> {};
166 struct Foo; 185struct Foo;
167 mod a; 186mod a;
168 mod b; 187mod b;
169 //- /a.rs 188//- /a.rs
170 impl crate::T for crate::Foo {} 189impl crate::T for crate::Foo {}
171 //- /b.rs 190 //^^^^^^^^^^
172 impl crate::T for crate::Foo {} 191//- /b.rs
173 ", 192impl crate::T for crate::Foo {}
174 &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], 193 //^^^^^^^^^^
194 "#,
175 ); 195 );
176 } 196 }
177 197
178 #[test] 198 #[test]
179 fn goto_implementation_all_impls() { 199 fn goto_implementation_all_impls() {
180 check_goto( 200 check(
181 " 201 r#"
182 //- /lib.rs 202//- /lib.rs
183 trait T {} 203trait T {}
184 struct Foo<|>; 204struct Foo<|>;
185 impl Foo {} 205impl Foo {}
186 impl T for Foo {} 206 //^^^
187 impl T for &Foo {} 207impl T for Foo {}
188 ", 208 //^^^
189 &[ 209impl T for &Foo {}
190 "impl IMPL_DEF FileId(1) 23..34", 210 //^^^^
191 "impl IMPL_DEF FileId(1) 35..52", 211"#,
192 "impl IMPL_DEF FileId(1) 53..71",
193 ],
194 ); 212 );
195 } 213 }
196 214
197 #[test] 215 #[test]
198 fn goto_implementation_to_builtin_derive() { 216 fn goto_implementation_to_builtin_derive() {
199 check_goto( 217 check(
200 " 218 r#"
201 //- /lib.rs 219 #[derive(Copy)]
202 #[derive(Copy)] 220//^^^^^^^^^^^^^^^
203 struct Foo<|>; 221struct Foo<|>;
204 ", 222
205 &["impl IMPL_DEF FileId(1) 0..15"], 223mod marker {
224 trait Copy {}
225}
226"#,
206 ); 227 );
207 } 228 }
208} 229}
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 91a3097fb..069cb283e 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,5 +1,5 @@
1use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 3
4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
5 5
@@ -25,8 +25,9 @@ pub(crate) fn goto_type_definition(
25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { 25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| {
26 let ty = match_ast! { 26 let ty = match_ast! {
27 match node { 27 match node {
28 ast::Expr(expr) => sema.type_of_expr(&expr)?, 28 ast::Expr(it) => sema.type_of_expr(&it)?,
29 ast::Pat(pat) => sema.type_of_pat(&pat)?, 29 ast::Pat(it) => sema.type_of_pat(&it)?,
30 ast::SelfParam(it) => sema.type_of_self(&it)?,
30 _ => return None, 31 _ => return None,
31 } 32 }
32 }; 33 };
@@ -34,7 +35,7 @@ pub(crate) fn goto_type_definition(
34 Some((ty, node)) 35 Some((ty, node))
35 })?; 36 })?;
36 37
37 let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; 38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
38 39
39 let nav = adt_def.to_nav(db); 40 let nav = adt_def.to_nav(db);
40 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
@@ -44,7 +45,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
44 return tokens.max_by_key(priority); 45 return tokens.max_by_key(priority);
45 fn priority(n: &SyntaxToken) -> usize { 46 fn priority(n: &SyntaxToken) -> usize {
46 match n.kind() { 47 match n.kind() {
47 IDENT | INT_NUMBER => 2, 48 IDENT | INT_NUMBER | T![self] => 2,
48 kind if kind.is_trivia() => 0, 49 kind if kind.is_trivia() => 0,
49 _ => 1, 50 _ => 1,
50 } 51 }
@@ -53,91 +54,98 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
53 54
54#[cfg(test)] 55#[cfg(test)]
55mod tests { 56mod tests {
56 use crate::mock_analysis::analysis_and_position; 57 use ra_db::FileRange;
57 58
58 fn check_goto(fixture: &str, expected: &str) { 59 use crate::mock_analysis::MockAnalysis;
59 let (analysis, pos) = analysis_and_position(fixture);
60 60
61 let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info; 61 fn check(ra_fixture: &str) {
62 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
63 let (expected, data) = mock.annotation();
64 assert!(data.is_empty());
65 let analysis = mock.analysis();
66
67 let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
62 assert_eq!(navs.len(), 1); 68 assert_eq!(navs.len(), 1);
63 let nav = navs.pop().unwrap(); 69 let nav = navs.pop().unwrap();
64 nav.assert_match(expected); 70 assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() });
65 } 71 }
66 72
67 #[test] 73 #[test]
68 fn goto_type_definition_works_simple() { 74 fn goto_type_definition_works_simple() {
69 check_goto( 75 check(
70 " 76 r#"
71 //- /lib.rs 77struct Foo;
72 struct Foo; 78 //^^^
73 fn foo() { 79fn foo() {
74 let f: Foo; 80 let f: Foo; f<|>
75 f<|> 81}
76 } 82"#,
77 ",
78 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
79 ); 83 );
80 } 84 }
81 85
82 #[test] 86 #[test]
83 fn goto_type_definition_works_simple_ref() { 87 fn goto_type_definition_works_simple_ref() {
84 check_goto( 88 check(
85 " 89 r#"
86 //- /lib.rs 90struct Foo;
87 struct Foo; 91 //^^^
88 fn foo() { 92fn foo() {
89 let f: &Foo; 93 let f: &Foo; f<|>
90 f<|> 94}
91 } 95"#,
92 ",
93 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
94 ); 96 );
95 } 97 }
96 98
97 #[test] 99 #[test]
98 fn goto_type_definition_works_through_macro() { 100 fn goto_type_definition_works_through_macro() {
99 check_goto( 101 check(
100 " 102 r#"
101 //- /lib.rs 103macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
102 macro_rules! id { 104struct Foo {}
103 ($($tt:tt)*) => { $($tt)* } 105 //^^^
104 } 106id! {
105 struct Foo {} 107 fn bar() { let f<|> = Foo {}; }
106 id! { 108}
107 fn bar() { 109"#,
108 let f<|> = Foo {};
109 }
110 }
111 ",
112 "Foo STRUCT_DEF FileId(1) 52..65 59..62",
113 ); 110 );
114 } 111 }
115 112
116 #[test] 113 #[test]
117 fn goto_type_definition_for_param() { 114 fn goto_type_definition_for_param() {
118 check_goto( 115 check(
119 " 116 r#"
120 //- /lib.rs 117struct Foo;
121 struct Foo; 118 //^^^
122 fn foo(<|>f: Foo) {} 119fn foo(<|>f: Foo) {}
123 ", 120"#,
124 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
125 ); 121 );
126 } 122 }
127 123
128 #[test] 124 #[test]
129 fn goto_type_definition_for_tuple_field() { 125 fn goto_type_definition_for_tuple_field() {
130 check_goto( 126 check(
131 " 127 r#"
132 //- /lib.rs 128struct Foo;
133 struct Foo; 129 //^^^
134 struct Bar(Foo); 130struct Bar(Foo);
135 fn foo() { 131fn foo() {
136 let bar = Bar(Foo); 132 let bar = Bar(Foo);
137 bar.<|>0; 133 bar.<|>0;
138 } 134}
139 ", 135"#,
140 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
141 ); 136 );
142 } 137 }
138
139 #[test]
140 fn goto_def_for_self_param() {
141 check(
142 r#"
143struct Foo;
144 //^^^
145impl Foo {
146 fn f(&self<|>) {}
147}
148"#,
149 )
150 }
143} 151}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index d96cb5596..a4c97e7f9 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,6 @@
1use std::iter::once;
2
3use hir::{ 1use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 2 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 ModuleSource, Semantics, 3 Module, ModuleDef, ModuleSource, Semantics,
6}; 4};
7use itertools::Itertools; 5use itertools::Itertools;
8use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
@@ -10,54 +8,66 @@ use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 8 defs::{classify_name, classify_name_ref, Definition},
11 RootDatabase, 9 RootDatabase,
12}; 10};
13use ra_syntax::{ 11use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
14 ast::{self, DocCommentsOwner}, 12use stdx::format_to;
15 match_ast, AstNode, 13use test_utils::mark;
16 SyntaxKind::*,
17 SyntaxToken, TokenAtOffset,
18};
19 14
20use crate::{ 15use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 16 display::{macro_label, ShortLabel, ToNav, TryToNav},
22 FilePosition, RangeInfo, 17 markup::Markup,
18 runnables::runnable,
19 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
23}; 20};
24 21
25/// Contains the results when hovering over an item 22#[derive(Clone, Debug, PartialEq, Eq)]
26#[derive(Debug, Default)] 23pub struct HoverConfig {
27pub struct HoverResult { 24 pub implementations: bool,
28 results: Vec<String>, 25 pub run: bool,
26 pub debug: bool,
27 pub goto_type_def: bool,
29} 28}
30 29
31impl HoverResult { 30impl Default for HoverConfig {
32 pub fn new() -> HoverResult { 31 fn default() -> Self {
33 Self::default() 32 Self { implementations: true, run: true, debug: true, goto_type_def: true }
34 } 33 }
34}
35 35
36 pub fn extend(&mut self, item: Option<String>) { 36impl HoverConfig {
37 self.results.extend(item); 37 pub const NO_ACTIONS: Self =
38 } 38 Self { implementations: false, run: false, debug: false, goto_type_def: false };
39 39
40 pub fn is_empty(&self) -> bool { 40 pub fn any(&self) -> bool {
41 self.results.is_empty() 41 self.implementations || self.runnable() || self.goto_type_def
42 } 42 }
43 43
44 pub fn len(&self) -> usize { 44 pub fn none(&self) -> bool {
45 self.results.len() 45 !self.any()
46 } 46 }
47 47
48 pub fn first(&self) -> Option<&str> { 48 pub fn runnable(&self) -> bool {
49 self.results.first().map(String::as_str) 49 self.run || self.debug
50 } 50 }
51}
51 52
52 pub fn results(&self) -> &[String] { 53#[derive(Debug, Clone)]
53 &self.results 54pub enum HoverAction {
54 } 55 Runnable(Runnable),
56 Implementaion(FilePosition),
57 GoToType(Vec<HoverGotoTypeData>),
58}
55 59
56 /// Returns the results converted into markup 60#[derive(Debug, Clone, Eq, PartialEq)]
57 /// for displaying in a UI 61pub struct HoverGotoTypeData {
58 pub fn to_markup(&self) -> String { 62 pub mod_path: String,
59 self.results.join("\n\n---\n") 63 pub nav: NavigationTarget,
60 } 64}
65
66/// Contains the results when hovering over an item
67#[derive(Debug, Default)]
68pub struct HoverResult {
69 pub markup: Markup,
70 pub actions: Vec<HoverAction>,
61} 71}
62 72
63// Feature: Hover 73// Feature: Hover
@@ -70,23 +80,32 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
70 let token = pick_best(file.token_at_offset(position.offset))?; 80 let token = pick_best(file.token_at_offset(position.offset))?;
71 let token = sema.descend_into_macros(token); 81 let token = sema.descend_into_macros(token);
72 82
73 let mut res = HoverResult::new(); 83 let mut res = HoverResult::default();
74 84
75 if let Some((node, name_kind)) = match_ast! { 85 let node = token.parent();
76 match (token.parent()) { 86 let definition = match_ast! {
77 ast::NameRef(name_ref) => { 87 match node {
78 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) 88 ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition()),
79 }, 89 ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition()),
80 ast::Name(name) => {
81 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
82 },
83 _ => None, 90 _ => None,
84 } 91 }
85 } { 92 };
86 let range = sema.original_range(&node).range; 93 if let Some(definition) = definition {
87 res.extend(hover_text_from_name_kind(db, name_kind)); 94 if let Some(markup) = hover_for_definition(db, definition) {
95 res.markup = markup;
96 if let Some(action) = show_implementations_action(db, definition) {
97 res.actions.push(action);
98 }
88 99
89 if !res.is_empty() { 100 if let Some(action) = runnable_action(&sema, definition, position.file_id) {
101 res.actions.push(action);
102 }
103
104 if let Some(action) = goto_type_action(db, definition) {
105 res.actions.push(action);
106 }
107
108 let range = sema.original_range(&node).range;
90 return Some(RangeInfo::new(range, res)); 109 return Some(RangeInfo::new(range, res));
91 } 110 }
92 } 111 }
@@ -97,35 +116,134 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
97 116
98 let ty = match_ast! { 117 let ty = match_ast! {
99 match node { 118 match node {
100 ast::MacroCall(_it) => { 119 ast::Expr(it) => sema.type_of_expr(&it)?,
101 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. 120 ast::Pat(it) => sema.type_of_pat(&it)?,
102 // (e.g expanding a builtin macro). So we give up here. 121 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
103 return None; 122 // (e.g expanding a builtin macro). So we give up here.
104 }, 123 ast::MacroCall(_it) => return None,
105 ast::Expr(it) => { 124 _ => return None,
106 sema.type_of_expr(&it)
107 },
108 ast::Pat(it) => {
109 sema.type_of_pat(&it)
110 },
111 _ => None,
112 } 125 }
113 }?; 126 };
114 127
115 res.extend(Some(rust_code_markup(&ty.display(db)))); 128 res.markup = Markup::fenced_block(&ty.display(db));
116 let range = sema.original_range(&node).range; 129 let range = sema.original_range(&node).range;
117 Some(RangeInfo::new(range, res)) 130 Some(RangeInfo::new(range, res))
118} 131}
119 132
120fn hover_text( 133fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
134 fn to_action(nav_target: NavigationTarget) -> HoverAction {
135 HoverAction::Implementaion(FilePosition {
136 file_id: nav_target.file_id(),
137 offset: nav_target.range().start(),
138 })
139 }
140
141 match def {
142 Definition::ModuleDef(it) => match it {
143 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))),
144 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))),
145 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
146 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
147 _ => None,
148 },
149 _ => None,
150 }
151}
152
153fn runnable_action(
154 sema: &Semantics<RootDatabase>,
155 def: Definition,
156 file_id: FileId,
157) -> Option<HoverAction> {
158 match def {
159 Definition::ModuleDef(it) => match it {
160 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
161 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
162 .map(|it| HoverAction::Runnable(it)),
163 _ => None,
164 },
165 ModuleDef::Function(it) => {
166 let src = it.source(sema.db);
167 if src.file_id != file_id.into() {
168 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
169 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
170
171 return None;
172 }
173
174 runnable(&sema, src.value.syntax().clone(), file_id)
175 .map(|it| HoverAction::Runnable(it))
176 }
177 _ => None,
178 },
179 _ => None,
180 }
181}
182
183fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
184 match def {
185 Definition::Local(it) => {
186 let mut targets: Vec<ModuleDef> = Vec::new();
187 let mut push_new_def = |item: ModuleDef| {
188 if !targets.contains(&item) {
189 targets.push(item);
190 }
191 };
192
193 it.ty(db).walk(db, |t| {
194 if let Some(adt) = t.as_adt() {
195 push_new_def(adt.into());
196 } else if let Some(trait_) = t.as_dyn_trait() {
197 push_new_def(trait_.into());
198 } else if let Some(traits) = t.as_impl_traits(db) {
199 traits.into_iter().for_each(|it| push_new_def(it.into()));
200 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
201 push_new_def(trait_.into());
202 }
203 });
204
205 let targets = targets
206 .into_iter()
207 .filter_map(|it| {
208 Some(HoverGotoTypeData {
209 mod_path: render_path(
210 db,
211 it.module(db)?,
212 it.name(db).map(|name| name.to_string()),
213 ),
214 nav: it.try_to_nav(db)?,
215 })
216 })
217 .collect();
218
219 Some(HoverAction::GoToType(targets))
220 }
221 _ => None,
222 }
223}
224
225fn hover_markup(
121 docs: Option<String>, 226 docs: Option<String>,
122 desc: Option<String>, 227 desc: Option<String>,
123 mod_path: Option<String>, 228 mod_path: Option<String>,
124) -> Option<String> { 229) -> Option<Markup> {
125 if let Some(desc) = desc { 230 match desc {
126 Some(rust_code_markup_with_doc(&desc, docs.as_deref(), mod_path.as_deref())) 231 Some(desc) => {
127 } else { 232 let mut buf = String::new();
128 docs 233
234 if let Some(mod_path) = mod_path {
235 if !mod_path.is_empty() {
236 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
237 }
238 }
239 format_to!(buf, "```rust\n{}\n```", desc);
240
241 if let Some(doc) = docs {
242 format_to!(buf, "\n___\n\n{}", doc);
243 }
244 Some(buf.into())
245 }
246 None => docs.map(Markup::from),
129 } 247 }
130} 248}
131 249
@@ -147,35 +265,35 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
147 .map(|name| name.to_string()) 265 .map(|name| name.to_string())
148} 266}
149 267
150fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 268fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
151 let mod_path = def.module(db).map(|module| { 269 let crate_name =
152 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) 270 db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string);
153 .chain( 271 let module_path = module
154 module 272 .path_to_root(db)
155 .path_to_root(db) 273 .into_iter()
156 .into_iter() 274 .rev()
157 .rev() 275 .flat_map(|it| it.name(db).map(|name| name.to_string()));
158 .map(|it| it.name(db).map(|name| name.to_string())), 276 crate_name.into_iter().chain(module_path).chain(item_name).join("::")
159 ) 277}
160 .chain(once(definition_owner_name(db, def))) 278
161 .flatten() 279fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
162 .join("::") 280 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
163 }); 281}
164 mod_path 282
165} 283fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
166 284 let mod_path = definition_mod_path(db, &def);
167fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
168 let mod_path = determine_mod_path(db, &def);
169 return match def { 285 return match def {
170 Definition::Macro(it) => { 286 Definition::Macro(it) => {
171 let src = it.source(db); 287 let src = it.source(db);
172 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) 288 let docs = Documentation::from_ast(&src.value).map(Into::into);
289 hover_markup(docs, Some(macro_label(&src.value)), mod_path)
173 } 290 }
174 Definition::Field(it) => { 291 Definition::Field(it) => {
175 let src = it.source(db); 292 let src = it.source(db);
176 match src.value { 293 match src.value {
177 FieldSource::Named(it) => { 294 FieldSource::Named(it) => {
178 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 295 let docs = Documentation::from_ast(&it).map(Into::into);
296 hover_markup(docs, it.short_label(), mod_path)
179 } 297 }
180 _ => None, 298 _ => None,
181 } 299 }
@@ -183,7 +301,8 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
183 Definition::ModuleDef(it) => match it { 301 Definition::ModuleDef(it) => match it {
184 ModuleDef::Module(it) => match it.definition_source(db).value { 302 ModuleDef::Module(it) => match it.definition_source(db).value {
185 ModuleSource::Module(it) => { 303 ModuleSource::Module(it) => {
186 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 304 let docs = Documentation::from_ast(&it).map(Into::into);
305 hover_markup(docs, it.short_label(), mod_path)
187 } 306 }
188 _ => None, 307 _ => None,
189 }, 308 },
@@ -196,22 +315,23 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
196 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 315 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
197 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 316 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
198 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 317 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
199 ModuleDef::BuiltinType(it) => Some(it.to_string()), 318 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()),
200 }, 319 },
201 Definition::Local(it) => Some(rust_code_markup(&it.ty(db).display(db))), 320 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))),
202 Definition::TypeParam(_) | Definition::SelfType(_) => { 321 Definition::TypeParam(_) | Definition::SelfType(_) => {
203 // FIXME: Hover for generic param 322 // FIXME: Hover for generic param
204 None 323 None
205 } 324 }
206 }; 325 };
207 326
208 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 327 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
209 where 328 where
210 D: HasSource<Ast = A>, 329 D: HasSource<Ast = A>,
211 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 330 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212 { 331 {
213 let src = def.source(db); 332 let src = def.source(db);
214 hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) 333 let docs = Documentation::from_ast(&src.value).map(Into::into);
334 hover_markup(docs, src.value.short_label(), mod_path)
215 } 335 }
216} 336}
217 337
@@ -220,7 +340,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
220 fn priority(n: &SyntaxToken) -> usize { 340 fn priority(n: &SyntaxToken) -> usize {
221 match n.kind() { 341 match n.kind() {
222 IDENT | INT_NUMBER => 3, 342 IDENT | INT_NUMBER => 3,
223 L_PAREN | R_PAREN => 2, 343 T!['('] | T![')'] => 2,
224 kind if kind.is_trivia() => 0, 344 kind if kind.is_trivia() => 0,
225 _ => 1, 345 _ => 1,
226 } 346 }
@@ -229,649 +349,682 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
229 349
230#[cfg(test)] 350#[cfg(test)]
231mod tests { 351mod tests {
352 use expect::{expect, Expect};
232 use ra_db::FileLoader; 353 use ra_db::FileLoader;
233 use ra_syntax::TextRange;
234 354
235 use crate::mock_analysis::{analysis_and_position, single_file_with_position}; 355 use crate::mock_analysis::analysis_and_position;
236 356
237 fn trim_markup(s: &str) -> &str { 357 use super::*;
238 s.trim_start_matches("```rust\n").trim_end_matches("\n```")
239 }
240 358
241 fn trim_markup_opt(s: Option<&str>) -> Option<&str> { 359 fn check_hover_no_result(ra_fixture: &str) {
242 s.map(trim_markup) 360 let (analysis, position) = analysis_and_position(ra_fixture);
361 assert!(analysis.hover(position).unwrap().is_none());
243 } 362 }
244 363
245 fn check_hover_result(fixture: &str, expected: &[&str]) -> String { 364 fn check(ra_fixture: &str, expect: Expect) {
246 let (analysis, position) = analysis_and_position(fixture); 365 let (analysis, position) = analysis_and_position(ra_fixture);
247 let hover = analysis.hover(position).unwrap().unwrap(); 366 let hover = analysis.hover(position).unwrap().unwrap();
248 let mut results = Vec::from(hover.info.results());
249 results.sort();
250
251 for (markup, expected) in
252 results.iter().zip(expected.iter().chain(std::iter::repeat(&"<missing>")))
253 {
254 assert_eq!(trim_markup(&markup), *expected);
255 }
256
257 assert_eq!(hover.info.len(), expected.len());
258 367
259 let content = analysis.db.file_text(position.file_id); 368 let content = analysis.db.file_text(position.file_id);
260 content[hover.range].to_string() 369 let hovered_element = &content[hover.range];
370
371 let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
372 expect.assert_eq(&actual)
261 } 373 }
262 374
263 fn check_hover_no_result(fixture: &str) { 375 fn check_actions(ra_fixture: &str, expect: Expect) {
264 let (analysis, position) = analysis_and_position(fixture); 376 let (analysis, position) = analysis_and_position(ra_fixture);
265 assert!(analysis.hover(position).unwrap().is_none()); 377 let hover = analysis.hover(position).unwrap().unwrap();
378 expect.assert_debug_eq(&hover.info.actions)
266 } 379 }
267 380
268 #[test] 381 #[test]
269 fn hover_shows_type_of_an_expression() { 382 fn hover_shows_type_of_an_expression() {
270 let (analysis, position) = single_file_with_position( 383 check(
271 " 384 r#"
272 pub fn foo() -> u32 { 1 } 385pub fn foo() -> u32 { 1 }
273 386
274 fn main() { 387fn main() {
275 let foo_test = foo()<|>; 388 let foo_test = foo()<|>;
276 } 389}
277 ", 390"#,
391 expect![[r#"
392 *foo()*
393 ```rust
394 u32
395 ```
396 "#]],
278 ); 397 );
279 let hover = analysis.hover(position).unwrap().unwrap();
280 assert_eq!(hover.range, TextRange::new(95.into(), 100.into()));
281 assert_eq!(trim_markup_opt(hover.info.first()), Some("u32"));
282 } 398 }
283 399
284 #[test] 400 #[test]
285 fn hover_shows_long_type_of_an_expression() { 401 fn hover_shows_long_type_of_an_expression() {
286 check_hover_result( 402 check(
287 r#" 403 r#"
288 //- /main.rs 404struct Scan<A, B, C> { a: A, b: B, c: C }
289 struct Scan<A, B, C> { 405struct Iter<I> { inner: I }
290 a: A, 406enum Option<T> { Some(T), None }
291 b: B,
292 c: C,
293 }
294 407
295 struct FakeIter<I> { 408struct OtherStruct<T> { i: T }
296 inner: I,
297 }
298
299 struct OtherStruct<T> {
300 i: T,
301 }
302 409
303 enum FakeOption<T> { 410fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
304 Some(T), 411 Iter { inner: Scan { a, b, c } }
305 None, 412}
306 }
307
308 fn scan<A, B, C>(a: A, b: B, c: C) -> FakeIter<Scan<OtherStruct<A>, B, C>> {
309 FakeIter { inner: Scan { a, b, c } }
310 }
311 413
312 fn main() { 414fn main() {
313 let num: i32 = 55; 415 let num: i32 = 55;
314 let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> FakeOption<u32> { 416 let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
315 FakeOption::Some(*memo + value) 417 Option::Some(*memo + value)
316 }; 418 };
317 let number = 5u32; 419 let number = 5u32;
318 let mut iter<|> = scan(OtherStruct { i: num }, closure, number); 420 let mut iter<|> = scan(OtherStruct { i: num }, closure, number);
319 } 421}
320 "#, 422"#,
321 &["FakeIter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> FakeOption<u32>, u32>>"], 423 expect![[r#"
424 *iter*
425 ```rust
426 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
427 ```
428 "#]],
322 ); 429 );
323 } 430 }
324 431
325 #[test] 432 #[test]
326 fn hover_shows_fn_signature() { 433 fn hover_shows_fn_signature() {
327 // Single file with result 434 // Single file with result
328 check_hover_result( 435 check(
329 r#" 436 r#"
330 //- /main.rs 437pub fn foo() -> u32 { 1 }
331 pub fn foo() -> u32 { 1 } 438
332 439fn main() { let foo_test = fo<|>o(); }
333 fn main() { 440"#,
334 let foo_test = fo<|>o(); 441 expect![[r#"
335 } 442 *foo*
336 "#, 443 ```rust
337 &["pub fn foo() -> u32"], 444 pub fn foo() -> u32
445 ```
446 "#]],
338 ); 447 );
339 448
340 // Multiple candidates but results are ambiguous. 449 // Multiple candidates but results are ambiguous.
341 check_hover_result( 450 check(
342 r#" 451 r#"
343 //- /a.rs 452//- /a.rs
344 pub fn foo() -> u32 { 1 } 453pub fn foo() -> u32 { 1 }
345 454
346 //- /b.rs 455//- /b.rs
347 pub fn foo() -> &str { "" } 456pub fn foo() -> &str { "" }
348 457
349 //- /c.rs 458//- /c.rs
350 pub fn foo(a: u32, b: u32) {} 459pub fn foo(a: u32, b: u32) {}
351 460
352 //- /main.rs 461//- /main.rs
353 mod a; 462mod a;
354 mod b; 463mod b;
355 mod c; 464mod c;
356 465
357 fn main() { 466fn main() { let foo_test = fo<|>o(); }
358 let foo_test = fo<|>o();
359 }
360 "#, 467 "#,
361 &["{unknown}"], 468 expect![[r#"
469 *foo*
470 ```rust
471 {unknown}
472 ```
473 "#]],
362 ); 474 );
363 } 475 }
364 476
365 #[test] 477 #[test]
366 fn hover_shows_fn_signature_with_type_params() { 478 fn hover_shows_fn_signature_with_type_params() {
367 check_hover_result( 479 check(
368 r#" 480 r#"
369 //- /main.rs 481pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
370 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
371 482
372 fn main() { 483fn main() { let foo_test = fo<|>o(); }
373 let foo_test = fo<|>o();
374 }
375 "#, 484 "#,
376 &["pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str"], 485 expect![[r#"
486 *foo*
487 ```rust
488 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str
489 ```
490 "#]],
377 ); 491 );
378 } 492 }
379 493
380 #[test] 494 #[test]
381 fn hover_shows_fn_signature_on_fn_name() { 495 fn hover_shows_fn_signature_on_fn_name() {
382 check_hover_result( 496 check(
383 r#" 497 r#"
384 //- /main.rs 498pub fn foo<|>(a: u32, b: u32) -> u32 {}
385 pub fn foo<|>(a: u32, b: u32) -> u32 {} 499
386 500fn main() { }
387 fn main() { 501"#,
388 } 502 expect![[r#"
389 "#, 503 *foo*
390 &["pub fn foo(a: u32, b: u32) -> u32"], 504 ```rust
505 pub fn foo(a: u32, b: u32) -> u32
506 ```
507 "#]],
391 ); 508 );
392 } 509 }
393 510
394 #[test] 511 #[test]
395 fn hover_shows_struct_field_info() { 512 fn hover_shows_struct_field_info() {
396 // Hovering over the field when instantiating 513 // Hovering over the field when instantiating
397 check_hover_result( 514 check(
398 r#" 515 r#"
399 //- /main.rs 516struct Foo { field_a: u32 }
400 struct Foo {
401 field_a: u32,
402 }
403 517
404 fn main() { 518fn main() {
405 let foo = Foo { 519 let foo = Foo { field_a<|>: 0, };
406 field_a<|>: 0, 520}
407 }; 521"#,
408 } 522 expect![[r#"
409 "#, 523 *field_a*
410 &["Foo\n```\n\n```rust\nfield_a: u32"], 524 ```rust
525 Foo
526 ```
527
528 ```rust
529 field_a: u32
530 ```
531 "#]],
411 ); 532 );
412 533
413 // Hovering over the field in the definition 534 // Hovering over the field in the definition
414 check_hover_result( 535 check(
415 r#" 536 r#"
416 //- /main.rs 537struct Foo { field_a<|>: u32 }
417 struct Foo {
418 field_a<|>: u32,
419 }
420 538
421 fn main() { 539fn main() {
422 let foo = Foo { 540 let foo = Foo { field_a: 0 };
423 field_a: 0, 541}
424 }; 542"#,
425 } 543 expect![[r#"
426 "#, 544 *field_a*
427 &["Foo\n```\n\n```rust\nfield_a: u32"], 545 ```rust
546 Foo
547 ```
548
549 ```rust
550 field_a: u32
551 ```
552 "#]],
428 ); 553 );
429 } 554 }
430 555
431 #[test] 556 #[test]
432 fn hover_const_static() { 557 fn hover_const_static() {
433 check_hover_result( 558 check(
434 r#" 559 r#"const foo<|>: u32 = 0;"#,
435 //- /main.rs 560 expect![[r#"
436 const foo<|>: u32 = 0; 561 *foo*
437 "#, 562 ```rust
438 &["const foo: u32"], 563 const foo: u32
564 ```
565 "#]],
439 ); 566 );
440 567 check(
441 check_hover_result( 568 r#"static foo<|>: u32 = 0;"#,
442 r#" 569 expect![[r#"
443 //- /main.rs 570 *foo*
444 static foo<|>: u32 = 0; 571 ```rust
445 "#, 572 static foo: u32
446 &["static foo: u32"], 573 ```
574 "#]],
447 ); 575 );
448 } 576 }
449 577
450 #[test] 578 #[test]
451 fn hover_default_generic_types() { 579 fn hover_default_generic_types() {
452 check_hover_result( 580 check(
453 r#" 581 r#"
454//- /main.rs 582struct Test<K, T = u8> { k: K, t: T }
455struct Test<K, T = u8> {
456 k: K,
457 t: T,
458}
459 583
460fn main() { 584fn main() {
461 let zz<|> = Test { t: 23, k: 33 }; 585 let zz<|> = Test { t: 23u8, k: 33 };
462}"#, 586}"#,
463 &["Test<i32, u8>"], 587 expect![[r#"
588 *zz*
589 ```rust
590 Test<i32, u8>
591 ```
592 "#]],
464 ); 593 );
465 } 594 }
466 595
467 #[test] 596 #[test]
468 fn hover_some() { 597 fn hover_some() {
469 let (analysis, position) = single_file_with_position( 598 check(
470 " 599 r#"
471 enum Option<T> { Some(T) } 600enum Option<T> { Some(T) }
472 use Option::Some; 601use Option::Some;
473 602
474 fn main() { 603fn main() { So<|>me(12); }
475 So<|>me(12); 604"#,
476 } 605 expect![[r#"
477 ", 606 *Some*
607 ```rust
608 Option
609 ```
610
611 ```rust
612 Some
613 ```
614 "#]],
478 ); 615 );
479 let hover = analysis.hover(position).unwrap().unwrap();
480 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome"));
481
482 let (analysis, position) = single_file_with_position(
483 "
484 enum Option<T> { Some(T) }
485 use Option::Some;
486 616
487 fn main() { 617 check(
488 let b<|>ar = Some(12); 618 r#"
489 } 619enum Option<T> { Some(T) }
490 ", 620use Option::Some;
621
622fn main() { let b<|>ar = Some(12); }
623"#,
624 expect![[r#"
625 *bar*
626 ```rust
627 Option<i32>
628 ```
629 "#]],
491 ); 630 );
492 let hover = analysis.hover(position).unwrap().unwrap();
493 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option<i32>"));
494 } 631 }
495 632
496 #[test] 633 #[test]
497 fn hover_enum_variant() { 634 fn hover_enum_variant() {
498 check_hover_result( 635 check(
499 r#" 636 r#"
500 //- /main.rs 637enum Option<T> {
501 enum Option<T> { 638 /// The None variant
502 /// The None variant 639 Non<|>e
503 Non<|>e 640}
504 } 641"#,
505 "#, 642 expect![[r#"
506 &[" 643 *None*
507Option 644 ```rust
508``` 645 Option
509 646 ```
510```rust 647
511None 648 ```rust
512``` 649 None
513___ 650 ```
514 651 ___
515The None variant 652
516 " 653 The None variant
517 .trim()], 654 "#]],
518 ); 655 );
519 656
520 check_hover_result( 657 check(
521 r#" 658 r#"
522 //- /main.rs 659enum Option<T> {
523 enum Option<T> { 660 /// The Some variant
524 /// The Some variant 661 Some(T)
525 Some(T) 662}
526 } 663fn main() {
527 fn main() { 664 let s = Option::Som<|>e(12);
528 let s = Option::Som<|>e(12); 665}
529 } 666"#,
530 "#, 667 expect![[r#"
531 &[" 668 *Some*
532Option 669 ```rust
533``` 670 Option
534 671 ```
535```rust 672
536Some 673 ```rust
537``` 674 Some
538___ 675 ```
539 676 ___
540The Some variant 677
541 " 678 The Some variant
542 .trim()], 679 "#]],
543 ); 680 );
544 } 681 }
545 682
546 #[test] 683 #[test]
547 fn hover_for_local_variable() { 684 fn hover_for_local_variable() {
548 let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); 685 check(
549 let hover = analysis.hover(position).unwrap().unwrap(); 686 r#"fn func(foo: i32) { fo<|>o; }"#,
550 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 687 expect![[r#"
688 *foo*
689 ```rust
690 i32
691 ```
692 "#]],
693 )
551 } 694 }
552 695
553 #[test] 696 #[test]
554 fn hover_for_local_variable_pat() { 697 fn hover_for_local_variable_pat() {
555 let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); 698 check(
556 let hover = analysis.hover(position).unwrap().unwrap(); 699 r#"fn func(fo<|>o: i32) {}"#,
557 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 700 expect![[r#"
701 *foo*
702 ```rust
703 i32
704 ```
705 "#]],
706 )
558 } 707 }
559 708
560 #[test] 709 #[test]
561 fn hover_local_var_edge() { 710 fn hover_local_var_edge() {
562 let (analysis, position) = single_file_with_position( 711 check(
563 " 712 r#"fn func(foo: i32) { if true { <|>foo; }; }"#,
564fn func(foo: i32) { if true { <|>foo; }; } 713 expect![[r#"
565", 714 *foo*
566 ); 715 ```rust
567 let hover = analysis.hover(position).unwrap().unwrap(); 716 i32
568 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 717 ```
718 "#]],
719 )
569 } 720 }
570 721
571 #[test] 722 #[test]
572 fn hover_for_param_edge() { 723 fn hover_for_param_edge() {
573 let (analysis, position) = single_file_with_position("fn func(<|>foo: i32) {}"); 724 check(
574 let hover = analysis.hover(position).unwrap().unwrap(); 725 r#"fn func(<|>foo: i32) {}"#,
575 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 726 expect![[r#"
727 *foo*
728 ```rust
729 i32
730 ```
731 "#]],
732 )
576 } 733 }
577 734
578 #[test] 735 #[test]
579 fn test_hover_infer_associated_method_result() { 736 fn test_hover_infer_associated_method_result() {
580 let (analysis, position) = single_file_with_position( 737 check(
581 " 738 r#"
582 struct Thing { x: u32 } 739struct Thing { x: u32 }
583 740
584 impl Thing { 741impl Thing {
585 fn new() -> Thing { 742 fn new() -> Thing { Thing { x: 0 } }
586 Thing { x: 0 } 743}
587 }
588 }
589 744
590 fn main() { 745fn main() { let foo_<|>test = Thing::new(); }
591 let foo_<|>test = Thing::new(); 746 "#,
592 } 747 expect![[r#"
593 ", 748 *foo_test*
594 ); 749 ```rust
595 let hover = analysis.hover(position).unwrap().unwrap(); 750 Thing
596 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 751 ```
752 "#]],
753 )
597 } 754 }
598 755
599 #[test] 756 #[test]
600 fn test_hover_infer_associated_method_exact() { 757 fn test_hover_infer_associated_method_exact() {
601 let (analysis, position) = single_file_with_position( 758 check(
602 " 759 r#"
603 mod wrapper { 760mod wrapper {
604 struct Thing { x: u32 } 761 struct Thing { x: u32 }
605 762
606 impl Thing { 763 impl Thing {
607 fn new() -> Thing { 764 fn new() -> Thing { Thing { x: 0 } }
608 Thing { x: 0 } 765 }
609 } 766}
610 }
611 }
612 767
613 fn main() { 768fn main() { let foo_test = wrapper::Thing::new<|>(); }
614 let foo_test = wrapper::Thing::new<|>(); 769"#,
615 } 770 expect![[r#"
616 ", 771 *new*
617 ); 772 ```rust
618 let hover = analysis.hover(position).unwrap().unwrap(); 773 wrapper::Thing
619 assert_eq!( 774 ```
620 trim_markup_opt(hover.info.first()), 775
621 Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing") 776 ```rust
622 ); 777 fn new() -> Thing
778 ```
779 "#]],
780 )
623 } 781 }
624 782
625 #[test] 783 #[test]
626 fn test_hover_infer_associated_const_in_pattern() { 784 fn test_hover_infer_associated_const_in_pattern() {
627 let (analysis, position) = single_file_with_position( 785 check(
628 " 786 r#"
629 struct X; 787struct X;
630 impl X { 788impl X {
631 const C: u32 = 1; 789 const C: u32 = 1;
632 } 790}
633 791
634 fn main() { 792fn main() {
635 match 1 { 793 match 1 {
636 X::C<|> => {}, 794 X::C<|> => {},
637 2 => {}, 795 2 => {},
638 _ => {} 796 _ => {}
639 }; 797 };
640 } 798}
641 ", 799"#,
642 ); 800 expect![[r#"
643 let hover = analysis.hover(position).unwrap().unwrap(); 801 *C*
644 assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32")); 802 ```rust
803 const C: u32
804 ```
805 "#]],
806 )
645 } 807 }
646 808
647 #[test] 809 #[test]
648 fn test_hover_self() { 810 fn test_hover_self() {
649 let (analysis, position) = single_file_with_position( 811 check(
650 " 812 r#"
651 struct Thing { x: u32 } 813struct Thing { x: u32 }
652 impl Thing { 814impl Thing {
653 fn new() -> Self { 815 fn new() -> Self { Self<|> { x: 0 } }
654 Self<|> { x: 0 } 816}
655 } 817"#,
656 } 818 expect![[r#"
657 ", 819 *Self { x: 0 }*
658 ); 820 ```rust
659 let hover = analysis.hover(position).unwrap().unwrap(); 821 Thing
660 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 822 ```
661 823 "#]],
662 /* FIXME: revive these tests 824 )
663 let (analysis, position) = single_file_with_position( 825 } /* FIXME: revive these tests
664 " 826 let (analysis, position) = analysis_and_position(
665 struct Thing { x: u32 } 827 "
666 impl Thing { 828 struct Thing { x: u32 }
667 fn new() -> Self<|> { 829 impl Thing {
668 Self { x: 0 } 830 fn new() -> Self<|> {
669 } 831 Self { x: 0 }
670 } 832 }
671 ", 833 }
672 ); 834 ",
673 835 );
674 let hover = analysis.hover(position).unwrap().unwrap(); 836
675 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 837 let hover = analysis.hover(position).unwrap().unwrap();
676 838 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing"));
677 let (analysis, position) = single_file_with_position( 839
678 " 840 let (analysis, position) = analysis_and_position(
679 enum Thing { A } 841 "
680 impl Thing { 842 enum Thing { A }
681 pub fn new() -> Self<|> { 843 impl Thing {
682 Thing::A 844 pub fn new() -> Self<|> {
683 } 845 Thing::A
684 } 846 }
685 ", 847 }
686 ); 848 ",
687 let hover = analysis.hover(position).unwrap().unwrap(); 849 );
688 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 850 let hover = analysis.hover(position).unwrap().unwrap();
689 851 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing"));
690 let (analysis, position) = single_file_with_position( 852
691 " 853 let (analysis, position) = analysis_and_position(
692 enum Thing { A } 854 "
693 impl Thing { 855 enum Thing { A }
694 pub fn thing(a: Self<|>) { 856 impl Thing {
695 } 857 pub fn thing(a: Self<|>) {
696 } 858 }
697 ", 859 }
698 ); 860 ",
699 let hover = analysis.hover(position).unwrap().unwrap(); 861 );
700 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 862 let hover = analysis.hover(position).unwrap().unwrap();
701 */ 863 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing"));
702 } 864 */
703 865
704 #[test] 866 #[test]
705 fn test_hover_shadowing_pat() { 867 fn test_hover_shadowing_pat() {
706 let (analysis, position) = single_file_with_position( 868 check(
707 " 869 r#"
708 fn x() {} 870fn x() {}
709 871
710 fn y() { 872fn y() {
711 let x = 0i32; 873 let x = 0i32;
712 x<|>; 874 x<|>;
713 } 875}
714 ", 876"#,
715 ); 877 expect![[r#"
716 let hover = analysis.hover(position).unwrap().unwrap(); 878 *x*
717 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 879 ```rust
880 i32
881 ```
882 "#]],
883 )
718 } 884 }
719 885
720 #[test] 886 #[test]
721 fn test_hover_macro_invocation() { 887 fn test_hover_macro_invocation() {
722 let (analysis, position) = single_file_with_position( 888 check(
723 " 889 r#"
724 macro_rules! foo { 890macro_rules! foo { () => {} }
725 () => {} 891
726 } 892fn f() { fo<|>o!(); }
727 893"#,
728 fn f() { 894 expect![[r#"
729 fo<|>o!(); 895 *foo*
730 } 896 ```rust
731 ", 897 macro_rules! foo
732 ); 898 ```
733 let hover = analysis.hover(position).unwrap().unwrap(); 899 "#]],
734 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); 900 )
735 } 901 }
736 902
737 #[test] 903 #[test]
738 fn test_hover_tuple_field() { 904 fn test_hover_tuple_field() {
739 let (analysis, position) = single_file_with_position( 905 check(
740 " 906 r#"struct TS(String, i32<|>);"#,
741 struct TS(String, i32<|>); 907 expect![[r#"
742 ", 908 *i32*
743 ); 909 i32
744 let hover = analysis.hover(position).unwrap().unwrap(); 910 "#]],
745 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 911 )
746 } 912 }
747 913
748 #[test] 914 #[test]
749 fn test_hover_through_macro() { 915 fn test_hover_through_macro() {
750 let hover_on = check_hover_result( 916 check(
751 " 917 r#"
752 //- /lib.rs 918macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
753 macro_rules! id { 919fn foo() {}
754 ($($tt:tt)*) => { $($tt)* } 920id! {
755 } 921 fn bar() { fo<|>o(); }
756 fn foo() {} 922}
757 id! { 923"#,
758 fn bar() { 924 expect![[r#"
759 fo<|>o(); 925 *foo*
760 } 926 ```rust
761 } 927 fn foo()
762 ", 928 ```
763 &["fn foo()"], 929 "#]],
764 ); 930 );
765
766 assert_eq!(hover_on, "foo")
767 } 931 }
768 932
769 #[test] 933 #[test]
770 fn test_hover_through_expr_in_macro() { 934 fn test_hover_through_expr_in_macro() {
771 let hover_on = check_hover_result( 935 check(
772 " 936 r#"
773 //- /lib.rs 937macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
774 macro_rules! id { 938fn foo(bar:u32) { let a = id!(ba<|>r); }
775 ($($tt:tt)*) => { $($tt)* } 939"#,
776 } 940 expect![[r#"
777 fn foo(bar:u32) { 941 *bar*
778 let a = id!(ba<|>r); 942 ```rust
779 } 943 u32
780 ", 944 ```
781 &["u32"], 945 "#]],
782 ); 946 );
783
784 assert_eq!(hover_on, "bar")
785 } 947 }
786 948
787 #[test] 949 #[test]
788 fn test_hover_through_expr_in_macro_recursive() { 950 fn test_hover_through_expr_in_macro_recursive() {
789 let hover_on = check_hover_result( 951 check(
790 " 952 r#"
791 //- /lib.rs 953macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
792 macro_rules! id_deep { 954macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
793 ($($tt:tt)*) => { $($tt)* } 955fn foo(bar:u32) { let a = id!(ba<|>r); }
794 } 956"#,
795 macro_rules! id { 957 expect![[r#"
796 ($($tt:tt)*) => { id_deep!($($tt)*) } 958 *bar*
797 } 959 ```rust
798 fn foo(bar:u32) { 960 u32
799 let a = id!(ba<|>r); 961 ```
800 } 962 "#]],
801 ",
802 &["u32"],
803 ); 963 );
804
805 assert_eq!(hover_on, "bar")
806 } 964 }
807 965
808 #[test] 966 #[test]
809 fn test_hover_through_func_in_macro_recursive() { 967 fn test_hover_through_func_in_macro_recursive() {
810 let hover_on = check_hover_result( 968 check(
811 " 969 r#"
812 //- /lib.rs 970macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
813 macro_rules! id_deep { 971macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
814 ($($tt:tt)*) => { $($tt)* } 972fn bar() -> u32 { 0 }
815 } 973fn foo() { let a = id!([0u32, bar(<|>)] ); }
816 macro_rules! id { 974"#,
817 ($($tt:tt)*) => { id_deep!($($tt)*) } 975 expect![[r#"
818 } 976 *bar()*
819 fn bar() -> u32 { 977 ```rust
820 0 978 u32
821 } 979 ```
822 fn foo() { 980 "#]],
823 let a = id!([0u32, bar(<|>)] );
824 }
825 ",
826 &["u32"],
827 ); 981 );
828
829 assert_eq!(hover_on, "bar()")
830 } 982 }
831 983
832 #[test] 984 #[test]
833 fn test_hover_through_literal_string_in_macro() { 985 fn test_hover_through_literal_string_in_macro() {
834 let hover_on = check_hover_result( 986 check(
835 r#" 987 r#"
836 //- /lib.rs 988macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
837 macro_rules! arr { 989fn foo() {
838 ($($tt:tt)*) => { [$($tt)*)] } 990 let mastered_for_itunes = "";
839 } 991 let _ = arr!("Tr<|>acks", &mastered_for_itunes);
840 fn foo() { 992}
841 let mastered_for_itunes = ""; 993"#,
842 let _ = arr!("Tr<|>acks", &mastered_for_itunes); 994 expect![[r#"
843 } 995 *"Tracks"*
844 "#, 996 ```rust
845 &["&str"], 997 &str
998 ```
999 "#]],
846 ); 1000 );
847
848 assert_eq!(hover_on, "\"Tracks\"");
849 } 1001 }
850 1002
851 #[test] 1003 #[test]
852 fn test_hover_through_assert_macro() { 1004 fn test_hover_through_assert_macro() {
853 let hover_on = check_hover_result( 1005 check(
854 r#" 1006 r#"
855 //- /lib.rs 1007#[rustc_builtin_macro]
856 #[rustc_builtin_macro] 1008macro_rules! assert {}
857 macro_rules! assert {}
858 1009
859 fn bar() -> bool { true } 1010fn bar() -> bool { true }
860 fn foo() { 1011fn foo() {
861 assert!(ba<|>r()); 1012 assert!(ba<|>r());
862 } 1013}
863 "#, 1014"#,
864 &["fn bar() -> bool"], 1015 expect![[r#"
1016 *bar*
1017 ```rust
1018 fn bar() -> bool
1019 ```
1020 "#]],
865 ); 1021 );
866
867 assert_eq!(hover_on, "bar");
868 } 1022 }
869 1023
870 #[test] 1024 #[test]
871 fn test_hover_through_literal_string_in_builtin_macro() { 1025 fn test_hover_through_literal_string_in_builtin_macro() {
872 check_hover_no_result( 1026 check_hover_no_result(
873 r#" 1027 r#"
874 //- /lib.rs
875 #[rustc_builtin_macro] 1028 #[rustc_builtin_macro]
876 macro_rules! format {} 1029 macro_rules! format {}
877 1030
@@ -884,71 +1037,1351 @@ fn func(foo: i32) { if true { <|>foo; }; }
884 1037
885 #[test] 1038 #[test]
886 fn test_hover_non_ascii_space_doc() { 1039 fn test_hover_non_ascii_space_doc() {
887 check_hover_result( 1040 check(
888 " 1041 "
889 //- /lib.rs 1042/// <- `\u{3000}` here
890 /// <- `\u{3000}` here 1043fn foo() { }
891 fn foo() {
892 }
893 1044
894 fn bar() { 1045fn bar() { fo<|>o(); }
895 fo<|>o(); 1046",
896 } 1047 expect![[r#"
897 ", 1048 *foo*
898 &["fn foo()\n```\n___\n\n<- `\u{3000}` here"], 1049 ```rust
1050 fn foo()
1051 ```
1052 ___
1053
1054 <- ` ` here
1055 "#]],
899 ); 1056 );
900 } 1057 }
901 1058
902 #[test] 1059 #[test]
903 fn test_hover_function_show_qualifiers() { 1060 fn test_hover_function_show_qualifiers() {
904 check_hover_result( 1061 check(
905 " 1062 r#"async fn foo<|>() {}"#,
906 //- /lib.rs 1063 expect![[r#"
907 async fn foo<|>() {} 1064 *foo*
908 ", 1065 ```rust
909 &["async fn foo()"], 1066 async fn foo()
1067 ```
1068 "#]],
910 ); 1069 );
911 check_hover_result( 1070 check(
912 " 1071 r#"pub const unsafe fn foo<|>() {}"#,
913 //- /lib.rs 1072 expect![[r#"
914 pub const unsafe fn foo<|>() {} 1073 *foo*
915 ", 1074 ```rust
916 &["pub const unsafe fn foo()"], 1075 pub const unsafe fn foo()
1076 ```
1077 "#]],
917 ); 1078 );
918 check_hover_result( 1079 check(
919 r#" 1080 r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#,
920 //- /lib.rs 1081 expect![[r#"
921 pub(crate) async unsafe extern "C" fn foo<|>() {} 1082 *foo*
922 "#, 1083 ```rust
923 &[r#"pub(crate) async unsafe extern "C" fn foo()"#], 1084 pub(crate) async unsafe extern "C" fn foo()
1085 ```
1086 "#]],
924 ); 1087 );
925 } 1088 }
926 1089
927 #[test] 1090 #[test]
928 fn test_hover_trait_show_qualifiers() { 1091 fn test_hover_trait_show_qualifiers() {
929 check_hover_result( 1092 check_actions(
930 " 1093 r"unsafe trait foo<|>() {}",
931 //- /lib.rs 1094 expect![[r#"
932 unsafe trait foo<|>() {} 1095 [
933 ", 1096 Implementaion(
934 &["unsafe trait foo"], 1097 FilePosition {
1098 file_id: FileId(
1099 1,
1100 ),
1101 offset: 13,
1102 },
1103 ),
1104 ]
1105 "#]],
935 ); 1106 );
936 } 1107 }
937 1108
938 #[test] 1109 #[test]
939 fn test_hover_mod_with_same_name_as_function() { 1110 fn test_hover_mod_with_same_name_as_function() {
940 check_hover_result( 1111 check(
941 " 1112 r#"
942 //- /lib.rs 1113use self::m<|>y::Bar;
943 use self::m<|>y::Bar; 1114mod my { pub struct Bar; }
1115
1116fn my() {}
1117"#,
1118 expect![[r#"
1119 *my*
1120 ```rust
1121 mod my
1122 ```
1123 "#]],
1124 );
1125 }
944 1126
945 mod my { 1127 #[test]
946 pub struct Bar; 1128 fn test_hover_struct_doc_comment() {
947 } 1129 check(
1130 r#"
1131/// bar docs
1132struct Bar;
1133
1134fn foo() { let bar = Ba<|>r; }
1135"#,
1136 expect![[r#"
1137 *Bar*
1138 ```rust
1139 struct Bar
1140 ```
1141 ___
1142
1143 bar docs
1144 "#]],
1145 );
1146 }
1147
1148 #[test]
1149 fn test_hover_struct_doc_attr() {
1150 check(
1151 r#"
1152#[doc = "bar docs"]
1153struct Bar;
1154
1155fn foo() { let bar = Ba<|>r; }
1156"#,
1157 expect![[r#"
1158 *Bar*
1159 ```rust
1160 struct Bar
1161 ```
1162 ___
1163
1164 bar docs
1165 "#]],
1166 );
1167 }
1168
1169 #[test]
1170 fn test_hover_struct_doc_attr_multiple_and_mixed() {
1171 check(
1172 r#"
1173/// bar docs 0
1174#[doc = "bar docs 1"]
1175#[doc = "bar docs 2"]
1176struct Bar;
1177
1178fn foo() { let bar = Ba<|>r; }
1179"#,
1180 expect![[r#"
1181 *Bar*
1182 ```rust
1183 struct Bar
1184 ```
1185 ___
1186
1187 bar docs 0
1188
1189 bar docs 1
1190
1191 bar docs 2
1192 "#]],
1193 );
1194 }
1195
1196 #[test]
1197 fn test_hover_macro_generated_struct_fn_doc_comment() {
1198 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1199
1200 check(
1201 r#"
1202macro_rules! bar {
1203 () => {
1204 struct Bar;
1205 impl Bar {
1206 /// Do the foo
1207 fn foo(&self) {}
1208 }
1209 }
1210}
1211
1212bar!();
1213
1214fn foo() { let bar = Bar; bar.fo<|>o(); }
1215"#,
1216 expect![[r#"
1217 *foo*
1218 ```rust
1219 Bar
1220 ```
1221
1222 ```rust
1223 fn foo(&self)
1224 ```
1225 ___
1226
1227 Do the foo
1228 "#]],
1229 );
1230 }
1231
1232 #[test]
1233 fn test_hover_macro_generated_struct_fn_doc_attr() {
1234 mark::check!(hover_macro_generated_struct_fn_doc_attr);
1235
1236 check(
1237 r#"
1238macro_rules! bar {
1239 () => {
1240 struct Bar;
1241 impl Bar {
1242 #[doc = "Do the foo"]
1243 fn foo(&self) {}
1244 }
1245 }
1246}
1247
1248bar!();
1249
1250fn foo() { let bar = Bar; bar.fo<|>o(); }
1251"#,
1252 expect![[r#"
1253 *foo*
1254 ```rust
1255 Bar
1256 ```
1257
1258 ```rust
1259 fn foo(&self)
1260 ```
1261 ___
1262
1263 Do the foo
1264 "#]],
1265 );
1266 }
1267
1268 #[test]
1269 fn test_hover_trait_has_impl_action() {
1270 check_actions(
1271 r#"trait foo<|>() {}"#,
1272 expect![[r#"
1273 [
1274 Implementaion(
1275 FilePosition {
1276 file_id: FileId(
1277 1,
1278 ),
1279 offset: 6,
1280 },
1281 ),
1282 ]
1283 "#]],
1284 );
1285 }
1286
1287 #[test]
1288 fn test_hover_struct_has_impl_action() {
1289 check_actions(
1290 r"struct foo<|>() {}",
1291 expect![[r#"
1292 [
1293 Implementaion(
1294 FilePosition {
1295 file_id: FileId(
1296 1,
1297 ),
1298 offset: 7,
1299 },
1300 ),
1301 ]
1302 "#]],
1303 );
1304 }
1305
1306 #[test]
1307 fn test_hover_union_has_impl_action() {
1308 check_actions(
1309 r#"union foo<|>() {}"#,
1310 expect![[r#"
1311 [
1312 Implementaion(
1313 FilePosition {
1314 file_id: FileId(
1315 1,
1316 ),
1317 offset: 6,
1318 },
1319 ),
1320 ]
1321 "#]],
1322 );
1323 }
1324
1325 #[test]
1326 fn test_hover_enum_has_impl_action() {
1327 check_actions(
1328 r"enum foo<|>() { A, B }",
1329 expect![[r#"
1330 [
1331 Implementaion(
1332 FilePosition {
1333 file_id: FileId(
1334 1,
1335 ),
1336 offset: 5,
1337 },
1338 ),
1339 ]
1340 "#]],
1341 );
1342 }
1343
1344 #[test]
1345 fn test_hover_test_has_action() {
1346 check_actions(
1347 r#"
1348#[test]
1349fn foo_<|>test() {}
1350"#,
1351 expect![[r#"
1352 [
1353 Runnable(
1354 Runnable {
1355 nav: NavigationTarget {
1356 file_id: FileId(
1357 1,
1358 ),
1359 full_range: 0..24,
1360 name: "foo_test",
1361 kind: FN_DEF,
1362 focus_range: Some(
1363 11..19,
1364 ),
1365 container_name: None,
1366 description: None,
1367 docs: None,
1368 },
1369 kind: Test {
1370 test_id: Path(
1371 "foo_test",
1372 ),
1373 attr: TestAttr {
1374 ignore: false,
1375 },
1376 },
1377 cfg_exprs: [],
1378 },
1379 ),
1380 ]
1381 "#]],
1382 );
1383 }
1384
1385 #[test]
1386 fn test_hover_test_mod_has_action() {
1387 check_actions(
1388 r#"
1389mod tests<|> {
1390 #[test]
1391 fn foo_test() {}
1392}
1393"#,
1394 expect![[r#"
1395 [
1396 Runnable(
1397 Runnable {
1398 nav: NavigationTarget {
1399 file_id: FileId(
1400 1,
1401 ),
1402 full_range: 0..46,
1403 name: "tests",
1404 kind: MODULE,
1405 focus_range: Some(
1406 4..9,
1407 ),
1408 container_name: None,
1409 description: None,
1410 docs: None,
1411 },
1412 kind: TestMod {
1413 path: "tests",
1414 },
1415 cfg_exprs: [],
1416 },
1417 ),
1418 ]
1419 "#]],
1420 );
1421 }
1422
1423 #[test]
1424 fn test_hover_struct_has_goto_type_action() {
1425 check_actions(
1426 r#"
1427struct S{ f1: u32 }
1428
1429fn main() { let s<|>t = S{ f1:0 }; }
1430 "#,
1431 expect![[r#"
1432 [
1433 GoToType(
1434 [
1435 HoverGotoTypeData {
1436 mod_path: "S",
1437 nav: NavigationTarget {
1438 file_id: FileId(
1439 1,
1440 ),
1441 full_range: 0..19,
1442 name: "S",
1443 kind: STRUCT_DEF,
1444 focus_range: Some(
1445 7..8,
1446 ),
1447 container_name: None,
1448 description: Some(
1449 "struct S",
1450 ),
1451 docs: None,
1452 },
1453 },
1454 ],
1455 ),
1456 ]
1457 "#]],
1458 );
1459 }
1460
1461 #[test]
1462 fn test_hover_generic_struct_has_goto_type_actions() {
1463 check_actions(
1464 r#"
1465struct Arg(u32);
1466struct S<T>{ f1: T }
1467
1468fn main() { let s<|>t = S{ f1:Arg(0) }; }
1469"#,
1470 expect![[r#"
1471 [
1472 GoToType(
1473 [
1474 HoverGotoTypeData {
1475 mod_path: "S",
1476 nav: NavigationTarget {
1477 file_id: FileId(
1478 1,
1479 ),
1480 full_range: 17..37,
1481 name: "S",
1482 kind: STRUCT_DEF,
1483 focus_range: Some(
1484 24..25,
1485 ),
1486 container_name: None,
1487 description: Some(
1488 "struct S",
1489 ),
1490 docs: None,
1491 },
1492 },
1493 HoverGotoTypeData {
1494 mod_path: "Arg",
1495 nav: NavigationTarget {
1496 file_id: FileId(
1497 1,
1498 ),
1499 full_range: 0..16,
1500 name: "Arg",
1501 kind: STRUCT_DEF,
1502 focus_range: Some(
1503 7..10,
1504 ),
1505 container_name: None,
1506 description: Some(
1507 "struct Arg",
1508 ),
1509 docs: None,
1510 },
1511 },
1512 ],
1513 ),
1514 ]
1515 "#]],
1516 );
1517 }
1518
1519 #[test]
1520 fn test_hover_generic_struct_has_flattened_goto_type_actions() {
1521 check_actions(
1522 r#"
1523struct Arg(u32);
1524struct S<T>{ f1: T }
1525
1526fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; }
1527 "#,
1528 expect![[r#"
1529 [
1530 GoToType(
1531 [
1532 HoverGotoTypeData {
1533 mod_path: "S",
1534 nav: NavigationTarget {
1535 file_id: FileId(
1536 1,
1537 ),
1538 full_range: 17..37,
1539 name: "S",
1540 kind: STRUCT_DEF,
1541 focus_range: Some(
1542 24..25,
1543 ),
1544 container_name: None,
1545 description: Some(
1546 "struct S",
1547 ),
1548 docs: None,
1549 },
1550 },
1551 HoverGotoTypeData {
1552 mod_path: "Arg",
1553 nav: NavigationTarget {
1554 file_id: FileId(
1555 1,
1556 ),
1557 full_range: 0..16,
1558 name: "Arg",
1559 kind: STRUCT_DEF,
1560 focus_range: Some(
1561 7..10,
1562 ),
1563 container_name: None,
1564 description: Some(
1565 "struct Arg",
1566 ),
1567 docs: None,
1568 },
1569 },
1570 ],
1571 ),
1572 ]
1573 "#]],
1574 );
1575 }
1576
1577 #[test]
1578 fn test_hover_tuple_has_goto_type_actions() {
1579 check_actions(
1580 r#"
1581struct A(u32);
1582struct B(u32);
1583mod M {
1584 pub struct C(u32);
1585}
1586
1587fn main() { let s<|>t = (A(1), B(2), M::C(3) ); }
1588"#,
1589 expect![[r#"
1590 [
1591 GoToType(
1592 [
1593 HoverGotoTypeData {
1594 mod_path: "A",
1595 nav: NavigationTarget {
1596 file_id: FileId(
1597 1,
1598 ),
1599 full_range: 0..14,
1600 name: "A",
1601 kind: STRUCT_DEF,
1602 focus_range: Some(
1603 7..8,
1604 ),
1605 container_name: None,
1606 description: Some(
1607 "struct A",
1608 ),
1609 docs: None,
1610 },
1611 },
1612 HoverGotoTypeData {
1613 mod_path: "B",
1614 nav: NavigationTarget {
1615 file_id: FileId(
1616 1,
1617 ),
1618 full_range: 15..29,
1619 name: "B",
1620 kind: STRUCT_DEF,
1621 focus_range: Some(
1622 22..23,
1623 ),
1624 container_name: None,
1625 description: Some(
1626 "struct B",
1627 ),
1628 docs: None,
1629 },
1630 },
1631 HoverGotoTypeData {
1632 mod_path: "M::C",
1633 nav: NavigationTarget {
1634 file_id: FileId(
1635 1,
1636 ),
1637 full_range: 42..60,
1638 name: "C",
1639 kind: STRUCT_DEF,
1640 focus_range: Some(
1641 53..54,
1642 ),
1643 container_name: None,
1644 description: Some(
1645 "pub struct C",
1646 ),
1647 docs: None,
1648 },
1649 },
1650 ],
1651 ),
1652 ]
1653 "#]],
1654 );
1655 }
1656
1657 #[test]
1658 fn test_hover_return_impl_trait_has_goto_type_action() {
1659 check_actions(
1660 r#"
1661trait Foo {}
1662fn foo() -> impl Foo {}
1663
1664fn main() { let s<|>t = foo(); }
1665"#,
1666 expect![[r#"
1667 [
1668 GoToType(
1669 [
1670 HoverGotoTypeData {
1671 mod_path: "Foo",
1672 nav: NavigationTarget {
1673 file_id: FileId(
1674 1,
1675 ),
1676 full_range: 0..12,
1677 name: "Foo",
1678 kind: TRAIT_DEF,
1679 focus_range: Some(
1680 6..9,
1681 ),
1682 container_name: None,
1683 description: Some(
1684 "trait Foo",
1685 ),
1686 docs: None,
1687 },
1688 },
1689 ],
1690 ),
1691 ]
1692 "#]],
1693 );
1694 }
1695
1696 #[test]
1697 fn test_hover_generic_return_impl_trait_has_goto_type_action() {
1698 check_actions(
1699 r#"
1700trait Foo<T> {}
1701struct S;
1702fn foo() -> impl Foo<S> {}
1703
1704fn main() { let s<|>t = foo(); }
1705"#,
1706 expect![[r#"
1707 [
1708 GoToType(
1709 [
1710 HoverGotoTypeData {
1711 mod_path: "Foo",
1712 nav: NavigationTarget {
1713 file_id: FileId(
1714 1,
1715 ),
1716 full_range: 0..15,
1717 name: "Foo",
1718 kind: TRAIT_DEF,
1719 focus_range: Some(
1720 6..9,
1721 ),
1722 container_name: None,
1723 description: Some(
1724 "trait Foo",
1725 ),
1726 docs: None,
1727 },
1728 },
1729 HoverGotoTypeData {
1730 mod_path: "S",
1731 nav: NavigationTarget {
1732 file_id: FileId(
1733 1,
1734 ),
1735 full_range: 16..25,
1736 name: "S",
1737 kind: STRUCT_DEF,
1738 focus_range: Some(
1739 23..24,
1740 ),
1741 container_name: None,
1742 description: Some(
1743 "struct S",
1744 ),
1745 docs: None,
1746 },
1747 },
1748 ],
1749 ),
1750 ]
1751 "#]],
1752 );
1753 }
1754
1755 #[test]
1756 fn test_hover_return_impl_traits_has_goto_type_action() {
1757 check_actions(
1758 r#"
1759trait Foo {}
1760trait Bar {}
1761fn foo() -> impl Foo + Bar {}
1762
1763fn main() { let s<|>t = foo(); }
1764 "#,
1765 expect![[r#"
1766 [
1767 GoToType(
1768 [
1769 HoverGotoTypeData {
1770 mod_path: "Foo",
1771 nav: NavigationTarget {
1772 file_id: FileId(
1773 1,
1774 ),
1775 full_range: 0..12,
1776 name: "Foo",
1777 kind: TRAIT_DEF,
1778 focus_range: Some(
1779 6..9,
1780 ),
1781 container_name: None,
1782 description: Some(
1783 "trait Foo",
1784 ),
1785 docs: None,
1786 },
1787 },
1788 HoverGotoTypeData {
1789 mod_path: "Bar",
1790 nav: NavigationTarget {
1791 file_id: FileId(
1792 1,
1793 ),
1794 full_range: 13..25,
1795 name: "Bar",
1796 kind: TRAIT_DEF,
1797 focus_range: Some(
1798 19..22,
1799 ),
1800 container_name: None,
1801 description: Some(
1802 "trait Bar",
1803 ),
1804 docs: None,
1805 },
1806 },
1807 ],
1808 ),
1809 ]
1810 "#]],
1811 );
1812 }
1813
1814 #[test]
1815 fn test_hover_generic_return_impl_traits_has_goto_type_action() {
1816 check_actions(
1817 r#"
1818trait Foo<T> {}
1819trait Bar<T> {}
1820struct S1 {}
1821struct S2 {}
1822
1823fn foo() -> impl Foo<S1> + Bar<S2> {}
1824
1825fn main() { let s<|>t = foo(); }
1826"#,
1827 expect![[r#"
1828 [
1829 GoToType(
1830 [
1831 HoverGotoTypeData {
1832 mod_path: "Foo",
1833 nav: NavigationTarget {
1834 file_id: FileId(
1835 1,
1836 ),
1837 full_range: 0..15,
1838 name: "Foo",
1839 kind: TRAIT_DEF,
1840 focus_range: Some(
1841 6..9,
1842 ),
1843 container_name: None,
1844 description: Some(
1845 "trait Foo",
1846 ),
1847 docs: None,
1848 },
1849 },
1850 HoverGotoTypeData {
1851 mod_path: "Bar",
1852 nav: NavigationTarget {
1853 file_id: FileId(
1854 1,
1855 ),
1856 full_range: 16..31,
1857 name: "Bar",
1858 kind: TRAIT_DEF,
1859 focus_range: Some(
1860 22..25,
1861 ),
1862 container_name: None,
1863 description: Some(
1864 "trait Bar",
1865 ),
1866 docs: None,
1867 },
1868 },
1869 HoverGotoTypeData {
1870 mod_path: "S1",
1871 nav: NavigationTarget {
1872 file_id: FileId(
1873 1,
1874 ),
1875 full_range: 32..44,
1876 name: "S1",
1877 kind: STRUCT_DEF,
1878 focus_range: Some(
1879 39..41,
1880 ),
1881 container_name: None,
1882 description: Some(
1883 "struct S1",
1884 ),
1885 docs: None,
1886 },
1887 },
1888 HoverGotoTypeData {
1889 mod_path: "S2",
1890 nav: NavigationTarget {
1891 file_id: FileId(
1892 1,
1893 ),
1894 full_range: 45..57,
1895 name: "S2",
1896 kind: STRUCT_DEF,
1897 focus_range: Some(
1898 52..54,
1899 ),
1900 container_name: None,
1901 description: Some(
1902 "struct S2",
1903 ),
1904 docs: None,
1905 },
1906 },
1907 ],
1908 ),
1909 ]
1910 "#]],
1911 );
1912 }
1913
1914 #[test]
1915 fn test_hover_arg_impl_trait_has_goto_type_action() {
1916 check_actions(
1917 r#"
1918trait Foo {}
1919fn foo(ar<|>g: &impl Foo) {}
1920"#,
1921 expect![[r#"
1922 [
1923 GoToType(
1924 [
1925 HoverGotoTypeData {
1926 mod_path: "Foo",
1927 nav: NavigationTarget {
1928 file_id: FileId(
1929 1,
1930 ),
1931 full_range: 0..12,
1932 name: "Foo",
1933 kind: TRAIT_DEF,
1934 focus_range: Some(
1935 6..9,
1936 ),
1937 container_name: None,
1938 description: Some(
1939 "trait Foo",
1940 ),
1941 docs: None,
1942 },
1943 },
1944 ],
1945 ),
1946 ]
1947 "#]],
1948 );
1949 }
1950
1951 #[test]
1952 fn test_hover_arg_impl_traits_has_goto_type_action() {
1953 check_actions(
1954 r#"
1955trait Foo {}
1956trait Bar<T> {}
1957struct S{}
1958
1959fn foo(ar<|>g: &impl Foo + Bar<S>) {}
1960"#,
1961 expect![[r#"
1962 [
1963 GoToType(
1964 [
1965 HoverGotoTypeData {
1966 mod_path: "Foo",
1967 nav: NavigationTarget {
1968 file_id: FileId(
1969 1,
1970 ),
1971 full_range: 0..12,
1972 name: "Foo",
1973 kind: TRAIT_DEF,
1974 focus_range: Some(
1975 6..9,
1976 ),
1977 container_name: None,
1978 description: Some(
1979 "trait Foo",
1980 ),
1981 docs: None,
1982 },
1983 },
1984 HoverGotoTypeData {
1985 mod_path: "Bar",
1986 nav: NavigationTarget {
1987 file_id: FileId(
1988 1,
1989 ),
1990 full_range: 13..28,
1991 name: "Bar",
1992 kind: TRAIT_DEF,
1993 focus_range: Some(
1994 19..22,
1995 ),
1996 container_name: None,
1997 description: Some(
1998 "trait Bar",
1999 ),
2000 docs: None,
2001 },
2002 },
2003 HoverGotoTypeData {
2004 mod_path: "S",
2005 nav: NavigationTarget {
2006 file_id: FileId(
2007 1,
2008 ),
2009 full_range: 29..39,
2010 name: "S",
2011 kind: STRUCT_DEF,
2012 focus_range: Some(
2013 36..37,
2014 ),
2015 container_name: None,
2016 description: Some(
2017 "struct S",
2018 ),
2019 docs: None,
2020 },
2021 },
2022 ],
2023 ),
2024 ]
2025 "#]],
2026 );
2027 }
2028
2029 #[test]
2030 fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
2031 check_actions(
2032 r#"
2033trait Foo<T> {}
2034struct S {}
2035fn foo(ar<|>g: &impl Foo<S>) {}
2036"#,
2037 expect![[r#"
2038 [
2039 GoToType(
2040 [
2041 HoverGotoTypeData {
2042 mod_path: "Foo",
2043 nav: NavigationTarget {
2044 file_id: FileId(
2045 1,
2046 ),
2047 full_range: 0..15,
2048 name: "Foo",
2049 kind: TRAIT_DEF,
2050 focus_range: Some(
2051 6..9,
2052 ),
2053 container_name: None,
2054 description: Some(
2055 "trait Foo",
2056 ),
2057 docs: None,
2058 },
2059 },
2060 HoverGotoTypeData {
2061 mod_path: "S",
2062 nav: NavigationTarget {
2063 file_id: FileId(
2064 1,
2065 ),
2066 full_range: 16..27,
2067 name: "S",
2068 kind: STRUCT_DEF,
2069 focus_range: Some(
2070 23..24,
2071 ),
2072 container_name: None,
2073 description: Some(
2074 "struct S",
2075 ),
2076 docs: None,
2077 },
2078 },
2079 ],
2080 ),
2081 ]
2082 "#]],
2083 );
2084 }
2085
2086 #[test]
2087 fn test_hover_dyn_return_has_goto_type_action() {
2088 check_actions(
2089 r#"
2090trait Foo {}
2091struct S;
2092impl Foo for S {}
2093
2094struct B<T>{}
2095fn foo() -> B<dyn Foo> {}
2096
2097fn main() { let s<|>t = foo(); }
2098"#,
2099 expect![[r#"
2100 [
2101 GoToType(
2102 [
2103 HoverGotoTypeData {
2104 mod_path: "B",
2105 nav: NavigationTarget {
2106 file_id: FileId(
2107 1,
2108 ),
2109 full_range: 42..55,
2110 name: "B",
2111 kind: STRUCT_DEF,
2112 focus_range: Some(
2113 49..50,
2114 ),
2115 container_name: None,
2116 description: Some(
2117 "struct B",
2118 ),
2119 docs: None,
2120 },
2121 },
2122 HoverGotoTypeData {
2123 mod_path: "Foo",
2124 nav: NavigationTarget {
2125 file_id: FileId(
2126 1,
2127 ),
2128 full_range: 0..12,
2129 name: "Foo",
2130 kind: TRAIT_DEF,
2131 focus_range: Some(
2132 6..9,
2133 ),
2134 container_name: None,
2135 description: Some(
2136 "trait Foo",
2137 ),
2138 docs: None,
2139 },
2140 },
2141 ],
2142 ),
2143 ]
2144 "#]],
2145 );
2146 }
2147
2148 #[test]
2149 fn test_hover_dyn_arg_has_goto_type_action() {
2150 check_actions(
2151 r#"
2152trait Foo {}
2153fn foo(ar<|>g: &dyn Foo) {}
2154"#,
2155 expect![[r#"
2156 [
2157 GoToType(
2158 [
2159 HoverGotoTypeData {
2160 mod_path: "Foo",
2161 nav: NavigationTarget {
2162 file_id: FileId(
2163 1,
2164 ),
2165 full_range: 0..12,
2166 name: "Foo",
2167 kind: TRAIT_DEF,
2168 focus_range: Some(
2169 6..9,
2170 ),
2171 container_name: None,
2172 description: Some(
2173 "trait Foo",
2174 ),
2175 docs: None,
2176 },
2177 },
2178 ],
2179 ),
2180 ]
2181 "#]],
2182 );
2183 }
2184
2185 #[test]
2186 fn test_hover_generic_dyn_arg_has_goto_type_action() {
2187 check_actions(
2188 r#"
2189trait Foo<T> {}
2190struct S {}
2191fn foo(ar<|>g: &dyn Foo<S>) {}
2192"#,
2193 expect![[r#"
2194 [
2195 GoToType(
2196 [
2197 HoverGotoTypeData {
2198 mod_path: "Foo",
2199 nav: NavigationTarget {
2200 file_id: FileId(
2201 1,
2202 ),
2203 full_range: 0..15,
2204 name: "Foo",
2205 kind: TRAIT_DEF,
2206 focus_range: Some(
2207 6..9,
2208 ),
2209 container_name: None,
2210 description: Some(
2211 "trait Foo",
2212 ),
2213 docs: None,
2214 },
2215 },
2216 HoverGotoTypeData {
2217 mod_path: "S",
2218 nav: NavigationTarget {
2219 file_id: FileId(
2220 1,
2221 ),
2222 full_range: 16..27,
2223 name: "S",
2224 kind: STRUCT_DEF,
2225 focus_range: Some(
2226 23..24,
2227 ),
2228 container_name: None,
2229 description: Some(
2230 "struct S",
2231 ),
2232 docs: None,
2233 },
2234 },
2235 ],
2236 ),
2237 ]
2238 "#]],
2239 );
2240 }
2241
2242 #[test]
2243 fn test_hover_goto_type_action_links_order() {
2244 check_actions(
2245 r#"
2246trait ImplTrait<T> {}
2247trait DynTrait<T> {}
2248struct B<T> {}
2249struct S {}
2250
2251fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
2252 "#,
2253 expect![[r#"
2254 [
2255 GoToType(
2256 [
2257 HoverGotoTypeData {
2258 mod_path: "ImplTrait",
2259 nav: NavigationTarget {
2260 file_id: FileId(
2261 1,
2262 ),
2263 full_range: 0..21,
2264 name: "ImplTrait",
2265 kind: TRAIT_DEF,
2266 focus_range: Some(
2267 6..15,
2268 ),
2269 container_name: None,
2270 description: Some(
2271 "trait ImplTrait",
2272 ),
2273 docs: None,
2274 },
2275 },
2276 HoverGotoTypeData {
2277 mod_path: "B",
2278 nav: NavigationTarget {
2279 file_id: FileId(
2280 1,
2281 ),
2282 full_range: 43..57,
2283 name: "B",
2284 kind: STRUCT_DEF,
2285 focus_range: Some(
2286 50..51,
2287 ),
2288 container_name: None,
2289 description: Some(
2290 "struct B",
2291 ),
2292 docs: None,
2293 },
2294 },
2295 HoverGotoTypeData {
2296 mod_path: "DynTrait",
2297 nav: NavigationTarget {
2298 file_id: FileId(
2299 1,
2300 ),
2301 full_range: 22..42,
2302 name: "DynTrait",
2303 kind: TRAIT_DEF,
2304 focus_range: Some(
2305 28..36,
2306 ),
2307 container_name: None,
2308 description: Some(
2309 "trait DynTrait",
2310 ),
2311 docs: None,
2312 },
2313 },
2314 HoverGotoTypeData {
2315 mod_path: "S",
2316 nav: NavigationTarget {
2317 file_id: FileId(
2318 1,
2319 ),
2320 full_range: 58..69,
2321 name: "S",
2322 kind: STRUCT_DEF,
2323 focus_range: Some(
2324 65..66,
2325 ),
2326 container_name: None,
2327 description: Some(
2328 "struct S",
2329 ),
2330 docs: None,
2331 },
2332 },
2333 ],
2334 ),
2335 ]
2336 "#]],
2337 );
2338 }
2339
2340 #[test]
2341 fn test_hover_associated_type_has_goto_type_action() {
2342 check_actions(
2343 r#"
2344trait Foo {
2345 type Item;
2346 fn get(self) -> Self::Item {}
2347}
948 2348
949 fn my() {} 2349struct Bar{}
950 ", 2350struct S{}
951 &["mod my"], 2351
2352impl Foo for S { type Item = Bar; }
2353
2354fn test() -> impl Foo { S {} }
2355
2356fn main() { let s<|>t = test().get(); }
2357"#,
2358 expect![[r#"
2359 [
2360 GoToType(
2361 [
2362 HoverGotoTypeData {
2363 mod_path: "Foo",
2364 nav: NavigationTarget {
2365 file_id: FileId(
2366 1,
2367 ),
2368 full_range: 0..62,
2369 name: "Foo",
2370 kind: TRAIT_DEF,
2371 focus_range: Some(
2372 6..9,
2373 ),
2374 container_name: None,
2375 description: Some(
2376 "trait Foo",
2377 ),
2378 docs: None,
2379 },
2380 },
2381 ],
2382 ),
2383 ]
2384 "#]],
952 ); 2385 );
953 } 2386 }
954} 2387}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 75bd3c96b..3cbae8a45 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{FileId, FunctionSignature}; 9use crate::{FileId, FunctionSignature};
@@ -112,7 +112,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 112 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 113 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 114 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 115 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 116 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 117 if ty.is_unknown() {
118 return None; 118 return None;
@@ -149,11 +149,10 @@ fn get_param_name_hints(
149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), 149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
150 _ => return None, 150 _ => return None,
151 }; 151 };
152 let args_count = args.clone().count();
153 152
154 let fn_signature = get_fn_signature(sema, &expr)?; 153 let fn_signature = get_fn_signature(sema, &expr)?;
155 let n_params_to_skip = 154 let n_params_to_skip =
156 if fn_signature.has_self_param && fn_signature.parameter_names.len() > args_count { 155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) {
157 1 156 1
158 } else { 157 } else {
159 0 158 0
@@ -259,6 +258,7 @@ fn should_show_param_name_hint(
259 if param_name.is_empty() 258 if param_name.is_empty()
260 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
261 || is_argument_similar_to_param_name(sema, argument, param_name) 260 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture")
262 { 262 {
263 return false; 263 return false;
264 } 264 }
@@ -271,7 +271,7 @@ fn should_show_param_name_hint(
271 271
272 // avoid displaying hints for common functions like map, filter, etc. 272 // avoid displaying hints for common functions like map, filter, etc.
273 // or other obvious words used in std 273 // or other obvious words used in std
274 parameters_len != 1 || !is_obvious_param(param_name) 274 !(parameters_len == 1 && is_obvious_param(param_name))
275} 275}
276 276
277fn is_argument_similar_to_param_name( 277fn is_argument_similar_to_param_name(
@@ -313,10 +313,8 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
313} 313}
314 314
315fn is_obvious_param(param_name: &str) -> bool { 315fn is_obvious_param(param_name: &str) -> bool {
316 let is_obvious_param_name = match param_name { 316 let is_obvious_param_name =
317 "predicate" | "value" | "pat" | "rhs" | "other" => true, 317 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
318 _ => false,
319 };
320 param_name.len() == 1 || is_obvious_param_name 318 param_name.len() == 1 || is_obvious_param_name
321} 319}
322 320
@@ -326,13 +324,13 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
326 // FIXME: Type::as_callable is broken for closures 324 // FIXME: Type::as_callable is broken for closures
327 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; 325 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
328 match callable_def { 326 match callable_def {
329 hir::CallableDef::FunctionId(it) => { 327 hir::CallableDefId::FunctionId(it) => {
330 Some(FunctionSignature::from_hir(sema.db, it.into())) 328 Some(FunctionSignature::from_hir(sema.db, it.into()))
331 } 329 }
332 hir::CallableDef::StructId(it) => { 330 hir::CallableDefId::StructId(it) => {
333 FunctionSignature::from_struct(sema.db, it.into()) 331 FunctionSignature::from_struct(sema.db, it.into())
334 } 332 }
335 hir::CallableDef::EnumVariantId(it) => { 333 hir::CallableDefId::EnumVariantId(it) => {
336 FunctionSignature::from_enum_variant(sema.db, it.into()) 334 FunctionSignature::from_enum_variant(sema.db, it.into())
337 } 335 }
338 } 336 }
@@ -347,581 +345,252 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
347 345
348#[cfg(test)] 346#[cfg(test)]
349mod tests { 347mod tests {
350 use crate::inlay_hints::InlayHintsConfig; 348 use expect::{expect, Expect};
351 use insta::assert_debug_snapshot; 349 use test_utils::extract_annotations;
350
351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
352 352
353 use crate::mock_analysis::single_file; 353 fn check(ra_fixture: &str) {
354 check_with_config(InlayHintsConfig::default(), ra_fixture);
355 }
356
357 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual);
364 }
365
366 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints)
370 }
354 371
355 #[test] 372 #[test]
356 fn param_hints_only() { 373 fn param_hints_only() {
357 let (analysis, file_id) = single_file( 374 check_with_config(
375 InlayHintsConfig {
376 parameter_hints: true,
377 type_hints: false,
378 chaining_hints: false,
379 max_length: None,
380 },
358 r#" 381 r#"
359 fn foo(a: i32, b: i32) -> i32 { a + b } 382fn foo(a: i32, b: i32) -> i32 { a + b }
360 fn main() { 383fn main() {
361 let _x = foo(4, 4); 384 let _x = foo(
362 }"#, 385 4,
386 //^ a
387 4,
388 //^ b
389 );
390}"#,
363 ); 391 );
364 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
365 [
366 InlayHint {
367 range: 106..107,
368 kind: ParameterHint,
369 label: "a",
370 },
371 InlayHint {
372 range: 109..110,
373 kind: ParameterHint,
374 label: "b",
375 },
376 ]"###);
377 } 392 }
378 393
379 #[test] 394 #[test]
380 fn hints_disabled() { 395 fn hints_disabled() {
381 let (analysis, file_id) = single_file( 396 check_with_config(
397 InlayHintsConfig {
398 type_hints: false,
399 parameter_hints: false,
400 chaining_hints: false,
401 max_length: None,
402 },
382 r#" 403 r#"
383 fn foo(a: i32, b: i32) -> i32 { a + b } 404fn foo(a: i32, b: i32) -> i32 { a + b }
384 fn main() { 405fn main() {
385 let _x = foo(4, 4); 406 let _x = foo(4, 4);
386 }"#, 407}"#,
387 ); 408 );
388 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
389 } 409 }
390 410
391 #[test] 411 #[test]
392 fn type_hints_only() { 412 fn type_hints_only() {
393 let (analysis, file_id) = single_file( 413 check_with_config(
414 InlayHintsConfig {
415 type_hints: true,
416 parameter_hints: false,
417 chaining_hints: false,
418 max_length: None,
419 },
394 r#" 420 r#"
395 fn foo(a: i32, b: i32) -> i32 { a + b } 421fn foo(a: i32, b: i32) -> i32 { a + b }
396 fn main() { 422fn main() {
397 let _x = foo(4, 4); 423 let _x = foo(4, 4);
398 }"#, 424 //^^ i32
425}"#,
399 ); 426 );
400 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
401 [
402 InlayHint {
403 range: 97..99,
404 kind: TypeHint,
405 label: "i32",
406 },
407 ]"###);
408 } 427 }
428
409 #[test] 429 #[test]
410 fn default_generic_types_should_not_be_displayed() { 430 fn default_generic_types_should_not_be_displayed() {
411 let (analysis, file_id) = single_file( 431 check(
412 r#" 432 r#"
413struct Test<K, T = u8> { 433struct Test<K, T = u8> { k: K, t: T }
414 k: K,
415 t: T,
416}
417 434
418fn main() { 435fn main() {
419 let zz = Test { t: 23, k: 33 }; 436 let zz = Test { t: 23u8, k: 33 };
437 //^^ Test<i32>
420 let zz_ref = &zz; 438 let zz_ref = &zz;
439 //^^^^^^ &Test<i32>
421}"#, 440}"#,
422 ); 441 );
423
424 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
425 [
426 InlayHint {
427 range: 69..71,
428 kind: TypeHint,
429 label: "Test<i32>",
430 },
431 InlayHint {
432 range: 105..111,
433 kind: TypeHint,
434 label: "&Test<i32>",
435 },
436 ]
437 "###
438 );
439 } 442 }
440 443
441 #[test] 444 #[test]
442 fn let_statement() { 445 fn let_statement() {
443 let (analysis, file_id) = single_file( 446 check(
444 r#" 447 r#"
445#[derive(PartialEq)] 448#[derive(PartialEq)]
446enum CustomOption<T> { 449enum Option<T> { None, Some(T) }
447 None,
448 Some(T),
449}
450 450
451#[derive(PartialEq)] 451#[derive(PartialEq)]
452struct Test { 452struct Test { a: Option<u32>, b: u8 }
453 a: CustomOption<u32>,
454 b: u8,
455}
456 453
457fn main() { 454fn main() {
458 struct InnerStruct {} 455 struct InnerStruct {}
459 456
460 let test = 54; 457 let test = 54;
458 //^^^^ i32
461 let test: i32 = 33; 459 let test: i32 = 33;
462 let mut test = 33; 460 let mut test = 33;
461 //^^^^^^^^ i32
463 let _ = 22; 462 let _ = 22;
464 let test = "test"; 463 let test = "test";
464 //^^^^ &str
465 let test = InnerStruct {}; 465 let test = InnerStruct {};
466 466
467 let test = vec![222]; 467 let test = unresolved();
468 let test: Vec<_> = (0..3).collect();
469 let test = (0..3).collect::<Vec<i128>>();
470 let test = (0..3).collect::<Vec<_>>();
471
472 let mut test = Vec::new();
473 test.push(333);
474 468
475 let test = (42, 'a'); 469 let test = (42, 'a');
476 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 470 //^^^^ (i32, char)
471 let (a, (b, (c,)) = (2, (3, (9.2,));
472 //^ i32 ^ i32 ^ f64
477 let &x = &92; 473 let &x = &92;
474 //^ i32
478}"#, 475}"#,
479 ); 476 );
480
481 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
482 [
483 InlayHint {
484 range: 193..197,
485 kind: TypeHint,
486 label: "i32",
487 },
488 InlayHint {
489 range: 236..244,
490 kind: TypeHint,
491 label: "i32",
492 },
493 InlayHint {
494 range: 275..279,
495 kind: TypeHint,
496 label: "&str",
497 },
498 InlayHint {
499 range: 539..543,
500 kind: TypeHint,
501 label: "(i32, char)",
502 },
503 InlayHint {
504 range: 566..567,
505 kind: TypeHint,
506 label: "i32",
507 },
508 InlayHint {
509 range: 570..571,
510 kind: TypeHint,
511 label: "i32",
512 },
513 InlayHint {
514 range: 573..574,
515 kind: TypeHint,
516 label: "i32",
517 },
518 InlayHint {
519 range: 577..578,
520 kind: TypeHint,
521 label: "f64",
522 },
523 InlayHint {
524 range: 580..581,
525 kind: TypeHint,
526 label: "f64",
527 },
528 InlayHint {
529 range: 584..585,
530 kind: TypeHint,
531 label: "i32",
532 },
533 InlayHint {
534 range: 627..628,
535 kind: TypeHint,
536 label: "i32",
537 },
538 ]
539 "###
540 );
541 } 477 }
542 478
543 #[test] 479 #[test]
544 fn closure_parameters() { 480 fn closure_parameters() {
545 let (analysis, file_id) = single_file( 481 check(
546 r#" 482 r#"
547fn main() { 483fn main() {
548 let mut start = 0; 484 let mut start = 0;
549 (0..2).for_each(|increment| { 485 //^^^^^^^^^ i32
550 start += increment; 486 (0..2).for_each(|increment| { start += increment; });
551 }); 487 //^^^^^^^^^ i32
552 488
553 let multiply = |a, b, c, d| a * b * c * d; 489 let multiply =
554 let _: i32 = multiply(1, 2, 3, 4); 490 //^^^^^^^^ |…| -> i32
491 | a, b| a * b
492 //^ i32 ^ i32
493 ;
494
495 let _: i32 = multiply(1, 2);
555 let multiply_ref = &multiply; 496 let multiply_ref = &multiply;
497 //^^^^^^^^^^^^ &|…| -> i32
556 498
557 let return_42 = || 42; 499 let return_42 = || 42;
500 //^^^^^^^^^ || -> i32
558}"#, 501}"#,
559 ); 502 );
560
561 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
562 [
563 InlayHint {
564 range: 21..30,
565 kind: TypeHint,
566 label: "i32",
567 },
568 InlayHint {
569 range: 57..66,
570 kind: TypeHint,
571 label: "i32",
572 },
573 InlayHint {
574 range: 115..123,
575 kind: TypeHint,
576 label: "|…| -> i32",
577 },
578 InlayHint {
579 range: 127..128,
580 kind: TypeHint,
581 label: "i32",
582 },
583 InlayHint {
584 range: 130..131,
585 kind: TypeHint,
586 label: "i32",
587 },
588 InlayHint {
589 range: 133..134,
590 kind: TypeHint,
591 label: "i32",
592 },
593 InlayHint {
594 range: 136..137,
595 kind: TypeHint,
596 label: "i32",
597 },
598 InlayHint {
599 range: 201..213,
600 kind: TypeHint,
601 label: "&|…| -> i32",
602 },
603 InlayHint {
604 range: 236..245,
605 kind: TypeHint,
606 label: "|| -> i32",
607 },
608 ]
609 "###
610 );
611 } 503 }
612 504
613 #[test] 505 #[test]
614 fn for_expression() { 506 fn for_expression() {
615 let (analysis, file_id) = single_file( 507 check(
616 r#" 508 r#"
617fn main() { 509fn main() {
618 let mut start = 0; 510 let mut start = 0;
619 for increment in 0..2 { 511 //^^^^^^^^^ i32
620 start += increment; 512 for increment in 0..2 { start += increment; }
621 } 513 //^^^^^^^^^ i32
622}"#, 514}"#,
623 ); 515 );
624
625 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
626 [
627 InlayHint {
628 range: 21..30,
629 kind: TypeHint,
630 label: "i32",
631 },
632 InlayHint {
633 range: 44..53,
634 kind: TypeHint,
635 label: "i32",
636 },
637 ]
638 "###
639 );
640 } 516 }
641 517
642 #[test] 518 #[test]
643 fn if_expr() { 519 fn if_expr() {
644 let (analysis, file_id) = single_file( 520 check(
645 r#" 521 r#"
646#[derive(PartialEq)] 522enum Option<T> { None, Some(T) }
647enum CustomOption<T> { 523use Option::*;
648 None,
649 Some(T),
650}
651
652#[derive(PartialEq)]
653struct Test {
654 a: CustomOption<u32>,
655 b: u8,
656}
657 524
658use CustomOption::*; 525struct Test { a: Option<u32>, b: u8 }
659 526
660fn main() { 527fn main() {
661 let test = Some(Test { a: Some(3), b: 1 }); 528 let test = Some(Test { a: Some(3), b: 1 });
529 //^^^^ Option<Test>
662 if let None = &test {}; 530 if let None = &test {};
663 if let test = &test {}; 531 if let test = &test {};
532 //^^^^ &Option<Test>
664 if let Some(test) = &test {}; 533 if let Some(test) = &test {};
665 if let Some(Test { a, b }) = &test {}; 534 //^^^^ &Test
666 if let Some(Test { a: x, b: y }) = &test {}; 535 if let Some(Test { a, b }) = &test {};
667 if let Some(Test { a: Some(x), b: y }) = &test {}; 536 //^ &Option<u32> ^ &u8
668 if let Some(Test { a: None, b: y }) = &test {}; 537 if let Some(Test { a: x, b: y }) = &test {};
538 //^ &Option<u32> ^ &u8
539 if let Some(Test { a: Some(x), b: y }) = &test {};
540 //^ &u32 ^ &u8
541 if let Some(Test { a: None, b: y }) = &test {};
542 //^ &u8
669 if let Some(Test { b: y, .. }) = &test {}; 543 if let Some(Test { b: y, .. }) = &test {};
670 544 //^ &u8
671 if test == None {} 545 if test == None {}
672}"#, 546}"#,
673 ); 547 );
674
675 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
676 [
677 InlayHint {
678 range: 188..192,
679 kind: TypeHint,
680 label: "CustomOption<Test>",
681 },
682 InlayHint {
683 range: 267..271,
684 kind: TypeHint,
685 label: "&CustomOption<Test>",
686 },
687 InlayHint {
688 range: 300..304,
689 kind: TypeHint,
690 label: "&Test",
691 },
692 InlayHint {
693 range: 341..342,
694 kind: TypeHint,
695 label: "&CustomOption<u32>",
696 },
697 InlayHint {
698 range: 344..345,
699 kind: TypeHint,
700 label: "&u8",
701 },
702 InlayHint {
703 range: 387..388,
704 kind: TypeHint,
705 label: "&CustomOption<u32>",
706 },
707 InlayHint {
708 range: 393..394,
709 kind: TypeHint,
710 label: "&u8",
711 },
712 InlayHint {
713 range: 441..442,
714 kind: TypeHint,
715 label: "&u32",
716 },
717 InlayHint {
718 range: 448..449,
719 kind: TypeHint,
720 label: "&u8",
721 },
722 InlayHint {
723 range: 500..501,
724 kind: TypeHint,
725 label: "&u8",
726 },
727 InlayHint {
728 range: 543..544,
729 kind: TypeHint,
730 label: "&u8",
731 },
732 ]
733 "###
734 );
735 } 548 }
736 549
737 #[test] 550 #[test]
738 fn while_expr() { 551 fn while_expr() {
739 let (analysis, file_id) = single_file( 552 check(
740 r#" 553 r#"
741#[derive(PartialEq)] 554enum Option<T> { None, Some(T) }
742enum CustomOption<T> { 555use Option::*;
743 None,
744 Some(T),
745}
746 556
747#[derive(PartialEq)] 557struct Test { a: Option<u32>, b: u8 }
748struct Test {
749 a: CustomOption<u32>,
750 b: u8,
751}
752
753use CustomOption::*;
754 558
755fn main() { 559fn main() {
756 let test = Some(Test { a: Some(3), b: 1 }); 560 let test = Some(Test { a: Some(3), b: 1 });
757 while let None = &test {}; 561 //^^^^ Option<Test>
758 while let test = &test {}; 562 while let Some(Test { a: Some(x), b: y }) = &test {};
759 while let Some(test) = &test {}; 563 //^ &u32 ^ &u8
760 while let Some(Test { a, b }) = &test {};
761 while let Some(Test { a: x, b: y }) = &test {};
762 while let Some(Test { a: Some(x), b: y }) = &test {};
763 while let Some(Test { a: None, b: y }) = &test {};
764 while let Some(Test { b: y, .. }) = &test {};
765
766 while test == None {}
767}"#, 564}"#,
768 ); 565 );
769
770 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
771 [
772 InlayHint {
773 range: 188..192,
774 kind: TypeHint,
775 label: "CustomOption<Test>",
776 },
777 InlayHint {
778 range: 273..277,
779 kind: TypeHint,
780 label: "&CustomOption<Test>",
781 },
782 InlayHint {
783 range: 309..313,
784 kind: TypeHint,
785 label: "&Test",
786 },
787 InlayHint {
788 range: 353..354,
789 kind: TypeHint,
790 label: "&CustomOption<u32>",
791 },
792 InlayHint {
793 range: 356..357,
794 kind: TypeHint,
795 label: "&u8",
796 },
797 InlayHint {
798 range: 402..403,
799 kind: TypeHint,
800 label: "&CustomOption<u32>",
801 },
802 InlayHint {
803 range: 408..409,
804 kind: TypeHint,
805 label: "&u8",
806 },
807 InlayHint {
808 range: 459..460,
809 kind: TypeHint,
810 label: "&u32",
811 },
812 InlayHint {
813 range: 466..467,
814 kind: TypeHint,
815 label: "&u8",
816 },
817 InlayHint {
818 range: 521..522,
819 kind: TypeHint,
820 label: "&u8",
821 },
822 InlayHint {
823 range: 567..568,
824 kind: TypeHint,
825 label: "&u8",
826 },
827 ]
828 "###
829 );
830 } 566 }
831 567
832 #[test] 568 #[test]
833 fn match_arm_list() { 569 fn match_arm_list() {
834 let (analysis, file_id) = single_file( 570 check(
835 r#" 571 r#"
836#[derive(PartialEq)] 572enum Option<T> { None, Some(T) }
837enum CustomOption<T> { 573use Option::*;
838 None,
839 Some(T),
840}
841
842#[derive(PartialEq)]
843struct Test {
844 a: CustomOption<u32>,
845 b: u8,
846}
847 574
848use CustomOption::*; 575struct Test { a: Option<u32>, b: u8 }
849 576
850fn main() { 577fn main() {
851 match Some(Test { a: Some(3), b: 1 }) { 578 match Some(Test { a: Some(3), b: 1 }) {
852 None => (), 579 None => (),
853 test => (), 580 test => (),
854 Some(test) => (), 581 //^^^^ Option<Test>
855 Some(Test { a, b }) => (),
856 Some(Test { a: x, b: y }) => (),
857 Some(Test { a: Some(x), b: y }) => (), 582 Some(Test { a: Some(x), b: y }) => (),
858 Some(Test { a: None, b: y }) => (), 583 //^ u32 ^ u8
859 Some(Test { b: y, .. }) => (),
860 _ => {} 584 _ => {}
861 } 585 }
862}"#, 586}"#,
863 ); 587 );
864
865 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
866 [
867 InlayHint {
868 range: 252..256,
869 kind: TypeHint,
870 label: "CustomOption<Test>",
871 },
872 InlayHint {
873 range: 277..281,
874 kind: TypeHint,
875 label: "Test",
876 },
877 InlayHint {
878 range: 310..311,
879 kind: TypeHint,
880 label: "CustomOption<u32>",
881 },
882 InlayHint {
883 range: 313..314,
884 kind: TypeHint,
885 label: "u8",
886 },
887 InlayHint {
888 range: 348..349,
889 kind: TypeHint,
890 label: "CustomOption<u32>",
891 },
892 InlayHint {
893 range: 354..355,
894 kind: TypeHint,
895 label: "u8",
896 },
897 InlayHint {
898 range: 394..395,
899 kind: TypeHint,
900 label: "u32",
901 },
902 InlayHint {
903 range: 401..402,
904 kind: TypeHint,
905 label: "u8",
906 },
907 InlayHint {
908 range: 445..446,
909 kind: TypeHint,
910 label: "u8",
911 },
912 InlayHint {
913 range: 480..481,
914 kind: TypeHint,
915 label: "u8",
916 },
917 ]
918 "###
919 );
920 } 588 }
921 589
922 #[test] 590 #[test]
923 fn hint_truncation() { 591 fn hint_truncation() {
924 let (analysis, file_id) = single_file( 592 check_with_config(
593 InlayHintsConfig { max_length: Some(8), ..Default::default() },
925 r#" 594 r#"
926struct Smol<T>(T); 595struct Smol<T>(T);
927 596
@@ -929,52 +598,25 @@ struct VeryLongOuterName<T>(T);
929 598
930fn main() { 599fn main() {
931 let a = Smol(0u32); 600 let a = Smol(0u32);
601 //^ Smol<u32>
932 let b = VeryLongOuterName(0usize); 602 let b = VeryLongOuterName(0usize);
603 //^ VeryLongOuterName<…>
933 let c = Smol(Smol(0u32)) 604 let c = Smol(Smol(0u32))
605 //^ Smol<Smol<…>>
934}"#, 606}"#,
935 ); 607 );
936
937 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
938 [
939 InlayHint {
940 range: 74..75,
941 kind: TypeHint,
942 label: "Smol<u32>",
943 },
944 InlayHint {
945 range: 98..99,
946 kind: TypeHint,
947 label: "VeryLongOuterName<…>",
948 },
949 InlayHint {
950 range: 137..138,
951 kind: TypeHint,
952 label: "Smol<Smol<…>>",
953 },
954 ]
955 "###
956 );
957 } 608 }
958 609
959 #[test] 610 #[test]
960 fn function_call_parameter_hint() { 611 fn function_call_parameter_hint() {
961 let (analysis, file_id) = single_file( 612 check(
962 r#" 613 r#"
963enum CustomOption<T> { 614enum Option<T> { None, Some(T) }
964 None, 615use Option::*;
965 Some(T),
966}
967use CustomOption::*;
968 616
969struct FileId {} 617struct FileId {}
970struct SmolStr {} 618struct SmolStr {}
971 619
972impl From<&str> for SmolStr {
973 fn from(_: &str) -> Self {
974 unimplemented!()
975 }
976}
977
978struct TextRange {} 620struct TextRange {}
979struct SyntaxKind {} 621struct SyntaxKind {}
980struct NavigationTarget {} 622struct NavigationTarget {}
@@ -982,18 +624,15 @@ struct NavigationTarget {}
982struct Test {} 624struct Test {}
983 625
984impl Test { 626impl Test {
985 fn method(&self, mut param: i32) -> i32 { 627 fn method(&self, mut param: i32) -> i32 { param * 2 }
986 param * 2
987 }
988 628
989 fn from_syntax( 629 fn from_syntax(
990 file_id: FileId, 630 file_id: FileId,
991 name: SmolStr, 631 name: SmolStr,
992 focus_range: CustomOption<TextRange>, 632 focus_range: Option<TextRange>,
993 full_range: TextRange, 633 full_range: TextRange,
994 kind: SyntaxKind, 634 kind: SyntaxKind,
995 docs: CustomOption<String>, 635 docs: Option<String>,
996 description: CustomOption<String>,
997 ) -> NavigationTarget { 636 ) -> NavigationTarget {
998 NavigationTarget {} 637 NavigationTarget {}
999 } 638 }
@@ -1005,108 +644,36 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1005 644
1006fn main() { 645fn main() {
1007 let not_literal = 1; 646 let not_literal = 1;
1008 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 647 //^^^^^^^^^^^ i32
648 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
649 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1009 let t: Test = Test {}; 650 let t: Test = Test {};
1010 t.method(123); 651 t.method(123);
1011 Test::method(&t, 3456); 652 //^^^ param
1012 653 Test::method(&t, 3456);
654 //^^ &self ^^^^ param
1013 Test::from_syntax( 655 Test::from_syntax(
1014 FileId {}, 656 FileId {},
657 //^^^^^^^^^ file_id
1015 "impl".into(), 658 "impl".into(),
659 //^^^^^^^^^^^^^ name
1016 None, 660 None,
661 //^^^^ focus_range
1017 TextRange {}, 662 TextRange {},
663 //^^^^^^^^^^^^ full_range
1018 SyntaxKind {}, 664 SyntaxKind {},
665 //^^^^^^^^^^^^^ kind
1019 None, 666 None,
1020 None, 667 //^^^^ docs
1021 ); 668 );
1022}"#, 669}"#,
1023 ); 670 );
1024
1025 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1026 [
1027 InlayHint {
1028 range: 798..809,
1029 kind: TypeHint,
1030 label: "i32",
1031 },
1032 InlayHint {
1033 range: 842..843,
1034 kind: ParameterHint,
1035 label: "foo",
1036 },
1037 InlayHint {
1038 range: 845..846,
1039 kind: ParameterHint,
1040 label: "bar",
1041 },
1042 InlayHint {
1043 range: 848..855,
1044 kind: ParameterHint,
1045 label: "msg",
1046 },
1047 InlayHint {
1048 range: 860..871,
1049 kind: ParameterHint,
1050 label: "last",
1051 },
1052 InlayHint {
1053 range: 914..917,
1054 kind: ParameterHint,
1055 label: "param",
1056 },
1057 InlayHint {
1058 range: 937..939,
1059 kind: ParameterHint,
1060 label: "&self",
1061 },
1062 InlayHint {
1063 range: 941..945,
1064 kind: ParameterHint,
1065 label: "param",
1066 },
1067 InlayHint {
1068 range: 980..989,
1069 kind: ParameterHint,
1070 label: "file_id",
1071 },
1072 InlayHint {
1073 range: 999..1012,
1074 kind: ParameterHint,
1075 label: "name",
1076 },
1077 InlayHint {
1078 range: 1022..1026,
1079 kind: ParameterHint,
1080 label: "focus_range",
1081 },
1082 InlayHint {
1083 range: 1036..1048,
1084 kind: ParameterHint,
1085 label: "full_range",
1086 },
1087 InlayHint {
1088 range: 1058..1071,
1089 kind: ParameterHint,
1090 label: "kind",
1091 },
1092 InlayHint {
1093 range: 1081..1085,
1094 kind: ParameterHint,
1095 label: "docs",
1096 },
1097 InlayHint {
1098 range: 1095..1099,
1099 kind: ParameterHint,
1100 label: "description",
1101 },
1102 ]
1103 "###
1104 );
1105 } 671 }
1106 672
1107 #[test] 673 #[test]
1108 fn omitted_parameters_hints_heuristics() { 674 fn omitted_parameters_hints_heuristics() {
1109 let (analysis, file_id) = single_file( 675 check_with_config(
676 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1110 r#" 677 r#"
1111fn map(f: i32) {} 678fn map(f: i32) {}
1112fn filter(predicate: i32) {} 679fn filter(predicate: i32) {}
@@ -1188,22 +755,15 @@ fn main() {
1188 let _: f64 = a.abs_sub(b); 755 let _: f64 = a.abs_sub(b);
1189}"#, 756}"#,
1190 ); 757 );
1191
1192 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1193 []
1194 "###
1195 );
1196 } 758 }
1197 759
1198 #[test] 760 #[test]
1199 fn unit_structs_have_no_type_hints() { 761 fn unit_structs_have_no_type_hints() {
1200 let (analysis, file_id) = single_file( 762 check_with_config(
763 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1201 r#" 764 r#"
1202enum CustomResult<T, E> { 765enum Result<T, E> { Ok(T), Err(E) }
1203 Ok(T), 766use Result::*;
1204 Err(E),
1205}
1206use CustomResult::*;
1207 767
1208struct SyntheticSyntax; 768struct SyntheticSyntax;
1209 769
@@ -1214,133 +774,155 @@ fn main() {
1214 } 774 }
1215}"#, 775}"#,
1216 ); 776 );
1217
1218 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1219 []
1220 "###
1221 );
1222 } 777 }
1223 778
1224 #[test] 779 #[test]
1225 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
1226 let (analysis, file_id) = single_file( 781 check_expect(
782 InlayHintsConfig {
783 parameter_hints: false,
784 type_hints: false,
785 chaining_hints: true,
786 max_length: None,
787 },
1227 r#" 788 r#"
1228 struct A(B); 789struct A(B);
1229 impl A { fn into_b(self) -> B { self.0 } } 790impl A { fn into_b(self) -> B { self.0 } }
1230 struct B(C); 791struct B(C);
1231 impl B { fn into_c(self) -> C { self.0 } } 792impl B { fn into_c(self) -> C { self.0 } }
1232 struct C; 793struct C;
1233 794
1234 fn main() { 795fn main() {
1235 let c = A(B(C)) 796 let c = A(B(C))
1236 .into_b() // This is a comment 797 .into_b() // This is a comment
1237 .into_c(); 798 .into_c();
1238 }"#, 799}
800"#,
801 expect![[r#"
802 [
803 InlayHint {
804 range: 147..172,
805 kind: ChainingHint,
806 label: "B",
807 },
808 InlayHint {
809 range: 147..154,
810 kind: ChainingHint,
811 label: "A",
812 },
813 ]
814 "#]],
1239 ); 815 );
1240 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1241 [
1242 InlayHint {
1243 range: 232..269,
1244 kind: ChainingHint,
1245 label: "B",
1246 },
1247 InlayHint {
1248 range: 232..239,
1249 kind: ChainingHint,
1250 label: "A",
1251 },
1252 ]"###);
1253 } 816 }
1254 817
1255 #[test] 818 #[test]
1256 fn chaining_hints_without_newlines() { 819 fn chaining_hints_without_newlines() {
1257 let (analysis, file_id) = single_file( 820 check_with_config(
821 InlayHintsConfig {
822 parameter_hints: false,
823 type_hints: false,
824 chaining_hints: true,
825 max_length: None,
826 },
1258 r#" 827 r#"
1259 struct A(B); 828struct A(B);
1260 impl A { fn into_b(self) -> B { self.0 } } 829impl A { fn into_b(self) -> B { self.0 } }
1261 struct B(C); 830struct B(C);
1262 impl B { fn into_c(self) -> C { self.0 } } 831impl B { fn into_c(self) -> C { self.0 } }
1263 struct C; 832struct C;
1264 833
1265 fn main() { 834fn main() {
1266 let c = A(B(C)).into_b().into_c(); 835 let c = A(B(C)).into_b().into_c();
1267 }"#, 836}"#,
1268 ); 837 );
1269 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1270 } 838 }
1271 839
1272 #[test] 840 #[test]
1273 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
1274 let (analysis, file_id) = single_file( 842 check_expect(
843 InlayHintsConfig {
844 parameter_hints: false,
845 type_hints: false,
846 chaining_hints: true,
847 max_length: None,
848 },
1275 r#" 849 r#"
1276 struct A { pub b: B } 850struct A { pub b: B }
1277 struct B { pub c: C } 851struct B { pub c: C }
1278 struct C(pub bool); 852struct C(pub bool);
1279 struct D; 853struct D;
1280 854
1281 impl D { 855impl D {
1282 fn foo(&self) -> i32 { 42 } 856 fn foo(&self) -> i32 { 42 }
1283 } 857}
1284 858
1285 fn main() { 859fn main() {
1286 let x = A { b: B { c: C(true) } } 860 let x = A { b: B { c: C(true) } }
1287 .b 861 .b
1288 .c 862 .c
1289 .0; 863 .0;
1290 let x = D 864 let x = D
1291 .foo(); 865 .foo();
1292 }"#, 866}"#,
867 expect![[r#"
868 [
869 InlayHint {
870 range: 143..190,
871 kind: ChainingHint,
872 label: "C",
873 },
874 InlayHint {
875 range: 143..179,
876 kind: ChainingHint,
877 label: "B",
878 },
879 ]
880 "#]],
1293 ); 881 );
1294 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1295 [
1296 InlayHint {
1297 range: 252..323,
1298 kind: ChainingHint,
1299 label: "C",
1300 },
1301 InlayHint {
1302 range: 252..300,
1303 kind: ChainingHint,
1304 label: "B",
1305 },
1306 ]
1307 "###);
1308 } 882 }
1309 883
1310 #[test] 884 #[test]
1311 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
1312 let (analysis, file_id) = single_file( 886 check_expect(
887 InlayHintsConfig {
888 parameter_hints: false,
889 type_hints: false,
890 chaining_hints: true,
891 max_length: None,
892 },
1313 r#" 893 r#"
1314 struct A<T>(T); 894struct A<T>(T);
1315 struct B<T>(T); 895struct B<T>(T);
1316 struct C<T>(T); 896struct C<T>(T);
1317 struct X<T,R>(T, R); 897struct X<T,R>(T, R);
1318 898
1319 impl<T> A<T> { 899impl<T> A<T> {
1320 fn new(t: T) -> Self { A(t) } 900 fn new(t: T) -> Self { A(t) }
1321 fn into_b(self) -> B<T> { B(self.0) } 901 fn into_b(self) -> B<T> { B(self.0) }
1322 } 902}
1323 impl<T> B<T> { 903impl<T> B<T> {
1324 fn into_c(self) -> C<T> { C(self.0) } 904 fn into_c(self) -> C<T> { C(self.0) }
1325 } 905}
1326 fn main() { 906fn main() {
1327 let c = A::new(X(42, true)) 907 let c = A::new(X(42, true))
1328 .into_b() 908 .into_b()
1329 .into_c(); 909 .into_c();
1330 }"#, 910}
911"#,
912 expect![[r#"
913 [
914 InlayHint {
915 range: 246..283,
916 kind: ChainingHint,
917 label: "B<X<i32, bool>>",
918 },
919 InlayHint {
920 range: 246..265,
921 kind: ChainingHint,
922 label: "A<X<i32, bool>>",
923 },
924 ]
925 "#]],
1331 ); 926 );
1332 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1333 [
1334 InlayHint {
1335 range: 403..452,
1336 kind: ChainingHint,
1337 label: "B<X<i32, bool>>",
1338 },
1339 InlayHint {
1340 range: 403..422,
1341 kind: ChainingHint,
1342 label: "A<X<i32, bool>>",
1343 },
1344 ]"###);
1345 } 927 }
1346} 928}
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index 5036c1fb0..6907c09e8 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -165,10 +165,7 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Opti
165} 165}
166 166
167fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { 167fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
168 match (left, right) { 168 matches!((left, right), (T![,], T![')']) | (T![,], T![']']))
169 (T![,], T![')']) | (T![,], T![']']) => true,
170 _ => false,
171 }
172} 169}
173 170
174#[cfg(test)] 171#[cfg(test)]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 12d5716e8..5d1f64e19 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -17,6 +17,7 @@ macro_rules! eprintln {
17 17
18pub mod mock_analysis; 18pub mod mock_analysis;
19 19
20mod markup;
20mod prime_caches; 21mod prime_caches;
21mod status; 22mod status;
22mod completion; 23mod completion;
@@ -47,7 +48,7 @@ use std::sync::Arc;
47use ra_cfg::CfgOptions; 48use ra_cfg::CfgOptions;
48use ra_db::{ 49use ra_db::{
49 salsa::{self, ParallelDatabase}, 50 salsa::{self, ParallelDatabase},
50 CheckCanceled, Env, FileLoader, SourceDatabase, 51 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
51}; 52};
52use ra_ide_db::{ 53use ra_ide_db::{
53 symbol_index::{self, FileSymbol}, 54 symbol_index::{self, FileSymbol},
@@ -59,6 +60,7 @@ use crate::display::ToNav;
59 60
60pub use crate::{ 61pub use crate::{
61 call_hierarchy::CallItem, 62 call_hierarchy::CallItem,
63 call_info::CallInfo,
62 completion::{ 64 completion::{
63 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, 65 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
64 }, 66 },
@@ -66,29 +68,31 @@ pub use crate::{
66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 68 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67 expand_macro::ExpandedMacro, 69 expand_macro::ExpandedMacro,
68 folding_ranges::{Fold, FoldKind}, 70 folding_ranges::{Fold, FoldKind},
69 hover::HoverResult, 71 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 72 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
73 markup::Markup,
71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 74 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
72 runnables::{Runnable, RunnableKind, TestId}, 75 runnables::{Runnable, RunnableKind, TestId},
73 ssr::SsrError,
74 syntax_highlighting::{ 76 syntax_highlighting::{
75 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange, 77 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange,
76 }, 78 },
77}; 79};
78 80
79pub use hir::Documentation; 81pub use hir::{Documentation, Semantics};
80pub use ra_assists::{AssistConfig, AssistId}; 82pub use ra_assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
81pub use ra_db::{ 83pub use ra_db::{
82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 84 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
85 SourceRootId,
83}; 86};
84pub use ra_ide_db::{ 87pub use ra_ide_db::{
85 change::{AnalysisChange, LibraryData}, 88 change::AnalysisChange,
86 line_index::{LineCol, LineIndex}, 89 line_index::{LineCol, LineIndex},
87 search::SearchScope, 90 search::SearchScope,
88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 91 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
89 symbol_index::Query, 92 symbol_index::Query,
90 RootDatabase, 93 RootDatabase,
91}; 94};
95pub use ra_ssr::SsrError;
92pub use ra_text_edit::{Indel, TextEdit}; 96pub use ra_text_edit::{Indel, TextEdit};
93 97
94pub type Cancelable<T> = Result<T, Canceled>; 98pub type Cancelable<T> = Result<T, Canceled>;
@@ -128,28 +132,12 @@ impl<T> RangeInfo<T> {
128 } 132 }
129} 133}
130 134
131/// Contains information about a call site. Specifically the
132/// `FunctionSignature`and current parameter.
133#[derive(Debug)]
134pub struct CallInfo {
135 pub signature: FunctionSignature,
136 pub active_parameter: Option<usize>,
137}
138
139/// `AnalysisHost` stores the current state of the world. 135/// `AnalysisHost` stores the current state of the world.
140#[derive(Debug)] 136#[derive(Debug)]
141pub struct AnalysisHost { 137pub struct AnalysisHost {
142 db: RootDatabase, 138 db: RootDatabase,
143} 139}
144 140
145#[derive(Debug)]
146pub struct Assist {
147 pub id: AssistId,
148 pub label: String,
149 pub group_label: Option<String>,
150 pub source_change: SourceChange,
151}
152
153impl AnalysisHost { 141impl AnalysisHost {
154 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 142 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
155 AnalysisHost { db: RootDatabase::new(lru_capacity) } 143 AnalysisHost { db: RootDatabase::new(lru_capacity) }
@@ -220,11 +208,14 @@ impl Analysis {
220 // `AnalysisHost` for creating a fully-featured analysis. 208 // `AnalysisHost` for creating a fully-featured analysis.
221 pub fn from_single_file(text: String) -> (Analysis, FileId) { 209 pub fn from_single_file(text: String) -> (Analysis, FileId) {
222 let mut host = AnalysisHost::default(); 210 let mut host = AnalysisHost::default();
223 let source_root = SourceRootId(0); 211 let file_id = FileId(0);
212 let mut file_set = FileSet::default();
213 file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string()));
214 let source_root = SourceRoot::new_local(file_set);
215
224 let mut change = AnalysisChange::new(); 216 let mut change = AnalysisChange::new();
225 change.add_root(source_root, true); 217 change.set_roots(vec![source_root]);
226 let mut crate_graph = CrateGraph::default(); 218 let mut crate_graph = CrateGraph::default();
227 let file_id = FileId(0);
228 // FIXME: cfg options 219 // FIXME: cfg options
229 // Default to enable test for single file. 220 // Default to enable test for single file.
230 let mut cfg_options = CfgOptions::default(); 221 let mut cfg_options = CfgOptions::default();
@@ -236,9 +227,8 @@ impl Analysis {
236 cfg_options, 227 cfg_options,
237 Env::default(), 228 Env::default(),
238 Default::default(), 229 Default::default(),
239 Default::default(),
240 ); 230 );
241 change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); 231 change.change_file(file_id, Some(Arc::new(text)));
242 change.set_crate_graph(crate_graph); 232 change.set_crate_graph(crate_graph);
243 host.apply_change(change); 233 host.apply_change(change);
244 (host.analysis(), file_id) 234 (host.analysis(), file_id)
@@ -390,7 +380,9 @@ impl Analysis {
390 position: FilePosition, 380 position: FilePosition,
391 search_scope: Option<SearchScope>, 381 search_scope: Option<SearchScope>,
392 ) -> Cancelable<Option<ReferenceSearchResult>> { 382 ) -> Cancelable<Option<ReferenceSearchResult>> {
393 self.with_db(|db| references::find_all_refs(db, position, search_scope).map(|it| it.info)) 383 self.with_db(|db| {
384 references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info)
385 })
394 } 386 }
395 387
396 /// Returns a short text describing element at position. 388 /// Returns a short text describing element at position.
@@ -448,12 +440,14 @@ impl Analysis {
448 440
449 /// Computes syntax highlighting for the given file 441 /// Computes syntax highlighting for the given file
450 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 442 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
451 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None)) 443 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
452 } 444 }
453 445
454 /// Computes syntax highlighting for the given file range. 446 /// Computes syntax highlighting for the given file range.
455 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { 447 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> {
456 self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range))) 448 self.with_db(|db| {
449 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
450 })
457 } 451 }
458 452
459 /// Computes syntax highlighting for the given file. 453 /// Computes syntax highlighting for the given file.
@@ -470,20 +464,23 @@ impl Analysis {
470 self.with_db(|db| completion::completions(db, config, position).map(Into::into)) 464 self.with_db(|db| completion::completions(db, config, position).map(Into::into))
471 } 465 }
472 466
473 /// Computes assists (aka code actions aka intentions) for the given 467 /// Computes resolved assists with source changes for the given position.
468 pub fn resolved_assists(
469 &self,
470 config: &AssistConfig,
471 frange: FileRange,
472 ) -> Cancelable<Vec<ResolvedAssist>> {
473 self.with_db(|db| ra_assists::Assist::resolved(db, config, frange))
474 }
475
476 /// Computes unresolved assists (aka code actions aka intentions) for the given
474 /// position. 477 /// position.
475 pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { 478 pub fn unresolved_assists(
476 self.with_db(|db| { 479 &self,
477 ra_assists::Assist::resolved(db, config, frange) 480 config: &AssistConfig,
478 .into_iter() 481 frange: FileRange,
479 .map(|assist| Assist { 482 ) -> Cancelable<Vec<Assist>> {
480 id: assist.assist.id, 483 self.with_db(|db| Assist::unresolved(db, config, frange))
481 label: assist.assist.label,
482 group_label: assist.assist.group.map(|it| it.0),
483 source_change: assist.source_change,
484 })
485 .collect()
486 })
487 } 484 }
488 485
489 /// Computes the set of diagnostics for the given file. 486 /// Computes the set of diagnostics for the given file.
@@ -508,7 +505,7 @@ impl Analysis {
508 ) -> Cancelable<Result<SourceChange, SsrError>> { 505 ) -> Cancelable<Result<SourceChange, SsrError>> {
509 self.with_db(|db| { 506 self.with_db(|db| {
510 let edits = ssr::parse_search_replace(query, parse_only, db)?; 507 let edits = ssr::parse_search_replace(query, parse_only, db)?;
511 Ok(SourceChange::source_file_edits(edits)) 508 Ok(SourceChange::from(edits))
512 }) 509 })
513 } 510 }
514 511
diff --git a/crates/ra_ide/src/markup.rs b/crates/ra_ide/src/markup.rs
new file mode 100644
index 000000000..60c193c40
--- /dev/null
+++ b/crates/ra_ide/src/markup.rs
@@ -0,0 +1,38 @@
1//! Markdown formatting.
2//!
3//! Sometimes, we want to display a "rich text" in the UI. At the moment, we use
4//! markdown for this purpose. It doesn't feel like a right option, but that's
5//! what is used by LSP, so let's keep it simple.
6use std::fmt;
7
8#[derive(Default, Debug)]
9pub struct Markup {
10 text: String,
11}
12
13impl From<Markup> for String {
14 fn from(markup: Markup) -> Self {
15 markup.text
16 }
17}
18
19impl From<String> for Markup {
20 fn from(text: String) -> Self {
21 Markup { text }
22 }
23}
24
25impl fmt::Display for Markup {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 fmt::Display::fmt(&self.text, f)
28 }
29}
30
31impl Markup {
32 pub fn as_str(&self) -> &str {
33 self.text.as_str()
34 }
35 pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
36 format!("```rust\n{}\n```", contents).into()
37 }
38}
diff --git a/crates/ra_ide/src/matching_brace.rs b/crates/ra_ide/src/matching_brace.rs
index 407a9636d..742d70c9c 100644
--- a/crates/ra_ide/src/matching_brace.rs
+++ b/crates/ra_ide/src/matching_brace.rs
@@ -1,8 +1,12 @@
1use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; 1use ra_syntax::{
2 ast::{self, AstNode},
3 SourceFile, SyntaxKind, TextSize, T,
4};
5use test_utils::mark;
2 6
3// Feature: Matching Brace 7// Feature: Matching Brace
4// 8//
5// If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, 9// If the cursor is on any brace (`<>(){}[]||`) which is a part of a brace-pair,
6// moves cursor to the matching brace. It uses the actual parser to determine 10// moves cursor to the matching brace. It uses the actual parser to determine
7// braces, so it won't confuse generics with comparisons. 11// braces, so it won't confuse generics with comparisons.
8// 12//
@@ -13,8 +17,8 @@ use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T};
13// |=== 17// |===
14pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 18pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
15 const BRACES: &[SyntaxKind] = 19 const BRACES: &[SyntaxKind] =
16 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; 20 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
17 let (brace_node, brace_idx) = file 21 let (brace_token, brace_idx) = file
18 .syntax() 22 .syntax()
19 .token_at_offset(offset) 23 .token_at_offset(offset)
20 .filter_map(|node| { 24 .filter_map(|node| {
@@ -22,9 +26,16 @@ pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
22 Some((node, idx)) 26 Some((node, idx))
23 }) 27 })
24 .next()?; 28 .next()?;
25 let parent = brace_node.parent(); 29 let parent = brace_token.parent();
30 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
31 mark::hit!(pipes_not_braces);
32 return None;
33 }
26 let matching_kind = BRACES[brace_idx ^ 1]; 34 let matching_kind = BRACES[brace_idx ^ 1];
27 let matching_node = parent.children_with_tokens().find(|node| node.kind() == matching_kind)?; 35 let matching_node = parent
36 .children_with_tokens()
37 .filter_map(|it| it.into_token())
38 .find(|node| node.kind() == matching_kind && node != &brace_token)?;
28 Some(matching_node.text_range().start()) 39 Some(matching_node.text_range().start())
29} 40}
30 41
@@ -48,5 +59,15 @@ mod tests {
48 } 59 }
49 60
50 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }"); 61 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
62 do_check("fn main() { |x: i32|<|> x * 2;}", "fn main() { <|>|x: i32| x * 2;}");
63 do_check("fn main() { <|>|x: i32| x * 2;}", "fn main() { |x: i32<|>| x * 2;}");
64
65 {
66 mark::check!(pipes_not_braces);
67 do_check(
68 "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
69 "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
70 );
71 }
51 } 72 }
52} 73}
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index ad78d2d93..b28054688 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -1,87 +1,24 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2
3use std::str::FromStr;
4use std::sync::Arc; 2use std::sync::Arc;
5 3
6use ra_cfg::CfgOptions; 4use ra_cfg::CfgOptions;
7use ra_db::{CrateName, Env, RelativePathBuf}; 5use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath};
8use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; 6use test_utils::{
7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER,
8};
9 9
10use crate::{ 10use crate::{
11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
12 SourceRootId,
13}; 12};
14 13
15#[derive(Debug)]
16enum MockFileData {
17 Plain { path: String, content: String },
18 Fixture(FixtureEntry),
19}
20
21impl MockFileData {
22 fn new(path: String, content: String) -> Self {
23 // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
24 // see https://github.com/rust-lang/rust/issues/69018
25 MockFileData::Plain { path, content }
26 }
27
28 fn path(&self) -> &str {
29 match self {
30 MockFileData::Plain { path, .. } => path.as_str(),
31 MockFileData::Fixture(f) => f.meta.path().as_str(),
32 }
33 }
34
35 fn content(&self) -> &str {
36 match self {
37 MockFileData::Plain { content, .. } => content,
38 MockFileData::Fixture(f) => f.text.as_str(),
39 }
40 }
41
42 fn cfg_options(&self) -> CfgOptions {
43 match self {
44 MockFileData::Fixture(f) => {
45 f.meta.cfg_options().map_or_else(Default::default, |o| o.clone())
46 }
47 _ => CfgOptions::default(),
48 }
49 }
50
51 fn edition(&self) -> Edition {
52 match self {
53 MockFileData::Fixture(f) => {
54 f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap())
55 }
56 _ => Edition::Edition2018,
57 }
58 }
59
60 fn env(&self) -> Env {
61 match self {
62 MockFileData::Fixture(f) => Env::from(f.meta.env()),
63 _ => Env::default(),
64 }
65 }
66}
67
68impl From<FixtureEntry> for MockFileData {
69 fn from(fixture: FixtureEntry) -> Self {
70 Self::Fixture(fixture)
71 }
72}
73
74/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 14/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
75/// from a set of in-memory files. 15/// from a set of in-memory files.
76#[derive(Debug, Default)] 16#[derive(Debug, Default)]
77pub struct MockAnalysis { 17pub struct MockAnalysis {
78 files: Vec<MockFileData>, 18 files: Vec<Fixture>,
79} 19}
80 20
81impl MockAnalysis { 21impl MockAnalysis {
82 pub fn new() -> MockAnalysis {
83 MockAnalysis::default()
84 }
85 /// Creates `MockAnalysis` using a fixture data in the following format: 22 /// Creates `MockAnalysis` using a fixture data in the following format:
86 /// 23 ///
87 /// ```not_rust 24 /// ```not_rust
@@ -92,106 +29,107 @@ impl MockAnalysis {
92 /// //- /foo.rs 29 /// //- /foo.rs
93 /// struct Baz; 30 /// struct Baz;
94 /// ``` 31 /// ```
95 pub fn with_files(fixture: &str) -> MockAnalysis { 32 pub fn with_files(ra_fixture: &str) -> MockAnalysis {
96 let mut res = MockAnalysis::new(); 33 let (res, pos) = MockAnalysis::with_fixture(ra_fixture);
97 for entry in parse_fixture(fixture) { 34 assert!(pos.is_none());
98 res.add_file_fixture(entry);
99 }
100 res 35 res
101 } 36 }
102 37
103 /// Same as `with_files`, but requires that a single file contains a `<|>` marker, 38 /// Same as `with_files`, but requires that a single file contains a `<|>` marker,
104 /// whose position is also returned. 39 /// whose position is also returned.
105 pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { 40 pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) {
41 let (res, position) = MockAnalysis::with_fixture(fixture);
42 let (file_id, range_or_offset) = position.expect("expected a marker (<|>)");
43 let offset = match range_or_offset {
44 RangeOrOffset::Range(_) => panic!(),
45 RangeOrOffset::Offset(it) => it,
46 };
47 (res, FilePosition { file_id, offset })
48 }
49
50 fn with_fixture(fixture: &str) -> (MockAnalysis, Option<(FileId, RangeOrOffset)>) {
106 let mut position = None; 51 let mut position = None;
107 let mut res = MockAnalysis::new(); 52 let mut res = MockAnalysis::default();
108 for entry in parse_fixture(fixture) { 53 for mut entry in Fixture::parse(fixture) {
109 if entry.text.contains(CURSOR_MARKER) { 54 if entry.text.contains(CURSOR_MARKER) {
110 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); 55 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
111 position = Some(res.add_file_fixture_with_position(entry)); 56 let (range_or_offset, text) = extract_range_or_offset(&entry.text);
57 entry.text = text;
58 let file_id = res.add_file_fixture(entry);
59 position = Some((file_id, range_or_offset));
112 } else { 60 } else {
113 res.add_file_fixture(entry); 61 res.add_file_fixture(entry);
114 } 62 }
115 } 63 }
116 let position = position.expect("expected a marker (<|>)");
117 (res, position) 64 (res, position)
118 } 65 }
119 66
120 pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId { 67 fn add_file_fixture(&mut self, fixture: Fixture) -> FileId {
121 let file_id = self.next_id(); 68 let file_id = FileId((self.files.len() + 1) as u32);
122 self.files.push(MockFileData::from(fixture)); 69 self.files.push(fixture);
123 file_id 70 file_id
124 } 71 }
125 72
126 pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition { 73 pub fn id_of(&self, path: &str) -> FileId {
127 let (offset, text) = extract_offset(&fixture.text); 74 let (file_id, _) =
128 fixture.text = text; 75 self.files().find(|(_, data)| path == data.path).expect("no file in this mock");
129 let file_id = self.next_id();
130 self.files.push(MockFileData::from(fixture));
131 FilePosition { file_id, offset }
132 }
133
134 pub fn add_file(&mut self, path: &str, text: &str) -> FileId {
135 let file_id = self.next_id();
136 self.files.push(MockFileData::new(path.to_string(), text.to_string()));
137 file_id 76 file_id
138 } 77 }
139 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { 78 pub fn annotations(&self) -> Vec<(FileRange, String)> {
140 let (offset, text) = extract_offset(text); 79 self.files()
141 let file_id = self.next_id(); 80 .flat_map(|(file_id, fixture)| {
142 self.files.push(MockFileData::new(path.to_string(), text)); 81 let annotations = extract_annotations(&fixture.text);
143 FilePosition { file_id, offset } 82 annotations
83 .into_iter()
84 .map(move |(range, data)| (FileRange { file_id, range }, data))
85 })
86 .collect()
144 } 87 }
145 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { 88 pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ {
146 let (range, text) = extract_range(text); 89 self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture))
147 let file_id = self.next_id();
148 self.files.push(MockFileData::new(path.to_string(), text));
149 FileRange { file_id, range }
150 } 90 }
151 pub fn id_of(&self, path: &str) -> FileId { 91 pub fn annotation(&self) -> (FileRange, String) {
152 let (idx, _) = self 92 let mut all = self.annotations();
153 .files 93 assert_eq!(all.len(), 1);
154 .iter() 94 all.pop().unwrap()
155 .enumerate()
156 .find(|(_, data)| path == data.path())
157 .expect("no file in this mock");
158 FileId(idx as u32 + 1)
159 } 95 }
160 pub fn analysis_host(self) -> AnalysisHost { 96 pub fn analysis_host(self) -> AnalysisHost {
161 let mut host = AnalysisHost::default(); 97 let mut host = AnalysisHost::default();
162 let source_root = SourceRootId(0);
163 let mut change = AnalysisChange::new(); 98 let mut change = AnalysisChange::new();
164 change.add_root(source_root, true); 99 let mut file_set = FileSet::default();
165 let mut crate_graph = CrateGraph::default(); 100 let mut crate_graph = CrateGraph::default();
166 let mut root_crate = None; 101 let mut root_crate = None;
167 for (i, data) in self.files.into_iter().enumerate() { 102 for (i, data) in self.files.into_iter().enumerate() {
168 let path = data.path(); 103 let path = data.path;
169 assert!(path.starts_with('/')); 104 assert!(path.starts_with('/'));
170 let path = RelativePathBuf::from_path(&path[1..]).unwrap(); 105
171 let cfg_options = data.cfg_options(); 106 let mut cfg = CfgOptions::default();
107 data.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into()));
108 data.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into()));
109 let edition: Edition =
110 data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018);
111
172 let file_id = FileId(i as u32 + 1); 112 let file_id = FileId(i as u32 + 1);
173 let edition = data.edition(); 113 let env = Env::from(data.env.iter());
174 let env = data.env();
175 if path == "/lib.rs" || path == "/main.rs" { 114 if path == "/lib.rs" || path == "/main.rs" {
176 root_crate = Some(crate_graph.add_crate_root( 115 root_crate = Some(crate_graph.add_crate_root(
177 file_id, 116 file_id,
178 edition, 117 edition,
179 None, 118 None,
180 cfg_options, 119 cfg,
181 env, 120 env,
182 Default::default(), 121 Default::default(),
183 Default::default(),
184 )); 122 ));
185 } else if path.ends_with("/lib.rs") { 123 } else if path.ends_with("/lib.rs") {
186 let crate_name = path.parent().unwrap().file_name().unwrap(); 124 let base = &path[..path.len() - "/lib.rs".len()];
125 let crate_name = &base[base.rfind('/').unwrap() + '/'.len_utf8()..];
187 let other_crate = crate_graph.add_crate_root( 126 let other_crate = crate_graph.add_crate_root(
188 file_id, 127 file_id,
189 edition, 128 edition,
190 Some(CrateName::new(crate_name).unwrap()), 129 Some(crate_name.to_string()),
191 cfg_options, 130 cfg,
192 env, 131 env,
193 Default::default(), 132 Default::default(),
194 Default::default(),
195 ); 133 );
196 if let Some(root_crate) = root_crate { 134 if let Some(root_crate) = root_crate {
197 crate_graph 135 crate_graph
@@ -199,19 +137,18 @@ impl MockAnalysis {
199 .unwrap(); 137 .unwrap();
200 } 138 }
201 } 139 }
202 change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned())); 140 let path = VfsPath::new_virtual_path(path.to_string());
141 file_set.insert(file_id, path);
142 change.change_file(file_id, Some(Arc::new(data.text).to_owned()));
203 } 143 }
204 change.set_crate_graph(crate_graph); 144 change.set_crate_graph(crate_graph);
145 change.set_roots(vec![SourceRoot::new_local(file_set)]);
205 host.apply_change(change); 146 host.apply_change(change);
206 host 147 host
207 } 148 }
208 pub fn analysis(self) -> Analysis { 149 pub fn analysis(self) -> Analysis {
209 self.analysis_host().analysis() 150 self.analysis_host().analysis()
210 } 151 }
211
212 fn next_id(&self) -> FileId {
213 FileId((self.files.len() + 1) as u32)
214 }
215} 152}
216 153
217/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 154/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
@@ -222,21 +159,18 @@ pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) {
222 159
223/// Creates analysis for a single file. 160/// Creates analysis for a single file.
224pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { 161pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) {
225 let mut mock = MockAnalysis::new(); 162 let mock = MockAnalysis::with_files(ra_fixture);
226 let file_id = mock.add_file("/main.rs", ra_fixture); 163 let file_id = mock.id_of("/main.rs");
227 (mock.analysis(), file_id) 164 (mock.analysis(), file_id)
228} 165}
229 166
230/// Creates analysis for a single file, returns position marked with <|>.
231pub fn single_file_with_position(ra_fixture: &str) -> (Analysis, FilePosition) {
232 let mut mock = MockAnalysis::new();
233 let pos = mock.add_file_with_position("/main.rs", ra_fixture);
234 (mock.analysis(), pos)
235}
236
237/// Creates analysis for a single file, returns range marked with a pair of <|>. 167/// Creates analysis for a single file, returns range marked with a pair of <|>.
238pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) { 168pub fn analysis_and_range(ra_fixture: &str) -> (Analysis, FileRange) {
239 let mut mock = MockAnalysis::new(); 169 let (res, position) = MockAnalysis::with_fixture(ra_fixture);
240 let pos = mock.add_file_with_range("/main.rs", ra_fixture); 170 let (file_id, range_or_offset) = position.expect("expected a marker (<|>)");
241 (mock.analysis(), pos) 171 let range = match range_or_offset {
172 RangeOrOffset::Range(it) => it,
173 RangeOrOffset::Offset(_) => panic!(),
174 };
175 (res.analysis(), FileRange { file_id, range })
242} 176}
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index fa1535da5..e3e0c7639 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -125,12 +125,12 @@ mod tests {
125 #[test] 125 #[test]
126 fn test_resolve_crate_root() { 126 fn test_resolve_crate_root() {
127 let mock = MockAnalysis::with_files( 127 let mock = MockAnalysis::with_files(
128 " 128 r#"
129 //- /bar.rs 129//- /bar.rs
130 mod foo; 130mod foo;
131 //- /foo.rs 131//- /foo.rs
132 // empty <|> 132// empty
133 ", 133"#,
134 ); 134 );
135 let root_file = mock.id_of("/bar.rs"); 135 let root_file = mock.id_of("/bar.rs");
136 let mod_file = mock.id_of("/foo.rs"); 136 let mod_file = mock.id_of("/foo.rs");
@@ -145,7 +145,6 @@ mod tests {
145 CfgOptions::default(), 145 CfgOptions::default(),
146 Env::default(), 146 Env::default(),
147 Default::default(), 147 Default::default(),
148 Default::default(),
149 ); 148 );
150 let mut change = AnalysisChange::new(); 149 let mut change = AnalysisChange::new();
151 change.set_crate_graph(crate_graph); 150 change.set_crate_graph(crate_graph);
diff --git a/crates/ra_ide/src/prime_caches.rs b/crates/ra_ide/src/prime_caches.rs
index 90bf7d25f..c5ab5a1d8 100644
--- a/crates/ra_ide/src/prime_caches.rs
+++ b/crates/ra_ide/src/prime_caches.rs
@@ -7,6 +7,6 @@ use crate::{FileId, RootDatabase};
7 7
8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) { 8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) {
9 for file in files { 9 for file in files {
10 let _ = crate::syntax_highlighting::highlight(db, file, None); 10 let _ = crate::syntax_highlighting::highlight(db, file, None, false);
11 } 11 }
12} 12}
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index bb40d2043..c2b0d5efe 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -86,12 +86,11 @@ impl IntoIterator for ReferenceSearchResult {
86} 86}
87 87
88pub(crate) fn find_all_refs( 88pub(crate) fn find_all_refs(
89 db: &RootDatabase, 89 sema: &Semantics<RootDatabase>,
90 position: FilePosition, 90 position: FilePosition,
91 search_scope: Option<SearchScope>, 91 search_scope: Option<SearchScope>,
92) -> Option<RangeInfo<ReferenceSearchResult>> { 92) -> Option<RangeInfo<ReferenceSearchResult>> {
93 let _p = profile("find_all_refs"); 93 let _p = profile("find_all_refs");
94 let sema = Semantics::new(db);
95 let syntax = sema.parse(position.file_id).syntax().clone(); 94 let syntax = sema.parse(position.file_id).syntax().clone();
96 95
97 let (opt_name, search_kind) = if let Some(name) = 96 let (opt_name, search_kind) = if let Some(name) =
@@ -108,15 +107,15 @@ pub(crate) fn find_all_refs(
108 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 107 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
109 108
110 let references = def 109 let references = def
111 .find_usages(db, search_scope) 110 .find_usages(sema, search_scope)
112 .into_iter() 111 .into_iter()
113 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 112 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
114 .collect(); 113 .collect();
115 114
116 let decl_range = def.try_to_nav(db)?.range(); 115 let decl_range = def.try_to_nav(sema.db)?.range();
117 116
118 let declaration = Declaration { 117 let declaration = Declaration {
119 nav: def.try_to_nav(db)?, 118 nav: def.try_to_nav(sema.db)?,
120 kind: ReferenceKind::Other, 119 kind: ReferenceKind::Other,
121 access: decl_access(&def, &syntax, decl_range), 120 access: decl_access(&def, &syntax, decl_range),
122 }; 121 };
@@ -191,244 +190,249 @@ fn get_struct_def_name_for_struct_literal_search(
191#[cfg(test)] 190#[cfg(test)]
192mod tests { 191mod tests {
193 use crate::{ 192 use crate::{
194 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, 193 mock_analysis::{analysis_and_position, MockAnalysis},
195 Declaration, Reference, ReferenceSearchResult, SearchScope, 194 Declaration, Reference, ReferenceSearchResult, SearchScope,
196 }; 195 };
197 196
198 #[test] 197 #[test]
199 fn test_struct_literal_after_space() { 198 fn test_struct_literal_after_space() {
200 let code = r#" 199 let refs = get_all_refs(
201 struct Foo <|>{ 200 r#"
202 a: i32, 201struct Foo <|>{
203 } 202 a: i32,
204 impl Foo { 203}
205 fn f() -> i32 { 42 } 204impl Foo {
206 } 205 fn f() -> i32 { 42 }
207 fn main() { 206}
208 let f: Foo; 207fn main() {
209 f = Foo {a: Foo::f()}; 208 let f: Foo;
210 }"#; 209 f = Foo {a: Foo::f()};
211 210}
212 let refs = get_all_refs(code); 211"#,
212 );
213 check_result( 213 check_result(
214 refs, 214 refs,
215 "Foo STRUCT_DEF FileId(1) 5..39 12..15 Other", 215 "Foo STRUCT_DEF FileId(1) 0..26 7..10 Other",
216 &["FileId(1) 138..141 StructLiteral"], 216 &["FileId(1) 101..104 StructLiteral"],
217 ); 217 );
218 } 218 }
219 219
220 #[test] 220 #[test]
221 fn test_struct_literal_befor_space() { 221 fn test_struct_literal_before_space() {
222 let code = r#" 222 let refs = get_all_refs(
223 struct Foo<|> {} 223 r#"
224 fn main() { 224struct Foo<|> {}
225 let f: Foo; 225 fn main() {
226 f = Foo {}; 226 let f: Foo;
227 }"#; 227 f = Foo {};
228 228}
229 let refs = get_all_refs(code); 229"#,
230 );
230 check_result( 231 check_result(
231 refs, 232 refs,
232 "Foo STRUCT_DEF FileId(1) 5..18 12..15 Other", 233 "Foo STRUCT_DEF FileId(1) 0..13 7..10 Other",
233 &["FileId(1) 54..57 Other", "FileId(1) 71..74 StructLiteral"], 234 &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"],
234 ); 235 );
235 } 236 }
236 237
237 #[test] 238 #[test]
238 fn test_struct_literal_with_generic_type() { 239 fn test_struct_literal_with_generic_type() {
239 let code = r#" 240 let refs = get_all_refs(
240 struct Foo<T> <|>{} 241 r#"
241 fn main() { 242struct Foo<T> <|>{}
242 let f: Foo::<i32>; 243 fn main() {
243 f = Foo {}; 244 let f: Foo::<i32>;
244 }"#; 245 f = Foo {};
245 246}
246 let refs = get_all_refs(code); 247"#,
248 );
247 check_result( 249 check_result(
248 refs, 250 refs,
249 "Foo STRUCT_DEF FileId(1) 5..21 12..15 Other", 251 "Foo STRUCT_DEF FileId(1) 0..16 7..10 Other",
250 &["FileId(1) 81..84 StructLiteral"], 252 &["FileId(1) 64..67 StructLiteral"],
251 ); 253 );
252 } 254 }
253 255
254 #[test] 256 #[test]
255 fn test_struct_literal_for_tuple() { 257 fn test_struct_literal_for_tuple() {
256 let code = r#" 258 let refs = get_all_refs(
257 struct Foo<|>(i32); 259 r#"
260struct Foo<|>(i32);
258 261
259 fn main() { 262fn main() {
260 let f: Foo; 263 let f: Foo;
261 f = Foo(1); 264 f = Foo(1);
262 }"#; 265}
263 266"#,
264 let refs = get_all_refs(code); 267 );
265 check_result( 268 check_result(
266 refs, 269 refs,
267 "Foo STRUCT_DEF FileId(1) 5..21 12..15 Other", 270 "Foo STRUCT_DEF FileId(1) 0..16 7..10 Other",
268 &["FileId(1) 71..74 StructLiteral"], 271 &["FileId(1) 54..57 StructLiteral"],
269 ); 272 );
270 } 273 }
271 274
272 #[test] 275 #[test]
273 fn test_find_all_refs_for_local() { 276 fn test_find_all_refs_for_local() {
274 let code = r#" 277 let refs = get_all_refs(
275 fn main() { 278 r#"
276 let mut i = 1; 279fn main() {
277 let j = 1; 280 let mut i = 1;
278 i = i<|> + j; 281 let j = 1;
282 i = i<|> + j;
279 283
280 { 284 {
281 i = 0; 285 i = 0;
282 } 286 }
283
284 i = 5;
285 }"#;
286 287
287 let refs = get_all_refs(code); 288 i = 5;
289}"#,
290 );
288 check_result( 291 check_result(
289 refs, 292 refs,
290 "i BIND_PAT FileId(1) 33..34 Other Write", 293 "i BIND_PAT FileId(1) 24..25 Other Write",
291 &[ 294 &[
292 "FileId(1) 67..68 Other Write", 295 "FileId(1) 50..51 Other Write",
293 "FileId(1) 71..72 Other Read", 296 "FileId(1) 54..55 Other Read",
294 "FileId(1) 101..102 Other Write", 297 "FileId(1) 76..77 Other Write",
295 "FileId(1) 127..128 Other Write", 298 "FileId(1) 94..95 Other Write",
296 ], 299 ],
297 ); 300 );
298 } 301 }
299 302
300 #[test] 303 #[test]
301 fn search_filters_by_range() { 304 fn search_filters_by_range() {
302 let code = r#" 305 let refs = get_all_refs(
303 fn foo() { 306 r#"
304 let spam<|> = 92; 307fn foo() {
305 spam + spam 308 let spam<|> = 92;
306 } 309 spam + spam
307 fn bar() { 310}
308 let spam = 92; 311fn bar() {
309 spam + spam 312 let spam = 92;
310 } 313 spam + spam
311 "#; 314}
312 let refs = get_all_refs(code); 315"#,
316 );
313 check_result( 317 check_result(
314 refs, 318 refs,
315 "spam BIND_PAT FileId(1) 44..48 Other", 319 "spam BIND_PAT FileId(1) 19..23 Other",
316 &["FileId(1) 71..75 Other Read", "FileId(1) 78..82 Other Read"], 320 &["FileId(1) 34..38 Other Read", "FileId(1) 41..45 Other Read"],
317 ); 321 );
318 } 322 }
319 323
320 #[test] 324 #[test]
321 fn test_find_all_refs_for_param_inside() { 325 fn test_find_all_refs_for_param_inside() {
322 let code = r#" 326 let refs = get_all_refs(
323 fn foo(i : u32) -> u32 { 327 r#"
324 i<|> 328fn foo(i : u32) -> u32 {
325 }"#; 329 i<|>
326 330}
327 let refs = get_all_refs(code); 331"#,
328 check_result(refs, "i BIND_PAT FileId(1) 12..13 Other", &["FileId(1) 38..39 Other Read"]); 332 );
333 check_result(refs, "i BIND_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]);
329 } 334 }
330 335
331 #[test] 336 #[test]
332 fn test_find_all_refs_for_fn_param() { 337 fn test_find_all_refs_for_fn_param() {
333 let code = r#" 338 let refs = get_all_refs(
334 fn foo(i<|> : u32) -> u32 { 339 r#"
335 i 340fn foo(i<|> : u32) -> u32 {
336 }"#; 341 i
337 342}
338 let refs = get_all_refs(code); 343"#,
339 check_result(refs, "i BIND_PAT FileId(1) 12..13 Other", &["FileId(1) 38..39 Other Read"]); 344 );
345 check_result(refs, "i BIND_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]);
340 } 346 }
341 347
342 #[test] 348 #[test]
343 fn test_find_all_refs_field_name() { 349 fn test_find_all_refs_field_name() {
344 let code = r#" 350 let refs = get_all_refs(
345 //- /lib.rs 351 r#"
346 struct Foo { 352//- /lib.rs
347 pub spam<|>: u32, 353struct Foo {
348 } 354 pub spam<|>: u32,
349 355}
350 fn main(s: Foo) {
351 let f = s.spam;
352 }
353 "#;
354 356
355 let refs = get_all_refs(code); 357fn main(s: Foo) {
358 let f = s.spam;
359}
360"#,
361 );
356 check_result( 362 check_result(
357 refs, 363 refs,
358 "spam RECORD_FIELD_DEF FileId(1) 66..79 70..74 Other", 364 "spam RECORD_FIELD_DEF FileId(1) 17..30 21..25 Other",
359 &["FileId(1) 152..156 Other Read"], 365 &["FileId(1) 67..71 Other Read"],
360 ); 366 );
361 } 367 }
362 368
363 #[test] 369 #[test]
364 fn test_find_all_refs_impl_item_name() { 370 fn test_find_all_refs_impl_item_name() {
365 let code = r#" 371 let refs = get_all_refs(
366 //- /lib.rs 372 r#"
367 struct Foo; 373struct Foo;
368 impl Foo { 374impl Foo {
369 fn f<|>(&self) { } 375 fn f<|>(&self) { }
370 } 376}
371 "#; 377"#,
372 378 );
373 let refs = get_all_refs(code); 379 check_result(refs, "f FN_DEF FileId(1) 27..43 30..31 Other", &[]);
374 check_result(refs, "f FN_DEF FileId(1) 88..104 91..92 Other", &[]);
375 } 380 }
376 381
377 #[test] 382 #[test]
378 fn test_find_all_refs_enum_var_name() { 383 fn test_find_all_refs_enum_var_name() {
379 let code = r#" 384 let refs = get_all_refs(
380 //- /lib.rs 385 r#"
381 enum Foo { 386enum Foo {
382 A, 387 A,
383 B<|>, 388 B<|>,
384 C, 389 C,
385 } 390}
386 "#; 391"#,
387 392 );
388 let refs = get_all_refs(code); 393 check_result(refs, "B ENUM_VARIANT FileId(1) 22..23 22..23 Other", &[]);
389 check_result(refs, "B ENUM_VARIANT FileId(1) 83..84 83..84 Other", &[]);
390 } 394 }
391 395
392 #[test] 396 #[test]
393 fn test_find_all_refs_two_modules() { 397 fn test_find_all_refs_two_modules() {
394 let code = r#" 398 let (analysis, pos) = analysis_and_position(
395 //- /lib.rs 399 r#"
396 pub mod foo; 400//- /lib.rs
397 pub mod bar; 401pub mod foo;
398 402pub mod bar;
399 fn f() { 403
400 let i = foo::Foo { n: 5 }; 404fn f() {
401 } 405 let i = foo::Foo { n: 5 };
402 406}
403 //- /foo.rs
404 use crate::bar;
405 407
406 pub struct Foo { 408//- /foo.rs
407 pub n: u32, 409use crate::bar;
408 }
409 410
410 fn f() { 411pub struct Foo {
411 let i = bar::Bar { n: 5 }; 412 pub n: u32,
412 } 413}
413 414
414 //- /bar.rs 415fn f() {
415 use crate::foo; 416 let i = bar::Bar { n: 5 };
417}
416 418
417 pub struct Bar { 419//- /bar.rs
418 pub n: u32, 420use crate::foo;
419 }
420 421
421 fn f() { 422pub struct Bar {
422 let i = foo::Foo<|> { n: 5 }; 423 pub n: u32,
423 } 424}
424 "#;
425 425
426 let (analysis, pos) = analysis_and_position(code); 426fn f() {
427 let i = foo::Foo<|> { n: 5 };
428}
429"#,
430 );
427 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 431 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
428 check_result( 432 check_result(
429 refs, 433 refs,
430 "Foo STRUCT_DEF FileId(2) 16..50 27..30 Other", 434 "Foo STRUCT_DEF FileId(2) 17..51 28..31 Other",
431 &["FileId(1) 52..55 StructLiteral", "FileId(3) 77..80 StructLiteral"], 435 &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"],
432 ); 436 );
433 } 437 }
434 438
@@ -437,53 +441,53 @@ mod tests {
437 // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. 441 // which is the whole `foo.rs`, and the second one is in `use foo::Foo`.
438 #[test] 442 #[test]
439 fn test_find_all_refs_decl_module() { 443 fn test_find_all_refs_decl_module() {
440 let code = r#" 444 let (analysis, pos) = analysis_and_position(
441 //- /lib.rs 445 r#"
442 mod foo<|>; 446//- /lib.rs
447mod foo<|>;
443 448
444 use foo::Foo; 449use foo::Foo;
445 450
446 fn f() { 451fn f() {
447 let i = Foo { n: 5 }; 452 let i = Foo { n: 5 };
448 } 453}
449
450 //- /foo.rs
451 pub struct Foo {
452 pub n: u32,
453 }
454 "#;
455 454
456 let (analysis, pos) = analysis_and_position(code); 455//- /foo.rs
456pub struct Foo {
457 pub n: u32,
458}
459"#,
460 );
457 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 461 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
458 check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 13..16 Other"]); 462 check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 14..17 Other"]);
459 } 463 }
460 464
461 #[test] 465 #[test]
462 fn test_find_all_refs_super_mod_vis() { 466 fn test_find_all_refs_super_mod_vis() {
463 let code = r#" 467 let (analysis, pos) = analysis_and_position(
464 //- /lib.rs 468 r#"
465 mod foo; 469//- /lib.rs
466 470mod foo;
467 //- /foo.rs
468 mod some;
469 use some::Foo;
470 471
471 fn f() { 472//- /foo.rs
472 let i = Foo { n: 5 }; 473mod some;
473 } 474use some::Foo;
474 475
475 //- /foo/some.rs 476fn f() {
476 pub(super) struct Foo<|> { 477 let i = Foo { n: 5 };
477 pub n: u32, 478}
478 }
479 "#;
480 479
481 let (analysis, pos) = analysis_and_position(code); 480//- /foo/some.rs
481pub(super) struct Foo<|> {
482 pub n: u32,
483}
484"#,
485 );
482 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 486 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
483 check_result( 487 check_result(
484 refs, 488 refs,
485 "Foo STRUCT_DEF FileId(3) 0..41 18..21 Other", 489 "Foo STRUCT_DEF FileId(3) 0..41 18..21 Other",
486 &["FileId(2) 20..23 Other", "FileId(2) 46..49 StructLiteral"], 490 &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"],
487 ); 491 );
488 } 492 }
489 493
@@ -510,7 +514,7 @@ mod tests {
510 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 514 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
511 check_result( 515 check_result(
512 refs, 516 refs,
513 "quux FN_DEF FileId(1) 18..34 25..29 Other", 517 "quux FN_DEF FileId(1) 19..35 26..30 Other",
514 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"], 518 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"],
515 ); 519 );
516 520
@@ -518,100 +522,105 @@ mod tests {
518 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); 522 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap();
519 check_result( 523 check_result(
520 refs, 524 refs,
521 "quux FN_DEF FileId(1) 18..34 25..29 Other", 525 "quux FN_DEF FileId(1) 19..35 26..30 Other",
522 &["FileId(3) 16..20 StructLiteral"], 526 &["FileId(3) 16..20 StructLiteral"],
523 ); 527 );
524 } 528 }
525 529
526 #[test] 530 #[test]
527 fn test_find_all_refs_macro_def() { 531 fn test_find_all_refs_macro_def() {
528 let code = r#" 532 let refs = get_all_refs(
529 #[macro_export] 533 r#"
530 macro_rules! m1<|> { () => (()) } 534#[macro_export]
531 535macro_rules! m1<|> { () => (()) }
532 fn foo() { 536
533 m1(); 537fn foo() {
534 m1(); 538 m1();
535 }"#; 539 m1();
536 540}
537 let refs = get_all_refs(code); 541"#,
542 );
538 check_result( 543 check_result(
539 refs, 544 refs,
540 "m1 MACRO_CALL FileId(1) 9..63 46..48 Other", 545 "m1 MACRO_CALL FileId(1) 0..46 29..31 Other",
541 &["FileId(1) 96..98 StructLiteral", "FileId(1) 114..116 StructLiteral"], 546 &["FileId(1) 63..65 StructLiteral", "FileId(1) 73..75 StructLiteral"],
542 ); 547 );
543 } 548 }
544 549
545 #[test] 550 #[test]
546 fn test_basic_highlight_read_write() { 551 fn test_basic_highlight_read_write() {
547 let code = r#" 552 let refs = get_all_refs(
548 fn foo() { 553 r#"
549 let mut i<|> = 0; 554fn foo() {
550 i = i + 1; 555 let mut i<|> = 0;
551 }"#; 556 i = i + 1;
552 557}
553 let refs = get_all_refs(code); 558"#,
559 );
554 check_result( 560 check_result(
555 refs, 561 refs,
556 "i BIND_PAT FileId(1) 40..41 Other Write", 562 "i BIND_PAT FileId(1) 23..24 Other Write",
557 &["FileId(1) 59..60 Other Write", "FileId(1) 63..64 Other Read"], 563 &["FileId(1) 34..35 Other Write", "FileId(1) 38..39 Other Read"],
558 ); 564 );
559 } 565 }
560 566
561 #[test] 567 #[test]
562 fn test_basic_highlight_field_read_write() { 568 fn test_basic_highlight_field_read_write() {
563 let code = r#" 569 let refs = get_all_refs(
564 struct S { 570 r#"
565 f: u32, 571struct S {
566 } 572 f: u32,
567 573}
568 fn foo() {
569 let mut s = S{f: 0};
570 s.f<|> = 0;
571 }"#;
572 574
573 let refs = get_all_refs(code); 575fn foo() {
576 let mut s = S{f: 0};
577 s.f<|> = 0;
578}
579"#,
580 );
574 check_result( 581 check_result(
575 refs, 582 refs,
576 "f RECORD_FIELD_DEF FileId(1) 32..38 32..33 Other", 583 "f RECORD_FIELD_DEF FileId(1) 15..21 15..16 Other",
577 &["FileId(1) 96..97 Other Read", "FileId(1) 117..118 Other Write"], 584 &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"],
578 ); 585 );
579 } 586 }
580 587
581 #[test] 588 #[test]
582 fn test_basic_highlight_decl_no_write() { 589 fn test_basic_highlight_decl_no_write() {
583 let code = r#" 590 let refs = get_all_refs(
584 fn foo() { 591 r#"
585 let i<|>; 592fn foo() {
586 i = 1; 593 let i<|>;
587 }"#; 594 i = 1;
588 595}
589 let refs = get_all_refs(code); 596"#,
590 check_result(refs, "i BIND_PAT FileId(1) 36..37 Other", &["FileId(1) 51..52 Other Write"]); 597 );
598 check_result(refs, "i BIND_PAT FileId(1) 19..20 Other", &["FileId(1) 26..27 Other Write"]);
591 } 599 }
592 600
593 #[test] 601 #[test]
594 fn test_find_struct_function_refs_outside_module() { 602 fn test_find_struct_function_refs_outside_module() {
595 let code = r#" 603 let refs = get_all_refs(
596 mod foo { 604 r#"
597 pub struct Foo; 605mod foo {
606 pub struct Foo;
598 607
599 impl Foo { 608 impl Foo {
600 pub fn new<|>() -> Foo { 609 pub fn new<|>() -> Foo {
601 Foo 610 Foo
602 }
603 }
604 } 611 }
612 }
613}
605 614
606 fn main() { 615fn main() {
607 let _f = foo::Foo::new(); 616 let _f = foo::Foo::new();
608 }"#; 617}
609 618"#,
610 let refs = get_all_refs(code); 619 );
611 check_result( 620 check_result(
612 refs, 621 refs,
613 "new FN_DEF FileId(1) 87..150 94..97 Other", 622 "new FN_DEF FileId(1) 54..101 61..64 Other",
614 &["FileId(1) 227..230 StructLiteral"], 623 &["FileId(1) 146..149 StructLiteral"],
615 ); 624 );
616 } 625 }
617 626
@@ -637,13 +646,13 @@ mod tests {
637 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 646 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
638 check_result( 647 check_result(
639 refs, 648 refs,
640 "f FN_DEF FileId(1) 25..34 28..29 Other", 649 "f FN_DEF FileId(1) 26..35 29..30 Other",
641 &["FileId(2) 11..12 Other", "FileId(2) 27..28 StructLiteral"], 650 &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"],
642 ); 651 );
643 } 652 }
644 653
645 fn get_all_refs(text: &str) -> ReferenceSearchResult { 654 fn get_all_refs(ra_fixture: &str) -> ReferenceSearchResult {
646 let (analysis, position) = single_file_with_position(text); 655 let (analysis, position) = analysis_and_position(ra_fixture);
647 analysis.find_all_refs(position, None).unwrap().unwrap() 656 analysis.find_all_refs(position, None).unwrap().unwrap()
648 } 657 }
649 658
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 28c6349b1..8735ec53c 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -1,11 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{ModuleSource, Semantics}; 3use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; 4use ra_db::SourceDatabaseExt;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::{
6 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
7 RootDatabase,
8};
6use ra_syntax::{ 9use ra_syntax::{
7 algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, 10 algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
8 AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 11 lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
9}; 12};
10use ra_text_edit::TextEdit; 13use ra_text_edit::TextEdit;
11use std::convert::TryInto; 14use std::convert::TryInto;
@@ -21,35 +24,53 @@ pub(crate) fn rename(
21 position: FilePosition, 24 position: FilePosition,
22 new_name: &str, 25 new_name: &str,
23) -> Option<RangeInfo<SourceChange>> { 26) -> Option<RangeInfo<SourceChange>> {
27 let sema = Semantics::new(db);
28
24 match lex_single_valid_syntax_kind(new_name)? { 29 match lex_single_valid_syntax_kind(new_name)? {
25 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), 30 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
26 SyntaxKind::SELF_KW => return rename_to_self(db, position), 31 SyntaxKind::SELF_KW => return rename_to_self(&sema, position),
27 _ => return None, 32 _ => return None,
28 } 33 }
29 34
30 let sema = Semantics::new(db);
31 let source_file = sema.parse(position.file_id); 35 let source_file = sema.parse(position.file_id);
32 let syntax = source_file.syntax(); 36 let syntax = source_file.syntax();
33 if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { 37 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
34 let range = ast_name.syntax().text_range(); 38 rename_mod(&sema, position, module, new_name)
35 rename_mod(&sema, &ast_name, &ast_module, position, new_name)
36 .map(|info| RangeInfo::new(range, info))
37 } else if let Some(self_token) = 39 } else if let Some(self_token) =
38 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 40 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
39 { 41 {
40 rename_self_to_param(db, position, self_token, new_name) 42 rename_self_to_param(&sema, position, self_token, new_name)
41 } else { 43 } else {
42 rename_reference(sema.db, position, new_name) 44 rename_reference(&sema, position, new_name)
43 } 45 }
44} 46}
45 47
46fn find_name_and_module_at_offset( 48fn find_module_at_offset(
47 syntax: &SyntaxNode, 49 sema: &Semantics<RootDatabase>,
48 position: FilePosition, 50 position: FilePosition,
49) -> Option<(ast::Name, ast::Module)> { 51 syntax: &SyntaxNode,
50 let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?; 52) -> Option<Module> {
51 let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; 53 let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
52 Some((ast_name, ast_module)) 54
55 let module = match_ast! {
56 match (ident.parent()) {
57 ast::NameRef(name_ref) => {
58 match classify_name_ref(sema, &name_ref)? {
59 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
60 _ => return None,
61 }
62 },
63 ast::Name(name) => {
64 match classify_name(&sema, &name)? {
65 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
66 _ => return None,
67 }
68 },
69 _ => return None,
70 }
71 };
72
73 Some(module)
53} 74}
54 75
55fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { 76fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
@@ -78,61 +99,53 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
78 99
79fn rename_mod( 100fn rename_mod(
80 sema: &Semantics<RootDatabase>, 101 sema: &Semantics<RootDatabase>,
81 ast_name: &ast::Name,
82 ast_module: &ast::Module,
83 position: FilePosition, 102 position: FilePosition,
103 module: Module,
84 new_name: &str, 104 new_name: &str,
85) -> Option<SourceChange> { 105) -> Option<RangeInfo<SourceChange>> {
86 let mut source_file_edits = Vec::new(); 106 let mut source_file_edits = Vec::new();
87 let mut file_system_edits = Vec::new(); 107 let mut file_system_edits = Vec::new();
88 if let Some(module) = sema.to_def(ast_module) { 108
89 let src = module.definition_source(sema.db); 109 let src = module.definition_source(sema.db);
90 let file_id = src.file_id.original_file(sema.db); 110 let file_id = src.file_id.original_file(sema.db);
91 match src.value { 111 match src.value {
92 ModuleSource::SourceFile(..) => { 112 ModuleSource::SourceFile(..) => {
93 let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); 113 // mod is defined in path/to/dir/mod.rs
94 // mod is defined in path/to/dir/mod.rs 114 let dst = if module.is_mod_rs(sema.db) {
95 let dst_path = if mod_path.file_stem() == Some("mod") { 115 format!("../{}/mod.rs", new_name)
96 mod_path 116 } else {
97 .parent() 117 format!("{}.rs", new_name)
98 .and_then(|p| p.parent()) 118 };
99 .or_else(|| Some(RelativePath::new(""))) 119 let move_file = FileSystemEdit::MoveFile { src: file_id, anchor: file_id, dst };
100 .map(|p| p.join(new_name).join("mod.rs")) 120 file_system_edits.push(move_file);
101 } else {
102 Some(mod_path.with_file_name(new_name).with_extension("rs"))
103 };
104 if let Some(path) = dst_path {
105 let move_file = FileSystemEdit::MoveFile {
106 src: file_id,
107 dst_source_root: sema.db.file_source_root(position.file_id),
108 dst_path: path,
109 };
110 file_system_edits.push(move_file);
111 }
112 }
113 ModuleSource::Module(..) => {}
114 } 121 }
122 ModuleSource::Module(..) => {}
115 } 123 }
116 124
117 let edit = SourceFileEdit { 125 if let Some(src) = module.declaration_source(sema.db) {
118 file_id: position.file_id, 126 let file_id = src.file_id.original_file(sema.db);
119 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 127 let name = src.value.name()?;
120 }; 128 let edit = SourceFileEdit {
121 source_file_edits.push(edit); 129 file_id,
122 130 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
123 if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { 131 };
124 let ref_edits = refs 132 source_file_edits.push(edit);
125 .references
126 .into_iter()
127 .map(|reference| source_edit_from_reference(reference, new_name));
128 source_file_edits.extend(ref_edits);
129 } 133 }
130 134
131 Some(SourceChange::from_edits(source_file_edits, file_system_edits)) 135 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
136 let ref_edits = refs
137 .references
138 .into_iter()
139 .map(|reference| source_edit_from_reference(reference, new_name));
140 source_file_edits.extend(ref_edits);
141
142 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
132} 143}
133 144
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 145fn rename_to_self(
135 let sema = Semantics::new(db); 146 sema: &Semantics<RootDatabase>,
147 position: FilePosition,
148) -> Option<RangeInfo<SourceChange>> {
136 let source_file = sema.parse(position.file_id); 149 let source_file = sema.parse(position.file_id);
137 let syn = source_file.syntax(); 150 let syn = source_file.syntax();
138 151
@@ -147,7 +160,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
147 _ => return None, // not renaming other types 160 _ => return None, // not renaming other types
148 }; 161 };
149 162
150 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 163 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
151 164
152 let param_range = first_param.syntax().text_range(); 165 let param_range = first_param.syntax().text_range();
153 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 166 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
@@ -171,7 +184,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 184 ),
172 }); 185 });
173 186
174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 187 Some(RangeInfo::new(range, SourceChange::from(edits)))
175} 188}
176 189
177fn text_edit_from_self_param( 190fn text_edit_from_self_param(
@@ -199,16 +212,15 @@ fn text_edit_from_self_param(
199} 212}
200 213
201fn rename_self_to_param( 214fn rename_self_to_param(
202 db: &RootDatabase, 215 sema: &Semantics<RootDatabase>,
203 position: FilePosition, 216 position: FilePosition,
204 self_token: SyntaxToken, 217 self_token: SyntaxToken,
205 new_name: &str, 218 new_name: &str,
206) -> Option<RangeInfo<SourceChange>> { 219) -> Option<RangeInfo<SourceChange>> {
207 let sema = Semantics::new(db);
208 let source_file = sema.parse(position.file_id); 220 let source_file = sema.parse(position.file_id);
209 let syn = source_file.syntax(); 221 let syn = source_file.syntax();
210 222
211 let text = db.file_text(position.file_id); 223 let text = sema.db.file_text(position.file_id);
212 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; 224 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?;
213 let search_range = fn_def.syntax().text_range(); 225 let search_range = fn_def.syntax().text_range();
214 226
@@ -234,15 +246,15 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 246 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 247 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 248
237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 249 Some(RangeInfo::new(range, SourceChange::from(edits)))
238} 250}
239 251
240fn rename_reference( 252fn rename_reference(
241 db: &RootDatabase, 253 sema: &Semantics<RootDatabase>,
242 position: FilePosition, 254 position: FilePosition,
243 new_name: &str, 255 new_name: &str,
244) -> Option<RangeInfo<SourceChange>> { 256) -> Option<RangeInfo<SourceChange>> {
245 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
246 258
247 let edit = refs 259 let edit = refs
248 .into_iter() 260 .into_iter()
@@ -253,57 +265,56 @@ fn rename_reference(
253 return None; 265 return None;
254 } 266 }
255 267
256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) 268 Some(RangeInfo::new(range, SourceChange::from(edit)))
257} 269}
258 270
259#[cfg(test)] 271#[cfg(test)]
260mod tests { 272mod tests {
261 use insta::assert_debug_snapshot; 273 use expect::{expect, Expect};
262 use ra_text_edit::TextEditBuilder; 274 use ra_text_edit::TextEditBuilder;
275 use stdx::trim_indent;
263 use test_utils::{assert_eq_text, mark}; 276 use test_utils::{assert_eq_text, mark};
264 277
265 use crate::{ 278 use crate::{mock_analysis::analysis_and_position, FileId};
266 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, 279
267 }; 280 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
281 let ra_fixture_after = &trim_indent(ra_fixture_after);
282 let (analysis, position) = analysis_and_position(ra_fixture_before);
283 let source_change = analysis.rename(position, new_name).unwrap();
284 let mut text_edit_builder = TextEditBuilder::default();
285 let mut file_id: Option<FileId> = None;
286 if let Some(change) = source_change {
287 for edit in change.info.source_file_edits {
288 file_id = Some(edit.file_id);
289 for indel in edit.edit.into_iter() {
290 text_edit_builder.replace(indel.delete, indel.insert);
291 }
292 }
293 }
294 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
295 text_edit_builder.finish().apply(&mut result);
296 assert_eq_text!(ra_fixture_after, &*result);
297 }
298
299 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
300 let (analysis, position) = analysis_and_position(ra_fixture);
301 let source_change = analysis.rename(position, new_name).unwrap().unwrap();
302 expect.assert_debug_eq(&source_change)
303 }
268 304
269 #[test] 305 #[test]
270 fn test_rename_to_underscore() { 306 fn test_rename_to_underscore() {
271 test_rename( 307 check("_", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let _ = 1; }"#);
272 r#"
273 fn main() {
274 let i<|> = 1;
275 }"#,
276 "_",
277 r#"
278 fn main() {
279 let _ = 1;
280 }"#,
281 );
282 } 308 }
283 309
284 #[test] 310 #[test]
285 fn test_rename_to_raw_identifier() { 311 fn test_rename_to_raw_identifier() {
286 test_rename( 312 check("r#fn", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
287 r#"
288 fn main() {
289 let i<|> = 1;
290 }"#,
291 "r#fn",
292 r#"
293 fn main() {
294 let r#fn = 1;
295 }"#,
296 );
297 } 313 }
298 314
299 #[test] 315 #[test]
300 fn test_rename_to_invalid_identifier() { 316 fn test_rename_to_invalid_identifier() {
301 let (analysis, position) = single_file_with_position( 317 let (analysis, position) = analysis_and_position(r#"fn main() { let i<|> = 1; }"#);
302 "
303 fn main() {
304 let i<|> = 1;
305 }",
306 );
307 let new_name = "invalid!"; 318 let new_name = "invalid!";
308 let source_change = analysis.rename(position, new_name).unwrap(); 319 let source_change = analysis.rename(position, new_name).unwrap();
309 assert!(source_change.is_none()); 320 assert!(source_change.is_none());
@@ -311,682 +322,689 @@ mod tests {
311 322
312 #[test] 323 #[test]
313 fn test_rename_for_local() { 324 fn test_rename_for_local() {
314 test_rename( 325 check(
326 "k",
315 r#" 327 r#"
316 fn main() { 328fn main() {
317 let mut i = 1; 329 let mut i = 1;
318 let j = 1; 330 let j = 1;
319 i = i<|> + j; 331 i = i<|> + j;
320 332
321 { 333 { i = 0; }
322 i = 0;
323 }
324 334
325 i = 5; 335 i = 5;
326 }"#, 336}
327 "k", 337"#,
328 r#" 338 r#"
329 fn main() { 339fn main() {
330 let mut k = 1; 340 let mut k = 1;
331 let j = 1; 341 let j = 1;
332 k = k + j; 342 k = k + j;
333 343
334 { 344 { k = 0; }
335 k = 0;
336 }
337 345
338 k = 5; 346 k = 5;
339 }"#, 347}
348"#,
340 ); 349 );
341 } 350 }
342 351
343 #[test] 352 #[test]
344 fn test_rename_for_macro_args() { 353 fn test_rename_for_macro_args() {
345 test_rename( 354 check(
346 r#"
347 macro_rules! foo {($i:ident) => {$i} }
348 fn main() {
349 let a<|> = "test";
350 foo!(a);
351 }"#,
352 "b", 355 "b",
353 r#" 356 r#"
354 macro_rules! foo {($i:ident) => {$i} } 357macro_rules! foo {($i:ident) => {$i} }
355 fn main() { 358fn main() {
356 let b = "test"; 359 let a<|> = "test";
357 foo!(b); 360 foo!(a);
358 }"#, 361}
362"#,
363 r#"
364macro_rules! foo {($i:ident) => {$i} }
365fn main() {
366 let b = "test";
367 foo!(b);
368}
369"#,
359 ); 370 );
360 } 371 }
361 372
362 #[test] 373 #[test]
363 fn test_rename_for_macro_args_rev() { 374 fn test_rename_for_macro_args_rev() {
364 test_rename( 375 check(
365 r#"
366 macro_rules! foo {($i:ident) => {$i} }
367 fn main() {
368 let a = "test";
369 foo!(a<|>);
370 }"#,
371 "b", 376 "b",
372 r#" 377 r#"
373 macro_rules! foo {($i:ident) => {$i} } 378macro_rules! foo {($i:ident) => {$i} }
374 fn main() { 379fn main() {
375 let b = "test"; 380 let a = "test";
376 foo!(b); 381 foo!(a<|>);
377 }"#, 382}
383"#,
384 r#"
385macro_rules! foo {($i:ident) => {$i} }
386fn main() {
387 let b = "test";
388 foo!(b);
389}
390"#,
378 ); 391 );
379 } 392 }
380 393
381 #[test] 394 #[test]
382 fn test_rename_for_macro_define_fn() { 395 fn test_rename_for_macro_define_fn() {
383 test_rename( 396 check(
384 r#"
385 macro_rules! define_fn {($id:ident) => { fn $id{} }}
386 define_fn!(foo);
387 fn main() {
388 fo<|>o();
389 }"#,
390 "bar", 397 "bar",
391 r#" 398 r#"
392 macro_rules! define_fn {($id:ident) => { fn $id{} }} 399macro_rules! define_fn {($id:ident) => { fn $id{} }}
393 define_fn!(bar); 400define_fn!(foo);
394 fn main() { 401fn main() {
395 bar(); 402 fo<|>o();
396 }"#, 403}
404"#,
405 r#"
406macro_rules! define_fn {($id:ident) => { fn $id{} }}
407define_fn!(bar);
408fn main() {
409 bar();
410}
411"#,
397 ); 412 );
398 } 413 }
399 414
400 #[test] 415 #[test]
401 fn test_rename_for_macro_define_fn_rev() { 416 fn test_rename_for_macro_define_fn_rev() {
402 test_rename( 417 check(
403 r#"
404 macro_rules! define_fn {($id:ident) => { fn $id{} }}
405 define_fn!(fo<|>o);
406 fn main() {
407 foo();
408 }"#,
409 "bar", 418 "bar",
410 r#" 419 r#"
411 macro_rules! define_fn {($id:ident) => { fn $id{} }} 420macro_rules! define_fn {($id:ident) => { fn $id{} }}
412 define_fn!(bar); 421define_fn!(fo<|>o);
413 fn main() { 422fn main() {
414 bar(); 423 foo();
415 }"#, 424}
425"#,
426 r#"
427macro_rules! define_fn {($id:ident) => { fn $id{} }}
428define_fn!(bar);
429fn main() {
430 bar();
431}
432"#,
416 ); 433 );
417 } 434 }
418 435
419 #[test] 436 #[test]
420 fn test_rename_for_param_inside() { 437 fn test_rename_for_param_inside() {
421 test_rename( 438 check("j", r#"fn foo(i : u32) -> u32 { i<|> }"#, r#"fn foo(j : u32) -> u32 { j }"#);
422 r#"
423 fn foo(i : u32) -> u32 {
424 i<|>
425 }"#,
426 "j",
427 r#"
428 fn foo(j : u32) -> u32 {
429 j
430 }"#,
431 );
432 } 439 }
433 440
434 #[test] 441 #[test]
435 fn test_rename_refs_for_fn_param() { 442 fn test_rename_refs_for_fn_param() {
436 test_rename( 443 check("j", r#"fn foo(i<|> : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
437 r#"
438 fn foo(i<|> : u32) -> u32 {
439 i
440 }"#,
441 "new_name",
442 r#"
443 fn foo(new_name : u32) -> u32 {
444 new_name
445 }"#,
446 );
447 } 444 }
448 445
449 #[test] 446 #[test]
450 fn test_rename_for_mut_param() { 447 fn test_rename_for_mut_param() {
451 test_rename( 448 check("j", r#"fn foo(mut i<|> : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
452 r#"
453 fn foo(mut i<|> : u32) -> u32 {
454 i
455 }"#,
456 "new_name",
457 r#"
458 fn foo(mut new_name : u32) -> u32 {
459 new_name
460 }"#,
461 );
462 } 449 }
463 450
464 #[test] 451 #[test]
465 fn test_rename_struct_field() { 452 fn test_rename_struct_field() {
466 test_rename( 453 check(
454 "j",
467 r#" 455 r#"
468 struct Foo { 456struct Foo { i<|>: i32 }
469 i<|>: i32,
470 }
471 457
472 impl Foo { 458impl Foo {
473 fn new(i: i32) -> Self { 459 fn new(i: i32) -> Self {
474 Self { i: i } 460 Self { i: i }
475 }
476 } 461 }
477 "#, 462}
478 "j", 463"#,
479 r#" 464 r#"
480 struct Foo { 465struct Foo { j: i32 }
481 j: i32,
482 }
483 466
484 impl Foo { 467impl Foo {
485 fn new(i: i32) -> Self { 468 fn new(i: i32) -> Self {
486 Self { j: i } 469 Self { j: i }
487 }
488 } 470 }
489 "#, 471}
472"#,
490 ); 473 );
491 } 474 }
492 475
493 #[test] 476 #[test]
494 fn test_rename_struct_field_for_shorthand() { 477 fn test_rename_struct_field_for_shorthand() {
495 mark::check!(test_rename_struct_field_for_shorthand); 478 mark::check!(test_rename_struct_field_for_shorthand);
496 test_rename( 479 check(
480 "j",
497 r#" 481 r#"
498 struct Foo { 482struct Foo { i<|>: i32 }
499 i<|>: i32,
500 }
501 483
502 impl Foo { 484impl Foo {
503 fn new(i: i32) -> Self { 485 fn new(i: i32) -> Self {
504 Self { i } 486 Self { i }
505 }
506 } 487 }
507 "#, 488}
508 "j", 489"#,
509 r#" 490 r#"
510 struct Foo { 491struct Foo { j: i32 }
511 j: i32,
512 }
513 492
514 impl Foo { 493impl Foo {
515 fn new(i: i32) -> Self { 494 fn new(i: i32) -> Self {
516 Self { j: i } 495 Self { j: i }
517 }
518 } 496 }
519 "#, 497}
498"#,
520 ); 499 );
521 } 500 }
522 501
523 #[test] 502 #[test]
524 fn test_rename_local_for_field_shorthand() { 503 fn test_rename_local_for_field_shorthand() {
525 mark::check!(test_rename_local_for_field_shorthand); 504 mark::check!(test_rename_local_for_field_shorthand);
526 test_rename( 505 check(
506 "j",
527 r#" 507 r#"
528 struct Foo { 508struct Foo { i: i32 }
529 i: i32,
530 }
531 509
532 impl Foo { 510impl Foo {
533 fn new(i<|>: i32) -> Self { 511 fn new(i<|>: i32) -> Self {
534 Self { i } 512 Self { i }
535 }
536 } 513 }
537 "#, 514}
538 "j", 515"#,
539 r#" 516 r#"
540 struct Foo { 517struct Foo { i: i32 }
541 i: i32,
542 }
543 518
544 impl Foo { 519impl Foo {
545 fn new(j: i32) -> Self { 520 fn new(j: i32) -> Self {
546 Self { i: j } 521 Self { i: j }
547 }
548 } 522 }
549 "#, 523}
524"#,
550 ); 525 );
551 } 526 }
552 527
553 #[test] 528 #[test]
554 fn test_field_shorthand_correct_struct() { 529 fn test_field_shorthand_correct_struct() {
555 test_rename( 530 check(
556 r#"
557 struct Foo {
558 i<|>: i32,
559 }
560
561 struct Bar {
562 i: i32,
563 }
564
565 impl Bar {
566 fn new(i: i32) -> Self {
567 Self { i }
568 }
569 }
570 "#,
571 "j", 531 "j",
572 r#" 532 r#"
573 struct Foo { 533struct Foo { i<|>: i32 }
574 j: i32, 534struct Bar { i: i32 }
575 }
576 535
577 struct Bar { 536impl Bar {
578 i: i32, 537 fn new(i: i32) -> Self {
538 Self { i }
579 } 539 }
540}
541"#,
542 r#"
543struct Foo { j: i32 }
544struct Bar { i: i32 }
580 545
581 impl Bar { 546impl Bar {
582 fn new(i: i32) -> Self { 547 fn new(i: i32) -> Self {
583 Self { i } 548 Self { i }
584 }
585 } 549 }
586 "#, 550}
551"#,
587 ); 552 );
588 } 553 }
589 554
590 #[test] 555 #[test]
591 fn test_shadow_local_for_struct_shorthand() { 556 fn test_shadow_local_for_struct_shorthand() {
592 test_rename( 557 check(
558 "j",
593 r#" 559 r#"
594 struct Foo { 560struct Foo { i: i32 }
595 i: i32,
596 }
597 561
598 fn baz(i<|>: i32) -> Self { 562fn baz(i<|>: i32) -> Self {
599 let x = Foo { i }; 563 let x = Foo { i };
600 { 564 {
601 let i = 0; 565 let i = 0;
602 Foo { i } 566 Foo { i }
603 }
604 } 567 }
605 "#, 568}
606 "j", 569"#,
607 r#" 570 r#"
608 struct Foo { 571struct Foo { i: i32 }
609 i: i32,
610 }
611 572
612 fn baz(j: i32) -> Self { 573fn baz(j: i32) -> Self {
613 let x = Foo { i: j }; 574 let x = Foo { i: j };
614 { 575 {
615 let i = 0; 576 let i = 0;
616 Foo { i } 577 Foo { i }
617 }
618 } 578 }
619 "#, 579}
580"#,
620 ); 581 );
621 } 582 }
622 583
623 #[test] 584 #[test]
624 fn test_rename_mod() { 585 fn test_rename_mod() {
625 let (analysis, position) = analysis_and_position( 586 check_expect(
626 " 587 "foo2",
627 //- /lib.rs 588 r#"
628 mod bar; 589//- /lib.rs
629 590mod bar;
630 //- /bar.rs 591
631 mod foo<|>; 592//- /bar.rs
632 593mod foo<|>;
633 //- /bar/foo.rs 594
634 // emtpy 595//- /bar/foo.rs
635 ", 596// empty
597"#,
598 expect![[r#"
599 RangeInfo {
600 range: 4..7,
601 info: SourceChange {
602 source_file_edits: [
603 SourceFileEdit {
604 file_id: FileId(
605 2,
606 ),
607 edit: TextEdit {
608 indels: [
609 Indel {
610 insert: "foo2",
611 delete: 4..7,
612 },
613 ],
614 },
615 },
616 ],
617 file_system_edits: [
618 MoveFile {
619 src: FileId(
620 3,
621 ),
622 anchor: FileId(
623 3,
624 ),
625 dst: "foo2.rs",
626 },
627 ],
628 is_snippet: false,
629 },
630 }
631 "#]],
636 ); 632 );
637 let new_name = "foo2"; 633 }
638 let source_change = analysis.rename(position, new_name).unwrap(); 634
639 assert_debug_snapshot!(&source_change, 635 #[test]
640@r###" 636 fn test_rename_mod_in_use_tree() {
641 Some( 637 check_expect(
642 RangeInfo { 638 "quux",
643 range: 4..7, 639 r#"
644 info: SourceChange { 640//- /main.rs
645 source_file_edits: [ 641pub mod foo;
646 SourceFileEdit { 642pub mod bar;
647 file_id: FileId( 643fn main() {}
648 2, 644
649 ), 645//- /foo.rs
650 edit: TextEdit { 646pub struct FooContent;
651 indels: [ 647
652 Indel { 648//- /bar.rs
653 insert: "foo2", 649use crate::foo<|>::FooContent;
654 delete: 4..7, 650"#,
655 }, 651 expect![[r#"
656 ], 652 RangeInfo {
653 range: 11..14,
654 info: SourceChange {
655 source_file_edits: [
656 SourceFileEdit {
657 file_id: FileId(
658 1,
659 ),
660 edit: TextEdit {
661 indels: [
662 Indel {
663 insert: "quux",
664 delete: 8..11,
665 },
666 ],
667 },
657 }, 668 },
658 }, 669 SourceFileEdit {
659 ], 670 file_id: FileId(
660 file_system_edits: [ 671 3,
661 MoveFile { 672 ),
662 src: FileId( 673 edit: TextEdit {
663 3, 674 indels: [
664 ), 675 Indel {
665 dst_source_root: SourceRootId( 676 insert: "quux",
666 0, 677 delete: 11..14,
667 ), 678 },
668 dst_path: "bar/foo2.rs", 679 ],
669 }, 680 },
670 ], 681 },
671 is_snippet: false, 682 ],
672 }, 683 file_system_edits: [
673 }, 684 MoveFile {
674 ) 685 src: FileId(
675 "###); 686 2,
687 ),
688 anchor: FileId(
689 2,
690 ),
691 dst: "quux.rs",
692 },
693 ],
694 is_snippet: false,
695 },
696 }
697 "#]],
698 );
676 } 699 }
677 700
678 #[test] 701 #[test]
679 fn test_rename_mod_in_dir() { 702 fn test_rename_mod_in_dir() {
680 let (analysis, position) = analysis_and_position( 703 check_expect(
681 " 704 "foo2",
682 //- /lib.rs 705 r#"
683 mod fo<|>o; 706//- /lib.rs
684 //- /foo/mod.rs 707mod fo<|>o;
685 // emtpy 708//- /foo/mod.rs
686 ", 709// emtpy
687 ); 710"#,
688 let new_name = "foo2"; 711 expect![[r#"
689 let source_change = analysis.rename(position, new_name).unwrap(); 712 RangeInfo {
690 assert_debug_snapshot!(&source_change, 713 range: 4..7,
691 @r###" 714 info: SourceChange {
692 Some( 715 source_file_edits: [
693 RangeInfo { 716 SourceFileEdit {
694 range: 4..7, 717 file_id: FileId(
695 info: SourceChange { 718 1,
696 source_file_edits: [ 719 ),
697 SourceFileEdit { 720 edit: TextEdit {
698 file_id: FileId( 721 indels: [
699 1, 722 Indel {
700 ), 723 insert: "foo2",
701 edit: TextEdit { 724 delete: 4..7,
702 indels: [ 725 },
703 Indel { 726 ],
704 insert: "foo2", 727 },
705 delete: 4..7,
706 },
707 ],
708 }, 728 },
709 }, 729 ],
710 ], 730 file_system_edits: [
711 file_system_edits: [ 731 MoveFile {
712 MoveFile { 732 src: FileId(
713 src: FileId( 733 2,
714 2, 734 ),
715 ), 735 anchor: FileId(
716 dst_source_root: SourceRootId( 736 2,
717 0, 737 ),
718 ), 738 dst: "../foo2/mod.rs",
719 dst_path: "foo2/mod.rs", 739 },
720 }, 740 ],
721 ], 741 is_snippet: false,
722 is_snippet: false, 742 },
723 }, 743 }
724 }, 744 "#]],
725 ) 745 );
726 "###
727 );
728 } 746 }
729 747
730 #[test] 748 #[test]
731 fn test_module_rename_in_path() { 749 fn test_rename_unusually_nested_mod() {
732 test_rename( 750 check_expect(
751 "bar",
733 r#" 752 r#"
734 mod <|>foo { 753//- /lib.rs
735 pub fn bar() {} 754mod outer { mod fo<|>o; }
755
756//- /outer/foo.rs
757// emtpy
758"#,
759 expect![[r#"
760 RangeInfo {
761 range: 16..19,
762 info: SourceChange {
763 source_file_edits: [
764 SourceFileEdit {
765 file_id: FileId(
766 1,
767 ),
768 edit: TextEdit {
769 indels: [
770 Indel {
771 insert: "bar",
772 delete: 16..19,
773 },
774 ],
775 },
776 },
777 ],
778 file_system_edits: [
779 MoveFile {
780 src: FileId(
781 2,
782 ),
783 anchor: FileId(
784 2,
785 ),
786 dst: "bar.rs",
787 },
788 ],
789 is_snippet: false,
790 },
791 }
792 "#]],
793 );
736 } 794 }
737 795
738 fn main() { 796 #[test]
739 foo::bar(); 797 fn test_module_rename_in_path() {
740 }"#, 798 check(
741 "baz", 799 "baz",
742 r#" 800 r#"
743 mod baz { 801mod <|>foo { pub fn bar() {} }
744 pub fn bar() {}
745 }
746 802
747 fn main() { 803fn main() { foo::bar(); }
748 baz::bar(); 804"#,
749 }"#, 805 r#"
806mod baz { pub fn bar() {} }
807
808fn main() { baz::bar(); }
809"#,
750 ); 810 );
751 } 811 }
752 812
753 #[test] 813 #[test]
754 fn test_rename_mod_filename_and_path() { 814 fn test_rename_mod_filename_and_path() {
755 let (analysis, position) = analysis_and_position( 815 check_expect(
756 " 816 "foo2",
757 //- /lib.rs 817 r#"
758 mod bar; 818//- /lib.rs
759 fn f() { 819mod bar;
760 bar::foo::fun() 820fn f() {
761 } 821 bar::foo::fun()
762 822}
763 //- /bar.rs
764 pub mod foo<|>;
765 823
766 //- /bar/foo.rs 824//- /bar.rs
767 // pub fn fun() {} 825pub mod foo<|>;
768 ", 826
769 ); 827//- /bar/foo.rs
770 let new_name = "foo2"; 828// pub fn fun() {}
771 let source_change = analysis.rename(position, new_name).unwrap(); 829"#,
772 assert_debug_snapshot!(&source_change, 830 expect![[r#"
773@r###" 831 RangeInfo {
774 Some( 832 range: 8..11,
775 RangeInfo { 833 info: SourceChange {
776 range: 8..11, 834 source_file_edits: [
777 info: SourceChange { 835 SourceFileEdit {
778 source_file_edits: [ 836 file_id: FileId(
779 SourceFileEdit { 837 2,
780 file_id: FileId( 838 ),
781 2, 839 edit: TextEdit {
782 ), 840 indels: [
783 edit: TextEdit { 841 Indel {
784 indels: [ 842 insert: "foo2",
785 Indel { 843 delete: 8..11,
786 insert: "foo2", 844 },
787 delete: 8..11, 845 ],
788 }, 846 },
789 ],
790 }, 847 },
791 }, 848 SourceFileEdit {
792 SourceFileEdit { 849 file_id: FileId(
793 file_id: FileId( 850 1,
794 1, 851 ),
795 ), 852 edit: TextEdit {
796 edit: TextEdit { 853 indels: [
797 indels: [ 854 Indel {
798 Indel { 855 insert: "foo2",
799 insert: "foo2", 856 delete: 27..30,
800 delete: 27..30, 857 },
801 }, 858 ],
802 ], 859 },
803 }, 860 },
804 }, 861 ],
805 ], 862 file_system_edits: [
806 file_system_edits: [ 863 MoveFile {
807 MoveFile { 864 src: FileId(
808 src: FileId( 865 3,
809 3, 866 ),
810 ), 867 anchor: FileId(
811 dst_source_root: SourceRootId( 868 3,
812 0, 869 ),
813 ), 870 dst: "foo2.rs",
814 dst_path: "bar/foo2.rs", 871 },
815 }, 872 ],
816 ], 873 is_snippet: false,
817 is_snippet: false, 874 },
818 }, 875 }
819 }, 876 "#]],
820 ) 877 );
821 "###);
822 } 878 }
823 879
824 #[test] 880 #[test]
825 fn test_enum_variant_from_module_1() { 881 fn test_enum_variant_from_module_1() {
826 test_rename( 882 check(
883 "Baz",
827 r#" 884 r#"
828 mod foo { 885mod foo {
829 pub enum Foo { 886 pub enum Foo { Bar<|> }
830 Bar<|>, 887}
831 }
832 }
833 888
834 fn func(f: foo::Foo) { 889fn func(f: foo::Foo) {
835 match f { 890 match f {
836 foo::Foo::Bar => {} 891 foo::Foo::Bar => {}
837 }
838 } 892 }
839 "#, 893}
840 "Baz", 894"#,
841 r#" 895 r#"
842 mod foo { 896mod foo {
843 pub enum Foo { 897 pub enum Foo { Baz }
844 Baz, 898}
845 }
846 }
847 899
848 fn func(f: foo::Foo) { 900fn func(f: foo::Foo) {
849 match f { 901 match f {
850 foo::Foo::Baz => {} 902 foo::Foo::Baz => {}
851 }
852 } 903 }
853 "#, 904}
905"#,
854 ); 906 );
855 } 907 }
856 908
857 #[test] 909 #[test]
858 fn test_enum_variant_from_module_2() { 910 fn test_enum_variant_from_module_2() {
859 test_rename( 911 check(
912 "baz",
860 r#" 913 r#"
861 mod foo { 914mod foo {
862 pub struct Foo { 915 pub struct Foo { pub bar<|>: uint }
863 pub bar<|>: uint, 916}
864 }
865 }
866 917
867 fn foo(f: foo::Foo) { 918fn foo(f: foo::Foo) {
868 let _ = f.bar; 919 let _ = f.bar;
869 } 920}
870 "#, 921"#,
871 "baz",
872 r#" 922 r#"
873 mod foo { 923mod foo {
874 pub struct Foo { 924 pub struct Foo { pub baz: uint }
875 pub baz: uint, 925}
876 }
877 }
878 926
879 fn foo(f: foo::Foo) { 927fn foo(f: foo::Foo) {
880 let _ = f.baz; 928 let _ = f.baz;
881 } 929}
882 "#, 930"#,
883 ); 931 );
884 } 932 }
885 933
886 #[test] 934 #[test]
887 fn test_parameter_to_self() { 935 fn test_parameter_to_self() {
888 test_rename( 936 check(
937 "self",
889 r#" 938 r#"
890 struct Foo { 939struct Foo { i: i32 }
891 i: i32,
892 }
893 940
894 impl Foo { 941impl Foo {
895 fn f(foo<|>: &mut Foo) -> i32 { 942 fn f(foo<|>: &mut Foo) -> i32 {
896 foo.i 943 foo.i
897 }
898 } 944 }
899 "#, 945}
900 "self", 946"#,
901 r#" 947 r#"
902 struct Foo { 948struct Foo { i: i32 }
903 i: i32,
904 }
905 949
906 impl Foo { 950impl Foo {
907 fn f(&mut self) -> i32 { 951 fn f(&mut self) -> i32 {
908 self.i 952 self.i
909 }
910 } 953 }
911 "#, 954}
955"#,
912 ); 956 );
913 } 957 }
914 958
915 #[test] 959 #[test]
916 fn test_self_to_parameter() { 960 fn test_self_to_parameter() {
917 test_rename( 961 check(
962 "foo",
918 r#" 963 r#"
919 struct Foo { 964struct Foo { i: i32 }
920 i: i32,
921 }
922 965
923 impl Foo { 966impl Foo {
924 fn f(&mut <|>self) -> i32 { 967 fn f(&mut <|>self) -> i32 {
925 self.i 968 self.i
926 }
927 } 969 }
928 "#, 970}
929 "foo", 971"#,
930 r#" 972 r#"
931 struct Foo { 973struct Foo { i: i32 }
932 i: i32,
933 }
934 974
935 impl Foo { 975impl Foo {
936 fn f(foo: &mut Foo) -> i32 { 976 fn f(foo: &mut Foo) -> i32 {
937 foo.i 977 foo.i
938 }
939 } 978 }
940 "#, 979}
980"#,
941 ); 981 );
942 } 982 }
943 983
944 #[test] 984 #[test]
945 fn test_self_in_path_to_parameter() { 985 fn test_self_in_path_to_parameter() {
946 test_rename( 986 check(
987 "foo",
947 r#" 988 r#"
948 struct Foo { 989struct Foo { i: i32 }
949 i: i32,
950 }
951 990
952 impl Foo { 991impl Foo {
953 fn f(&self) -> i32 { 992 fn f(&self) -> i32 {
954 let self_var = 1; 993 let self_var = 1;
955 self<|>.i 994 self<|>.i
956 }
957 } 995 }
958 "#, 996}
959 "foo", 997"#,
960 r#" 998 r#"
961 struct Foo { 999struct Foo { i: i32 }
962 i: i32,
963 }
964 1000
965 impl Foo { 1001impl Foo {
966 fn f(foo: &Foo) -> i32 { 1002 fn f(foo: &Foo) -> i32 {
967 let self_var = 1; 1003 let self_var = 1;
968 foo.i 1004 foo.i
969 }
970 } 1005 }
971 "#, 1006}
1007"#,
972 ); 1008 );
973 } 1009 }
974
975 fn test_rename(text: &str, new_name: &str, expected: &str) {
976 let (analysis, position) = single_file_with_position(text);
977 let source_change = analysis.rename(position, new_name).unwrap();
978 let mut text_edit_builder = TextEditBuilder::default();
979 let mut file_id: Option<FileId> = None;
980 if let Some(change) = source_change {
981 for edit in change.info.source_file_edits {
982 file_id = Some(edit.file_id);
983 for indel in edit.edit.into_iter() {
984 text_edit_builder.replace(indel.delete, indel.insert);
985 }
986 }
987 }
988 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
989 text_edit_builder.finish().apply(&mut result);
990 assert_eq_text!(expected, &*result);
991 }
992} 1010}
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..ed15d6494 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,31 +1,31 @@
1use std::fmt;
2
1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
2use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
3use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
4use ra_syntax::{ 7use ra_syntax::{
5 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
6 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode,
7}; 10};
8 11
9use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
10use ast::DocCommentsOwner;
11use ra_cfg::CfgExpr;
12use std::fmt::Display;
13 13
14#[derive(Debug)] 14#[derive(Debug, Clone)]
15pub struct Runnable { 15pub struct Runnable {
16 pub range: TextRange, 16 pub nav: NavigationTarget,
17 pub kind: RunnableKind, 17 pub kind: RunnableKind,
18 pub cfg_exprs: Vec<CfgExpr>, 18 pub cfg_exprs: Vec<CfgExpr>,
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug, Clone)]
22pub enum TestId { 22pub enum TestId {
23 Name(String), 23 Name(String),
24 Path(String), 24 Path(String),
25} 25}
26 26
27impl Display for TestId { 27impl fmt::Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self { 29 match self {
30 TestId::Name(name) => write!(f, "{}", name), 30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path), 31 TestId::Path(path) => write!(f, "{}", path),
@@ -33,7 +33,7 @@ impl Display for TestId {
33 } 33 }
34} 34}
35 35
36#[derive(Debug)] 36#[derive(Debug, Clone)]
37pub enum RunnableKind { 37pub enum RunnableKind {
38 Test { test_id: TestId, attr: TestAttr }, 38 Test { test_id: TestId, attr: TestAttr },
39 TestMod { path: String }, 39 TestMod { path: String },
@@ -42,6 +42,42 @@ pub enum RunnableKind {
42 Bin, 42 Bin,
43} 43}
44 44
45#[derive(Debug, Eq, PartialEq)]
46pub struct RunnableAction {
47 pub run_title: &'static str,
48 pub debugee: bool,
49}
50
51const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true };
52const DOCTEST: RunnableAction =
53 RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false };
54const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true };
55const BIN: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run", debugee: true };
56
57impl Runnable {
58 // test package::module::testname
59 pub fn label(&self, target: Option<String>) -> String {
60 match &self.kind {
61 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
62 RunnableKind::TestMod { path } => format!("test-mod {}", path),
63 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
64 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
65 RunnableKind::Bin => {
66 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
67 }
68 }
69 }
70
71 pub fn action(&self) -> &'static RunnableAction {
72 match &self.kind {
73 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST,
74 RunnableKind::DocTest { .. } => &DOCTEST,
75 RunnableKind::Bench { .. } => &BENCH,
76 RunnableKind::Bin => &BIN,
77 }
78 }
79}
80
45// Feature: Run 81// Feature: Run
46// 82//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 83// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
@@ -59,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
59 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 95 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
60} 96}
61 97
62fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { 98pub(crate) fn runnable(
99 sema: &Semantics<RootDatabase>,
100 item: SyntaxNode,
101 file_id: FileId,
102) -> Option<Runnable> {
63 match_ast! { 103 match_ast! {
64 match item { 104 match item {
65 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::FnDef(it) => runnable_fn(sema, it, file_id),
@@ -131,10 +171,19 @@ fn runnable_fn(
131 let cfg_exprs = 171 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 172 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 173
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 174 let nav = if let RunnableKind::DocTest { .. } = kind {
175 NavigationTarget::from_doc_commented(
176 sema.db,
177 InFile::new(file_id.into(), &fn_def),
178 InFile::new(file_id.into(), &fn_def),
179 )
180 } else {
181 NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def))
182 };
183 Some(Runnable { nav, kind, cfg_exprs })
135} 184}
136 185
137#[derive(Debug)] 186#[derive(Debug, Copy, Clone)]
138pub struct TestAttr { 187pub struct TestAttr {
139 pub ignore: bool, 188 pub ignore: bool,
140} 189}
@@ -183,7 +232,6 @@ fn runnable_mod(
183 if !has_test_function { 232 if !has_test_function {
184 return None; 233 return None;
185 } 234 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 235 let module_def = sema.to_def(&module)?;
188 236
189 let path = module_def 237 let path = module_def
@@ -197,366 +245,586 @@ fn runnable_mod(
197 let cfg_exprs = 245 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 246 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 247
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 248 let nav = module_def.to_nav(sema.db);
249 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 250}
202 251
203#[cfg(test)] 252#[cfg(test)]
204mod tests { 253mod tests {
205 use insta::assert_debug_snapshot; 254 use expect::{expect, Expect};
206 255
207 use crate::mock_analysis::analysis_and_position; 256 use crate::mock_analysis::analysis_and_position;
208 257
258 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST};
259
260 fn check(
261 ra_fixture: &str,
262 // FIXME: fold this into `expect` as well
263 actions: &[&RunnableAction],
264 expect: Expect,
265 ) {
266 let (analysis, position) = analysis_and_position(ra_fixture);
267 let runnables = analysis.runnables(position.file_id).unwrap();
268 expect.assert_debug_eq(&runnables);
269 assert_eq!(
270 actions,
271 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
272 );
273 }
274
209 #[test] 275 #[test]
210 fn test_runnables() { 276 fn test_runnables() {
211 let (analysis, pos) = analysis_and_position( 277 check(
212 r#" 278 r#"
213 //- /lib.rs 279//- /lib.rs
214 <|> //empty 280<|>
215 fn main() {} 281fn main() {}
216 282
217 #[test] 283#[test]
218 fn test_foo() {} 284fn test_foo() {}
219 285
220 #[test] 286#[test]
221 #[ignore] 287#[ignore]
222 fn test_foo() {} 288fn test_foo() {}
223 "#, 289
224 ); 290#[bench]
225 let runnables = analysis.runnables(pos.file_id).unwrap(); 291fn bench() {}
226 assert_debug_snapshot!(&runnables, 292"#,
227 @r###" 293 &[&BIN, &TEST, &TEST, &BENCH],
228 [ 294 expect![[r#"
229 Runnable { 295 [
230 range: 1..21, 296 Runnable {
231 kind: Bin, 297 nav: NavigationTarget {
232 cfg_exprs: [], 298 file_id: FileId(
233 }, 299 1,
234 Runnable { 300 ),
235 range: 22..46, 301 full_range: 1..13,
236 kind: Test { 302 name: "main",
237 test_id: Path( 303 kind: FN_DEF,
238 "test_foo", 304 focus_range: Some(
239 ), 305 4..8,
240 attr: TestAttr { 306 ),
241 ignore: false, 307 container_name: None,
308 description: None,
309 docs: None,
310 },
311 kind: Bin,
312 cfg_exprs: [],
313 },
314 Runnable {
315 nav: NavigationTarget {
316 file_id: FileId(
317 1,
318 ),
319 full_range: 15..39,
320 name: "test_foo",
321 kind: FN_DEF,
322 focus_range: Some(
323 26..34,
324 ),
325 container_name: None,
326 description: None,
327 docs: None,
328 },
329 kind: Test {
330 test_id: Path(
331 "test_foo",
332 ),
333 attr: TestAttr {
334 ignore: false,
335 },
336 },
337 cfg_exprs: [],
338 },
339 Runnable {
340 nav: NavigationTarget {
341 file_id: FileId(
342 1,
343 ),
344 full_range: 41..75,
345 name: "test_foo",
346 kind: FN_DEF,
347 focus_range: Some(
348 62..70,
349 ),
350 container_name: None,
351 description: None,
352 docs: None,
353 },
354 kind: Test {
355 test_id: Path(
356 "test_foo",
357 ),
358 attr: TestAttr {
359 ignore: true,
360 },
361 },
362 cfg_exprs: [],
242 }, 363 },
243 }, 364 Runnable {
244 cfg_exprs: [], 365 nav: NavigationTarget {
245 }, 366 file_id: FileId(
246 Runnable { 367 1,
247 range: 47..81, 368 ),
248 kind: Test { 369 full_range: 77..99,
249 test_id: Path( 370 name: "bench",
250 "test_foo", 371 kind: FN_DEF,
251 ), 372 focus_range: Some(
252 attr: TestAttr { 373 89..94,
253 ignore: true, 374 ),
375 container_name: None,
376 description: None,
377 docs: None,
378 },
379 kind: Bench {
380 test_id: Path(
381 "bench",
382 ),
383 },
384 cfg_exprs: [],
254 }, 385 },
255 }, 386 ]
256 cfg_exprs: [], 387 "#]],
257 }, 388 );
258 ]
259 "###
260 );
261 } 389 }
262 390
263 #[test] 391 #[test]
264 fn test_runnables_doc_test() { 392 fn test_runnables_doc_test() {
265 let (analysis, pos) = analysis_and_position( 393 check(
266 r#" 394 r#"
267 //- /lib.rs 395//- /lib.rs
268 <|> //empty 396<|>
269 fn main() {} 397fn main() {}
270 398
271 /// ``` 399/// ```
272 /// let x = 5; 400/// let x = 5;
273 /// ``` 401/// ```
274 fn foo() {} 402fn foo() {}
275 "#, 403"#,
404 &[&BIN, &DOCTEST],
405 expect![[r#"
406 [
407 Runnable {
408 nav: NavigationTarget {
409 file_id: FileId(
410 1,
411 ),
412 full_range: 1..13,
413 name: "main",
414 kind: FN_DEF,
415 focus_range: Some(
416 4..8,
417 ),
418 container_name: None,
419 description: None,
420 docs: None,
421 },
422 kind: Bin,
423 cfg_exprs: [],
424 },
425 Runnable {
426 nav: NavigationTarget {
427 file_id: FileId(
428 1,
429 ),
430 full_range: 15..57,
431 name: "foo",
432 kind: FN_DEF,
433 focus_range: None,
434 container_name: None,
435 description: None,
436 docs: None,
437 },
438 kind: DocTest {
439 test_id: Path(
440 "foo",
441 ),
442 },
443 cfg_exprs: [],
444 },
445 ]
446 "#]],
276 ); 447 );
277 let runnables = analysis.runnables(pos.file_id).unwrap();
278 assert_debug_snapshot!(&runnables,
279 @r###"
280 [
281 Runnable {
282 range: 1..21,
283 kind: Bin,
284 cfg_exprs: [],
285 },
286 Runnable {
287 range: 22..64,
288 kind: DocTest {
289 test_id: Path(
290 "foo",
291 ),
292 },
293 cfg_exprs: [],
294 },
295 ]
296 "###
297 );
298 } 448 }
299 449
300 #[test] 450 #[test]
301 fn test_runnables_doc_test_in_impl() { 451 fn test_runnables_doc_test_in_impl() {
302 let (analysis, pos) = analysis_and_position( 452 check(
303 r#" 453 r#"
304 //- /lib.rs 454//- /lib.rs
305 <|> //empty 455<|>
306 fn main() {} 456fn main() {}
307 457
308 struct Data; 458struct Data;
309 impl Data { 459impl Data {
310 /// ``` 460 /// ```
311 /// let x = 5; 461 /// let x = 5;
312 /// ``` 462 /// ```
313 fn foo() {} 463 fn foo() {}
314 } 464}
315 "#, 465"#,
466 &[&BIN, &DOCTEST],
467 expect![[r#"
468 [
469 Runnable {
470 nav: NavigationTarget {
471 file_id: FileId(
472 1,
473 ),
474 full_range: 1..13,
475 name: "main",
476 kind: FN_DEF,
477 focus_range: Some(
478 4..8,
479 ),
480 container_name: None,
481 description: None,
482 docs: None,
483 },
484 kind: Bin,
485 cfg_exprs: [],
486 },
487 Runnable {
488 nav: NavigationTarget {
489 file_id: FileId(
490 1,
491 ),
492 full_range: 44..98,
493 name: "foo",
494 kind: FN_DEF,
495 focus_range: None,
496 container_name: None,
497 description: None,
498 docs: None,
499 },
500 kind: DocTest {
501 test_id: Path(
502 "Data::foo",
503 ),
504 },
505 cfg_exprs: [],
506 },
507 ]
508 "#]],
316 ); 509 );
317 let runnables = analysis.runnables(pos.file_id).unwrap();
318 assert_debug_snapshot!(&runnables,
319 @r###"
320 [
321 Runnable {
322 range: 1..21,
323 kind: Bin,
324 cfg_exprs: [],
325 },
326 Runnable {
327 range: 51..105,
328 kind: DocTest {
329 test_id: Path(
330 "Data::foo",
331 ),
332 },
333 cfg_exprs: [],
334 },
335 ]
336 "###
337 );
338 } 510 }
339 511
340 #[test] 512 #[test]
341 fn test_runnables_module() { 513 fn test_runnables_module() {
342 let (analysis, pos) = analysis_and_position( 514 check(
343 r#" 515 r#"
344 //- /lib.rs 516//- /lib.rs
345 <|> //empty 517<|>
346 mod test_mod { 518mod test_mod {
347 #[test] 519 #[test]
348 fn test_foo1() {} 520 fn test_foo1() {}
349 } 521}
350 "#, 522"#,
351 ); 523 &[&TEST, &TEST],
352 let runnables = analysis.runnables(pos.file_id).unwrap(); 524 expect![[r#"
353 assert_debug_snapshot!(&runnables, 525 [
354 @r###" 526 Runnable {
355 [ 527 nav: NavigationTarget {
356 Runnable { 528 file_id: FileId(
357 range: 1..59, 529 1,
358 kind: TestMod { 530 ),
359 path: "test_mod", 531 full_range: 1..51,
360 }, 532 name: "test_mod",
361 cfg_exprs: [], 533 kind: MODULE,
362 }, 534 focus_range: Some(
363 Runnable { 535 5..13,
364 range: 28..57, 536 ),
365 kind: Test { 537 container_name: None,
366 test_id: Path( 538 description: None,
367 "test_mod::test_foo1", 539 docs: None,
368 ), 540 },
369 attr: TestAttr { 541 kind: TestMod {
370 ignore: false, 542 path: "test_mod",
543 },
544 cfg_exprs: [],
371 }, 545 },
372 }, 546 Runnable {
373 cfg_exprs: [], 547 nav: NavigationTarget {
374 }, 548 file_id: FileId(
375 ] 549 1,
376 "### 550 ),
377 ); 551 full_range: 20..49,
552 name: "test_foo1",
553 kind: FN_DEF,
554 focus_range: Some(
555 35..44,
556 ),
557 container_name: None,
558 description: None,
559 docs: None,
560 },
561 kind: Test {
562 test_id: Path(
563 "test_mod::test_foo1",
564 ),
565 attr: TestAttr {
566 ignore: false,
567 },
568 },
569 cfg_exprs: [],
570 },
571 ]
572 "#]],
573 );
378 } 574 }
379 575
380 #[test] 576 #[test]
381 fn test_runnables_one_depth_layer_module() { 577 fn test_runnables_one_depth_layer_module() {
382 let (analysis, pos) = analysis_and_position( 578 check(
383 r#" 579 r#"
384 //- /lib.rs 580//- /lib.rs
385 <|> //empty 581<|>
386 mod foo { 582mod foo {
387 mod test_mod { 583 mod test_mod {
388 #[test] 584 #[test]
389 fn test_foo1() {} 585 fn test_foo1() {}
390 } 586 }
391 } 587}
392 "#, 588"#,
393 ); 589 &[&TEST, &TEST],
394 let runnables = analysis.runnables(pos.file_id).unwrap(); 590 expect![[r#"
395 assert_debug_snapshot!(&runnables, 591 [
396 @r###" 592 Runnable {
397 [ 593 nav: NavigationTarget {
398 Runnable { 594 file_id: FileId(
399 range: 23..85, 595 1,
400 kind: TestMod { 596 ),
401 path: "foo::test_mod", 597 full_range: 15..77,
402 }, 598 name: "test_mod",
403 cfg_exprs: [], 599 kind: MODULE,
404 }, 600 focus_range: Some(
405 Runnable { 601 19..27,
406 range: 46..79, 602 ),
407 kind: Test { 603 container_name: None,
408 test_id: Path( 604 description: None,
409 "foo::test_mod::test_foo1", 605 docs: None,
410 ), 606 },
411 attr: TestAttr { 607 kind: TestMod {
412 ignore: false, 608 path: "foo::test_mod",
609 },
610 cfg_exprs: [],
611 },
612 Runnable {
613 nav: NavigationTarget {
614 file_id: FileId(
615 1,
616 ),
617 full_range: 38..71,
618 name: "test_foo1",
619 kind: FN_DEF,
620 focus_range: Some(
621 57..66,
622 ),
623 container_name: None,
624 description: None,
625 docs: None,
626 },
627 kind: Test {
628 test_id: Path(
629 "foo::test_mod::test_foo1",
630 ),
631 attr: TestAttr {
632 ignore: false,
633 },
634 },
635 cfg_exprs: [],
413 }, 636 },
414 }, 637 ]
415 cfg_exprs: [], 638 "#]],
416 }, 639 );
417 ]
418 "###
419 );
420 } 640 }
421 641
422 #[test] 642 #[test]
423 fn test_runnables_multiple_depth_module() { 643 fn test_runnables_multiple_depth_module() {
424 let (analysis, pos) = analysis_and_position( 644 check(
425 r#" 645 r#"
426 //- /lib.rs 646//- /lib.rs
427 <|> //empty 647<|>
428 mod foo { 648mod foo {
429 mod bar { 649 mod bar {
430 mod test_mod { 650 mod test_mod {
431 #[test] 651 #[test]
432 fn test_foo1() {} 652 fn test_foo1() {}
433 }
434 }
435 } 653 }
436 "#, 654 }
437 ); 655}
438 let runnables = analysis.runnables(pos.file_id).unwrap(); 656"#,
439 assert_debug_snapshot!(&runnables, 657 &[&TEST, &TEST],
440 @r###" 658 expect![[r#"
441 [ 659 [
442 Runnable { 660 Runnable {
443 range: 41..115, 661 nav: NavigationTarget {
444 kind: TestMod { 662 file_id: FileId(
445 path: "foo::bar::test_mod", 663 1,
446 }, 664 ),
447 cfg_exprs: [], 665 full_range: 33..107,
448 }, 666 name: "test_mod",
449 Runnable { 667 kind: MODULE,
450 range: 68..105, 668 focus_range: Some(
451 kind: Test { 669 37..45,
452 test_id: Path( 670 ),
453 "foo::bar::test_mod::test_foo1", 671 container_name: None,
454 ), 672 description: None,
455 attr: TestAttr { 673 docs: None,
456 ignore: false, 674 },
675 kind: TestMod {
676 path: "foo::bar::test_mod",
677 },
678 cfg_exprs: [],
457 }, 679 },
458 }, 680 Runnable {
459 cfg_exprs: [], 681 nav: NavigationTarget {
460 }, 682 file_id: FileId(
461 ] 683 1,
462 "### 684 ),
463 ); 685 full_range: 60..97,
686 name: "test_foo1",
687 kind: FN_DEF,
688 focus_range: Some(
689 83..92,
690 ),
691 container_name: None,
692 description: None,
693 docs: None,
694 },
695 kind: Test {
696 test_id: Path(
697 "foo::bar::test_mod::test_foo1",
698 ),
699 attr: TestAttr {
700 ignore: false,
701 },
702 },
703 cfg_exprs: [],
704 },
705 ]
706 "#]],
707 );
464 } 708 }
465 709
466 #[test] 710 #[test]
467 fn test_runnables_with_feature() { 711 fn test_runnables_with_feature() {
468 let (analysis, pos) = analysis_and_position( 712 check(
469 r#" 713 r#"
470 //- /lib.rs crate:foo cfg:feature=foo 714//- /lib.rs crate:foo cfg:feature=foo
471 <|> //empty 715<|>
472 #[test] 716#[test]
473 #[cfg(feature = "foo")] 717#[cfg(feature = "foo")]
474 fn test_foo1() {} 718fn test_foo1() {}
475 "#, 719"#,
476 ); 720 &[&TEST],
477 let runnables = analysis.runnables(pos.file_id).unwrap(); 721 expect![[r#"
478 assert_debug_snapshot!(&runnables, 722 [
479 @r###" 723 Runnable {
480 [ 724 nav: NavigationTarget {
481 Runnable { 725 file_id: FileId(
482 range: 1..58, 726 1,
483 kind: Test { 727 ),
484 test_id: Path( 728 full_range: 1..50,
485 "test_foo1", 729 name: "test_foo1",
486 ), 730 kind: FN_DEF,
487 attr: TestAttr { 731 focus_range: Some(
488 ignore: false, 732 36..45,
489 }, 733 ),
490 }, 734 container_name: None,
491 cfg_exprs: [ 735 description: None,
492 KeyValue { 736 docs: None,
493 key: "feature", 737 },
494 value: "foo", 738 kind: Test {
739 test_id: Path(
740 "test_foo1",
741 ),
742 attr: TestAttr {
743 ignore: false,
744 },
745 },
746 cfg_exprs: [
747 KeyValue {
748 key: "feature",
749 value: "foo",
750 },
751 ],
495 }, 752 },
496 ], 753 ]
497 }, 754 "#]],
498 ] 755 );
499 "###
500 );
501 } 756 }
502 757
503 #[test] 758 #[test]
504 fn test_runnables_with_features() { 759 fn test_runnables_with_features() {
505 let (analysis, pos) = analysis_and_position( 760 check(
506 r#" 761 r#"
507 //- /lib.rs crate:foo cfg:feature=foo,feature=bar 762//- /lib.rs crate:foo cfg:feature=foo,feature=bar
508 <|> //empty 763<|>
509 #[test] 764#[test]
510 #[cfg(all(feature = "foo", feature = "bar"))] 765#[cfg(all(feature = "foo", feature = "bar"))]
511 fn test_foo1() {} 766fn test_foo1() {}
512 "#, 767"#,
513 ); 768 &[&TEST],
514 let runnables = analysis.runnables(pos.file_id).unwrap(); 769 expect![[r#"
515 assert_debug_snapshot!(&runnables, 770 [
516 @r###" 771 Runnable {
517 [ 772 nav: NavigationTarget {
518 Runnable { 773 file_id: FileId(
519 range: 1..80, 774 1,
520 kind: Test { 775 ),
521 test_id: Path( 776 full_range: 1..72,
522 "test_foo1", 777 name: "test_foo1",
523 ), 778 kind: FN_DEF,
524 attr: TestAttr { 779 focus_range: Some(
525 ignore: false, 780 58..67,
526 }, 781 ),
527 }, 782 container_name: None,
528 cfg_exprs: [ 783 description: None,
529 All( 784 docs: None,
530 [ 785 },
531 KeyValue { 786 kind: Test {
532 key: "feature", 787 test_id: Path(
533 value: "foo", 788 "test_foo1",
534 }, 789 ),
535 KeyValue { 790 attr: TestAttr {
536 key: "feature", 791 ignore: false,
537 value: "bar",
538 }, 792 },
793 },
794 cfg_exprs: [
795 All(
796 [
797 KeyValue {
798 key: "feature",
799 value: "foo",
800 },
801 KeyValue {
802 key: "feature",
803 value: "bar",
804 },
805 ],
806 ),
539 ], 807 ],
540 ), 808 },
541 ], 809 ]
542 }, 810 "#]],
543 ] 811 );
544 "###
545 );
546 } 812 }
547 813
548 #[test] 814 #[test]
549 fn test_runnables_no_test_function_in_module() { 815 fn test_runnables_no_test_function_in_module() {
550 let (analysis, pos) = analysis_and_position( 816 check(
551 r#" 817 r#"
552 //- /lib.rs 818//- /lib.rs
553 <|> //empty 819<|>
554 mod test_mod { 820mod test_mod {
555 fn foo1() {} 821 fn foo1() {}
556 } 822}
557 "#, 823"#,
824 &[],
825 expect![[r#"
826 []
827 "#]],
558 ); 828 );
559 let runnables = analysis.runnables(pos.file_id).unwrap();
560 assert!(runnables.is_empty())
561 } 829 }
562} 830}
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
deleted file mode 100644
index 68fc589bc..000000000
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ /dev/null
@@ -1,41 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span>(<span class="variable declaration">ra_fixture</span>: &<span class="builtin_type">str</span>) {}
32
33<span class="keyword">fn</span> <span class="function declaration">main</span>() {
34 <span class="function">fixture</span>(<span class="string_literal">r#"</span>
35 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> {
36 <span class="keyword">fn</span> <span class="function declaration">foo</span>() {
37 <span class="macro">println!</span>(<span class="string_literal">"2 + 2 = {}"</span>, <span class="numeric_literal">4</span>);
38 }
39 }<span class="string_literal">"#</span>
40 );
41}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
deleted file mode 100644
index 326744361..000000000
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ /dev/null
@@ -1,84 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> {
32 ($($arg:tt)*) =&gt; ({
33 $<span class="keyword">crate</span>::io::_print($<span class="keyword">crate</span>::format_args_nl!($($arg)*));
34 })
35}
36#[rustc_builtin_macro]
37<span class="macro">macro_rules!</span> <span class="macro declaration">format_args_nl</span> {
38 ($fmt:expr) =&gt; {{ <span class="comment">/* compiler built-in */</span> }};
39 ($fmt:expr, $($args:tt)*) =&gt; {{ <span class="comment">/* compiler built-in */</span> }};
40}
41
42<span class="keyword">fn</span> <span class="function declaration">main</span>() {
43 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
44 <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// =&gt; "Hello"</span>
45 <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// =&gt; "Hello, world!"</span>
46 <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// =&gt; "The number is 1"</span>
47 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// =&gt; "(3, 4)"</span>
48 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// =&gt; "4"</span>
49 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "1 2"</span>
50 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// =&gt; "0042" with leading zerosV</span>
51 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1 1 2"</span>
52 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
55 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
56 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
58 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
59 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
60 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
61 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
62 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
63 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
64 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
67 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
68 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
69 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
70 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
71 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
72 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
73 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
74 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
75 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
76 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
77 <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>);
78 <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>);
79
80 <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
81
82 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
83 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
84}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
deleted file mode 100644
index 352e35095..000000000
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ /dev/null
@@ -1,101 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span>
32<span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
33 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>,
34 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>,
35}
36
37<span class="keyword">trait</span> <span class="trait declaration">Bar</span> {
38 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span>;
39}
40
41<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> {
42 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span> {
43 <span class="self_keyword">self</span>.<span class="field">x</span>
44 }
45}
46
47<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span>: <span class="builtin_type">i32</span> = <span class="numeric_literal">0</span>;
48
49<span class="keyword">fn</span> <span class="function declaration">foo</span>&lt;<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>&gt;() -&gt; <span class="type_param">T</span> {
50 <span class="function">foo</span>::&lt;<span class="lifetime">'a</span>, <span class="builtin_type">i32</span>&gt;()
51}
52
53<span class="macro">macro_rules!</span> <span class="macro declaration">def_fn</span> {
54 ($($tt:tt)*) =&gt; {$($tt)*}
55}
56
57<span class="macro">def_fn!</span> {
58 <span class="keyword">fn</span> <span class="function declaration">bar</span>() -&gt; <span class="builtin_type">u32</span> {
59 <span class="numeric_literal">100</span>
60 }
61}
62
63<span class="comment">// comment</span>
64<span class="keyword">fn</span> <span class="function declaration">main</span>() {
65 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
66
67 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>();
68 <span class="keyword control">if</span> <span class="bool_literal">true</span> {
69 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>;
70 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
71 }
72 <span class="keyword unsafe">unsafe</span> {
73 <span class="variable mutable">vec</span>.<span class="unresolved_reference">set_len</span>(<span class="numeric_literal">0</span>);
74 <span class="static mutable">STATIC_MUT</span> = <span class="numeric_literal">1</span>;
75 }
76
77 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> {
78 <span class="comment">// Do nothing</span>
79 }
80
81 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>;
82 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
83 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
84
85 <span class="variable mutable">y</span>;
86}
87
88<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
89 <span class="enum_variant declaration">Some</span>(<span class="type_param">T</span>),
90 <span class="enum_variant declaration">None</span>,
91}
92<span class="keyword">use</span> <span class="enum">Option</span>::*;
93
94<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; {
95 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; {
96 <span class="keyword control">match</span> <span class="variable">other</span> {
97 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(),
98 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>,
99 }
100 }
101}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
deleted file mode 100644
index 2a0294f71..000000000
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ /dev/null
@@ -1,42 +0,0 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span>() {
32 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
33 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
34 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
35
36 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string_literal">"other color please!"</span>;
37 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.<span class="unresolved_reference">to_string</span>();
38}
39
40<span class="keyword">fn</span> <span class="function declaration">bar</span>() {
41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
42}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 93e9aee1d..b3e9e5dfe 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,37 +1,31 @@
1use std::{collections::HashMap, iter::once, str::FromStr}; 1use ra_db::SourceDatabaseExt;
2
3use ra_db::{SourceDatabase, SourceDatabaseExt};
4use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 2use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
5use ra_syntax::ast::{
6 make::try_expr_from_text, ArgList, AstToken, CallExpr, Comment, Expr, MethodCallExpr,
7 RecordField, RecordLit,
8};
9use ra_syntax::{AstNode, SyntaxElement, SyntaxKind, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap;
12 3
13use crate::SourceFileEdit; 4use crate::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrError, SsrRule};
14 6
15#[derive(Debug, PartialEq)] 7// Feature: Structural Search and Replace
16pub struct SsrError(String);
17
18impl std::fmt::Display for SsrError {
19 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
20 write!(f, "Parse error: {}", self.0)
21 }
22}
23
24impl std::error::Error for SsrError {}
25
26// Feature: Structural Seach and Replace
27// 8//
28// Search and replace with named wildcards that will match any expression. 9// Search and replace with named wildcards that will match any expression, type, path, pattern or item.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 10// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. 11// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
12// Within a macro call, a placeholder will match up until whatever token follows the placeholder.
13//
14// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
15//
16// Supported constraints:
17//
18// |===
19// | Constraint | Restricts placeholder
20//
21// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
22// | not(a) | Negates the constraint `a`
23// |===
24//
31// Available via the command `rust-analyzer.ssr`. 25// Available via the command `rust-analyzer.ssr`.
32// 26//
33// ```rust 27// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] 28// // Using structural search replace command [foo($a, $b) ==>> ($a).foo($b)]
35// 29//
36// // BEFORE 30// // BEFORE
37// String::from(foo(y + 5, z)) 31// String::from(foo(y + 5, z))
@@ -46,584 +40,24 @@ impl std::error::Error for SsrError {}
46// | VS Code | **Rust Analyzer: Structural Search Replace** 40// | VS Code | **Rust Analyzer: Structural Search Replace**
47// |=== 41// |===
48pub fn parse_search_replace( 42pub fn parse_search_replace(
49 query: &str, 43 rule: &str,
50 parse_only: bool, 44 parse_only: bool,
51 db: &RootDatabase, 45 db: &RootDatabase,
52) -> Result<Vec<SourceFileEdit>, SsrError> { 46) -> Result<Vec<SourceFileEdit>, SsrError> {
53 let mut edits = vec![]; 47 let mut edits = vec![];
54 let query: SsrQuery = query.parse()?; 48 let rule: SsrRule = rule.parse()?;
55 if parse_only { 49 if parse_only {
56 return Ok(edits); 50 return Ok(edits);
57 } 51 }
52 let mut match_finder = MatchFinder::new(db);
53 match_finder.add_rule(rule);
58 for &root in db.local_roots().iter() { 54 for &root in db.local_roots().iter() {
59 let sr = db.source_root(root); 55 let sr = db.source_root(root);
60 for file_id in sr.walk() { 56 for file_id in sr.iter() {
61 let matches = find(&query.pattern, db.parse(file_id).tree().syntax()); 57 if let Some(edit) = match_finder.edits_for_file(file_id) {
62 if !matches.matches.is_empty() { 58 edits.push(SourceFileEdit { file_id, edit });
63 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) });
64 } 59 }
65 } 60 }
66 } 61 }
67 Ok(edits) 62 Ok(edits)
68} 63}
69
70#[derive(Debug)]
71struct SsrQuery {
72 pattern: SsrPattern,
73 template: SsrTemplate,
74}
75
76#[derive(Debug)]
77struct SsrPattern {
78 pattern: SyntaxNode,
79 vars: Vec<Var>,
80}
81
82/// represents an `$var` in an SSR query
83#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84struct Var(String);
85
86#[derive(Debug)]
87struct SsrTemplate {
88 template: SyntaxNode,
89 placeholders: FxHashMap<SyntaxNode, Var>,
90}
91
92type Binding = HashMap<Var, SyntaxNode>;
93
94#[derive(Debug)]
95struct Match {
96 place: SyntaxNode,
97 binding: Binding,
98 ignored_comments: Vec<Comment>,
99}
100
101#[derive(Debug)]
102struct SsrMatches {
103 matches: Vec<Match>,
104}
105
106impl FromStr for SsrQuery {
107 type Err = SsrError;
108
109 fn from_str(query: &str) -> Result<SsrQuery, SsrError> {
110 let mut it = query.split("==>>");
111 let pattern = it.next().expect("at least empty string").trim();
112 let mut template = it
113 .next()
114 .ok_or_else(|| SsrError("Cannot find delemiter `==>>`".into()))?
115 .trim()
116 .to_string();
117 if it.next().is_some() {
118 return Err(SsrError("More than one delimiter found".into()));
119 }
120 let mut vars = vec![];
121 let mut it = pattern.split('$');
122 let mut pattern = it.next().expect("something").to_string();
123
124 for part in it.map(split_by_var) {
125 let (var, var_type, remainder) = part?;
126 is_expr(var_type)?;
127 let new_var = create_name(var, &mut vars)?;
128 pattern.push_str(new_var);
129 pattern.push_str(remainder);
130 template = replace_in_template(template, var, new_var);
131 }
132
133 let template = try_expr_from_text(&template)
134 .ok_or(SsrError("Template is not an expression".into()))?
135 .syntax()
136 .clone();
137 let mut placeholders = FxHashMap::default();
138
139 traverse(&template, &mut |n| {
140 if let Some(v) = vars.iter().find(|v| v.0.as_str() == n.text()) {
141 placeholders.insert(n.clone(), v.clone());
142 false
143 } else {
144 true
145 }
146 });
147
148 let pattern = SsrPattern {
149 pattern: try_expr_from_text(&pattern)
150 .ok_or(SsrError("Pattern is not an expression".into()))?
151 .syntax()
152 .clone(),
153 vars,
154 };
155 let template = SsrTemplate { template, placeholders };
156 Ok(SsrQuery { pattern, template })
157 }
158}
159
160fn traverse(node: &SyntaxNode, go: &mut impl FnMut(&SyntaxNode) -> bool) {
161 if !go(node) {
162 return;
163 }
164 for ref child in node.children() {
165 traverse(child, go);
166 }
167}
168
169fn split_by_var(s: &str) -> Result<(&str, &str, &str), SsrError> {
170 let end_of_name = s.find(':').ok_or_else(|| SsrError("Use $<name>:expr".into()))?;
171 let name = &s[0..end_of_name];
172 is_name(name)?;
173 let type_begin = end_of_name + 1;
174 let type_length =
175 s[type_begin..].find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
176 let type_name = &s[type_begin..type_begin + type_length];
177 Ok((name, type_name, &s[type_begin + type_length..]))
178}
179
180fn is_name(s: &str) -> Result<(), SsrError> {
181 if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
182 Ok(())
183 } else {
184 Err(SsrError("Name can contain only alphanumerics and _".into()))
185 }
186}
187
188fn is_expr(s: &str) -> Result<(), SsrError> {
189 if s == "expr" {
190 Ok(())
191 } else {
192 Err(SsrError("Only $<name>:expr is supported".into()))
193 }
194}
195
196fn replace_in_template(template: String, var: &str, new_var: &str) -> String {
197 let name = format!("${}", var);
198 template.replace(&name, new_var)
199}
200
201fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrError> {
202 let sanitized_name = format!("__search_pattern_{}", name);
203 if vars.iter().any(|a| a.0 == sanitized_name) {
204 return Err(SsrError(format!("Name `{}` repeats more than once", name)));
205 }
206 vars.push(Var(sanitized_name));
207 Ok(&vars.last().unwrap().0)
208}
209
210fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
211 fn check_record_lit(
212 pattern: RecordLit,
213 code: RecordLit,
214 placeholders: &[Var],
215 match_: Match,
216 ) -> Option<Match> {
217 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
218
219 let mut pattern_fields: Vec<RecordField> =
220 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
221 let mut code_fields: Vec<RecordField> =
222 code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
223
224 if pattern_fields.len() != code_fields.len() {
225 return None;
226 }
227
228 let by_name = |a: &RecordField, b: &RecordField| {
229 a.name_ref()
230 .map(|x| x.syntax().text().to_string())
231 .cmp(&b.name_ref().map(|x| x.syntax().text().to_string()))
232 };
233 pattern_fields.sort_by(by_name);
234 code_fields.sort_by(by_name);
235
236 pattern_fields.into_iter().zip(code_fields.into_iter()).fold(
237 Some(match_),
238 |accum, (a, b)| {
239 accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_))
240 },
241 )
242 }
243
244 fn check_call_and_method_call(
245 pattern: CallExpr,
246 code: MethodCallExpr,
247 placeholders: &[Var],
248 match_: Match,
249 ) -> Option<Match> {
250 let (pattern_name, pattern_type_args) = if let Some(Expr::PathExpr(path_exr)) =
251 pattern.expr()
252 {
253 let segment = path_exr.path().and_then(|p| p.segment());
254 (segment.as_ref().and_then(|s| s.name_ref()), segment.and_then(|s| s.type_arg_list()))
255 } else {
256 (None, None)
257 };
258 let match_ = check_opt_nodes(pattern_name, code.name_ref(), placeholders, match_)?;
259 let match_ =
260 check_opt_nodes(pattern_type_args, code.type_arg_list(), placeholders, match_)?;
261 let pattern_args = pattern.syntax().children().find_map(ArgList::cast)?.args();
262 let code_args = code.syntax().children().find_map(ArgList::cast)?.args();
263 let code_args = once(code.expr()?).chain(code_args);
264 check_iter(pattern_args, code_args, placeholders, match_)
265 }
266
267 fn check_method_call_and_call(
268 pattern: MethodCallExpr,
269 code: CallExpr,
270 placeholders: &[Var],
271 match_: Match,
272 ) -> Option<Match> {
273 let (code_name, code_type_args) = if let Some(Expr::PathExpr(path_exr)) = code.expr() {
274 let segment = path_exr.path().and_then(|p| p.segment());
275 (segment.as_ref().and_then(|s| s.name_ref()), segment.and_then(|s| s.type_arg_list()))
276 } else {
277 (None, None)
278 };
279 let match_ = check_opt_nodes(pattern.name_ref(), code_name, placeholders, match_)?;
280 let match_ =
281 check_opt_nodes(pattern.type_arg_list(), code_type_args, placeholders, match_)?;
282 let code_args = code.syntax().children().find_map(ArgList::cast)?.args();
283 let pattern_args = pattern.syntax().children().find_map(ArgList::cast)?.args();
284 let pattern_args = once(pattern.expr()?).chain(pattern_args);
285 check_iter(pattern_args, code_args, placeholders, match_)
286 }
287
288 fn check_opt_nodes(
289 pattern: Option<impl AstNode>,
290 code: Option<impl AstNode>,
291 placeholders: &[Var],
292 match_: Match,
293 ) -> Option<Match> {
294 match (pattern, code) {
295 (Some(pattern), Some(code)) => check(
296 &pattern.syntax().clone().into(),
297 &code.syntax().clone().into(),
298 placeholders,
299 match_,
300 ),
301 (None, None) => Some(match_),
302 _ => None,
303 }
304 }
305
306 fn check_iter<T, I1, I2>(
307 mut pattern: I1,
308 mut code: I2,
309 placeholders: &[Var],
310 match_: Match,
311 ) -> Option<Match>
312 where
313 T: AstNode,
314 I1: Iterator<Item = T>,
315 I2: Iterator<Item = T>,
316 {
317 pattern
318 .by_ref()
319 .zip(code.by_ref())
320 .fold(Some(match_), |accum, (a, b)| {
321 accum.and_then(|match_| {
322 check(
323 &a.syntax().clone().into(),
324 &b.syntax().clone().into(),
325 placeholders,
326 match_,
327 )
328 })
329 })
330 .filter(|_| pattern.next().is_none() && code.next().is_none())
331 }
332
333 fn check(
334 pattern: &SyntaxElement,
335 code: &SyntaxElement,
336 placeholders: &[Var],
337 mut match_: Match,
338 ) -> Option<Match> {
339 match (&pattern, &code) {
340 (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
341 if pattern.text() == code.text() {
342 Some(match_)
343 } else {
344 None
345 }
346 }
347 (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
348 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
349 match_.binding.insert(Var(pattern.text().to_string()), code.clone());
350 Some(match_)
351 } else {
352 if let (Some(pattern), Some(code)) =
353 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
354 {
355 check_record_lit(pattern, code, placeholders, match_)
356 } else if let (Some(pattern), Some(code)) =
357 (CallExpr::cast(pattern.clone()), MethodCallExpr::cast(code.clone()))
358 {
359 check_call_and_method_call(pattern, code, placeholders, match_)
360 } else if let (Some(pattern), Some(code)) =
361 (MethodCallExpr::cast(pattern.clone()), CallExpr::cast(code.clone()))
362 {
363 check_method_call_and_call(pattern, code, placeholders, match_)
364 } else {
365 let mut pattern_children = pattern
366 .children_with_tokens()
367 .filter(|element| !element.kind().is_trivia());
368 let mut code_children = code
369 .children_with_tokens()
370 .filter(|element| !element.kind().is_trivia());
371 let new_ignored_comments =
372 code.children_with_tokens().filter_map(|element| {
373 element.as_token().and_then(|token| Comment::cast(token.clone()))
374 });
375 match_.ignored_comments.extend(new_ignored_comments);
376 pattern_children
377 .by_ref()
378 .zip(code_children.by_ref())
379 .fold(Some(match_), |accum, (a, b)| {
380 accum.and_then(|match_| check(&a, &b, placeholders, match_))
381 })
382 .filter(|_| {
383 pattern_children.next().is_none() && code_children.next().is_none()
384 })
385 }
386 }
387 }
388 _ => None,
389 }
390 }
391 let kind = pattern.pattern.kind();
392 let matches = code
393 .descendants()
394 .filter(|n| {
395 n.kind() == kind
396 || (kind == SyntaxKind::CALL_EXPR && n.kind() == SyntaxKind::METHOD_CALL_EXPR)
397 || (kind == SyntaxKind::METHOD_CALL_EXPR && n.kind() == SyntaxKind::CALL_EXPR)
398 })
399 .filter_map(|code| {
400 let match_ =
401 Match { place: code.clone(), binding: HashMap::new(), ignored_comments: vec![] };
402 check(&pattern.pattern.clone().into(), &code.into(), &pattern.vars, match_)
403 })
404 .collect();
405 SsrMatches { matches }
406}
407
408fn replace(matches: &SsrMatches, template: &SsrTemplate) -> TextEdit {
409 let mut builder = TextEditBuilder::default();
410 for match_ in &matches.matches {
411 builder.replace(
412 match_.place.text_range(),
413 render_replace(&match_.binding, &match_.ignored_comments, template),
414 );
415 }
416 builder.finish()
417}
418
419fn render_replace(
420 binding: &Binding,
421 ignored_comments: &Vec<Comment>,
422 template: &SsrTemplate,
423) -> String {
424 let edit = {
425 let mut builder = TextEditBuilder::default();
426 for element in template.template.descendants() {
427 if let Some(var) = template.placeholders.get(&element) {
428 builder.replace(element.text_range(), binding[var].to_string())
429 }
430 }
431 for comment in ignored_comments {
432 builder.insert(template.template.text_range().end(), comment.syntax().to_string())
433 }
434 builder.finish()
435 };
436
437 let mut text = template.template.text().to_string();
438 edit.apply(&mut text);
439 text
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445 use ra_syntax::SourceFile;
446
447 fn parse_error_text(query: &str) -> String {
448 format!("{}", query.parse::<SsrQuery>().unwrap_err())
449 }
450
451 #[test]
452 fn parser_happy_case() {
453 let result: SsrQuery = "foo($a:expr, $b:expr) ==>> bar($b, $a)".parse().unwrap();
454 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)");
455 assert_eq!(result.pattern.vars.len(), 2);
456 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
457 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b");
458 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)");
459 }
460
461 #[test]
462 fn parser_empty_query() {
463 assert_eq!(parse_error_text(""), "Parse error: Cannot find delemiter `==>>`");
464 }
465
466 #[test]
467 fn parser_no_delimiter() {
468 assert_eq!(parse_error_text("foo()"), "Parse error: Cannot find delemiter `==>>`");
469 }
470
471 #[test]
472 fn parser_two_delimiters() {
473 assert_eq!(
474 parse_error_text("foo() ==>> a ==>> b "),
475 "Parse error: More than one delimiter found"
476 );
477 }
478
479 #[test]
480 fn parser_no_pattern_type() {
481 assert_eq!(parse_error_text("foo($a) ==>>"), "Parse error: Use $<name>:expr");
482 }
483
484 #[test]
485 fn parser_invalid_name() {
486 assert_eq!(
487 parse_error_text("foo($a+:expr) ==>>"),
488 "Parse error: Name can contain only alphanumerics and _"
489 );
490 }
491
492 #[test]
493 fn parser_invalid_type() {
494 assert_eq!(
495 parse_error_text("foo($a:ident) ==>>"),
496 "Parse error: Only $<name>:expr is supported"
497 );
498 }
499
500 #[test]
501 fn parser_repeated_name() {
502 assert_eq!(
503 parse_error_text("foo($a:expr, $a:expr) ==>>"),
504 "Parse error: Name `a` repeats more than once"
505 );
506 }
507
508 #[test]
509 fn parser_invlid_pattern() {
510 assert_eq!(parse_error_text(" ==>> ()"), "Parse error: Pattern is not an expression");
511 }
512
513 #[test]
514 fn parser_invlid_template() {
515 assert_eq!(parse_error_text("() ==>> )"), "Parse error: Template is not an expression");
516 }
517
518 #[test]
519 fn parse_match_replace() {
520 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap();
521 let input = "fn main() { foo(1+2); }";
522
523 let code = SourceFile::parse(input).tree();
524 let matches = find(&query.pattern, code.syntax());
525 assert_eq!(matches.matches.len(), 1);
526 assert_eq!(matches.matches[0].place.text(), "foo(1+2)");
527 assert_eq!(matches.matches[0].binding.len(), 1);
528 assert_eq!(
529 matches.matches[0].binding[&Var("__search_pattern_x".to_string())].text(),
530 "1+2"
531 );
532
533 let edit = replace(&matches, &query.template);
534 let mut after = input.to_string();
535 edit.apply(&mut after);
536 assert_eq!(after, "fn main() { bar(1+2); }");
537 }
538
539 fn assert_ssr_transform(query: &str, input: &str, result: &str) {
540 let query: SsrQuery = query.parse().unwrap();
541 let code = SourceFile::parse(input).tree();
542 let matches = find(&query.pattern, code.syntax());
543 let edit = replace(&matches, &query.template);
544 let mut after = input.to_string();
545 edit.apply(&mut after);
546 assert_eq!(after, result);
547 }
548
549 #[test]
550 fn ssr_function_to_method() {
551 assert_ssr_transform(
552 "my_function($a:expr, $b:expr) ==>> ($a).my_method($b)",
553 "loop { my_function( other_func(x, y), z + w) }",
554 "loop { (other_func(x, y)).my_method(z + w) }",
555 )
556 }
557
558 #[test]
559 fn ssr_nested_function() {
560 assert_ssr_transform(
561 "foo($a:expr, $b:expr, $c:expr) ==>> bar($c, baz($a, $b))",
562 "fn main { foo (x + value.method(b), x+y-z, true && false) }",
563 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
564 )
565 }
566
567 #[test]
568 fn ssr_expected_spacing() {
569 assert_ssr_transform(
570 "foo($x:expr) + bar() ==>> bar($x)",
571 "fn main() { foo(5) + bar() }",
572 "fn main() { bar(5) }",
573 );
574 }
575
576 #[test]
577 fn ssr_with_extra_space() {
578 assert_ssr_transform(
579 "foo($x:expr ) + bar() ==>> bar($x)",
580 "fn main() { foo( 5 ) +bar( ) }",
581 "fn main() { bar(5) }",
582 );
583 }
584
585 #[test]
586 fn ssr_keeps_nested_comment() {
587 assert_ssr_transform(
588 "foo($x:expr) ==>> bar($x)",
589 "fn main() { foo(other(5 /* using 5 */)) }",
590 "fn main() { bar(other(5 /* using 5 */)) }",
591 )
592 }
593
594 #[test]
595 fn ssr_keeps_comment() {
596 assert_ssr_transform(
597 "foo($x:expr) ==>> bar($x)",
598 "fn main() { foo(5 /* using 5 */) }",
599 "fn main() { bar(5)/* using 5 */ }",
600 )
601 }
602
603 #[test]
604 fn ssr_struct_lit() {
605 assert_ssr_transform(
606 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)",
607 "fn main() { foo{b:2, a:1} }",
608 "fn main() { foo::new(1, 2) }",
609 )
610 }
611
612 #[test]
613 fn ssr_call_and_method_call() {
614 assert_ssr_transform(
615 "foo::<'a>($a:expr, $b:expr)) ==>> foo2($a, $b)",
616 "fn main() { get().bar.foo::<'a>(1); }",
617 "fn main() { foo2(get().bar, 1); }",
618 )
619 }
620
621 #[test]
622 fn ssr_method_call_and_call() {
623 assert_ssr_transform(
624 "$o:expr.foo::<i32>($a:expr)) ==>> $o.foo2($a)",
625 "fn main() { X::foo::<i32>(x, 1); }",
626 "fn main() { x.foo2(1); }",
627 )
628 }
629}
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 5b7992920..08e6f69cb 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -2,10 +2,7 @@ use std::{fmt, iter::FromIterator, sync::Arc};
2 2
3use hir::MacroFile; 3use hir::MacroFile;
4use ra_db::{ 4use ra_db::{
5 salsa::{ 5 salsa::debug::{DebugQueryTable, TableEntry},
6 debug::{DebugQueryTable, TableEntry},
7 Database,
8 },
9 FileTextQuery, SourceRootId, 6 FileTextQuery, SourceRootId,
10}; 7};
11use ra_ide_db::{ 8use ra_ide_db::{
@@ -14,14 +11,15 @@ use ra_ide_db::{
14}; 11};
15use ra_prof::{memory_usage, Bytes}; 12use ra_prof::{memory_usage, Bytes};
16use ra_syntax::{ast, Parse, SyntaxNode}; 13use ra_syntax::{ast, Parse, SyntaxNode};
14use rustc_hash::FxHashMap;
17 15
18use crate::FileId; 16use crate::FileId;
19 17
20fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 18fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
21 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>() 19 ra_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
22} 20}
23fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
24 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() 22 hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>()
25} 23}
26 24
27// Feature: Status 25// Feature: Status
@@ -34,10 +32,10 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
34// | VS Code | **Rust Analyzer: Status** 32// | VS Code | **Rust Analyzer: Status**
35// |=== 33// |===
36pub(crate) fn status(db: &RootDatabase) -> String { 34pub(crate) fn status(db: &RootDatabase) -> String {
37 let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); 35 let files_stats = FileTextQuery.in_db(db).entries::<FilesStats>();
38 let syntax_tree_stats = syntax_tree_stats(db); 36 let syntax_tree_stats = syntax_tree_stats(db);
39 let macro_syntax_tree_stats = macro_syntax_tree_stats(db); 37 let macro_syntax_tree_stats = macro_syntax_tree_stats(db);
40 let symbols_stats = db.query(LibrarySymbolsQuery).entries::<LibrarySymbolsStats>(); 38 let symbols_stats = LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>();
41 format!( 39 format!(
42 "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago", 40 "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago",
43 files_stats, 41 files_stats,
@@ -123,20 +121,24 @@ struct LibrarySymbolsStats {
123 121
124impl fmt::Display for LibrarySymbolsStats { 122impl fmt::Display for LibrarySymbolsStats {
125 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 123 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
126 write!(fmt, "{} ({}) symbols", self.total, self.size,) 124 write!(fmt, "{} ({}) symbols", self.total, self.size)
127 } 125 }
128} 126}
129 127
130impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats { 128impl FromIterator<TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>
129 for LibrarySymbolsStats
130{
131 fn from_iter<T>(iter: T) -> LibrarySymbolsStats 131 fn from_iter<T>(iter: T) -> LibrarySymbolsStats
132 where 132 where
133 T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>, 133 T: IntoIterator<Item = TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>,
134 { 134 {
135 let mut res = LibrarySymbolsStats::default(); 135 let mut res = LibrarySymbolsStats::default();
136 for entry in iter { 136 for entry in iter {
137 let value = entry.value.unwrap(); 137 let value = entry.value.unwrap();
138 res.total += value.len(); 138 for symbols in value.values() {
139 res.size += value.memory_size(); 139 res.total += symbols.len();
140 res.size += symbols.memory_size();
141 }
140 } 142 }
141 res 143 res
142 } 144 }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..6ac44c2c0 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,6 @@
1mod tags; 1mod tags;
2mod html; 2mod html;
3mod injection;
3#[cfg(test)] 4#[cfg(test)]
4mod tests; 5mod tests;
5 6
@@ -10,14 +11,14 @@ use ra_ide_db::{
10}; 11};
11use ra_prof::profile; 12use ra_prof::profile;
12use ra_syntax::{ 13use ra_syntax::{
13 ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, 14 ast::{self, HasFormatSpecifier},
14 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 15 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement,
15 SyntaxKind::*, 16 SyntaxKind::*,
16 SyntaxToken, TextRange, WalkEvent, T, 17 TextRange, WalkEvent, T,
17}; 18};
18use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
19 20
20use crate::{call_info::ActiveParameter, Analysis, FileId}; 21use crate::FileId;
21 22
22use ast::FormatSpecifier; 23use ast::FormatSpecifier;
23pub(crate) use html::highlight_as_html; 24pub(crate) use html::highlight_as_html;
@@ -43,6 +44,7 @@ pub(crate) fn highlight(
43 db: &RootDatabase, 44 db: &RootDatabase,
44 file_id: FileId, 45 file_id: FileId,
45 range_to_highlight: Option<TextRange>, 46 range_to_highlight: Option<TextRange>,
47 syntactic_name_ref_highlighting: bool,
46) -> Vec<HighlightedRange> { 48) -> Vec<HighlightedRange> {
47 let _p = profile("highlight"); 49 let _p = profile("highlight");
48 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
@@ -103,6 +105,7 @@ pub(crate) fn highlight(
103 if let Some((highlight, binding_hash)) = highlight_element( 105 if let Some((highlight, binding_hash)) = highlight_element(
104 &sema, 106 &sema,
105 &mut bindings_shadow_count, 107 &mut bindings_shadow_count,
108 syntactic_name_ref_highlighting,
106 name.syntax().clone().into(), 109 name.syntax().clone().into(),
107 ) { 110 ) {
108 stack.add(HighlightedRange { 111 stack.add(HighlightedRange {
@@ -118,7 +121,23 @@ pub(crate) fn highlight(
118 assert!(current_macro_call == Some(mc)); 121 assert!(current_macro_call == Some(mc));
119 current_macro_call = None; 122 current_macro_call = None;
120 format_string = None; 123 format_string = None;
121 continue; 124 }
125 _ => (),
126 }
127
128 // Check for Rust code in documentation
129 match &event {
130 WalkEvent::Leave(NodeOrToken::Node(node)) => {
131 if let Some((doctest, range_mapping, new_comments)) =
132 injection::extract_doc_comments(node)
133 {
134 injection::highlight_doc_comment(
135 doctest,
136 range_mapping,
137 new_comments,
138 &mut stack,
139 );
140 }
122 } 141 }
123 _ => (), 142 _ => (),
124 } 143 }
@@ -130,7 +149,7 @@ pub(crate) fn highlight(
130 149
131 let range = element.text_range(); 150 let range = element.text_range();
132 151
133 let element_to_highlight = if current_macro_call.is_some() { 152 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
134 // Inside a macro -- expand it first 153 // Inside a macro -- expand it first
135 let token = match element.clone().into_token() { 154 let token = match element.clone().into_token() {
136 Some(it) if it.parent().kind() == TOKEN_TREE => it, 155 Some(it) if it.parent().kind() == TOKEN_TREE => it,
@@ -142,23 +161,25 @@ pub(crate) fn highlight(
142 // Check if macro takes a format string and remember it for highlighting later. 161 // Check if macro takes a format string and remember it for highlighting later.
143 // The macros that accept a format string expand to a compiler builtin macros 162 // The macros that accept a format string expand to a compiler builtin macros
144 // `format_args` and `format_args_nl`. 163 // `format_args` and `format_args_nl`.
145 if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { 164 if let Some(name) = parent
146 if let Some(name) = 165 .parent()
147 fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) 166 .and_then(ast::MacroCall::cast)
148 { 167 .and_then(|mc| mc.path())
149 match name.text().as_str() { 168 .and_then(|p| p.segment())
150 "format_args" | "format_args_nl" => { 169 .and_then(|s| s.name_ref())
151 format_string = parent 170 {
152 .children_with_tokens() 171 match name.text().as_str() {
153 .filter(|t| t.kind() != WHITESPACE) 172 "format_args" | "format_args_nl" => {
154 .nth(1) 173 format_string = parent
155 .filter(|e| { 174 .children_with_tokens()
156 ast::String::can_cast(e.kind()) 175 .filter(|t| t.kind() != WHITESPACE)
157 || ast::RawString::can_cast(e.kind()) 176 .nth(1)
158 }) 177 .filter(|e| {
159 } 178 ast::String::can_cast(e.kind())
160 _ => {} 179 || ast::RawString::can_cast(e.kind())
180 })
161 } 181 }
182 _ => {}
162 } 183 }
163 } 184 }
164 185
@@ -173,22 +194,25 @@ pub(crate) fn highlight(
173 194
174 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { 195 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
175 let expanded = element_to_highlight.as_token().unwrap().clone(); 196 let expanded = element_to_highlight.as_token().unwrap().clone();
176 if highlight_injection(&mut stack, &sema, token, expanded).is_some() { 197 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() {
177 continue; 198 continue;
178 } 199 }
179 } 200 }
180 201
181 let is_format_string = format_string.as_ref() == Some(&element_to_highlight); 202 let is_format_string = format_string.as_ref() == Some(&element_to_highlight);
182 203
183 if let Some((highlight, binding_hash)) = 204 if let Some((highlight, binding_hash)) = highlight_element(
184 highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) 205 &sema,
185 { 206 &mut bindings_shadow_count,
207 syntactic_name_ref_highlighting,
208 element_to_highlight.clone(),
209 ) {
186 stack.add(HighlightedRange { range, highlight, binding_hash }); 210 stack.add(HighlightedRange { range, highlight, binding_hash });
187 if let Some(string) = 211 if let Some(string) =
188 element_to_highlight.as_token().cloned().and_then(ast::String::cast) 212 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
189 { 213 {
190 stack.push();
191 if is_format_string { 214 if is_format_string {
215 stack.push();
192 string.lex_format_specifier(|piece_range, kind| { 216 string.lex_format_specifier(|piece_range, kind| {
193 if let Some(highlight) = highlight_format_specifier(kind) { 217 if let Some(highlight) = highlight_format_specifier(kind) {
194 stack.add(HighlightedRange { 218 stack.add(HighlightedRange {
@@ -198,13 +222,27 @@ pub(crate) fn highlight(
198 }); 222 });
199 } 223 }
200 }); 224 });
225 stack.pop();
226 }
227 // Highlight escape sequences
228 if let Some(char_ranges) = string.char_ranges() {
229 stack.push();
230 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
231 if string.text()[piece_range.start().into()..].starts_with('\\') {
232 stack.add(HighlightedRange {
233 range: piece_range + range.start(),
234 highlight: HighlightTag::EscapeSequence.into(),
235 binding_hash: None,
236 });
237 }
238 }
239 stack.pop_and_inject(None);
201 } 240 }
202 stack.pop();
203 } else if let Some(string) = 241 } else if let Some(string) =
204 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) 242 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast)
205 { 243 {
206 stack.push();
207 if is_format_string { 244 if is_format_string {
245 stack.push();
208 string.lex_format_specifier(|piece_range, kind| { 246 string.lex_format_specifier(|piece_range, kind| {
209 if let Some(highlight) = highlight_format_specifier(kind) { 247 if let Some(highlight) = highlight_format_specifier(kind) {
210 stack.add(HighlightedRange { 248 stack.add(HighlightedRange {
@@ -214,8 +252,8 @@ pub(crate) fn highlight(
214 }); 252 });
215 } 253 }
216 }); 254 });
255 stack.pop();
217 } 256 }
218 stack.pop();
219 } 257 }
220 } 258 }
221 } 259 }
@@ -259,9 +297,8 @@ impl HighlightedRangeStack {
259 let mut parent = prev.pop().unwrap(); 297 let mut parent = prev.pop().unwrap();
260 for ele in children { 298 for ele in children {
261 assert!(parent.range.contains_range(ele.range)); 299 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone(); 300
263 parent.range = TextRange::new(parent.range.start(), ele.range.start()); 301 let cloned = Self::intersect(&mut parent, &ele);
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() { 302 if !parent.range.is_empty() {
266 prev.push(parent); 303 prev.push(parent);
267 } 304 }
@@ -274,6 +311,92 @@ impl HighlightedRangeStack {
274 } 311 }
275 } 312 }
276 313
314 /// Intersects the `HighlightedRange` `parent` with `child`.
315 /// `parent` is mutated in place, becoming the range before `child`.
316 /// Returns the range (of the same type as `parent`) *after* `child`.
317 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
318 assert!(parent.range.contains_range(child.range));
319
320 let mut cloned = parent.clone();
321 parent.range = TextRange::new(parent.range.start(), child.range.start());
322 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
323
324 cloned
325 }
326
327 /// Remove the `HighlightRange` of `parent` that's currently covered by `child`.
328 fn intersect_partial(parent: &mut HighlightedRange, child: &HighlightedRange) {
329 assert!(
330 parent.range.start() <= child.range.start()
331 && parent.range.end() >= child.range.start()
332 && child.range.end() > parent.range.end()
333 );
334
335 parent.range = TextRange::new(parent.range.start(), child.range.start());
336 }
337
338 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
339 /// can only modify the last range currently on the stack.
340 /// Can be used to do injections that span multiple ranges, like the
341 /// doctest injection below.
342 /// If `overwrite_parent` is non-optional, the highlighting of the parent range
343 /// is overwritten with the argument.
344 ///
345 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
346 /// latter is computationally more expensive.
347 fn pop_and_inject(&mut self, overwrite_parent: Option<Highlight>) {
348 let mut children = self.stack.pop().unwrap();
349 let prev = self.stack.last_mut().unwrap();
350 children.sort_by_key(|range| range.range.start());
351 prev.sort_by_key(|range| range.range.start());
352
353 for child in children {
354 if let Some(idx) =
355 prev.iter().position(|parent| parent.range.contains_range(child.range))
356 {
357 if let Some(tag) = overwrite_parent {
358 prev[idx].highlight = tag;
359 }
360
361 let cloned = Self::intersect(&mut prev[idx], &child);
362 let insert_idx = if prev[idx].range.is_empty() {
363 prev.remove(idx);
364 idx
365 } else {
366 idx + 1
367 };
368 prev.insert(insert_idx, child);
369 if !cloned.range.is_empty() {
370 prev.insert(insert_idx + 1, cloned);
371 }
372 } else {
373 let maybe_idx =
374 prev.iter().position(|parent| parent.range.contains(child.range.start()));
375 match (overwrite_parent, maybe_idx) {
376 (Some(_), Some(idx)) => {
377 Self::intersect_partial(&mut prev[idx], &child);
378 let insert_idx = if prev[idx].range.is_empty() {
379 prev.remove(idx);
380 idx
381 } else {
382 idx + 1
383 };
384 prev.insert(insert_idx, child);
385 }
386 (_, None) => {
387 let idx = prev
388 .binary_search_by_key(&child.range.start(), |range| range.range.start())
389 .unwrap_or_else(|x| x);
390 prev.insert(idx, child);
391 }
392 _ => {
393 unreachable!("child range should be completely contained in parent range");
394 }
395 }
396 }
397 }
398 }
399
277 fn add(&mut self, range: HighlightedRange) { 400 fn add(&mut self, range: HighlightedRange) {
278 self.stack 401 self.stack
279 .last_mut() 402 .last_mut()
@@ -335,6 +458,7 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
335fn highlight_element( 458fn highlight_element(
336 sema: &Semantics<RootDatabase>, 459 sema: &Semantics<RootDatabase>,
337 bindings_shadow_count: &mut FxHashMap<Name, u32>, 460 bindings_shadow_count: &mut FxHashMap<Name, u32>,
461 syntactic_name_ref_highlighting: bool,
338 element: SyntaxElement, 462 element: SyntaxElement,
339) -> Option<(Highlight, Option<u64>)> { 463) -> Option<(Highlight, Option<u64>)> {
340 let db = sema.db; 464 let db = sema.db;
@@ -363,6 +487,7 @@ fn highlight_element(
363 highlight_name(db, def) | HighlightModifier::Definition 487 highlight_name(db, def) | HighlightModifier::Definition
364 } 488 }
365 Some(NameClass::ConstReference(def)) => highlight_name(db, def), 489 Some(NameClass::ConstReference(def)) => highlight_name(db, def),
490 Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
366 None => highlight_name_by_syntax(name) | HighlightModifier::Definition, 491 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
367 } 492 }
368 } 493 }
@@ -387,12 +512,20 @@ fn highlight_element(
387 } 512 }
388 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), 513 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
389 }, 514 },
515 None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref),
390 None => HighlightTag::UnresolvedReference.into(), 516 None => HighlightTag::UnresolvedReference.into(),
391 } 517 }
392 } 518 }
393 519
394 // Simple token-based highlighting 520 // Simple token-based highlighting
395 COMMENT => HighlightTag::Comment.into(), 521 COMMENT => {
522 let comment = element.into_token().and_then(ast::Comment::cast)?;
523 let h = HighlightTag::Comment;
524 match comment.kind().doc {
525 Some(_) => h | HighlightModifier::Documentation,
526 None => h.into(),
527 }
528 }
396 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), 529 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(),
397 ATTR => HighlightTag::Attribute.into(), 530 ATTR => HighlightTag::Attribute.into(),
398 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), 531 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
@@ -406,6 +539,21 @@ fn highlight_element(
406 _ => h, 539 _ => h,
407 } 540 }
408 } 541 }
542 T![*] => {
543 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
544
545 let expr = prefix_expr.expr()?;
546 let ty = sema.type_of_expr(&expr)?;
547 if !ty.is_raw_ptr() {
548 return None;
549 } else {
550 HighlightTag::Operator | HighlightModifier::Unsafe
551 }
552 }
553 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
554 Highlight::new(HighlightTag::Macro)
555 }
556 p if p.is_punct() => HighlightTag::Punctuation.into(),
409 557
410 k if k.is_keyword() => { 558 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 559 let h = Highlight::new(HighlightTag::Keyword);
@@ -419,10 +567,31 @@ fn highlight_element(
419 | T![return] 567 | T![return]
420 | T![while] 568 | T![while]
421 | T![in] => h | HighlightModifier::ControlFlow, 569 | T![in] => h | HighlightModifier::ControlFlow,
422 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, 570 T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow,
423 T![unsafe] => h | HighlightModifier::Unsafe, 571 T![unsafe] => h | HighlightModifier::Unsafe,
424 T![true] | T![false] => HighlightTag::BoolLiteral.into(), 572 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
425 T![self] => HighlightTag::SelfKeyword.into(), 573 T![self] => {
574 let self_param_is_mut = element
575 .parent()
576 .and_then(ast::SelfParam::cast)
577 .and_then(|p| p.mut_token())
578 .is_some();
579 // closure to enforce lazyness
580 let self_path = || {
581 sema.resolve_path(&element.parent()?.parent().and_then(ast::Path::cast)?)
582 };
583 if self_param_is_mut
584 || matches!(self_path(),
585 Some(hir::PathResolution::Local(local))
586 if local.is_self(db)
587 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
588 )
589 {
590 HighlightTag::SelfKeyword | HighlightModifier::Mutable
591 } else {
592 HighlightTag::SelfKeyword.into()
593 }
594 }
426 _ => h, 595 _ => h,
427 } 596 }
428 } 597 }
@@ -445,7 +614,7 @@ fn highlight_element(
445 } 614 }
446} 615}
447 616
448fn is_child_of_impl(element: SyntaxElement) -> bool { 617fn is_child_of_impl(element: &SyntaxElement) -> bool {
449 match element.parent() { 618 match element.parent() {
450 Some(e) => e.kind() == IMPL_DEF, 619 Some(e) => e.kind() == IMPL_DEF,
451 _ => false, 620 _ => false,
@@ -458,7 +627,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 627 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 628 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 629 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 630 hir::ModuleDef::Function(func) => {
631 let mut h = HighlightTag::Function.into();
632 if func.is_unsafe(db) {
633 h |= HighlightModifier::Unsafe;
634 }
635 return h;
636 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 637 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 638 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 639 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
@@ -477,9 +652,10 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
477 }, 652 },
478 Definition::SelfType(_) => HighlightTag::SelfType, 653 Definition::SelfType(_) => HighlightTag::SelfType,
479 Definition::TypeParam(_) => HighlightTag::TypeParam, 654 Definition::TypeParam(_) => HighlightTag::TypeParam,
480 // FIXME: distinguish between locals and parameters
481 Definition::Local(local) => { 655 Definition::Local(local) => {
482 let mut h = Highlight::new(HighlightTag::Local); 656 let tag =
657 if local.is_param(db) { HighlightTag::ValueParam } else { HighlightTag::Local };
658 let mut h = Highlight::new(tag);
483 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 659 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
484 h |= HighlightModifier::Mutable; 660 h |= HighlightModifier::Mutable;
485 } 661 }
@@ -517,41 +693,52 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
517 tag.into() 693 tag.into()
518} 694}
519 695
520fn highlight_injection( 696fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
521 acc: &mut HighlightedRangeStack, 697 let default = HighlightTag::UnresolvedReference;
522 sema: &Semantics<RootDatabase>,
523 literal: ast::RawString,
524 expanded: SyntaxToken,
525) -> Option<()> {
526 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
527 if !active_parameter.name.starts_with("ra_fixture") {
528 return None;
529 }
530 let value = literal.value()?;
531 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
532
533 if let Some(range) = literal.open_quote_text_range() {
534 acc.add(HighlightedRange {
535 range,
536 highlight: HighlightTag::StringLiteral.into(),
537 binding_hash: None,
538 })
539 }
540 698
541 for mut h in analysis.highlight(tmp_file_id).unwrap() { 699 let parent = match name.syntax().parent() {
542 if let Some(r) = literal.map_range_up(h.range) { 700 Some(it) => it,
543 h.range = r; 701 _ => return default.into(),
544 acc.add(h) 702 };
545 }
546 }
547 703
548 if let Some(range) = literal.close_quote_text_range() { 704 let tag = match parent.kind() {
549 acc.add(HighlightedRange { 705 METHOD_CALL_EXPR => HighlightTag::Function,
550 range, 706 FIELD_EXPR => HighlightTag::Field,
551 highlight: HighlightTag::StringLiteral.into(), 707 PATH_SEGMENT => {
552 binding_hash: None, 708 let path = match parent.parent().and_then(ast::Path::cast) {
553 }) 709 Some(it) => it,
554 } 710 _ => return default.into(),
711 };
712 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
713 Some(it) => it,
714 _ => {
715 // within path, decide whether it is module or adt by checking for uppercase name
716 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
717 HighlightTag::Struct
718 } else {
719 HighlightTag::Module
720 }
721 .into();
722 }
723 };
724 let parent = match expr.syntax().parent() {
725 Some(it) => it,
726 None => return default.into(),
727 };
555 728
556 Some(()) 729 match parent.kind() {
730 CALL_EXPR => HighlightTag::Function,
731 _ => {
732 if name.text().chars().next().unwrap_or_default().is_uppercase() {
733 HighlightTag::Struct
734 } else {
735 HighlightTag::Constant
736 }
737 }
738 }
739 }
740 _ => default,
741 };
742
743 tag.into()
557} 744}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..0be55bca9 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -19,7 +19,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
19 ) 19 )
20 } 20 }
21 21
22 let ranges = highlight(db, file_id, None); 22 let ranges = highlight(db, file_id, None, false);
23 let text = parse.tree().syntax().to_string(); 23 let text = parse.tree().syntax().to_string();
24 let mut prev_pos = TextSize::from(0); 24 let mut prev_pos = TextSize::from(0);
25 let mut buf = String::new(); 25 let mut buf = String::new();
@@ -64,11 +64,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
64 64
65.lifetime { color: #DFAF8F; font-style: italic; } 65.lifetime { color: #DFAF8F; font-style: italic; }
66.comment { color: #7F9F7F; } 66.comment { color: #7F9F7F; }
67.documentation { color: #629755; }
68.injected { opacity: 0.65 ; }
67.struct, .enum { color: #7CB8BB; } 69.struct, .enum { color: #7CB8BB; }
68.enum_variant { color: #BDE0F3; } 70.enum_variant { color: #BDE0F3; }
69.string_literal { color: #CC9393; } 71.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 72.field { color: #94BFF3; }
71.function { color: #93E0E3; } 73.function { color: #93E0E3; }
74.function.unsafe { color: #BC8383; }
75.operator.unsafe { color: #BC8383; }
72.parameter { color: #94BFF3; } 76.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 77.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 78.type { color: #7CB8BB; }
@@ -79,12 +83,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
79.bool_literal { color: #BFE6EB; } 83.bool_literal { color: #BFE6EB; }
80.macro { color: #94BFF3; } 84.macro { color: #94BFF3; }
81.module { color: #AFD8AF; } 85.module { color: #AFD8AF; }
86.value_param { color: #DCDCCC; }
82.variable { color: #DCDCCC; } 87.variable { color: #DCDCCC; }
83.format_specifier { color: #CC696B; } 88.format_specifier { color: #CC696B; }
84.mutable { text-decoration: underline; } 89.mutable { text-decoration: underline; }
85 90.escape_sequence { color: #94BFF3; }
86.keyword { color: #F0DFAF; font-weight: bold; } 91.keyword { color: #F0DFAF; font-weight: bold; }
87.keyword.unsafe { color: #BC8383; font-weight: bold; } 92.keyword.unsafe { color: #BC8383; font-weight: bold; }
88.control { font-style: italic; } 93.control { font-style: italic; }
94
95.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
89</style> 96</style>
90"; 97";
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
new file mode 100644
index 000000000..8665b480f
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -0,0 +1,188 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use ast::{HasQuotes, HasStringValue};
6use hir::Semantics;
7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use stdx::SepBy;
9
10use crate::{
11 call_info::ActiveParameter, Analysis, Highlight, HighlightModifier, HighlightTag,
12 HighlightedRange, RootDatabase,
13};
14
15use super::HighlightedRangeStack;
16
17pub(super) fn highlight_injection(
18 acc: &mut HighlightedRangeStack,
19 sema: &Semantics<RootDatabase>,
20 literal: ast::RawString,
21 expanded: SyntaxToken,
22) -> Option<()> {
23 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
24 if !active_parameter.name.starts_with("ra_fixture") {
25 return None;
26 }
27 let value = literal.value()?;
28 let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned());
29
30 if let Some(range) = literal.open_quote_text_range() {
31 acc.add(HighlightedRange {
32 range,
33 highlight: HighlightTag::StringLiteral.into(),
34 binding_hash: None,
35 })
36 }
37
38 for mut h in analysis.highlight(tmp_file_id).unwrap() {
39 if let Some(r) = literal.map_range_up(h.range) {
40 h.range = r;
41 acc.add(h)
42 }
43 }
44
45 if let Some(range) = literal.close_quote_text_range() {
46 acc.add(HighlightedRange {
47 range,
48 highlight: HighlightTag::StringLiteral.into(),
49 binding_hash: None,
50 })
51 }
52
53 Some(())
54}
55
56/// Mapping from extracted documentation code to original code
57type RangesMap = BTreeMap<TextSize, TextSize>;
58
59const RUSTDOC_FENCE: &'static str = "```";
60const RUSTDOC_FENCE_TOKENS: &[&'static str] =
61 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
62
63/// Extracts Rust code from documentation comments as well as a mapping from
64/// the extracted source code back to the original source ranges.
65/// Lastly, a vector of new comment highlight ranges (spanning only the
66/// comment prefix) is returned which is used in the syntax highlighting
67/// injection to replace the previous (line-spanning) comment ranges.
68pub(super) fn extract_doc_comments(
69 node: &SyntaxNode,
70) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
71 // wrap the doctest into function body to get correct syntax highlighting
72 let prefix = "fn doctest() {\n";
73 let suffix = "}\n";
74 // Mapping from extracted documentation code to original code
75 let mut range_mapping: RangesMap = BTreeMap::new();
76 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
77 let mut is_codeblock = false;
78 let mut is_doctest = false;
79 // Replace the original, line-spanning comment ranges by new, only comment-prefix
80 // spanning comment ranges.
81 let mut new_comments = Vec::new();
82 let doctest = node
83 .children_with_tokens()
84 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
85 .filter(|comment| comment.kind().doc.is_some())
86 .filter(|comment| {
87 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
88 is_codeblock = !is_codeblock;
89 // Check whether code is rust by inspecting fence guards
90 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
91 let is_rust =
92 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
93 is_doctest = is_codeblock && is_rust;
94 false
95 } else {
96 is_doctest
97 }
98 })
99 .map(|comment| {
100 let prefix_len = comment.prefix().len();
101 let line: &str = comment.text().as_str();
102 let range = comment.syntax().text_range();
103
104 // whitespace after comment is ignored
105 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
106 prefix_len + ws.len_utf8()
107 } else {
108 prefix_len
109 };
110
111 // lines marked with `#` should be ignored in output, we skip the `#` char
112 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
113 pos + ws.len_utf8()
114 } else {
115 pos
116 };
117
118 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
119 new_comments.push(HighlightedRange {
120 range: TextRange::new(
121 range.start(),
122 range.start() + TextSize::try_from(pos).unwrap(),
123 ),
124 highlight: HighlightTag::Comment | HighlightModifier::Documentation,
125 binding_hash: None,
126 });
127 line_start += range.len() - TextSize::try_from(pos).unwrap();
128 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
129
130 line[pos..].to_owned()
131 })
132 .sep_by("\n")
133 .to_string();
134
135 if doctest.is_empty() {
136 return None;
137 }
138
139 let doctest = format!("{}{}{}", prefix, doctest, suffix);
140 Some((doctest, range_mapping, new_comments))
141}
142
143/// Injection of syntax highlighting of doctests.
144pub(super) fn highlight_doc_comment(
145 text: String,
146 range_mapping: RangesMap,
147 new_comments: Vec<HighlightedRange>,
148 stack: &mut HighlightedRangeStack,
149) {
150 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
151
152 stack.push();
153 for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
154 // Determine start offset and end offset in case of multi-line ranges
155 let mut start_offset = None;
156 let mut end_offset = None;
157 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
158 // It's possible for orig_line_start - line_start to be negative. Add h.range.start()
159 // here and remove it from the end range after the loop below so that the values are
160 // always non-negative.
161 let offset = h.range.start() + orig_line_start - line_start;
162 if line_start <= &h.range.start() {
163 start_offset.get_or_insert(offset);
164 break;
165 } else {
166 end_offset.get_or_insert(offset);
167 }
168 }
169 if let Some(start_offset) = start_offset {
170 h.range = TextRange::new(
171 start_offset,
172 h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(),
173 );
174
175 h.highlight |= HighlightModifier::Injected;
176 stack.add(h);
177 }
178 }
179
180 // Inject the comment prefix highlight ranges
181 stack.push();
182 for comment in new_comments {
183 stack.add(comment);
184 }
185 stack.pop_and_inject(None);
186 stack
187 .pop_and_inject(Some(Highlight::from(HighlightTag::Generic) | HighlightModifier::Injected));
188}
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..49ec94bdc 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -23,13 +23,16 @@ pub enum HighlightTag {
23 Constant, 23 Constant,
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 EscapeSequence,
26 Field, 27 Field,
27 Function, 28 Function,
29 Generic,
28 Keyword, 30 Keyword,
29 Lifetime, 31 Lifetime,
30 Macro, 32 Macro,
31 Module, 33 Module,
32 NumericLiteral, 34 NumericLiteral,
35 Punctuation,
33 SelfKeyword, 36 SelfKeyword,
34 SelfType, 37 SelfType,
35 Static, 38 Static,
@@ -39,6 +42,7 @@ pub enum HighlightTag {
39 TypeAlias, 42 TypeAlias,
40 TypeParam, 43 TypeParam,
41 Union, 44 Union,
45 ValueParam,
42 Local, 46 Local,
43 UnresolvedReference, 47 UnresolvedReference,
44 FormatSpecifier, 48 FormatSpecifier,
@@ -55,6 +59,8 @@ pub enum HighlightModifier {
55 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 59 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
56 /// not. 60 /// not.
57 Definition, 61 Definition,
62 Documentation,
63 Injected,
58 Mutable, 64 Mutable,
59 Unsafe, 65 Unsafe,
60} 66}
@@ -71,13 +77,18 @@ impl HighlightTag {
71 HighlightTag::Constant => "constant", 77 HighlightTag::Constant => "constant",
72 HighlightTag::Enum => "enum", 78 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 79 HighlightTag::EnumVariant => "enum_variant",
80 HighlightTag::EscapeSequence => "escape_sequence",
74 HighlightTag::Field => "field", 81 HighlightTag::Field => "field",
82 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 83 HighlightTag::Function => "function",
84 HighlightTag::Generic => "generic",
76 HighlightTag::Keyword => "keyword", 85 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 86 HighlightTag::Lifetime => "lifetime",
87 HighlightTag::Punctuation => "punctuation",
78 HighlightTag::Macro => "macro", 88 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 89 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 90 HighlightTag::NumericLiteral => "numeric_literal",
91 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 92 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 93 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 94 HighlightTag::Static => "static",
@@ -87,10 +98,9 @@ impl HighlightTag {
87 HighlightTag::TypeAlias => "type_alias", 98 HighlightTag::TypeAlias => "type_alias",
88 HighlightTag::TypeParam => "type_param", 99 HighlightTag::TypeParam => "type_param",
89 HighlightTag::Union => "union", 100 HighlightTag::Union => "union",
101 HighlightTag::ValueParam => "value_param",
90 HighlightTag::Local => "variable", 102 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 103 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 104 }
95 } 105 }
96} 106}
@@ -106,6 +116,8 @@ impl HighlightModifier {
106 HighlightModifier::Attribute, 116 HighlightModifier::Attribute,
107 HighlightModifier::ControlFlow, 117 HighlightModifier::ControlFlow,
108 HighlightModifier::Definition, 118 HighlightModifier::Definition,
119 HighlightModifier::Documentation,
120 HighlightModifier::Injected,
109 HighlightModifier::Mutable, 121 HighlightModifier::Mutable,
110 HighlightModifier::Unsafe, 122 HighlightModifier::Unsafe,
111 ]; 123 ];
@@ -115,6 +127,8 @@ impl HighlightModifier {
115 HighlightModifier::Attribute => "attribute", 127 HighlightModifier::Attribute => "attribute",
116 HighlightModifier::ControlFlow => "control", 128 HighlightModifier::ControlFlow => "control",
117 HighlightModifier::Definition => "declaration", 129 HighlightModifier::Definition => "declaration",
130 HighlightModifier::Documentation => "documentation",
131 HighlightModifier::Injected => "injected",
118 HighlightModifier::Mutable => "mutable", 132 HighlightModifier::Mutable => "mutable",
119 HighlightModifier::Unsafe => "unsafe", 133 HighlightModifier::Unsafe => "unsafe",
120 } 134 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da..87a6e2523 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -1,15 +1,13 @@
1use std::fs; 1use std::fs;
2 2
3use test_utils::{assert_eq_text, project_dir, read_text}; 3use expect::{expect_file, ExpectFile};
4use test_utils::project_dir;
4 5
5use crate::{ 6use crate::{mock_analysis::single_file, FileRange, TextRange};
6 mock_analysis::{single_file, MockAnalysis},
7 FileRange, TextRange,
8};
9 7
10#[test] 8#[test]
11fn test_highlighting() { 9fn test_highlighting() {
12 let (analysis, file_id) = single_file( 10 check_highlighting(
13 r#" 11 r#"
14#[derive(Clone, Debug)] 12#[derive(Clone, Debug)]
15struct Foo { 13struct Foo {
@@ -27,6 +25,16 @@ impl Bar for Foo {
27 } 25 }
28} 26}
29 27
28impl Foo {
29 fn baz(mut self) -> i32 {
30 self.x
31 }
32
33 fn qux(&mut self) {
34 self.x = 0;
35 }
36}
37
30static mut STATIC_MUT: i32 = 0; 38static mut STATIC_MUT: i32 = 0;
31 39
32fn foo<'a, T>() -> T { 40fn foo<'a, T>() -> T {
@@ -43,6 +51,12 @@ def_fn! {
43 } 51 }
44} 52}
45 53
54macro_rules! noop {
55 ($expr:expr) => {
56 $expr
57 }
58}
59
46// comment 60// comment
47fn main() { 61fn main() {
48 println!("Hello, {}!", 92); 62 println!("Hello, {}!", 92);
@@ -61,10 +75,14 @@ fn main() {
61 // Do nothing 75 // Do nothing
62 } 76 }
63 77
78 noop!(noop!(1));
79
64 let mut x = 42; 80 let mut x = 42;
65 let y = &mut x; 81 let y = &mut x;
66 let z = &y; 82 let z = &y;
67 83
84 let Foo { x: z, y } = Foo { x: z, y };
85
68 y; 86 y;
69} 87}
70 88
@@ -84,17 +102,14 @@ impl<T> Option<T> {
84} 102}
85"# 103"#
86 .trim(), 104 .trim(),
105 expect_file!["crates/ra_ide/test_data/highlighting.html"],
106 false,
87 ); 107 );
88 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
89 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
90 let expected_html = &read_text(&dst_file);
91 fs::write(dst_file, &actual_html).unwrap();
92 assert_eq_text!(expected_html, actual_html);
93} 108}
94 109
95#[test] 110#[test]
96fn test_rainbow_highlighting() { 111fn test_rainbow_highlighting() {
97 let (analysis, file_id) = single_file( 112 check_highlighting(
98 r#" 113 r#"
99fn main() { 114fn main() {
100 let hello = "hello"; 115 let hello = "hello";
@@ -110,12 +125,9 @@ fn bar() {
110} 125}
111"# 126"#
112 .trim(), 127 .trim(),
128 expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"],
129 true,
113 ); 130 );
114 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
115 let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
116 let expected_html = &read_text(&dst_file);
117 fs::write(dst_file, &actual_html).unwrap();
118 assert_eq_text!(expected_html, actual_html);
119} 131}
120 132
121#[test] 133#[test]
@@ -123,12 +135,10 @@ fn accidentally_quadratic() {
123 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic"); 135 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
124 let src = fs::read_to_string(file).unwrap(); 136 let src = fs::read_to_string(file).unwrap();
125 137
126 let mut mock = MockAnalysis::new(); 138 let (analysis, file_id) = single_file(&src);
127 let file_id = mock.add_file("/main.rs", &src);
128 let host = mock.analysis_host();
129 139
130 // let t = std::time::Instant::now(); 140 // let t = std::time::Instant::now();
131 let _ = host.analysis().highlight(file_id).unwrap(); 141 let _ = analysis.highlight(file_id).unwrap();
132 // eprintln!("elapsed: {:?}", t.elapsed()); 142 // eprintln!("elapsed: {:?}", t.elapsed());
133} 143}
134 144
@@ -136,16 +146,17 @@ fn accidentally_quadratic() {
136fn test_ranges() { 146fn test_ranges() {
137 let (analysis, file_id) = single_file( 147 let (analysis, file_id) = single_file(
138 r#" 148 r#"
139 #[derive(Clone, Debug)] 149#[derive(Clone, Debug)]
140 struct Foo { 150struct Foo {
141 pub x: i32, 151 pub x: i32,
142 pub y: i32, 152 pub y: i32,
143 }"#, 153}
154"#,
144 ); 155 );
145 156
146 // The "x" 157 // The "x"
147 let highlights = &analysis 158 let highlights = &analysis
148 .highlight_range(FileRange { file_id, range: TextRange::at(82.into(), 1.into()) }) 159 .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
149 .unwrap(); 160 .unwrap();
150 161
151 assert_eq!(&highlights[0].highlight.to_string(), "field.declaration"); 162 assert_eq!(&highlights[0].highlight.to_string(), "field.declaration");
@@ -153,7 +164,7 @@ fn test_ranges() {
153 164
154#[test] 165#[test]
155fn test_flattening() { 166fn test_flattening() {
156 let (analysis, file_id) = single_file( 167 check_highlighting(
157 r##" 168 r##"
158fn fixture(ra_fixture: &str) {} 169fn fixture(ra_fixture: &str) {}
159 170
@@ -167,13 +178,9 @@ fn main() {
167 ); 178 );
168}"## 179}"##
169 .trim(), 180 .trim(),
181 expect_file!["crates/ra_ide/test_data/highlight_injection.html"],
182 false,
170 ); 183 );
171
172 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html");
173 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
174 let expected_html = &read_text(&dst_file);
175 fs::write(dst_file, &actual_html).unwrap();
176 assert_eq_text!(expected_html, actual_html);
177} 184}
178 185
179#[test] 186#[test]
@@ -192,7 +199,7 @@ macro_rules! test {}
192fn test_string_highlighting() { 199fn test_string_highlighting() {
193 // The format string detection is based on macro-expansion, 200 // The format string detection is based on macro-expansion,
194 // thus, we have to copy the macro definition from `std` 201 // thus, we have to copy the macro definition from `std`
195 let (analysis, file_id) = single_file( 202 check_highlighting(
196 r#" 203 r#"
197macro_rules! println { 204macro_rules! println {
198 ($($arg:tt)*) => ({ 205 ($($arg:tt)*) => ({
@@ -218,6 +225,7 @@ fn main() {
218 println!("{argument}", argument = "test"); // => "test" 225 println!("{argument}", argument = "test"); // => "test"
219 println!("{name} {}", 1, name = 2); // => "2 1" 226 println!("{name} {}", 1, name = 2); // => "2 1"
220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" 227 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
228 println!("{{{}}}", 2); // => "{2}"
221 println!("Hello {:5}!", "x"); 229 println!("Hello {:5}!", "x");
222 println!("Hello {:1$}!", "x", 5); 230 println!("Hello {:1$}!", "x", 5);
223 println!("Hello {1:0$}!", 5, "x"); 231 println!("Hello {1:0$}!", 5, "x");
@@ -245,15 +253,128 @@ fn main() {
245 253
246 println!(r"Hello, {}!", "world"); 254 println!(r"Hello, {}!", "world");
247 255
256 // escape sequences
257 println!("Hello\nWorld");
258 println!("\u{48}\x65\x6C\x6C\x6F World");
259
248 println!("{\x41}", A = 92); 260 println!("{\x41}", A = 92);
249 println!("{ничоси}", ничоси = 92); 261 println!("{ничоси}", ничоси = 92);
250}"# 262}"#
251 .trim(), 263 .trim(),
264 expect_file!["crates/ra_ide/test_data/highlight_strings.html"],
265 false,
266 );
267}
268
269#[test]
270fn test_unsafe_highlighting() {
271 check_highlighting(
272 r#"
273unsafe fn unsafe_fn() {}
274
275struct HasUnsafeFn;
276
277impl HasUnsafeFn {
278 unsafe fn unsafe_method(&self) {}
279}
280
281fn main() {
282 let x = &5 as *const usize;
283 unsafe {
284 unsafe_fn();
285 HasUnsafeFn.unsafe_method();
286 let y = *(x);
287 let z = -x;
288 }
289}
290"#
291 .trim(),
292 expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"],
293 false,
294 );
295}
296
297#[test]
298fn test_highlight_doctest() {
299 check_highlighting(
300 r#"
301/// ```
302/// let _ = "early doctests should not go boom";
303/// ```
304struct Foo {
305 bar: bool,
306}
307
308impl Foo {
309 pub const bar: bool = true;
310
311 /// Constructs a new `Foo`.
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// # #![allow(unused_mut)]
317 /// let mut foo: Foo = Foo::new();
318 /// ```
319 pub const fn new() -> Foo {
320 Foo { bar: true }
321 }
322
323 /// `bar` method on `Foo`.
324 ///
325 /// # Examples
326 ///
327 /// ```
328 /// use x::y;
329 ///
330 /// let foo = Foo::new();
331 ///
332 /// // calls bar on foo
333 /// assert!(foo.bar());
334 ///
335 /// let bar = foo.bar || Foo::bar;
336 ///
337 /// /* multi-line
338 /// comment */
339 ///
340 /// let multi_line_string = "Foo
341 /// bar
342 /// ";
343 ///
344 /// ```
345 ///
346 /// ```rust,no_run
347 /// let foobar = Foo::new().bar();
348 /// ```
349 ///
350 /// ```sh
351 /// echo 1
352 /// ```
353 pub fn foo(&self) -> bool {
354 true
355 }
356}
357
358/// ```
359/// noop!(1);
360/// ```
361macro_rules! noop {
362 ($expr:expr) => {
363 $expr
364 }
365}
366"#
367 .trim(),
368 expect_file!["crates/ra_ide/test_data/highlight_doctest.html"],
369 false,
252 ); 370 );
371}
253 372
254 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_strings.html"); 373/// Highlights the code given by the `ra_fixture` argument, renders the
255 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); 374/// result as HTML, and compares it with the HTML file given as `snapshot`.
256 let expected_html = &read_text(&dst_file); 375/// Note that the `snapshot` file is overwritten by the rendered HTML.
257 fs::write(dst_file, &actual_html).unwrap(); 376fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
258 assert_eq_text!(expected_html, actual_html); 377 let (analysis, file_id) = single_file(ra_fixture);
378 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
379 expect.assert_eq(actual_html)
259} 380}
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index a341684fd..f716a3861 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -104,7 +104,7 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St
104mod tests { 104mod tests {
105 use test_utils::assert_eq_text; 105 use test_utils::assert_eq_text;
106 106
107 use crate::mock_analysis::{single_file, single_file_with_range}; 107 use crate::mock_analysis::{analysis_and_range, single_file};
108 108
109 #[test] 109 #[test]
110 fn test_syntax_tree_without_range() { 110 fn test_syntax_tree_without_range() {
@@ -184,7 +184,7 @@ [email protected]
184 184
185 #[test] 185 #[test]
186 fn test_syntax_tree_with_range() { 186 fn test_syntax_tree_with_range() {
187 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); 187 let (analysis, range) = analysis_and_range(r#"<|>fn foo() {}<|>"#.trim());
188 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 188 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
189 189
190 assert_eq_text!( 190 assert_eq_text!(
@@ -206,7 +206,7 @@ [email protected]
206 .trim() 206 .trim()
207 ); 207 );
208 208
209 let (analysis, range) = single_file_with_range( 209 let (analysis, range) = analysis_and_range(
210 r#"fn test() { 210 r#"fn test() {
211 <|>assert!(" 211 <|>assert!("
212 fn foo() { 212 fn foo() {
@@ -242,7 +242,7 @@ [email protected]
242 242
243 #[test] 243 #[test]
244 fn test_syntax_tree_inside_string() { 244 fn test_syntax_tree_inside_string() {
245 let (analysis, range) = single_file_with_range( 245 let (analysis, range) = analysis_and_range(
246 r#"fn test() { 246 r#"fn test() {
247 assert!(" 247 assert!("
248<|>fn foo() { 248<|>fn foo() {
@@ -276,7 +276,7 @@ [email protected]
276 ); 276 );
277 277
278 // With a raw string 278 // With a raw string
279 let (analysis, range) = single_file_with_range( 279 let (analysis, range) = analysis_and_range(
280 r###"fn test() { 280 r###"fn test() {
281 assert!(r#" 281 assert!(r#"
282<|>fn foo() { 282<|>fn foo() {
@@ -310,7 +310,7 @@ [email protected]
310 ); 310 );
311 311
312 // With a raw string 312 // With a raw string
313 let (analysis, range) = single_file_with_range( 313 let (analysis, range) = analysis_and_range(
314 r###"fn test() { 314 r###"fn test() {
315 assert!(r<|>#" 315 assert!(r<|>#"
316fn foo() { 316fn foo() {
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 67e2c33a0..83776d2b6 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -17,11 +17,13 @@ mod on_enter;
17 17
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::RootDatabase; 20use ra_ide_db::{source_change::SourceFileEdit, RootDatabase};
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
24 AstNode, SourceFile, TextRange, TextSize, 24 AstNode, SourceFile,
25 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
26 TextRange, TextSize,
25}; 27};
26 28
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
@@ -47,8 +49,8 @@ pub(crate) fn on_char_typed(
47 assert!(TRIGGER_CHARS.contains(char_typed)); 49 assert!(TRIGGER_CHARS.contains(char_typed));
48 let file = &db.parse(position.file_id).tree(); 50 let file = &db.parse(position.file_id).tree();
49 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 51 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
50 let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; 52 let edit = on_char_typed_inner(file, position.offset, char_typed)?;
51 Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) 53 Some(SourceFileEdit { file_id: position.file_id, edit }.into())
52} 54}
53 55
54fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { 56fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
@@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
98 }; 100 };
99 let current_indent_len = TextSize::of(current_indent); 101 let current_indent_len = TextSize::of(current_indent);
100 102
103 let parent = whitespace.syntax().parent();
101 // Make sure dot is a part of call chain 104 // Make sure dot is a part of call chain
102 let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; 105 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
103 let prev_indent = leading_indent(field_expr.syntax())?; 106 return None;
107 }
108 let prev_indent = leading_indent(&parent)?;
104 let target_indent = format!(" {}", prev_indent); 109 let target_indent = format!(" {}", prev_indent);
105 let target_indent_len = TextSize::of(&target_indent); 110 let target_indent_len = TextSize::of(&target_indent);
106 if current_indent_len == target_indent_len { 111 if current_indent_len == target_indent_len {
@@ -143,11 +148,11 @@ mod tests {
143 }) 148 })
144 } 149 }
145 150
146 fn type_char(char_typed: char, before: &str, after: &str) { 151 fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
147 let actual = do_type_char(char_typed, before) 152 let actual = do_type_char(char_typed, ra_fixture_before)
148 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 153 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
149 154
150 assert_eq_text!(after, &actual); 155 assert_eq_text!(ra_fixture_after, &actual);
151 } 156 }
152 157
153 fn type_char_noop(char_typed: char, before: &str) { 158 fn type_char_noop(char_typed: char, before: &str) {
@@ -249,6 +254,27 @@ fn foo() {
249 } 254 }
250 255
251 #[test] 256 #[test]
257 fn indents_new_chain_call_with_let() {
258 type_char(
259 '.',
260 r#"
261fn main() {
262 let _ = foo
263 <|>
264 bar()
265}
266"#,
267 r#"
268fn main() {
269 let _ = foo
270 .
271 bar()
272}
273"#,
274 );
275 }
276
277 #[test]
252 fn indents_continued_chain_call() { 278 fn indents_continued_chain_call() {
253 type_char( 279 type_char(
254 '.', 280 '.',
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index a40d8af9c..2faaa8ff0 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -75,23 +75,22 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
75 75
76#[cfg(test)] 76#[cfg(test)]
77mod tests { 77mod tests {
78 use test_utils::{assert_eq_text, extract_offset}; 78 use test_utils::assert_eq_text;
79 79
80 use crate::mock_analysis::single_file; 80 use crate::mock_analysis::analysis_and_position;
81 81 use stdx::trim_indent;
82 use super::*;
83 82
84 fn apply_on_enter(before: &str) -> Option<String> { 83 fn apply_on_enter(before: &str) -> Option<String> {
85 let (offset, before) = extract_offset(before); 84 let (analysis, position) = analysis_and_position(&before);
86 let (analysis, file_id) = single_file(&before); 85 let result = analysis.on_enter(position).unwrap()?;
87 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;
88 86
89 let mut actual = before.to_string(); 87 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
90 result.apply(&mut actual); 88 result.apply(&mut actual);
91 Some(actual) 89 Some(actual)
92 } 90 }
93 91
94 fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) { 92 fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
93 let ra_fixture_after = &trim_indent(ra_fixture_after);
95 let actual = apply_on_enter(ra_fixture_before).unwrap(); 94 let actual = apply_on_enter(ra_fixture_before).unwrap();
96 assert_eq_text!(ra_fixture_after, &actual); 95 assert_eq_text!(ra_fixture_after, &actual);
97 } 96 }