diff options
Diffstat (limited to 'crates/ra_ide_api/src')
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; | |||
2 | use ra_db::SourceDatabase; | 2 | use ra_db::SourceDatabase; |
3 | use ra_syntax::{ | 3 | use 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 | }; |
9 | use hir::Docs; | ||
10 | 8 | ||
11 | use crate::{FilePosition, CallInfo, db::RootDatabase}; | 9 | use 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. |
14 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { | 12 | pub(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 | ||
77 | impl<'a> FnCallNode<'a> { | 82 | impl<'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 | ||
109 | impl CallInfo { | 114 | impl 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 | ||
118 | fn 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} |
151 | fn bar() { foo(<|>3, ); }"#, | 154 | fn 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, ); }"#, | |||
162 | fn bar() { foo(3, <|>); }"#, | 165 | fn 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, <|>); }"#, | |||
173 | fn bar() { foo(<|>); }"#, | 176 | fn 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} | ||
187 | fn bar() { foo(<|>3, ); }"#, | ||
188 | ); | ||
189 | |||
190 | assert_eq!(info.parameters(), ["x: T", "y: U"]); | ||
191 | assert_eq!( | ||
192 | info.label(), | ||
193 | r#" | ||
194 | fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 | ||
195 | where 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 {} | ||
207 | fn bar() { foo(<|>); }"#, | ||
208 | ); | ||
209 | |||
210 | assert!(info.parameters().is_empty()); | ||
211 | assert_eq!( | ||
212 | info.label(), | ||
213 | r#" | ||
214 | fn foo<T>() -> T | ||
215 | where 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{}} } |
184 | fn bar() {let _ : F = F::new(<|>);}"#, | 226 | fn 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#" |
349 | pub trait WriteHandler<E> | 391 | struct WriteHandler<E>; |
350 | where | 392 | |
351 | Self: Actor, | 393 | impl<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 | ||
370 | pub fn foo() { | 410 | pub 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; | |||
13 | mod complete_postfix; | 13 | mod complete_postfix; |
14 | 14 | ||
15 | use ra_db::SourceDatabase; | 15 | use ra_db::SourceDatabase; |
16 | use ra_syntax::{ast::{self, AstNode}, SyntaxKind::{ATTR, COMMENT}}; | ||
17 | 16 | ||
18 | use crate::{ | 17 | use 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 | |||
74 | pub 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 | |||
92 | pub 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 | |||
103 | pub 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). |
6 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(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)] |
57 | mod tests { | 50 | mod 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)] |
56 | mod tests { | 56 | mod 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 @@ | |||
1 | use hir::Resolution; | 1 | use hir::{Resolution, Either}; |
2 | use ra_syntax::AstNode; | 2 | use ra_syntax::AstNode; |
3 | use test_utils::tested_by; | 3 | use 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. |
6 | pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(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 | }; |
8 | use hir::{source_binder, Resolver}; | 8 | use hir::source_binder; |
9 | 9 | ||
10 | use crate::{db, FilePosition}; | 10 | use crate::{db, FilePosition}; |
11 | 11 | ||
@@ -14,11 +14,10 @@ use crate::{db, FilePosition}; | |||
14 | #[derive(Debug)] | 14 | #[derive(Debug)] |
15 | pub(crate) struct CompletionContext<'a> { | 15 | pub(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 | ||
7 | use crate::completion::{ | 7 | use crate::completion::{ |
8 | Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem, | 8 | Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem, |
9 | }; | ||
10 | |||
11 | use 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 | --- |
2 | created: "2019-02-18T09:22:24.188564584Z" | 2 | created: "2019-04-04T14:52:24.531844100Z" |
3 | creator: insta@0.6.2 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.941335305Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.939676100Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-01-22T14:45:00.552379600+00:00" | ||
3 | creator: [email protected] | ||
4 | expression: kind_completions | ||
5 | source: "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 | --- | ||
2 | created: "2019-01-22T14:45:00.552379600+00:00" | ||
3 | creator: [email protected] | ||
4 | expression: kind_completions | ||
5 | source: "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 | --- | ||
2 | created: "2019-02-18T09:22:23.949634602Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.949634355Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.974417169Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- |
2 | created: "2019-02-18T09:22:24.182964414Z" | 2 | created: "2019-04-04T14:52:24.525395600Z" |
3 | creator: insta@0.6.2 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.939645902Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.940872916Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.940872918Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-02-18T09:22:23.939710971Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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 | --- | ||
2 | created: "2019-04-05T23:00:18.283812700Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: 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)] |
21 | pub(crate) struct RootDatabase { | 21 | pub(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 | ||
63 | impl 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)] |
70 | pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { | 61 | pub(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 | |||
4 | mod function_signature; | ||
5 | mod navigation_target; | ||
6 | mod structure; | ||
7 | |||
8 | use crate::db::RootDatabase; | ||
9 | use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}}; | ||
10 | |||
11 | pub use navigation_target::NavigationTarget; | ||
12 | pub use structure::{StructureNode, file_structure}; | ||
13 | pub use function_signature::FunctionSignature; | ||
14 | |||
15 | pub(crate) fn function_label(node: &ast::FnDef) -> String { | ||
16 | FunctionSignature::from(node).to_string() | ||
17 | } | ||
18 | |||
19 | pub(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 | |||
30 | pub(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 | |||
41 | pub(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 | |||
50 | pub(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 | |||
58 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
59 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
60 | } | ||
61 | |||
62 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
63 | where | ||
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`. | ||
76 | pub(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 @@ | |||
1 | use super::{where_predicates, generic_parameters}; | ||
2 | use crate::db; | ||
3 | use std::fmt::{self, Display}; | ||
4 | use join_to_string::join; | ||
5 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | ||
6 | use std::convert::From; | ||
7 | use hir::{Docs, Documentation}; | ||
8 | |||
9 | /// Contains information about a function signature | ||
10 | #[derive(Debug)] | ||
11 | pub 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 | |||
28 | impl 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 | |||
41 | impl 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 | |||
71 | impl 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 @@ | |||
1 | use ra_db::FileId; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use 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 | }; |
6 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; | 8 | use hir::{ModuleSource, FieldSource, ImplItem, Either}; |
7 | 9 | ||
8 | use crate::{FileSymbol, db::RootDatabase}; | 10 | use 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 | --- |
2 | created: "2019-02-05T22:03:50.763530100Z" | 2 | created: "2019-04-08T09:44:50.196004400Z" |
3 | creator: insta@0.6.1 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/structure.rs | 4 | source: crates/ra_ide_api/src/display/structure.rs |
5 | expression: structure | 5 | expression: 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 | }; |
7 | use test_utils::tested_by; | 7 | use test_utils::tested_by; |
8 | use hir::Resolution; | ||
9 | 8 | ||
10 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | 9 | use 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 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::SourceDatabase; |
2 | use ra_syntax::{ | 2 | use 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 | }; |
6 | use hir::HirDisplay; | 6 | use hir::HirDisplay; |
7 | 7 | ||
8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | 8 | use 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 | |||
148 | fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
149 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
150 | } | ||
151 | |||
152 | fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
153 | where | ||
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`. | ||
166 | fn 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 | |||
174 | impl 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 @@ | |||
13 | mod db; | 13 | mod db; |
14 | pub mod mock_analysis; | 14 | pub mod mock_analysis; |
15 | mod symbol_index; | 15 | mod symbol_index; |
16 | mod navigation_target; | ||
17 | mod change; | 16 | mod change; |
18 | 17 | ||
19 | mod status; | 18 | mod status; |
@@ -34,9 +33,9 @@ mod folding_ranges; | |||
34 | mod line_index; | 33 | mod line_index; |
35 | mod line_index_utils; | 34 | mod line_index_utils; |
36 | mod join_lines; | 35 | mod join_lines; |
37 | mod structure; | ||
38 | mod typing; | 36 | mod typing; |
39 | mod matching_brace; | 37 | mod matching_brace; |
38 | mod display; | ||
40 | 39 | ||
41 | #[cfg(test)] | 40 | #[cfg(test)] |
42 | mod marks; | 41 | mod 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 | ||
77 | pub use ra_db::{ | 75 | pub use ra_db::{ |
@@ -243,9 +241,7 @@ impl<T> RangeInfo<T> { | |||
243 | 241 | ||
244 | #[derive(Debug)] | 242 | #[derive(Debug)] |
245 | pub struct CallInfo { | 243 | pub 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 @@ | |||
1 | use relative_path::{RelativePath, RelativePathBuf}; | 1 | use relative_path::{RelativePath, RelativePathBuf}; |
2 | use hir::{ModuleSource, source_binder}; | 2 | use hir::{ModuleSource, source_binder, Either}; |
3 | use ra_db::{SourceDatabase}; | 3 | use ra_db::{SourceDatabase}; |
4 | use ra_syntax::{ | 4 | use 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> { | |||
275 | mod tests { | 275 | mod 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 | }; |