aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/call_info.rs157
-rw-r--r--crates/ra_ide_api/src/completion.rs41
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs198
-rw-r--r--crates/ra_ide_api/src/completion/complete_fn_param.rs85
-rw-r--r--crates/ra_ide_api/src/completion/complete_path.rs10
-rw-r--r--crates/ra_ide_api/src/completion/complete_pattern.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs4
-rw-r--r--crates/ra_ide_api/src/completion/complete_struct_literal.rs11
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs15
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs5
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap14
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__method_attr_filtering.snap16
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__method_completion.snap16
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__no_non_self_method.snap7
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__no_struct_field_completion_for_method_call.snap7
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_last_param.snap15
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_nth_param.snap15
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_trait_param.snap15
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap6
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion.snap16
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_autoderef.snap24
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_self.snap27
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_completion.snap24
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap16
-rw-r--r--crates/ra_ide_api/src/db.rs9
-rw-r--r--crates/ra_ide_api/src/display.rs82
-rw-r--r--crates/ra_ide_api/src/display/function_signature.rs101
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs (renamed from crates/ra_ide_api/src/navigation_target.rs)105
-rw-r--r--crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap (renamed from crates/ra_ide_api/src/snapshots/tests__file_structure.snap)6
-rw-r--r--crates/ra_ide_api/src/display/structure.rs (renamed from crates/ra_ide_api/src/structure.rs)0
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs149
-rw-r--r--crates/ra_ide_api/src/hover.rs128
-rw-r--r--crates/ra_ide_api/src/lib.rs12
-rw-r--r--crates/ra_ide_api/src/references.rs29
-rw-r--r--crates/ra_ide_api/src/runnables.rs1
-rw-r--r--crates/ra_ide_api/src/status.rs7
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs2
37 files changed, 706 insertions, 671 deletions
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index 29fa7d30b..4413aec73 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -2,13 +2,11 @@ use test_utils::tested_by;
2use ra_db::SourceDatabase; 2use ra_db::SourceDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SyntaxNode, TextUnit, 4 AstNode, SyntaxNode, TextUnit,
5 SyntaxKind::FN_DEF,
6 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
7 algo::find_node_at_offset, 6 algo::find_node_at_offset,
8}; 7};
9use hir::Docs;
10 8
11use crate::{FilePosition, CallInfo, db::RootDatabase}; 9use crate::{FilePosition, CallInfo, FunctionSignature, db::RootDatabase};
12 10
13/// Computes parameter information for the given call expression. 11/// Computes parameter information for the given call expression.
14pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 12pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
@@ -19,19 +17,26 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
19 let calling_node = FnCallNode::with_node(syntax, position.offset)?; 17 let calling_node = FnCallNode::with_node(syntax, position.offset)?;
20 let name_ref = calling_node.name_ref()?; 18 let name_ref = calling_node.name_ref()?;
21 19
22 // Resolve the function's NameRef (NOTE: this isn't entirely accurate). 20 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
23 let file_symbols = crate::symbol_index::index_resolve(db, name_ref); 21 let function = match calling_node {
24 let symbol = file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF)?; 22 FnCallNode::CallExpr(expr) => {
25 let fn_file = db.parse(symbol.file_id); 23 //FIXME: apply subst
26 let fn_def = symbol.ptr.to_node(&fn_file); 24 let (callable_def, _subst) =
27 let fn_def = ast::FnDef::cast(fn_def).unwrap(); 25 analyzer.type_of(db, expr.expr()?.into())?.as_callable()?;
28 let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?; 26 match callable_def {
27 hir::CallableDef::Function(it) => it,
28 //FIXME: handle other callables
29 _ => return None,
30 }
31 }
32 FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(expr)?,
33 };
29 34
30 let mut call_info = CallInfo::new(db, function, fn_def)?; 35 let mut call_info = CallInfo::new(db, function);
31 36
32 // If we have a calling expression let's find which argument we are on 37 // If we have a calling expression let's find which argument we are on
33 let num_params = call_info.parameters.len(); 38 let num_params = call_info.parameters().len();
34 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 39 let has_self = function.signature(db).has_self_param();
35 40
36 if num_params == 1 { 41 if num_params == 1 {
37 if !has_self { 42 if !has_self {
@@ -75,7 +80,7 @@ enum FnCallNode<'a> {
75} 80}
76 81
77impl<'a> FnCallNode<'a> { 82impl<'a> FnCallNode<'a> {
78 pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> { 83 fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
79 if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) { 84 if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
80 return Some(FnCallNode::CallExpr(expr)); 85 return Some(FnCallNode::CallExpr(expr));
81 } 86 }
@@ -85,7 +90,7 @@ impl<'a> FnCallNode<'a> {
85 None 90 None
86 } 91 }
87 92
88 pub fn name_ref(&self) -> Option<&'a ast::NameRef> { 93 fn name_ref(&self) -> Option<&'a ast::NameRef> {
89 match *self { 94 match *self {
90 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() { 95 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
91 ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, 96 ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
@@ -98,7 +103,7 @@ impl<'a> FnCallNode<'a> {
98 } 103 }
99 } 104 }
100 105
101 pub fn arg_list(&self) -> Option<&'a ast::ArgList> { 106 fn arg_list(&self) -> Option<&'a ast::ArgList> {
102 match *self { 107 match *self {
103 FnCallNode::CallExpr(expr) => expr.arg_list(), 108 FnCallNode::CallExpr(expr) => expr.arg_list(),
104 FnCallNode::MethodCallExpr(expr) => expr.arg_list(), 109 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
@@ -107,28 +112,15 @@ impl<'a> FnCallNode<'a> {
107} 112}
108 113
109impl CallInfo { 114impl CallInfo {
110 fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option<Self> { 115 fn new(db: &RootDatabase, function: hir::Function) -> Self {
111 let label = crate::completion::function_label(node)?; 116 let signature = FunctionSignature::from_hir(db, function);
112 let doc = function.docs(db);
113 117
114 Some(CallInfo { parameters: param_list(node), label, doc, active_parameter: None }) 118 CallInfo { signature, active_parameter: None }
115 } 119 }
116}
117 120
118fn param_list(node: &ast::FnDef) -> Vec<String> { 121 fn parameters(&self) -> &[String] {
119 let mut res = vec![]; 122 &self.signature.parameters
120 if let Some(param_list) = node.param_list() {
121 if let Some(self_param) = param_list.self_param() {
122 res.push(self_param.syntax().text().to_string())
123 }
124
125 // Maybe use param.pat here? See if we can just extract the name?
126 //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
127 res.extend(
128 param_list.params().filter_map(|p| p.pat()).map(|pat| pat.syntax().text().to_string()),
129 );
130 } 123 }
131 res
132} 124}
133 125
134#[cfg(test)] 126#[cfg(test)]
@@ -139,19 +131,30 @@ mod tests {
139 131
140 use super::*; 132 use super::*;
141 133
134 // These are only used when testing
135 impl CallInfo {
136 fn doc(&self) -> Option<hir::Documentation> {
137 self.signature.doc.clone()
138 }
139
140 fn label(&self) -> String {
141 self.signature.to_string()
142 }
143 }
144
142 fn call_info(text: &str) -> CallInfo { 145 fn call_info(text: &str) -> CallInfo {
143 let (analysis, position) = single_file_with_position(text); 146 let (analysis, position) = single_file_with_position(text);
144 analysis.call_info(position).unwrap().unwrap() 147 analysis.call_info(position).unwrap().unwrap()
145 } 148 }
146 149
147 #[test] 150 #[test]
148 fn test_fn_signature_two_args_first() { 151 fn test_fn_signature_two_args_firstx() {
149 let info = call_info( 152 let info = call_info(
150 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 153 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
151fn bar() { foo(<|>3, ); }"#, 154fn bar() { foo(<|>3, ); }"#,
152 ); 155 );
153 156
154 assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); 157 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
155 assert_eq!(info.active_parameter, Some(0)); 158 assert_eq!(info.active_parameter, Some(0));
156 } 159 }
157 160
@@ -162,7 +165,7 @@ fn bar() { foo(<|>3, ); }"#,
162fn bar() { foo(3, <|>); }"#, 165fn bar() { foo(3, <|>); }"#,
163 ); 166 );
164 167
165 assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); 168 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
166 assert_eq!(info.active_parameter, Some(1)); 169 assert_eq!(info.active_parameter, Some(1));
167 } 170 }
168 171
@@ -173,18 +176,57 @@ fn bar() { foo(3, <|>); }"#,
173fn bar() { foo(<|>); }"#, 176fn bar() { foo(<|>); }"#,
174 ); 177 );
175 178
176 assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); 179 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
180 assert_eq!(info.active_parameter, Some(0));
181 }
182
183 #[test]
184 fn test_fn_signature_two_args_first_generics() {
185 let info = call_info(
186 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
187fn bar() { foo(<|>3, ); }"#,
188 );
189
190 assert_eq!(info.parameters(), ["x: T", "y: U"]);
191 assert_eq!(
192 info.label(),
193 r#"
194fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
195where T: Copy + Display,
196 U: Debug
197 "#
198 .trim()
199 );
177 assert_eq!(info.active_parameter, Some(0)); 200 assert_eq!(info.active_parameter, Some(0));
178 } 201 }
179 202
180 #[test] 203 #[test]
204 fn test_fn_signature_no_params() {
205 let info = call_info(
206 r#"fn foo<T>() -> T where T: Copy + Display {}
207fn bar() { foo(<|>); }"#,
208 );
209
210 assert!(info.parameters().is_empty());
211 assert_eq!(
212 info.label(),
213 r#"
214fn foo<T>() -> T
215where T: Copy + Display
216 "#
217 .trim()
218 );
219 assert!(info.active_parameter.is_none());
220 }
221
222 #[test]
181 fn test_fn_signature_for_impl() { 223 fn test_fn_signature_for_impl() {
182 let info = call_info( 224 let info = call_info(
183 r#"struct F; impl F { pub fn new() { F{}} } 225 r#"struct F; impl F { pub fn new() { F{}} }
184fn bar() {let _ : F = F::new(<|>);}"#, 226fn bar() {let _ : F = F::new(<|>);}"#,
185 ); 227 );
186 228
187 assert_eq!(info.parameters, Vec::<String>::new()); 229 assert!(info.parameters().is_empty());
188 assert_eq!(info.active_parameter, None); 230 assert_eq!(info.active_parameter, None);
189 } 231 }
190 232
@@ -206,7 +248,7 @@ fn bar() {
206}"#, 248}"#,
207 ); 249 );
208 250
209 assert_eq!(info.parameters, vec!["&self".to_string()]); 251 assert_eq!(info.parameters(), ["&self"]);
210 assert_eq!(info.active_parameter, None); 252 assert_eq!(info.active_parameter, None);
211 } 253 }
212 254
@@ -228,7 +270,7 @@ fn bar() {
228}"#, 270}"#,
229 ); 271 );
230 272
231 assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]); 273 assert_eq!(info.parameters(), ["&self", "x: i32"]);
232 assert_eq!(info.active_parameter, Some(1)); 274 assert_eq!(info.active_parameter, Some(1));
233 } 275 }
234 276
@@ -248,10 +290,10 @@ fn bar() {
248"#, 290"#,
249 ); 291 );
250 292
251 assert_eq!(info.parameters, vec!["j".to_string()]); 293 assert_eq!(info.parameters(), ["j: u32"]);
252 assert_eq!(info.active_parameter, Some(0)); 294 assert_eq!(info.active_parameter, Some(0));
253 assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); 295 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
254 assert_eq!(info.doc.map(|it| it.into()), Some("test".to_string())); 296 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
255 } 297 }
256 298
257 #[test] 299 #[test]
@@ -276,11 +318,11 @@ pub fn do() {
276}"#, 318}"#,
277 ); 319 );
278 320
279 assert_eq!(info.parameters, vec!["x".to_string()]); 321 assert_eq!(info.parameters(), ["x: i32"]);
280 assert_eq!(info.active_parameter, Some(0)); 322 assert_eq!(info.active_parameter, Some(0));
281 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 323 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
282 assert_eq!( 324 assert_eq!(
283 info.doc.map(|it| it.into()), 325 info.doc().map(|it| it.into()),
284 Some( 326 Some(
285 r#"Adds one to the number given. 327 r#"Adds one to the number given.
286 328
@@ -322,11 +364,11 @@ pub fn do_it() {
322}"#, 364}"#,
323 ); 365 );
324 366
325 assert_eq!(info.parameters, vec!["x".to_string()]); 367 assert_eq!(info.parameters(), ["x: i32"]);
326 assert_eq!(info.active_parameter, Some(0)); 368 assert_eq!(info.active_parameter, Some(0));
327 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 369 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
328 assert_eq!( 370 assert_eq!(
329 info.doc.map(|it| it.into()), 371 info.doc().map(|it| it.into()),
330 Some( 372 Some(
331 r#"Adds one to the number given. 373 r#"Adds one to the number given.
332 374
@@ -346,11 +388,9 @@ assert_eq!(6, my_crate::add_one(5));
346 fn test_fn_signature_with_docs_from_actix() { 388 fn test_fn_signature_with_docs_from_actix() {
347 let info = call_info( 389 let info = call_info(
348 r#" 390 r#"
349pub trait WriteHandler<E> 391struct WriteHandler<E>;
350where 392
351 Self: Actor, 393impl<E> WriteHandler<E> {
352 Self::Context: ActorContext,
353{
354 /// Method is called when writer emits error. 394 /// Method is called when writer emits error.
355 /// 395 ///
356 /// If this method returns `ErrorAction::Continue` writer processing 396 /// If this method returns `ErrorAction::Continue` writer processing
@@ -367,18 +407,17 @@ where
367 } 407 }
368} 408}
369 409
370pub fn foo() { 410pub fn foo(mut r: WriteHandler<()>) {
371 WriteHandler r;
372 r.finished(<|>); 411 r.finished(<|>);
373} 412}
374 413
375"#, 414"#,
376 ); 415 );
377 416
378 assert_eq!(info.parameters, vec!["&mut self".to_string(), "ctx".to_string()]); 417 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]);
379 assert_eq!(info.active_parameter, Some(1)); 418 assert_eq!(info.active_parameter, Some(1));
380 assert_eq!( 419 assert_eq!(
381 info.doc.map(|it| it.into()), 420 info.doc().map(|it| it.into()),
382 Some( 421 Some(
383 r#"Method is called when writer finishes. 422 r#"Method is called when writer finishes.
384 423
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs
index a846a7a3c..deff59cd3 100644
--- a/crates/ra_ide_api/src/completion.rs
+++ b/crates/ra_ide_api/src/completion.rs
@@ -13,7 +13,6 @@ mod complete_scope;
13mod complete_postfix; 13mod complete_postfix;
14 14
15use ra_db::SourceDatabase; 15use ra_db::SourceDatabase;
16use ra_syntax::{ast::{self, AstNode}, SyntaxKind::{ATTR, COMMENT}};
17 16
18use crate::{ 17use crate::{
19 db, 18 db,
@@ -70,43 +69,3 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti
70 complete_postfix::complete_postfix(&mut acc, &ctx); 69 complete_postfix::complete_postfix(&mut acc, &ctx);
71 Some(acc) 70 Some(acc)
72} 71}
73
74pub fn function_label(node: &ast::FnDef) -> Option<String> {
75 let label: String = if let Some(body) = node.body() {
76 let body_range = body.syntax().range();
77 let label: String = node
78 .syntax()
79 .children_with_tokens()
80 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
81 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) // Filter out comments and attrs
82 .map(|node| node.to_string())
83 .collect();
84 label
85 } else {
86 node.syntax().text().to_string()
87 };
88
89 Some(label.trim().to_owned())
90}
91
92pub fn const_label(node: &ast::ConstDef) -> String {
93 let label: String = node
94 .syntax()
95 .children_with_tokens()
96 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
97 .map(|node| node.to_string())
98 .collect();
99
100 label.trim().to_owned()
101}
102
103pub fn type_label(node: &ast::TypeAliasDef) -> String {
104 let label: String = node
105 .syntax()
106 .children_with_tokens()
107 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
108 .map(|node| node.to_string())
109 .collect();
110
111 label.trim().to_owned()
112}
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 18b2d68d5..4a111aba5 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete dot accesses, i.e. fields or methods (currently only fields). 5/// Complete dot accesses, i.e. fields or methods (currently only fields).
6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) { 7 let receiver_ty = match ctx.dot_receiver.and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
8 (Some(function), Some(receiver)) => (function, receiver), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(receiver) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let receiver_ty = infer_result[expr].clone();
18 if !ctx.is_call { 11 if !ctx.is_call {
19 complete_fields(acc, ctx, receiver_ty.clone()); 12 complete_fields(acc, ctx, receiver_ty.clone());
20 } 13 }
@@ -55,29 +48,41 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty
55 48
56#[cfg(test)] 49#[cfg(test)]
57mod tests { 50mod tests {
58 use crate::completion::{check_completion, CompletionKind}; 51 use crate::completion::{do_completion, CompletionKind, CompletionItem};
52 use insta::assert_debug_snapshot_matches;
59 53
60 fn check_ref_completion(name: &str, code: &str) { 54 fn do_ref_completion(code: &str) -> Vec<CompletionItem> {
61 check_completion(name, code, CompletionKind::Reference); 55 do_completion(code, CompletionKind::Reference)
62 } 56 }
63 57
64 #[test] 58 #[test]
65 fn test_struct_field_completion() { 59 fn test_struct_field_completion() {
66 check_ref_completion( 60 assert_debug_snapshot_matches!(
67 "struct_field_completion", 61 do_ref_completion(
68 r" 62 r"
69 struct A { the_field: u32 } 63 struct A { the_field: u32 }
70 fn foo(a: A) { 64 fn foo(a: A) {
71 a.<|> 65 a.<|>
72 } 66 }
73 ", 67 ",
68 ),
69 @r###"[
70 CompletionItem {
71 label: "the_field",
72 source_range: [94; 94),
73 delete: [94; 94),
74 insert: "the_field",
75 kind: Field,
76 detail: "u32"
77 }
78]"###
74 ); 79 );
75 } 80 }
76 81
77 #[test] 82 #[test]
78 fn test_struct_field_completion_self() { 83 fn test_struct_field_completion_self() {
79 check_ref_completion( 84 assert_debug_snapshot_matches!(
80 "struct_field_completion_self", 85 do_ref_completion(
81 r" 86 r"
82 struct A { 87 struct A {
83 /// This is the_field 88 /// This is the_field
@@ -89,13 +94,35 @@ mod tests {
89 } 94 }
90 } 95 }
91 ", 96 ",
97 ),
98 @r###"[
99 CompletionItem {
100 label: "foo",
101 source_range: [187; 187),
102 delete: [187; 187),
103 insert: "foo()$0",
104 kind: Method,
105 detail: "fn foo(self)"
106 },
107 CompletionItem {
108 label: "the_field",
109 source_range: [187; 187),
110 delete: [187; 187),
111 insert: "the_field",
112 kind: Field,
113 detail: "(u32,)",
114 documentation: Documentation(
115 "This is the_field"
116 )
117 }
118]"###
92 ); 119 );
93 } 120 }
94 121
95 #[test] 122 #[test]
96 fn test_struct_field_completion_autoderef() { 123 fn test_struct_field_completion_autoderef() {
97 check_ref_completion( 124 assert_debug_snapshot_matches!(
98 "struct_field_completion_autoderef", 125 do_ref_completion(
99 r" 126 r"
100 struct A { the_field: (u32, i32) } 127 struct A { the_field: (u32, i32) }
101 impl A { 128 impl A {
@@ -104,26 +131,47 @@ mod tests {
104 } 131 }
105 } 132 }
106 ", 133 ",
134 ),
135 @r###"[
136 CompletionItem {
137 label: "foo",
138 source_range: [126; 126),
139 delete: [126; 126),
140 insert: "foo()$0",
141 kind: Method,
142 detail: "fn foo(&self)"
143 },
144 CompletionItem {
145 label: "the_field",
146 source_range: [126; 126),
147 delete: [126; 126),
148 insert: "the_field",
149 kind: Field,
150 detail: "(u32, i32)"
151 }
152]"###
107 ); 153 );
108 } 154 }
109 155
110 #[test] 156 #[test]
111 fn test_no_struct_field_completion_for_method_call() { 157 fn test_no_struct_field_completion_for_method_call() {
112 check_ref_completion( 158 assert_debug_snapshot_matches!(
113 "no_struct_field_completion_for_method_call", 159 do_ref_completion(
114 r" 160 r"
115 struct A { the_field: u32 } 161 struct A { the_field: u32 }
116 fn foo(a: A) { 162 fn foo(a: A) {
117 a.<|>() 163 a.<|>()
118 } 164 }
119 ", 165 ",
166 ),
167 @"[]"
120 ); 168 );
121 } 169 }
122 170
123 #[test] 171 #[test]
124 fn test_method_completion() { 172 fn test_method_completion() {
125 check_ref_completion( 173 assert_debug_snapshot_matches!(
126 "method_completion", 174 do_ref_completion(
127 r" 175 r"
128 struct A {} 176 struct A {}
129 impl A { 177 impl A {
@@ -133,13 +181,24 @@ mod tests {
133 a.<|> 181 a.<|>
134 } 182 }
135 ", 183 ",
184 ),
185 @r###"[
186 CompletionItem {
187 label: "the_method",
188 source_range: [144; 144),
189 delete: [144; 144),
190 insert: "the_method()$0",
191 kind: Method,
192 detail: "fn the_method(&self)"
193 }
194]"###
136 ); 195 );
137 } 196 }
138 197
139 #[test] 198 #[test]
140 fn test_no_non_self_method() { 199 fn test_no_non_self_method() {
141 check_ref_completion( 200 assert_debug_snapshot_matches!(
142 "no_non_self_method", 201 do_ref_completion(
143 r" 202 r"
144 struct A {} 203 struct A {}
145 impl A { 204 impl A {
@@ -149,13 +208,15 @@ mod tests {
149 a.<|> 208 a.<|>
150 } 209 }
151 ", 210 ",
211 ),
212 @"[]"
152 ); 213 );
153 } 214 }
154 215
155 #[test] 216 #[test]
156 fn test_method_attr_filtering() { 217 fn test_method_attr_filtering() {
157 check_ref_completion( 218 assert_debug_snapshot_matches!(
158 "method_attr_filtering", 219 do_ref_completion(
159 r" 220 r"
160 struct A {} 221 struct A {}
161 impl A { 222 impl A {
@@ -169,26 +230,56 @@ mod tests {
169 a.<|> 230 a.<|>
170 } 231 }
171 ", 232 ",
233 ),
234 @r###"[
235 CompletionItem {
236 label: "the_method",
237 source_range: [249; 249),
238 delete: [249; 249),
239 insert: "the_method()$0",
240 kind: Method,
241 detail: "fn the_method(&self)"
242 }
243]"###
172 ); 244 );
173 } 245 }
174 246
175 #[test] 247 #[test]
176 fn test_tuple_field_completion() { 248 fn test_tuple_field_completion() {
177 check_ref_completion( 249 assert_debug_snapshot_matches!(
178 "tuple_field_completion", 250 do_ref_completion(
179 r" 251 r"
180 fn foo() { 252 fn foo() {
181 let b = (0, 3.14); 253 let b = (0, 3.14);
182 b.<|> 254 b.<|>
183 } 255 }
184 ", 256 ",
257 ),
258 @r###"[
259 CompletionItem {
260 label: "0",
261 source_range: [75; 75),
262 delete: [75; 75),
263 insert: "0",
264 kind: Field,
265 detail: "i32"
266 },
267 CompletionItem {
268 label: "1",
269 source_range: [75; 75),
270 delete: [75; 75),
271 insert: "1",
272 kind: Field,
273 detail: "f64"
274 }
275]"###
185 ); 276 );
186 } 277 }
187 278
188 #[test] 279 #[test]
189 fn test_tuple_field_inference() { 280 fn test_tuple_field_inference() {
190 check_ref_completion( 281 assert_debug_snapshot_matches!(
191 "tuple_field_inference", 282 do_ref_completion(
192 r" 283 r"
193 pub struct S; 284 pub struct S;
194 impl S { 285 impl S {
@@ -204,6 +295,41 @@ mod tests {
204 } 295 }
205 } 296 }
206 ", 297 ",
298 ),
299 @r###"[
300 CompletionItem {
301 label: "blah",
302 source_range: [299; 300),
303 delete: [299; 300),
304 insert: "blah()$0",
305 kind: Method,
306 detail: "pub fn blah(&self)"
307 }
308]"###
309 );
310 }
311
312 #[test]
313 fn test_completion_works_in_consts() {
314 assert_debug_snapshot_matches!(
315 do_ref_completion(
316 r"
317 struct A { the_field: u32 }
318 const X: u32 = {
319 A { the_field: 92 }.<|>
320 };
321 ",
322 ),
323 @r###"[
324 CompletionItem {
325 label: "the_field",
326 source_range: [106; 106),
327 delete: [106; 106),
328 insert: "the_field",
329 kind: Field,
330 detail: "u32"
331 }
332]"###
207 ); 333 );
208 } 334 }
209} 335}
diff --git a/crates/ra_ide_api/src/completion/complete_fn_param.rs b/crates/ra_ide_api/src/completion/complete_fn_param.rs
index f87ccdeb9..85ef62f52 100644
--- a/crates/ra_ide_api/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide_api/src/completion/complete_fn_param.rs
@@ -54,48 +54,79 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 54
55#[cfg(test)] 55#[cfg(test)]
56mod tests { 56mod tests {
57 use crate::completion::{check_completion, CompletionKind}; 57 use crate::completion::{do_completion, CompletionItem, CompletionKind};
58 use insta::assert_debug_snapshot_matches;
58 59
59 fn check_magic_completion(name: &str, code: &str) { 60 fn do_magic_completion(code: &str) -> Vec<CompletionItem> {
60 check_completion(name, code, CompletionKind::Magic); 61 do_completion(code, CompletionKind::Magic)
61 } 62 }
62 63
63 #[test] 64 #[test]
64 fn test_param_completion_last_param() { 65 fn test_param_completion_last_param() {
65 check_magic_completion( 66 assert_debug_snapshot_matches!(
66 "param_completion_last_param", 67 do_magic_completion(
67 r" 68 r"
68 fn foo(file_id: FileId) {} 69 fn foo(file_id: FileId) {}
69 fn bar(file_id: FileId) {} 70 fn bar(file_id: FileId) {}
70 fn baz(file<|>) {} 71 fn baz(file<|>) {}
71 ", 72 ",
73 ),
74 @r###"[
75 CompletionItem {
76 label: "file_id: FileId",
77 source_range: [110; 114),
78 delete: [110; 114),
79 insert: "file_id: FileId",
80 lookup: "file_id"
81 }
82]"###
72 ); 83 );
73 } 84 }
74 85
75 #[test] 86 #[test]
76 fn test_param_completion_nth_param() { 87 fn test_param_completion_nth_param() {
77 check_magic_completion( 88 assert_debug_snapshot_matches!(
78 "param_completion_nth_param", 89 do_magic_completion(
79 r" 90 r"
80 fn foo(file_id: FileId) {} 91 fn foo(file_id: FileId) {}
81 fn bar(file_id: FileId) {} 92 fn bar(file_id: FileId) {}
82 fn baz(file<|>, x: i32) {} 93 fn baz(file<|>, x: i32) {}
83 ", 94 ",
95 ),
96 @r###"[
97 CompletionItem {
98 label: "file_id: FileId",
99 source_range: [110; 114),
100 delete: [110; 114),
101 insert: "file_id: FileId",
102 lookup: "file_id"
103 }
104]"###
84 ); 105 );
85 } 106 }
86 107
87 #[test] 108 #[test]
88 fn test_param_completion_trait_param() { 109 fn test_param_completion_trait_param() {
89 check_magic_completion( 110 assert_debug_snapshot_matches!(
90 "param_completion_trait_param", 111 do_magic_completion(
91 r" 112 r"
92 pub(crate) trait SourceRoot { 113 pub(crate) trait SourceRoot {
93 pub fn contains(&self, file_id: FileId) -> bool; 114 pub fn contains(&self, file_id: FileId) -> bool;
94 pub fn module_map(&self) -> &ModuleMap; 115 pub fn module_map(&self) -> &ModuleMap;
95 pub fn lines(&self, file_id: FileId) -> &LineIndex; 116 pub fn lines(&self, file_id: FileId) -> &LineIndex;
96 pub fn syntax(&self, file<|>) 117 pub fn syntax(&self, file<|>)
97 } 118 }
98 ", 119 ",
120 ),
121 @r###"[
122 CompletionItem {
123 label: "file_id: FileId",
124 source_range: [289; 293),
125 delete: [289; 293),
126 insert: "file_id: FileId",
127 lookup: "file_id"
128 }
129]"###
99 ); 130 );
100 } 131 }
101} 132}
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs
index e54fe7b7e..bc03a7095 100644
--- a/crates/ra_ide_api/src/completion/complete_path.rs
+++ b/crates/ra_ide_api/src/completion/complete_path.rs
@@ -1,4 +1,4 @@
1use hir::Resolution; 1use hir::{Resolution, Either};
2use ra_syntax::AstNode; 2use ra_syntax::AstNode;
3use test_utils::tested_by; 3use test_utils::tested_by;
4 4
@@ -9,7 +9,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
9 Some(path) => path.clone(), 9 Some(path) => path.clone(),
10 _ => return, 10 _ => return,
11 }; 11 };
12 let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() { 12 let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path).take_types() {
13 Some(Resolution::Def(def)) => def, 13 Some(Resolution::Def(def)) => def,
14 _ => return, 14 _ => return,
15 }; 15 };
@@ -19,10 +19,8 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
19 for (name, res) in module_scope.entries() { 19 for (name, res) in module_scope.entries() {
20 if Some(module) == ctx.module { 20 if Some(module) == ctx.module {
21 if let Some(import) = res.import { 21 if let Some(import) = res.import {
22 if let hir::ImportSource::UseTree(tree) = 22 if let Either::A(use_tree) = module.import_source(ctx.db, import) {
23 module.import_source(ctx.db, import) 23 if use_tree.syntax().range().contains_inclusive(ctx.offset) {
24 {
25 if tree.syntax().range().contains_inclusive(ctx.offset) {
26 // for `use self::foo<|>`, don't suggest `foo` as a completion 24 // for `use self::foo<|>`, don't suggest `foo` as a completion
27 tested_by!(dont_complete_current_use); 25 tested_by!(dont_complete_current_use);
28 continue; 26 continue;
diff --git a/crates/ra_ide_api/src/completion/complete_pattern.rs b/crates/ra_ide_api/src/completion/complete_pattern.rs
index 7abcd019b..0ef248687 100644
--- a/crates/ra_ide_api/src/completion/complete_pattern.rs
+++ b/crates/ra_ide_api/src/completion/complete_pattern.rs
@@ -7,7 +7,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 } 7 }
8 // FIXME: ideally, we should look at the type we are matching against and 8 // FIXME: ideally, we should look at the type we are matching against and
9 // suggest variants + auto-imports 9 // suggest variants + auto-imports
10 let names = ctx.resolver.all_names(ctx.db); 10 let names = ctx.analyzer.all_names(ctx.db);
11 for (name, res) in names.into_iter() { 11 for (name, res) in names.into_iter() {
12 let r = res.as_ref(); 12 let r = res.as_ref();
13 let def = match r.take_types().or(r.take_values()) { 13 let def = match r.take_types().or(r.take_values()) {
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index 6146b7bb6..fd256fc3b 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -4,7 +4,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
4 if !ctx.is_trivial_path { 4 if !ctx.is_trivial_path {
5 return; 5 return;
6 } 6 }
7 let names = ctx.resolver.all_names(ctx.db); 7 let names = ctx.analyzer.all_names(ctx.db);
8 8
9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); 9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
10} 10}
@@ -145,7 +145,7 @@ mod tests {
145 check_reference_completion( 145 check_reference_completion(
146 "dont_show_both_completions_for_shadowing", 146 "dont_show_both_completions_for_shadowing",
147 r" 147 r"
148 fn foo() -> { 148 fn foo() {
149 let bar = 92; 149 let bar = 92;
150 { 150 {
151 let bar = 62; 151 let bar = 62;
diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
index f58bcd03e..48fbf67f7 100644
--- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs
+++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete fields in fields literals. 5/// Complete fields in fields literals.
6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, struct_lit) = match (&ctx.function, ctx.struct_lit_syntax) { 7 let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) {
8 (Some(function), Some(struct_lit)) => (function, struct_lit), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(struct_lit.into()) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let ty = infer_result[expr].clone();
18 let (adt, substs) = match ty.as_adt() { 11 let (adt, substs) = match ty.as_adt() {
19 Some(res) => res, 12 Some(res) => res,
20 _ => return, 13 _ => return,
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 65dffa470..359f2cffa 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 algo::{find_token_at_offset, find_covering_element, find_node_at_offset}, 5 algo::{find_token_at_offset, find_covering_element, find_node_at_offset},
6 SyntaxKind::*, 6 SyntaxKind::*,
7}; 7};
8use hir::{source_binder, Resolver}; 8use hir::source_binder;
9 9
10use crate::{db, FilePosition}; 10use crate::{db, FilePosition};
11 11
@@ -14,11 +14,10 @@ use crate::{db, FilePosition};
14#[derive(Debug)] 14#[derive(Debug)]
15pub(crate) struct CompletionContext<'a> { 15pub(crate) struct CompletionContext<'a> {
16 pub(super) db: &'a db::RootDatabase, 16 pub(super) db: &'a db::RootDatabase,
17 pub(super) analyzer: hir::SourceAnalyzer,
17 pub(super) offset: TextUnit, 18 pub(super) offset: TextUnit,
18 pub(super) token: SyntaxToken<'a>, 19 pub(super) token: SyntaxToken<'a>,
19 pub(super) resolver: Resolver,
20 pub(super) module: Option<hir::Module>, 20 pub(super) module: Option<hir::Module>,
21 pub(super) function: Option<hir::Function>,
22 pub(super) function_syntax: Option<&'a ast::FnDef>, 21 pub(super) function_syntax: Option<&'a ast::FnDef>,
23 pub(super) use_item_syntax: Option<&'a ast::UseItem>, 22 pub(super) use_item_syntax: Option<&'a ast::UseItem>,
24 pub(super) struct_lit_syntax: Option<&'a ast::StructLit>, 23 pub(super) struct_lit_syntax: Option<&'a ast::StructLit>,
@@ -47,16 +46,16 @@ impl<'a> CompletionContext<'a> {
47 original_file: &'a SourceFile, 46 original_file: &'a SourceFile,
48 position: FilePosition, 47 position: FilePosition,
49 ) -> Option<CompletionContext<'a>> { 48 ) -> Option<CompletionContext<'a>> {
50 let resolver = source_binder::resolver_for_position(db, position);
51 let module = source_binder::module_from_position(db, position); 49 let module = source_binder::module_from_position(db, position);
52 let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; 50 let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?;
51 let analyzer =
52 hir::SourceAnalyzer::new(db, position.file_id, token.parent(), Some(position.offset));
53 let mut ctx = CompletionContext { 53 let mut ctx = CompletionContext {
54 db, 54 db,
55 analyzer,
55 token, 56 token,
56 offset: position.offset, 57 offset: position.offset,
57 resolver,
58 module, 58 module,
59 function: None,
60 function_syntax: None, 59 function_syntax: None,
61 use_item_syntax: None, 60 use_item_syntax: None,
62 struct_lit_syntax: None, 61 struct_lit_syntax: None,
@@ -147,10 +146,6 @@ impl<'a> CompletionContext<'a> {
147 .ancestors() 146 .ancestors()
148 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 147 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
149 .find_map(ast::FnDef::cast); 148 .find_map(ast::FnDef::cast);
150 if let (Some(module), Some(fn_def)) = (self.module, self.function_syntax) {
151 let function = source_binder::function_from_module(self.db, module, fn_def);
152 self.function = Some(function);
153 }
154 149
155 let parent = match name_ref.syntax().parent() { 150 let parent = match name_ref.syntax().parent() {
156 Some(it) => it, 151 Some(it) => it,
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs
index 28c8f83ab..9aa346688 100644
--- a/crates/ra_ide_api/src/completion/presentation.rs
+++ b/crates/ra_ide_api/src/completion/presentation.rs
@@ -6,6 +6,9 @@ use ra_syntax::ast::NameOwner;
6 6
7use crate::completion::{ 7use crate::completion::{
8 Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem, 8 Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem,
9};
10
11use crate::display::{
9 function_label, const_label, type_label, 12 function_label, const_label, type_label,
10}; 13};
11 14
@@ -101,7 +104,7 @@ impl Completions {
101 CompletionItemKind::Function 104 CompletionItemKind::Function
102 }) 105 })
103 .set_documentation(func.docs(ctx.db)) 106 .set_documentation(func.docs(ctx.db))
104 .set_detail(detail); 107 .detail(detail);
105 // If not an import, add parenthesis automatically. 108 // If not an import, add parenthesis automatically.
106 if ctx.use_item_syntax.is_none() && !ctx.is_call { 109 if ctx.use_item_syntax.is_none() && !ctx.is_call {
107 tested_by!(inserts_parens_for_function_calls); 110 tested_by!(inserts_parens_for_function_calls);
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap
index 87691b304..34adcda6c 100644
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap
+++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap
@@ -1,23 +1,23 @@
1--- 1---
2created: "2019-02-18T09:22:24.188564584Z" 2created: "2019-04-04T14:52:24.531844100Z"
3creator: insta@0.6.2 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/completion/completion_item.rs 4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions 5expression: kind_completions
6--- 6---
7[ 7[
8 CompletionItem { 8 CompletionItem {
9 label: "bar", 9 label: "bar",
10 source_range: [129; 129), 10 source_range: [126; 126),
11 delete: [129; 129), 11 delete: [126; 126),
12 insert: "bar", 12 insert: "bar",
13 kind: Binding 13 kind: Binding
14 }, 14 },
15 CompletionItem { 15 CompletionItem {
16 label: "foo", 16 label: "foo",
17 source_range: [129; 129), 17 source_range: [126; 126),
18 delete: [129; 129), 18 delete: [126; 126),
19 insert: "foo()$0", 19 insert: "foo()$0",
20 kind: Function, 20 kind: Function,
21 detail: "fn foo() ->" 21 detail: "fn foo()"
22 } 22 }
23] 23]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__method_attr_filtering.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__method_attr_filtering.snap
deleted file mode 100644
index ce8af2159..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__method_attr_filtering.snap
+++ /dev/null
@@ -1,16 +0,0 @@
1---
2created: "2019-02-18T09:22:23.941335305Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "the_method",
10 source_range: [249; 249),
11 delete: [249; 249),
12 insert: "the_method()$0",
13 kind: Method,
14 detail: "fn the_method(&self)"
15 }
16]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__method_completion.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__method_completion.snap
deleted file mode 100644
index 41a10de14..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__method_completion.snap
+++ /dev/null
@@ -1,16 +0,0 @@
1---
2created: "2019-02-18T09:22:23.939676100Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "the_method",
10 source_range: [144; 144),
11 delete: [144; 144),
12 insert: "the_method()$0",
13 kind: Method,
14 detail: "fn the_method(&self)"
15 }
16]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__no_non_self_method.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__no_non_self_method.snap
deleted file mode 100644
index 7cc827532..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__no_non_self_method.snap
+++ /dev/null
@@ -1,7 +0,0 @@
1---
2created: "2019-01-22T14:45:00.552379600+00:00"
3creator: [email protected]
4expression: kind_completions
5source: "crates\\ra_ide_api\\src\\completion\\completion_item.rs"
6---
7[]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__no_struct_field_completion_for_method_call.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__no_struct_field_completion_for_method_call.snap
deleted file mode 100644
index 7cc827532..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__no_struct_field_completion_for_method_call.snap
+++ /dev/null
@@ -1,7 +0,0 @@
1---
2created: "2019-01-22T14:45:00.552379600+00:00"
3creator: [email protected]
4expression: kind_completions
5source: "crates\\ra_ide_api\\src\\completion\\completion_item.rs"
6---
7[]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_last_param.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_last_param.snap
deleted file mode 100644
index cab77f5a2..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_last_param.snap
+++ /dev/null
@@ -1,15 +0,0 @@
1---
2created: "2019-02-18T09:22:23.949634602Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "file_id: FileId",
10 source_range: [98; 102),
11 delete: [98; 102),
12 insert: "file_id: FileId",
13 lookup: "file_id"
14 }
15]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_nth_param.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_nth_param.snap
deleted file mode 100644
index 8fbee160c..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_nth_param.snap
+++ /dev/null
@@ -1,15 +0,0 @@
1---
2created: "2019-02-18T09:22:23.949634355Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "file_id: FileId",
10 source_range: [98; 102),
11 delete: [98; 102),
12 insert: "file_id: FileId",
13 lookup: "file_id"
14 }
15]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_trait_param.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_trait_param.snap
deleted file mode 100644
index 76eeadb6d..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__param_completion_trait_param.snap
+++ /dev/null
@@ -1,15 +0,0 @@
1---
2created: "2019-02-18T09:22:23.974417169Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "file_id: FileId",
10 source_range: [269; 273),
11 delete: [269; 273),
12 insert: "file_id: FileId",
13 lookup: "file_id"
14 }
15]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap
index 0738cf466..ff36df707 100644
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap
+++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap
@@ -1,6 +1,6 @@
1--- 1---
2created: "2019-02-18T09:22:24.182964414Z" 2created: "2019-04-04T14:52:24.525395600Z"
3creator: insta@0.6.2 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/completion/completion_item.rs 4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions 5expression: kind_completions
6--- 6---
@@ -18,6 +18,6 @@ expression: kind_completions
18 delete: [47; 47), 18 delete: [47; 47),
19 insert: "x()$0", 19 insert: "x()$0",
20 kind: Function, 20 kind: Function,
21 detail: "fn x() ->" 21 detail: "fn x()"
22 } 22 }
23] 23]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion.snap
deleted file mode 100644
index 58271b873..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion.snap
+++ /dev/null
@@ -1,16 +0,0 @@
1---
2created: "2019-02-18T09:22:23.939645902Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "the_field",
10 source_range: [85; 85),
11 delete: [85; 85),
12 insert: "the_field",
13 kind: Field,
14 detail: "u32"
15 }
16]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_autoderef.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_autoderef.snap
deleted file mode 100644
index b38867b81..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_autoderef.snap
+++ /dev/null
@@ -1,24 +0,0 @@
1---
2created: "2019-02-18T09:22:23.940872916Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "foo",
10 source_range: [126; 126),
11 delete: [126; 126),
12 insert: "foo()$0",
13 kind: Method,
14 detail: "fn foo(&self)"
15 },
16 CompletionItem {
17 label: "the_field",
18 source_range: [126; 126),
19 delete: [126; 126),
20 insert: "the_field",
21 kind: Field,
22 detail: "(u32, i32)"
23 }
24]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_self.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_self.snap
deleted file mode 100644
index 8e5cab43e..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_field_completion_self.snap
+++ /dev/null
@@ -1,27 +0,0 @@
1---
2created: "2019-02-18T09:22:23.940872918Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "foo",
10 source_range: [187; 187),
11 delete: [187; 187),
12 insert: "foo()$0",
13 kind: Method,
14 detail: "fn foo(self)"
15 },
16 CompletionItem {
17 label: "the_field",
18 source_range: [187; 187),
19 delete: [187; 187),
20 insert: "the_field",
21 kind: Field,
22 detail: "(u32,)",
23 documentation: Documentation(
24 "This is the_field"
25 )
26 }
27]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_completion.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_completion.snap
deleted file mode 100644
index 3f2780621..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_completion.snap
+++ /dev/null
@@ -1,24 +0,0 @@
1---
2created: "2019-02-18T09:22:23.939710971Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "0",
10 source_range: [75; 75),
11 delete: [75; 75),
12 insert: "0",
13 kind: Field,
14 detail: "i32"
15 },
16 CompletionItem {
17 label: "1",
18 source_range: [75; 75),
19 delete: [75; 75),
20 insert: "1",
21 kind: Field,
22 detail: "f64"
23 }
24]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap
deleted file mode 100644
index 72c8973b8..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__tuple_field_inference.snap
+++ /dev/null
@@ -1,16 +0,0 @@
1---
2created: "2019-04-05T23:00:18.283812700Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "blah",
10 source_range: [299; 300),
11 delete: [299; 300),
12 insert: "blah()$0",
13 kind: Method,
14 detail: "pub fn blah(&self)"
15 }
16]
diff --git a/crates/ra_ide_api/src/db.rs b/crates/ra_ide_api/src/db.rs
index ea4255d35..33d3903bb 100644
--- a/crates/ra_ide_api/src/db.rs
+++ b/crates/ra_ide_api/src/db.rs
@@ -20,7 +20,6 @@ use crate::{LineIndex, symbol_index::{self, SymbolsDatabase}};
20#[derive(Debug)] 20#[derive(Debug)]
21pub(crate) struct RootDatabase { 21pub(crate) struct RootDatabase {
22 runtime: salsa::Runtime<RootDatabase>, 22 runtime: salsa::Runtime<RootDatabase>,
23 interner: Arc<hir::HirInterner>,
24 pub(crate) last_gc: time::Instant, 23 pub(crate) last_gc: time::Instant,
25 pub(crate) last_gc_check: time::Instant, 24 pub(crate) last_gc_check: time::Instant,
26} 25}
@@ -38,7 +37,6 @@ impl Default for RootDatabase {
38 fn default() -> RootDatabase { 37 fn default() -> RootDatabase {
39 let mut db = RootDatabase { 38 let mut db = RootDatabase {
40 runtime: salsa::Runtime::default(), 39 runtime: salsa::Runtime::default(),
41 interner: Default::default(),
42 last_gc: time::Instant::now(), 40 last_gc: time::Instant::now(),
43 last_gc_check: time::Instant::now(), 41 last_gc_check: time::Instant::now(),
44 }; 42 };
@@ -53,19 +51,12 @@ impl salsa::ParallelDatabase for RootDatabase {
53 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { 51 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
54 salsa::Snapshot::new(RootDatabase { 52 salsa::Snapshot::new(RootDatabase {
55 runtime: self.runtime.snapshot(self), 53 runtime: self.runtime.snapshot(self),
56 interner: Arc::clone(&self.interner),
57 last_gc: self.last_gc.clone(), 54 last_gc: self.last_gc.clone(),
58 last_gc_check: self.last_gc_check.clone(), 55 last_gc_check: self.last_gc_check.clone(),
59 }) 56 })
60 } 57 }
61} 58}
62 59
63impl AsRef<hir::HirInterner> for RootDatabase {
64 fn as_ref(&self) -> &hir::HirInterner {
65 &self.interner
66 }
67}
68
69#[salsa::query_group(LineIndexDatabaseStorage)] 60#[salsa::query_group(LineIndexDatabaseStorage)]
70pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { 61pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled {
71 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; 62 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
diff --git a/crates/ra_ide_api/src/display.rs b/crates/ra_ide_api/src/display.rs
new file mode 100644
index 000000000..1b06abf94
--- /dev/null
+++ b/crates/ra_ide_api/src/display.rs
@@ -0,0 +1,82 @@
1//! This module contains utilities for turning SyntaxNodes and HIR types
2//! into types that may be used to render in a UI.
3
4mod function_signature;
5mod navigation_target;
6mod structure;
7
8use crate::db::RootDatabase;
9use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}};
10
11pub use navigation_target::NavigationTarget;
12pub use structure::{StructureNode, file_structure};
13pub use function_signature::FunctionSignature;
14
15pub(crate) fn function_label(node: &ast::FnDef) -> String {
16 FunctionSignature::from(node).to_string()
17}
18
19pub(crate) fn const_label(node: &ast::ConstDef) -> String {
20 let label: String = node
21 .syntax()
22 .children_with_tokens()
23 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
24 .map(|node| node.to_string())
25 .collect();
26
27 label.trim().to_owned()
28}
29
30pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String {
31 let label: String = node
32 .syntax()
33 .children_with_tokens()
34 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
35 .map(|node| node.to_string())
36 .collect();
37
38 label.trim().to_owned()
39}
40
41pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> {
42 let mut res = vec![];
43 if let Some(type_params) = node.type_param_list() {
44 res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string()));
45 res.extend(type_params.type_params().map(|p| p.syntax().text().to_string()));
46 }
47 res
48}
49
50pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
51 let mut res = vec![];
52 if let Some(clause) = node.where_clause() {
53 res.extend(clause.predicates().map(|p| p.syntax().text().to_string()));
54 }
55 res
56}
57
58pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
59 rust_code_markup_with_doc::<_, &str>(val, None)
60}
61
62pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
63where
64 CODE: AsRef<str>,
65 DOC: AsRef<str>,
66{
67 if let Some(doc) = doc {
68 format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
69 } else {
70 format!("```rust\n{}\n```", val.as_ref())
71 }
72}
73
74// FIXME: this should not really use navigation target. Rather, approximately
75// resolved symbol should return a `DefId`.
76pub(crate) fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
77 match (nav.description(db), nav.docs(db)) {
78 (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
79 (None, Some(docs)) => Some(docs),
80 _ => None,
81 }
82}
diff --git a/crates/ra_ide_api/src/display/function_signature.rs b/crates/ra_ide_api/src/display/function_signature.rs
new file mode 100644
index 000000000..d09950bce
--- /dev/null
+++ b/crates/ra_ide_api/src/display/function_signature.rs
@@ -0,0 +1,101 @@
1use super::{where_predicates, generic_parameters};
2use crate::db;
3use std::fmt::{self, Display};
4use join_to_string::join;
5use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
6use std::convert::From;
7use hir::{Docs, Documentation};
8
9/// Contains information about a function signature
10#[derive(Debug)]
11pub struct FunctionSignature {
12 /// Optional visibility
13 pub visibility: Option<String>,
14 /// Name of the function
15 pub name: Option<String>,
16 /// Documentation for the function
17 pub doc: Option<Documentation>,
18 /// Generic parameters
19 pub generic_parameters: Vec<String>,
20 /// Parameters of the function
21 pub parameters: Vec<String>,
22 /// Optional return type
23 pub ret_type: Option<String>,
24 /// Where predicates
25 pub where_predicates: Vec<String>,
26}
27
28impl FunctionSignature {
29 pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
30 self.doc = doc;
31 self
32 }
33
34 pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self {
35 let doc = function.docs(db);
36 let (_, ast_node) = function.source(db);
37 FunctionSignature::from(&*ast_node).with_doc_opt(doc)
38 }
39}
40
41impl From<&'_ ast::FnDef> for FunctionSignature {
42 fn from(node: &ast::FnDef) -> FunctionSignature {
43 fn param_list(node: &ast::FnDef) -> Vec<String> {
44 let mut res = vec![];
45 if let Some(param_list) = node.param_list() {
46 if let Some(self_param) = param_list.self_param() {
47 res.push(self_param.syntax().text().to_string())
48 }
49
50 res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
51 }
52 res
53 }
54
55 FunctionSignature {
56 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
57 name: node.name().map(|n| n.text().to_string()),
58 ret_type: node
59 .ret_type()
60 .and_then(|r| r.type_ref())
61 .map(|n| n.syntax().text().to_string()),
62 parameters: param_list(node),
63 generic_parameters: generic_parameters(node),
64 where_predicates: where_predicates(node),
65 // docs are processed separately
66 doc: None,
67 }
68 }
69}
70
71impl Display for FunctionSignature {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 if let Some(t) = &self.visibility {
74 write!(f, "{} ", t)?;
75 }
76
77 if let Some(name) = &self.name {
78 write!(f, "fn {}", name)?;
79 }
80
81 if !self.generic_parameters.is_empty() {
82 join(self.generic_parameters.iter())
83 .separator(", ")
84 .surround_with("<", ">")
85 .to_fmt(f)?;
86 }
87
88 join(self.parameters.iter()).separator(", ").surround_with("(", ")").to_fmt(f)?;
89
90 if let Some(t) = &self.ret_type {
91 write!(f, " -> {}", t)?;
92 }
93
94 if !self.where_predicates.is_empty() {
95 write!(f, "\nwhere ")?;
96 join(self.where_predicates.iter()).separator(",\n ").to_fmt(f)?;
97 }
98
99 Ok(())
100 }
101}
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index f6d7f3192..84645287d 100644
--- a/crates/ra_ide_api/src/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -1,9 +1,11 @@
1use ra_db::FileId; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, 3 SyntaxNode, AstNode, SmolStr, TextRange, TreeArc, AstPtr,
4 SyntaxKind::{self, NAME}, 4 SyntaxKind::{self, NAME},
5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
6 algo::visit::{visitor, Visitor},
5}; 7};
6use hir::{ModuleSource, FieldSource, Name, ImplItem}; 8use hir::{ModuleSource, FieldSource, ImplItem, Either};
7 9
8use crate::{FileSymbol, db::RootDatabase}; 10use crate::{FileSymbol, db::RootDatabase};
9 11
@@ -72,15 +74,25 @@ impl NavigationTarget {
72 } 74 }
73 } 75 }
74 76
75 pub(crate) fn from_scope_entry( 77 pub(crate) fn from_pat(
78 db: &RootDatabase,
76 file_id: FileId, 79 file_id: FileId,
77 name: Name, 80 pat: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
78 ptr: SyntaxNodePtr,
79 ) -> NavigationTarget { 81 ) -> NavigationTarget {
82 let file = db.parse(file_id);
83 let (name, full_range) = match pat {
84 Either::A(pat) => match pat.to_node(&file).kind() {
85 ast::PatKind::BindPat(pat) => {
86 return NavigationTarget::from_bind_pat(file_id, &pat)
87 }
88 _ => ("_".into(), pat.syntax_node_ptr().range()),
89 },
90 Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()),
91 };
80 NavigationTarget { 92 NavigationTarget {
81 file_id, 93 file_id,
82 name: name.to_string().into(), 94 name,
83 full_range: ptr.range(), 95 full_range,
84 focus_range: None, 96 focus_range: None,
85 kind: NAME, 97 kind: NAME,
86 container_name: None, 98 container_name: None,
@@ -227,6 +239,7 @@ impl NavigationTarget {
227 239
228 /// Allows `NavigationTarget` to be created from a `NameOwner` 240 /// Allows `NavigationTarget` to be created from a `NameOwner`
229 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { 241 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget {
242 //FIXME: use `_` instead of empty string
230 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 243 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
231 let focus_range = node.name().map(|it| it.syntax().range()); 244 let focus_range = node.name().map(|it| it.syntax().range());
232 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) 245 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax())
@@ -248,4 +261,80 @@ impl NavigationTarget {
248 container_name: None, 261 container_name: None,
249 } 262 }
250 } 263 }
264
265 pub(crate) fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> {
266 let source_file = db.parse(self.file_id());
267 let source_file = source_file.syntax();
268 let node = source_file
269 .descendants()
270 .find(|node| node.kind() == self.kind() && node.range() == self.full_range())?
271 .to_owned();
272 Some(node)
273 }
274
275 pub(crate) fn docs(&self, db: &RootDatabase) -> Option<String> {
276 let node = self.node(db)?;
277 fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> {
278 node.doc_comment_text()
279 }
280
281 visitor()
282 .visit(doc_comments::<ast::FnDef>)
283 .visit(doc_comments::<ast::StructDef>)
284 .visit(doc_comments::<ast::EnumDef>)
285 .visit(doc_comments::<ast::TraitDef>)
286 .visit(doc_comments::<ast::Module>)
287 .visit(doc_comments::<ast::TypeAliasDef>)
288 .visit(doc_comments::<ast::ConstDef>)
289 .visit(doc_comments::<ast::StaticDef>)
290 .visit(doc_comments::<ast::NamedFieldDef>)
291 .visit(doc_comments::<ast::EnumVariant>)
292 .accept(&node)?
293 }
294
295 /// Get a description of this node.
296 ///
297 /// e.g. `struct Name`, `enum Name`, `fn Name`
298 pub(crate) fn description(&self, db: &RootDatabase) -> Option<String> {
299 // FIXME: After type inference is done, add type information to improve the output
300 let node = self.node(db)?;
301
302 fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String>
303 where
304 T: NameOwner + VisibilityOwner + TypeAscriptionOwner,
305 {
306 let mut string = visit_node(node, prefix)?;
307
308 if let Some(type_ref) = node.ascribed_type() {
309 string.push_str(": ");
310 type_ref.syntax().text().push_to(&mut string);
311 }
312
313 Some(string)
314 }
315
316 fn visit_node<T>(node: &T, label: &str) -> Option<String>
317 where
318 T: NameOwner + VisibilityOwner,
319 {
320 let mut string =
321 node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default();
322 string.push_str(label);
323 string.push_str(node.name()?.text().as_str());
324 Some(string)
325 }
326
327 visitor()
328 .visit(|node: &ast::FnDef| Some(crate::display::function_label(node)))
329 .visit(|node: &ast::StructDef| visit_node(node, "struct "))
330 .visit(|node: &ast::EnumDef| visit_node(node, "enum "))
331 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
332 .visit(|node: &ast::Module| visit_node(node, "mod "))
333 .visit(|node: &ast::TypeAliasDef| visit_node(node, "type "))
334 .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const "))
335 .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static "))
336 .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, ""))
337 .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string()))
338 .accept(&node)?
339 }
251} 340}
diff --git a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap
index 2efa8e22c..32dd99484 100644
--- a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap
+++ b/crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap
@@ -1,7 +1,7 @@
1--- 1---
2created: "2019-02-05T22:03:50.763530100Z" 2created: "2019-04-08T09:44:50.196004400Z"
3creator: insta@0.6.1 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/structure.rs 4source: crates/ra_ide_api/src/display/structure.rs
5expression: structure 5expression: structure
6--- 6---
7[ 7[
diff --git a/crates/ra_ide_api/src/structure.rs b/crates/ra_ide_api/src/display/structure.rs
index ec2c9bbc6..ec2c9bbc6 100644
--- a/crates/ra_ide_api/src/structure.rs
+++ b/crates/ra_ide_api/src/display/structure.rs
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 660b43cfa..40a2bd148 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -5,7 +5,6 @@ use ra_syntax::{
5 SyntaxNode, 5 SyntaxNode,
6}; 6};
7use test_utils::tested_by; 7use test_utils::tested_by;
8use hir::Resolution;
9 8
10use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; 9use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
11 10
@@ -48,126 +47,70 @@ pub(crate) fn reference_definition(
48) -> ReferenceResult { 47) -> ReferenceResult {
49 use self::ReferenceResult::*; 48 use self::ReferenceResult::*;
50 49
51 let function = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()); 50 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
52
53 if let Some(function) = function {
54 // Check if it is a method
55 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
56 tested_by!(goto_definition_works_for_methods);
57 let infer_result = function.infer(db);
58 let source_map = function.body_source_map(db);
59 let expr = ast::Expr::cast(method_call.syntax()).unwrap();
60 if let Some(func) =
61 source_map.node_expr(expr).and_then(|it| infer_result.method_resolution(it))
62 {
63 return Exact(NavigationTarget::from_function(db, func));
64 };
65 }
66 // It could also be a field access
67 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
68 tested_by!(goto_definition_works_for_fields);
69 let infer_result = function.infer(db);
70 let source_map = function.body_source_map(db);
71 let expr = ast::Expr::cast(field_expr.syntax()).unwrap();
72 if let Some(field) =
73 source_map.node_expr(expr).and_then(|it| infer_result.field_resolution(it))
74 {
75 return Exact(NavigationTarget::from_field(db, field));
76 };
77 }
78 51
79 // It could also be a named field 52 // Special cases:
80 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
81 tested_by!(goto_definition_works_for_named_fields);
82 53
83 let infer_result = function.infer(db); 54 // Check if it is a method
84 let source_map = function.body_source_map(db); 55 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
56 tested_by!(goto_definition_works_for_methods);
57 if let Some(func) = analyzer.resolve_method_call(method_call) {
58 return Exact(NavigationTarget::from_function(db, func));
59 }
60 }
61 // It could also be a field access
62 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
63 tested_by!(goto_definition_works_for_fields);
64 if let Some(field) = analyzer.resolve_field(field_expr) {
65 return Exact(NavigationTarget::from_field(db, field));
66 };
67 }
85 68
86 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); 69 // It could also be a named field
70 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
71 tested_by!(goto_definition_works_for_named_fields);
87 72
88 if let Some(expr) = struct_lit.and_then(|lit| source_map.node_expr(lit.into())) { 73 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
89 let ty = infer_result[expr].clone();
90 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
91 let hir_path = hir::Path::from_name_ref(name_ref);
92 let hir_name = hir_path.as_ident().unwrap();
93 74
94 if let Some(field) = s.field(db, hir_name) { 75 if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) {
95 return Exact(NavigationTarget::from_field(db, field)); 76 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
96 } 77 let hir_path = hir::Path::from_name_ref(name_ref);
78 let hir_name = hir_path.as_ident().unwrap();
79
80 if let Some(field) = s.field(db, hir_name) {
81 return Exact(NavigationTarget::from_field(db, field));
97 } 82 }
98 } 83 }
99 } 84 }
100 } 85 }
101 86
102 // Try name resolution 87 // General case, a path or a local:
103 let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); 88 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
104 if let Some(path) = 89 if let Some(resolved) = analyzer.resolve_path(db, path) {
105 name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast) 90 match resolved {
106 { 91 hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)),
107 let resolved = resolver.resolve_path(db, &path); 92 hir::PathResolution::LocalBinding(pat) => {
108 match resolved.clone().take_types().or_else(|| resolved.take_values()) { 93 let nav = NavigationTarget::from_pat(db, file_id, pat);
109 Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), 94 return Exact(nav);
110 Some(Resolution::LocalBinding(pat)) => {
111 let body = resolver.body().expect("no body for local binding");
112 let source_map = body.owner().body_source_map(db);
113 let ptr = source_map.pat_syntax(pat).expect("pattern not found in syntax mapping");
114 let name =
115 path.as_ident().cloned().expect("local binding from a multi-segment path");
116 let nav = NavigationTarget::from_scope_entry(file_id, name, ptr);
117 return Exact(nav);
118 }
119 Some(Resolution::GenericParam(..)) => {
120 // FIXME: go to the generic param def
121 }
122 Some(Resolution::SelfType(impl_block)) => {
123 let ty = impl_block.target_ty(db);
124
125 if let Some((def_id, _)) = ty.as_adt() {
126 return Exact(NavigationTarget::from_adt_def(db, def_id));
127 } 95 }
128 } 96 hir::PathResolution::GenericParam(..) => {
129 None => { 97 // FIXME: go to the generic param def
130 // If we failed to resolve then check associated items 98 }
131 if let Some(function) = function { 99 hir::PathResolution::SelfType(impl_block) => {
132 // Resolve associated item for path expressions 100 let ty = impl_block.target_ty(db);
133 if let Some(path_expr) =
134 name_ref.syntax().ancestors().find_map(ast::PathExpr::cast)
135 {
136 let infer_result = function.infer(db);
137 let source_map = function.body_source_map(db);
138
139 if let Some(expr) = ast::Expr::cast(path_expr.syntax()) {
140 if let Some(res) = source_map
141 .node_expr(expr)
142 .and_then(|it| infer_result.assoc_resolutions_for_expr(it.into()))
143 {
144 return Exact(NavigationTarget::from_impl_item(db, res));
145 }
146 }
147 }
148 101
149 // Resolve associated item for path patterns 102 if let Some((def_id, _)) = ty.as_adt() {
150 if let Some(path_pat) = 103 return Exact(NavigationTarget::from_adt_def(db, def_id));
151 name_ref.syntax().ancestors().find_map(ast::PathPat::cast)
152 {
153 let infer_result = function.infer(db);
154 let source_map = function.body_source_map(db);
155
156 let pat: &ast::Pat = path_pat.into();
157
158 if let Some(res) = source_map
159 .node_pat(pat)
160 .and_then(|it| infer_result.assoc_resolutions_for_pat(it.into()))
161 {
162 return Exact(NavigationTarget::from_impl_item(db, res));
163 }
164 } 104 }
165 } 105 }
106 hir::PathResolution::AssocItem(assoc) => {
107 return Exact(NavigationTarget::from_impl_item(db, assoc));
108 }
166 } 109 }
167 } 110 }
168 } 111 }
169 112
170 // If that fails try the index based approach. 113 // Fallback index based approach:
171 let navs = crate::symbol_index::index_resolve(db, name_ref) 114 let navs = crate::symbol_index::index_resolve(db, name_ref)
172 .into_iter() 115 .into_iter()
173 .map(NavigationTarget::from_symbol) 116 .map(NavigationTarget::from_symbol)
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index bfa7cd67a..397b56786 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -1,11 +1,11 @@
1use ra_db::SourceDatabase; 1use ra_db::SourceDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, 3 AstNode, ast,
4 algo::{find_covering_element, find_node_at_offset, find_token_at_offset, visit::{visitor, Visitor}}, 4 algo::{find_covering_element, find_node_at_offset, find_token_at_offset},
5}; 5};
6use hir::HirDisplay; 6use hir::HirDisplay;
7 7
8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; 8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, display::{rust_code_markup, doc_text_for}};
9 9
10/// Contains the results when hovering over an item 10/// Contains the results when hovering over an item
11#[derive(Debug, Clone)] 11#[derive(Debug, Clone)]
@@ -132,121 +132,15 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
132 .ancestors() 132 .ancestors()
133 .take_while(|it| it.range() == leaf_node.range()) 133 .take_while(|it| it.range() == leaf_node.range())
134 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?; 134 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?;
135 let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?; 135 let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, node, None);
136 let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?; 136 let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) {
137 let infer = function.infer(db); 137 ty
138 let source_map = function.body_source_map(db); 138 } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
139 if let Some(expr) = ast::Expr::cast(node).and_then(|e| source_map.node_expr(e)) { 139 ty
140 Some(infer[expr].display(db).to_string())
141 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| source_map.node_pat(p)) {
142 Some(infer[pat].display(db).to_string())
143 } else { 140 } else {
144 None 141 return None;
145 } 142 };
146} 143 Some(ty.display(db).to_string())
147
148fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
149 rust_code_markup_with_doc::<_, &str>(val, None)
150}
151
152fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
153where
154 CODE: AsRef<str>,
155 DOC: AsRef<str>,
156{
157 if let Some(doc) = doc {
158 format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
159 } else {
160 format!("```rust\n{}\n```", val.as_ref())
161 }
162}
163
164// FIXME: this should not really use navigation target. Rather, approximately
165// resolved symbol should return a `DefId`.
166fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
167 match (nav.description(db), nav.docs(db)) {
168 (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
169 (None, Some(docs)) => Some(docs),
170 _ => None,
171 }
172}
173
174impl NavigationTarget {
175 fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> {
176 let source_file = db.parse(self.file_id());
177 let source_file = source_file.syntax();
178 let node = source_file
179 .descendants()
180 .find(|node| node.kind() == self.kind() && node.range() == self.full_range())?
181 .to_owned();
182 Some(node)
183 }
184
185 fn docs(&self, db: &RootDatabase) -> Option<String> {
186 let node = self.node(db)?;
187 fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> {
188 node.doc_comment_text()
189 }
190
191 visitor()
192 .visit(doc_comments::<ast::FnDef>)
193 .visit(doc_comments::<ast::StructDef>)
194 .visit(doc_comments::<ast::EnumDef>)
195 .visit(doc_comments::<ast::TraitDef>)
196 .visit(doc_comments::<ast::Module>)
197 .visit(doc_comments::<ast::TypeAliasDef>)
198 .visit(doc_comments::<ast::ConstDef>)
199 .visit(doc_comments::<ast::StaticDef>)
200 .visit(doc_comments::<ast::NamedFieldDef>)
201 .visit(doc_comments::<ast::EnumVariant>)
202 .accept(&node)?
203 }
204
205 /// Get a description of this node.
206 ///
207 /// e.g. `struct Name`, `enum Name`, `fn Name`
208 fn description(&self, db: &RootDatabase) -> Option<String> {
209 // FIXME: After type inference is done, add type information to improve the output
210 let node = self.node(db)?;
211
212 fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String>
213 where
214 T: NameOwner + VisibilityOwner + TypeAscriptionOwner,
215 {
216 let mut string = visit_node(node, prefix)?;
217
218 if let Some(type_ref) = node.ascribed_type() {
219 string.push_str(": ");
220 type_ref.syntax().text().push_to(&mut string);
221 }
222
223 Some(string)
224 }
225
226 fn visit_node<T>(node: &T, label: &str) -> Option<String>
227 where
228 T: NameOwner + VisibilityOwner,
229 {
230 let mut string =
231 node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default();
232 string.push_str(label);
233 string.push_str(node.name()?.text().as_str());
234 Some(string)
235 }
236
237 visitor()
238 .visit(crate::completion::function_label)
239 .visit(|node: &ast::StructDef| visit_node(node, "struct "))
240 .visit(|node: &ast::EnumDef| visit_node(node, "enum "))
241 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
242 .visit(|node: &ast::Module| visit_node(node, "mod "))
243 .visit(|node: &ast::TypeAliasDef| visit_node(node, "type "))
244 .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const "))
245 .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static "))
246 .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, ""))
247 .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string()))
248 .accept(&node)?
249 }
250} 144}
251 145
252#[cfg(test)] 146#[cfg(test)]
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 9063f78a9..d25795adc 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -13,7 +13,6 @@
13mod db; 13mod db;
14pub mod mock_analysis; 14pub mod mock_analysis;
15mod symbol_index; 15mod symbol_index;
16mod navigation_target;
17mod change; 16mod change;
18 17
19mod status; 18mod status;
@@ -34,9 +33,9 @@ mod folding_ranges;
34mod line_index; 33mod line_index;
35mod line_index_utils; 34mod line_index_utils;
36mod join_lines; 35mod join_lines;
37mod structure;
38mod typing; 36mod typing;
39mod matching_brace; 37mod matching_brace;
38mod display;
40 39
41#[cfg(test)] 40#[cfg(test)]
42mod marks; 41mod marks;
@@ -62,7 +61,6 @@ pub use crate::{
62 change::{AnalysisChange, LibraryData}, 61 change::{AnalysisChange, LibraryData},
63 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 62 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
64 runnables::{Runnable, RunnableKind}, 63 runnables::{Runnable, RunnableKind},
65 navigation_target::NavigationTarget,
66 references::ReferenceSearchResult, 64 references::ReferenceSearchResult,
67 assists::{Assist, AssistId}, 65 assists::{Assist, AssistId},
68 hover::{HoverResult}, 66 hover::{HoverResult},
@@ -70,8 +68,8 @@ pub use crate::{
70 line_index_utils::translate_offset_with_edit, 68 line_index_utils::translate_offset_with_edit,
71 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
72 syntax_highlighting::HighlightedRange, 70 syntax_highlighting::HighlightedRange,
73 structure::{StructureNode, file_structure},
74 diagnostics::Severity, 71 diagnostics::Severity,
72 display::{FunctionSignature, NavigationTarget, StructureNode, file_structure},
75}; 73};
76 74
77pub use ra_db::{ 75pub use ra_db::{
@@ -243,9 +241,7 @@ impl<T> RangeInfo<T> {
243 241
244#[derive(Debug)] 242#[derive(Debug)]
245pub struct CallInfo { 243pub struct CallInfo {
246 pub label: String, 244 pub signature: FunctionSignature,
247 pub doc: Option<Documentation>,
248 pub parameters: Vec<String>,
249 pub active_parameter: Option<usize>, 245 pub active_parameter: Option<usize>,
250} 246}
251 247
@@ -387,7 +383,7 @@ impl Analysis {
387 /// file outline. 383 /// file outline.
388 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 384 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
389 let file = self.db.parse(file_id); 385 let file = self.db.parse(file_id);
390 structure::file_structure(&file) 386 file_structure(&file)
391 } 387 }
392 388
393 /// Returns the set of folding ranges. 389 /// Returns the set of folding ranges.
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index 20bbf11a3..9f655d83c 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -1,5 +1,5 @@
1use relative_path::{RelativePath, RelativePathBuf}; 1use relative_path::{RelativePath, RelativePathBuf};
2use hir::{ModuleSource, source_binder}; 2use hir::{ModuleSource, source_binder, Either};
3use ra_db::{SourceDatabase}; 3use ra_db::{SourceDatabase};
4use ra_syntax::{ 4use ra_syntax::{
5 AstNode, SyntaxNode, SourceFile, 5 AstNode, SyntaxNode, SourceFile,
@@ -61,11 +61,10 @@ pub(crate) fn find_all_refs(
61 position: FilePosition, 61 position: FilePosition,
62) -> Option<ReferenceSearchResult> { 62) -> Option<ReferenceSearchResult> {
63 let file = db.parse(position.file_id); 63 let file = db.parse(position.file_id);
64 let (binding, descr) = find_binding(db, &file, position)?; 64 let (binding, analyzer) = find_binding(db, &file, position)?;
65 let declaration = NavigationTarget::from_bind_pat(position.file_id, binding); 65 let declaration = NavigationTarget::from_bind_pat(position.file_id, binding);
66 66
67 let references = descr 67 let references = analyzer
68 .scopes(db)
69 .find_all_refs(binding) 68 .find_all_refs(binding)
70 .into_iter() 69 .into_iter()
71 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) 70 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
@@ -77,21 +76,21 @@ pub(crate) fn find_all_refs(
77 db: &RootDatabase, 76 db: &RootDatabase,
78 source_file: &'a SourceFile, 77 source_file: &'a SourceFile,
79 position: FilePosition, 78 position: FilePosition,
80 ) -> Option<(&'a ast::BindPat, hir::Function)> { 79 ) -> Option<(&'a ast::BindPat, hir::SourceAnalyzer)> {
81 let syntax = source_file.syntax(); 80 let syntax = source_file.syntax();
82 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { 81 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
83 let descr = 82 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
84 source_binder::function_from_child_node(db, position.file_id, binding.syntax())?; 83 return Some((binding, analyzer));
85 return Some((binding, descr));
86 }; 84 };
87 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; 85 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
88 let descr = 86 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
89 source_binder::function_from_child_node(db, position.file_id, name_ref.syntax())?; 87 let resolved = analyzer.resolve_local_name(name_ref)?;
90 let scope = descr.scopes(db); 88 if let Either::A(ptr) = resolved.ptr() {
91 let resolved = scope.resolve_local_name(name_ref)?; 89 if let ast::PatKind::BindPat(binding) = ptr.to_node(source_file).kind() {
92 let resolved = resolved.ptr().to_node(source_file); 90 return Some((binding, analyzer));
93 let binding = find_node_at_offset::<ast::BindPat>(syntax, resolved.range().end())?; 91 }
94 Some((binding, descr)) 92 }
93 None
95 } 94 }
96} 95}
97 96
diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs
index 2395930f0..3969076a8 100644
--- a/crates/ra_ide_api/src/runnables.rs
+++ b/crates/ra_ide_api/src/runnables.rs
@@ -65,7 +65,6 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: &ast::Module) -> Opt
65 let range = module.syntax().range(); 65 let range = module.syntax().range();
66 let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?; 66 let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?;
67 67
68 // FIXME: thread cancellation instead of `.ok`ing
69 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); 68 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
70 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 69 Some(Runnable { range, kind: RunnableKind::TestMod { path } })
71} 70}
diff --git a/crates/ra_ide_api/src/status.rs b/crates/ra_ide_api/src/status.rs
index e0fc1c123..d99a4e750 100644
--- a/crates/ra_ide_api/src/status.rs
+++ b/crates/ra_ide_api/src/status.rs
@@ -23,16 +23,11 @@ pub(crate) fn status(db: &RootDatabase) -> String {
23 let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); 23 let files_stats = db.query(FileTextQuery).entries::<FilesStats>();
24 let syntax_tree_stats = syntax_tree_stats(db); 24 let syntax_tree_stats = syntax_tree_stats(db);
25 let symbols_stats = db.query(LibrarySymbolsQuery).entries::<LibrarySymbolsStats>(); 25 let symbols_stats = db.query(LibrarySymbolsQuery).entries::<LibrarySymbolsStats>();
26 let n_defs = {
27 let interner: &hir::HirInterner = db.as_ref();
28 interner.len()
29 };
30 format!( 26 format!(
31 "{}\n{}\n{}\n{} defs\n\nmemory:\n{}\ngc {:?} seconds ago", 27 "{}\n{}\n{}\n\n\nmemory:\n{}\ngc {:?} seconds ago",
32 files_stats, 28 files_stats,
33 symbols_stats, 29 symbols_stats,
34 syntax_tree_stats, 30 syntax_tree_stats,
35 n_defs,
36 MemoryStats::current(), 31 MemoryStats::current(),
37 db.last_gc.elapsed().as_secs(), 32 db.last_gc.elapsed().as_secs(),
38 ) 33 )
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs
index 0eadc4e71..914d3fc71 100644
--- a/crates/ra_ide_api/src/symbol_index.rs
+++ b/crates/ra_ide_api/src/symbol_index.rs
@@ -275,7 +275,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
275mod tests { 275mod tests {
276 use ra_syntax::SmolStr; 276 use ra_syntax::SmolStr;
277 use crate::{ 277 use crate::{
278 navigation_target::NavigationTarget, 278 display::NavigationTarget,
279 mock_analysis::single_file, 279 mock_analysis::single_file,
280 Query, 280 Query,
281}; 281};