diff options
-rw-r--r-- | crates/ra_analysis/src/call_info.rs | 451 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 114 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 21 | ||||
-rw-r--r-- | crates/ra_analysis/tests/test/main.rs | 261 | ||||
-rw-r--r-- | crates/ra_hir/src/function.rs | 127 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 28 |
7 files changed, 482 insertions, 522 deletions
diff --git a/crates/ra_analysis/src/call_info.rs b/crates/ra_analysis/src/call_info.rs new file mode 100644 index 000000000..1dac95584 --- /dev/null +++ b/crates/ra_analysis/src/call_info.rs | |||
@@ -0,0 +1,451 @@ | |||
1 | use std::cmp::{max, min}; | ||
2 | |||
3 | use ra_db::{SyntaxDatabase, Cancelable}; | ||
4 | use ra_syntax::{ | ||
5 | AstNode, SyntaxNode, TextUnit, TextRange, | ||
6 | SyntaxKind::FN_DEF, | ||
7 | ast::{self, ArgListOwner, DocCommentsOwner}, | ||
8 | }; | ||
9 | use ra_editor::find_node_at_offset; | ||
10 | |||
11 | use crate::{FilePosition, CallInfo, db::RootDatabase}; | ||
12 | |||
13 | /// Computes parameter information for the given call expression. | ||
14 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> { | ||
15 | let file = db.source_file(position.file_id); | ||
16 | let syntax = file.syntax(); | ||
17 | |||
18 | // Find the calling expression and it's NameRef | ||
19 | let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset)); | ||
20 | let name_ref = ctry!(calling_node.name_ref()); | ||
21 | |||
22 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | ||
23 | let file_symbols = db.index_resolve(name_ref)?; | ||
24 | let symbol = ctry!(file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF)); | ||
25 | let fn_file = db.source_file(symbol.file_id); | ||
26 | let fn_def = symbol.ptr.resolve(&fn_file); | ||
27 | let fn_def = ast::FnDef::cast(&fn_def).unwrap(); | ||
28 | let mut call_info = ctry!(CallInfo::new(fn_def)); | ||
29 | // If we have a calling expression let's find which argument we are on | ||
30 | let num_params = call_info.parameters.len(); | ||
31 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); | ||
32 | |||
33 | if num_params == 1 { | ||
34 | if !has_self { | ||
35 | call_info.active_parameter = Some(0); | ||
36 | } | ||
37 | } else if num_params > 1 { | ||
38 | // Count how many parameters into the call we are. | ||
39 | // TODO: This is best effort for now and should be fixed at some point. | ||
40 | // It may be better to see where we are in the arg_list and then check | ||
41 | // where offset is in that list (or beyond). | ||
42 | // Revisit this after we get documentation comments in. | ||
43 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
44 | let start = arg_list.syntax().range().start(); | ||
45 | |||
46 | let range_search = TextRange::from_to(start, position.offset); | ||
47 | let mut commas: usize = arg_list | ||
48 | .syntax() | ||
49 | .text() | ||
50 | .slice(range_search) | ||
51 | .to_string() | ||
52 | .matches(',') | ||
53 | .count(); | ||
54 | |||
55 | // If we have a method call eat the first param since it's just self. | ||
56 | if has_self { | ||
57 | commas += 1; | ||
58 | } | ||
59 | |||
60 | call_info.active_parameter = Some(commas); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | Ok(Some(call_info)) | ||
65 | } | ||
66 | |||
67 | enum FnCallNode<'a> { | ||
68 | CallExpr(&'a ast::CallExpr), | ||
69 | MethodCallExpr(&'a ast::MethodCallExpr), | ||
70 | } | ||
71 | |||
72 | impl<'a> FnCallNode<'a> { | ||
73 | pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> { | ||
74 | if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) { | ||
75 | return Some(FnCallNode::CallExpr(expr)); | ||
76 | } | ||
77 | if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) { | ||
78 | return Some(FnCallNode::MethodCallExpr(expr)); | ||
79 | } | ||
80 | None | ||
81 | } | ||
82 | |||
83 | pub fn name_ref(&self) -> Option<&'a ast::NameRef> { | ||
84 | match *self { | ||
85 | FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() { | ||
86 | ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, | ||
87 | _ => return None, | ||
88 | }), | ||
89 | |||
90 | FnCallNode::MethodCallExpr(call_expr) => call_expr | ||
91 | .syntax() | ||
92 | .children() | ||
93 | .filter_map(ast::NameRef::cast) | ||
94 | .nth(0), | ||
95 | } | ||
96 | } | ||
97 | |||
98 | pub fn arg_list(&self) -> Option<&'a ast::ArgList> { | ||
99 | match *self { | ||
100 | FnCallNode::CallExpr(expr) => expr.arg_list(), | ||
101 | FnCallNode::MethodCallExpr(expr) => expr.arg_list(), | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | impl CallInfo { | ||
107 | fn new(node: &ast::FnDef) -> Option<Self> { | ||
108 | let mut doc = None; | ||
109 | |||
110 | // Strip the body out for the label. | ||
111 | let mut label: String = if let Some(body) = node.body() { | ||
112 | let body_range = body.syntax().range(); | ||
113 | let label: String = node | ||
114 | .syntax() | ||
115 | .children() | ||
116 | .filter(|child| !child.range().is_subrange(&body_range)) | ||
117 | .map(|node| node.text().to_string()) | ||
118 | .collect(); | ||
119 | label | ||
120 | } else { | ||
121 | node.syntax().text().to_string() | ||
122 | }; | ||
123 | |||
124 | if let Some((comment_range, docs)) = extract_doc_comments(node) { | ||
125 | let comment_range = comment_range | ||
126 | .checked_sub(node.syntax().range().start()) | ||
127 | .unwrap(); | ||
128 | let start = comment_range.start().to_usize(); | ||
129 | let end = comment_range.end().to_usize(); | ||
130 | |||
131 | // Remove the comment from the label | ||
132 | label.replace_range(start..end, ""); | ||
133 | |||
134 | // Massage markdown | ||
135 | let mut processed_lines = Vec::new(); | ||
136 | let mut in_code_block = false; | ||
137 | for line in docs.lines() { | ||
138 | if line.starts_with("```") { | ||
139 | in_code_block = !in_code_block; | ||
140 | } | ||
141 | |||
142 | let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { | ||
143 | "```rust".into() | ||
144 | } else { | ||
145 | line.to_string() | ||
146 | }; | ||
147 | |||
148 | processed_lines.push(line); | ||
149 | } | ||
150 | |||
151 | if !processed_lines.is_empty() { | ||
152 | doc = Some(processed_lines.join("\n")); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | Some(CallInfo { | ||
157 | parameters: param_list(node), | ||
158 | label: label.trim().to_owned(), | ||
159 | doc, | ||
160 | active_parameter: None, | ||
161 | }) | ||
162 | } | ||
163 | } | ||
164 | |||
165 | fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> { | ||
166 | if node.doc_comments().count() == 0 { | ||
167 | return None; | ||
168 | } | ||
169 | |||
170 | let comment_text = node.doc_comment_text(); | ||
171 | |||
172 | let (begin, end) = node | ||
173 | .doc_comments() | ||
174 | .map(|comment| comment.syntax().range()) | ||
175 | .map(|range| (range.start().to_usize(), range.end().to_usize())) | ||
176 | .fold((std::usize::MAX, std::usize::MIN), |acc, range| { | ||
177 | (min(acc.0, range.0), max(acc.1, range.1)) | ||
178 | }); | ||
179 | |||
180 | let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end)); | ||
181 | |||
182 | Some((range, comment_text)) | ||
183 | } | ||
184 | |||
185 | fn param_list(node: &ast::FnDef) -> Vec<String> { | ||
186 | let mut res = vec![]; | ||
187 | if let Some(param_list) = node.param_list() { | ||
188 | if let Some(self_param) = param_list.self_param() { | ||
189 | res.push(self_param.syntax().text().to_string()) | ||
190 | } | ||
191 | |||
192 | // Maybe use param.pat here? See if we can just extract the name? | ||
193 | //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); | ||
194 | res.extend( | ||
195 | param_list | ||
196 | .params() | ||
197 | .filter_map(|p| p.pat()) | ||
198 | .map(|pat| pat.syntax().text().to_string()), | ||
199 | ); | ||
200 | } | ||
201 | res | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use super::*; | ||
207 | |||
208 | use crate::mock_analysis::single_file_with_position; | ||
209 | |||
210 | fn call_info(text: &str) -> CallInfo { | ||
211 | let (analysis, position) = single_file_with_position(text); | ||
212 | analysis.call_info(position).unwrap().unwrap() | ||
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn test_fn_signature_two_args_first() { | ||
217 | let info = call_info( | ||
218 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | ||
219 | fn bar() { foo(<|>3, ); }"#, | ||
220 | ); | ||
221 | |||
222 | assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); | ||
223 | assert_eq!(info.active_parameter, Some(0)); | ||
224 | } | ||
225 | |||
226 | #[test] | ||
227 | fn test_fn_signature_two_args_second() { | ||
228 | let info = call_info( | ||
229 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | ||
230 | fn bar() { foo(3, <|>); }"#, | ||
231 | ); | ||
232 | |||
233 | assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); | ||
234 | assert_eq!(info.active_parameter, Some(1)); | ||
235 | } | ||
236 | |||
237 | #[test] | ||
238 | fn test_fn_signature_for_impl() { | ||
239 | let info = call_info( | ||
240 | r#"struct F; impl F { pub fn new() { F{}} } | ||
241 | fn bar() {let _ : F = F::new(<|>);}"#, | ||
242 | ); | ||
243 | |||
244 | assert_eq!(info.parameters, Vec::<String>::new()); | ||
245 | assert_eq!(info.active_parameter, None); | ||
246 | } | ||
247 | |||
248 | #[test] | ||
249 | fn test_fn_signature_for_method_self() { | ||
250 | let info = call_info( | ||
251 | r#"struct F; | ||
252 | impl F { | ||
253 | pub fn new() -> F{ | ||
254 | F{} | ||
255 | } | ||
256 | |||
257 | pub fn do_it(&self) {} | ||
258 | } | ||
259 | |||
260 | fn bar() { | ||
261 | let f : F = F::new(); | ||
262 | f.do_it(<|>); | ||
263 | }"#, | ||
264 | ); | ||
265 | |||
266 | assert_eq!(info.parameters, vec!["&self".to_string()]); | ||
267 | assert_eq!(info.active_parameter, None); | ||
268 | } | ||
269 | |||
270 | #[test] | ||
271 | fn test_fn_signature_for_method_with_arg() { | ||
272 | let info = call_info( | ||
273 | r#"struct F; | ||
274 | impl F { | ||
275 | pub fn new() -> F{ | ||
276 | F{} | ||
277 | } | ||
278 | |||
279 | pub fn do_it(&self, x: i32) {} | ||
280 | } | ||
281 | |||
282 | fn bar() { | ||
283 | let f : F = F::new(); | ||
284 | f.do_it(<|>); | ||
285 | }"#, | ||
286 | ); | ||
287 | |||
288 | assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]); | ||
289 | assert_eq!(info.active_parameter, Some(1)); | ||
290 | } | ||
291 | |||
292 | #[test] | ||
293 | fn test_fn_signature_with_docs_simple() { | ||
294 | let info = call_info( | ||
295 | r#" | ||
296 | /// test | ||
297 | // non-doc-comment | ||
298 | fn foo(j: u32) -> u32 { | ||
299 | j | ||
300 | } | ||
301 | |||
302 | fn bar() { | ||
303 | let _ = foo(<|>); | ||
304 | } | ||
305 | "#, | ||
306 | ); | ||
307 | |||
308 | assert_eq!(info.parameters, vec!["j".to_string()]); | ||
309 | assert_eq!(info.active_parameter, Some(0)); | ||
310 | assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); | ||
311 | assert_eq!(info.doc, Some("test".into())); | ||
312 | } | ||
313 | |||
314 | #[test] | ||
315 | fn test_fn_signature_with_docs() { | ||
316 | let info = call_info( | ||
317 | r#" | ||
318 | /// Adds one to the number given. | ||
319 | /// | ||
320 | /// # Examples | ||
321 | /// | ||
322 | /// ``` | ||
323 | /// let five = 5; | ||
324 | /// | ||
325 | /// assert_eq!(6, my_crate::add_one(5)); | ||
326 | /// ``` | ||
327 | pub fn add_one(x: i32) -> i32 { | ||
328 | x + 1 | ||
329 | } | ||
330 | |||
331 | pub fn do() { | ||
332 | add_one(<|> | ||
333 | }"#, | ||
334 | ); | ||
335 | |||
336 | assert_eq!(info.parameters, vec!["x".to_string()]); | ||
337 | assert_eq!(info.active_parameter, Some(0)); | ||
338 | assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); | ||
339 | assert_eq!( | ||
340 | info.doc, | ||
341 | Some( | ||
342 | r#"Adds one to the number given. | ||
343 | |||
344 | # Examples | ||
345 | |||
346 | ```rust | ||
347 | let five = 5; | ||
348 | |||
349 | assert_eq!(6, my_crate::add_one(5)); | ||
350 | ```"# | ||
351 | .into() | ||
352 | ) | ||
353 | ); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn test_fn_signature_with_docs_impl() { | ||
358 | let info = call_info( | ||
359 | r#" | ||
360 | struct addr; | ||
361 | impl addr { | ||
362 | /// Adds one to the number given. | ||
363 | /// | ||
364 | /// # Examples | ||
365 | /// | ||
366 | /// ``` | ||
367 | /// let five = 5; | ||
368 | /// | ||
369 | /// assert_eq!(6, my_crate::add_one(5)); | ||
370 | /// ``` | ||
371 | pub fn add_one(x: i32) -> i32 { | ||
372 | x + 1 | ||
373 | } | ||
374 | } | ||
375 | |||
376 | pub fn do_it() { | ||
377 | addr {}; | ||
378 | addr::add_one(<|>); | ||
379 | }"#, | ||
380 | ); | ||
381 | |||
382 | assert_eq!(info.parameters, vec!["x".to_string()]); | ||
383 | assert_eq!(info.active_parameter, Some(0)); | ||
384 | assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); | ||
385 | assert_eq!( | ||
386 | info.doc, | ||
387 | Some( | ||
388 | r#"Adds one to the number given. | ||
389 | |||
390 | # Examples | ||
391 | |||
392 | ```rust | ||
393 | let five = 5; | ||
394 | |||
395 | assert_eq!(6, my_crate::add_one(5)); | ||
396 | ```"# | ||
397 | .into() | ||
398 | ) | ||
399 | ); | ||
400 | } | ||
401 | |||
402 | #[test] | ||
403 | fn test_fn_signature_with_docs_from_actix() { | ||
404 | let info = call_info( | ||
405 | r#" | ||
406 | pub trait WriteHandler<E> | ||
407 | where | ||
408 | Self: Actor, | ||
409 | Self::Context: ActorContext, | ||
410 | { | ||
411 | /// Method is called when writer emits error. | ||
412 | /// | ||
413 | /// If this method returns `ErrorAction::Continue` writer processing | ||
414 | /// continues otherwise stream processing stops. | ||
415 | fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running { | ||
416 | Running::Stop | ||
417 | } | ||
418 | |||
419 | /// Method is called when writer finishes. | ||
420 | /// | ||
421 | /// By default this method stops actor's `Context`. | ||
422 | fn finished(&mut self, ctx: &mut Self::Context) { | ||
423 | ctx.stop() | ||
424 | } | ||
425 | } | ||
426 | |||
427 | pub fn foo() { | ||
428 | WriteHandler r; | ||
429 | r.finished(<|>); | ||
430 | } | ||
431 | |||
432 | "#, | ||
433 | ); | ||
434 | |||
435 | assert_eq!( | ||
436 | info.parameters, | ||
437 | vec!["&mut self".to_string(), "ctx".to_string()] | ||
438 | ); | ||
439 | assert_eq!(info.active_parameter, Some(1)); | ||
440 | assert_eq!( | ||
441 | info.doc, | ||
442 | Some( | ||
443 | r#"Method is called when writer finishes. | ||
444 | |||
445 | By default this method stops actor's `Context`."# | ||
446 | .into() | ||
447 | ) | ||
448 | ); | ||
449 | } | ||
450 | |||
451 | } | ||
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 98554dd4c..b3f75fdbe 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -3,13 +3,13 @@ use std::sync::Arc; | |||
3 | use salsa::Database; | 3 | use salsa::Database; |
4 | 4 | ||
5 | use hir::{ | 5 | use hir::{ |
6 | self, FnSignatureInfo, Problem, source_binder, | 6 | self, Problem, source_binder, |
7 | }; | 7 | }; |
8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
9 | use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity}; | 9 | use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity}; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | SyntaxNode, TextRange, TextUnit, AstNode, SourceFile, | 11 | TextRange, AstNode, SourceFile, |
12 | ast::{self, ArgListOwner, NameOwner}, | 12 | ast::{self, NameOwner}, |
13 | SyntaxKind::*, | 13 | SyntaxKind::*, |
14 | }; | 14 | }; |
15 | 15 | ||
@@ -262,75 +262,6 @@ impl db::RootDatabase { | |||
262 | .collect() | 262 | .collect() |
263 | } | 263 | } |
264 | 264 | ||
265 | pub(crate) fn resolve_callable( | ||
266 | &self, | ||
267 | position: FilePosition, | ||
268 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { | ||
269 | let file = self.source_file(position.file_id); | ||
270 | let syntax = file.syntax(); | ||
271 | |||
272 | // Find the calling expression and it's NameRef | ||
273 | let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset)); | ||
274 | let name_ref = ctry!(calling_node.name_ref()); | ||
275 | |||
276 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | ||
277 | let file_symbols = self.index_resolve(name_ref)?; | ||
278 | for symbol in file_symbols { | ||
279 | if symbol.ptr.kind() == FN_DEF { | ||
280 | let fn_file = self.source_file(symbol.file_id); | ||
281 | let fn_def = symbol.ptr.resolve(&fn_file); | ||
282 | let fn_def = ast::FnDef::cast(&fn_def).unwrap(); | ||
283 | let descr = ctry!(source_binder::function_from_source( | ||
284 | self, | ||
285 | symbol.file_id, | ||
286 | fn_def | ||
287 | )?); | ||
288 | if let Some(descriptor) = descr.signature_info(self) { | ||
289 | // If we have a calling expression let's find which argument we are on | ||
290 | let mut current_parameter = None; | ||
291 | |||
292 | let num_params = descriptor.params.len(); | ||
293 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); | ||
294 | |||
295 | if num_params == 1 { | ||
296 | if !has_self { | ||
297 | current_parameter = Some(0); | ||
298 | } | ||
299 | } else if num_params > 1 { | ||
300 | // Count how many parameters into the call we are. | ||
301 | // TODO: This is best effort for now and should be fixed at some point. | ||
302 | // It may be better to see where we are in the arg_list and then check | ||
303 | // where offset is in that list (or beyond). | ||
304 | // Revisit this after we get documentation comments in. | ||
305 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
306 | let start = arg_list.syntax().range().start(); | ||
307 | |||
308 | let range_search = TextRange::from_to(start, position.offset); | ||
309 | let mut commas: usize = arg_list | ||
310 | .syntax() | ||
311 | .text() | ||
312 | .slice(range_search) | ||
313 | .to_string() | ||
314 | .matches(',') | ||
315 | .count(); | ||
316 | |||
317 | // If we have a method call eat the first param since it's just self. | ||
318 | if has_self { | ||
319 | commas += 1; | ||
320 | } | ||
321 | |||
322 | current_parameter = Some(commas); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | return Ok(Some((descriptor, current_parameter))); | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | Ok(None) | ||
332 | } | ||
333 | |||
334 | pub(crate) fn rename( | 265 | pub(crate) fn rename( |
335 | &self, | 266 | &self, |
336 | position: FilePosition, | 267 | position: FilePosition, |
@@ -375,42 +306,3 @@ impl SourceChange { | |||
375 | } | 306 | } |
376 | } | 307 | } |
377 | } | 308 | } |
378 | |||
379 | enum FnCallNode<'a> { | ||
380 | CallExpr(&'a ast::CallExpr), | ||
381 | MethodCallExpr(&'a ast::MethodCallExpr), | ||
382 | } | ||
383 | |||
384 | impl<'a> FnCallNode<'a> { | ||
385 | pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> { | ||
386 | if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) { | ||
387 | return Some(FnCallNode::CallExpr(expr)); | ||
388 | } | ||
389 | if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) { | ||
390 | return Some(FnCallNode::MethodCallExpr(expr)); | ||
391 | } | ||
392 | None | ||
393 | } | ||
394 | |||
395 | pub fn name_ref(&self) -> Option<&'a ast::NameRef> { | ||
396 | match *self { | ||
397 | FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() { | ||
398 | ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, | ||
399 | _ => return None, | ||
400 | }), | ||
401 | |||
402 | FnCallNode::MethodCallExpr(call_expr) => call_expr | ||
403 | .syntax() | ||
404 | .children() | ||
405 | .filter_map(ast::NameRef::cast) | ||
406 | .nth(0), | ||
407 | } | ||
408 | } | ||
409 | |||
410 | pub fn arg_list(&self) -> Option<&'a ast::ArgList> { | ||
411 | match *self { | ||
412 | FnCallNode::CallExpr(expr) => expr.arg_list(), | ||
413 | FnCallNode::MethodCallExpr(expr) => expr.arg_list(), | ||
414 | } | ||
415 | } | ||
416 | } | ||
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index ec400ffe2..771a349c8 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -22,6 +22,7 @@ mod symbol_index; | |||
22 | 22 | ||
23 | mod extend_selection; | 23 | mod extend_selection; |
24 | mod hover; | 24 | mod hover; |
25 | mod call_info; | ||
25 | mod syntax_highlighting; | 26 | mod syntax_highlighting; |
26 | 27 | ||
27 | use std::{fmt, sync::Arc}; | 28 | use std::{fmt, sync::Arc}; |
@@ -39,7 +40,6 @@ pub use crate::{ | |||
39 | completion::{CompletionItem, CompletionItemKind, InsertText}, | 40 | completion::{CompletionItem, CompletionItemKind, InsertText}, |
40 | runnables::{Runnable, RunnableKind}, | 41 | runnables::{Runnable, RunnableKind}, |
41 | }; | 42 | }; |
42 | pub use hir::FnSignatureInfo; | ||
43 | pub use ra_editor::{Fold, FoldKind, HighlightedRange, LineIndex, Severity, StructureNode}; | 43 | pub use ra_editor::{Fold, FoldKind, HighlightedRange, LineIndex, Severity, StructureNode}; |
44 | 44 | ||
45 | pub use ra_db::{ | 45 | pub use ra_db::{ |
@@ -272,6 +272,14 @@ impl<T> RangeInfo<T> { | |||
272 | } | 272 | } |
273 | } | 273 | } |
274 | 274 | ||
275 | #[derive(Debug)] | ||
276 | pub struct CallInfo { | ||
277 | pub label: String, | ||
278 | pub doc: Option<String>, | ||
279 | pub parameters: Vec<String>, | ||
280 | pub active_parameter: Option<usize>, | ||
281 | } | ||
282 | |||
275 | /// `AnalysisHost` stores the current state of the world. | 283 | /// `AnalysisHost` stores the current state of the world. |
276 | #[derive(Debug, Default)] | 284 | #[derive(Debug, Default)] |
277 | pub struct AnalysisHost { | 285 | pub struct AnalysisHost { |
@@ -391,6 +399,10 @@ impl Analysis { | |||
391 | pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> { | 399 | pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> { |
392 | hover::hover(&*self.db, position) | 400 | hover::hover(&*self.db, position) |
393 | } | 401 | } |
402 | /// Computes parameter information for the given call expression. | ||
403 | pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> { | ||
404 | call_info::call_info(&*self.db, position) | ||
405 | } | ||
394 | /// Returns a `mod name;` declaration which created the current module. | 406 | /// Returns a `mod name;` declaration which created the current module. |
395 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { | 407 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { |
396 | self.db.parent_module(position) | 408 | self.db.parent_module(position) |
@@ -425,13 +437,6 @@ impl Analysis { | |||
425 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { | 437 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { |
426 | self.db.diagnostics(file_id) | 438 | self.db.diagnostics(file_id) |
427 | } | 439 | } |
428 | /// Computes parameter information for the given call expression. | ||
429 | pub fn resolve_callable( | ||
430 | &self, | ||
431 | position: FilePosition, | ||
432 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { | ||
433 | self.db.resolve_callable(position) | ||
434 | } | ||
435 | /// Computes the type of the expression at the given position. | 440 | /// Computes the type of the expression at the given position. |
436 | pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { | 441 | pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { |
437 | hover::type_of(&*self.db, frange) | 442 | hover::type_of(&*self.db, frange) |
diff --git a/crates/ra_analysis/tests/test/main.rs b/crates/ra_analysis/tests/test/main.rs index 1f70af12a..2c0735cb5 100644 --- a/crates/ra_analysis/tests/test/main.rs +++ b/crates/ra_analysis/tests/test/main.rs | |||
@@ -5,14 +5,9 @@ use test_utils::{assert_eq_dbg, assert_eq_text}; | |||
5 | 5 | ||
6 | use ra_analysis::{ | 6 | use ra_analysis::{ |
7 | mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, | 7 | mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, |
8 | AnalysisChange, CrateGraph, FileId, FnSignatureInfo, Query | 8 | AnalysisChange, CrateGraph, FileId, Query |
9 | }; | 9 | }; |
10 | 10 | ||
11 | fn get_signature(text: &str) -> (FnSignatureInfo, Option<usize>) { | ||
12 | let (analysis, position) = single_file_with_position(text); | ||
13 | analysis.resolve_callable(position).unwrap().unwrap() | ||
14 | } | ||
15 | |||
16 | #[test] | 11 | #[test] |
17 | fn test_unresolved_module_diagnostic() { | 12 | fn test_unresolved_module_diagnostic() { |
18 | let (analysis, file_id) = single_file("mod foo;"); | 13 | let (analysis, file_id) = single_file("mod foo;"); |
@@ -99,260 +94,6 @@ fn test_resolve_crate_root() { | |||
99 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); | 94 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); |
100 | } | 95 | } |
101 | 96 | ||
102 | #[test] | ||
103 | fn test_fn_signature_two_args_first() { | ||
104 | let (desc, param) = get_signature( | ||
105 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | ||
106 | fn bar() { foo(<|>3, ); }"#, | ||
107 | ); | ||
108 | |||
109 | assert_eq!(desc.name, "foo".to_string()); | ||
110 | assert_eq!(desc.params, vec!("x".to_string(), "y".to_string())); | ||
111 | assert_eq!(desc.ret_type, Some("-> u32".into())); | ||
112 | assert_eq!(param, Some(0)); | ||
113 | } | ||
114 | |||
115 | #[test] | ||
116 | fn test_fn_signature_two_args_second() { | ||
117 | let (desc, param) = get_signature( | ||
118 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | ||
119 | fn bar() { foo(3, <|>); }"#, | ||
120 | ); | ||
121 | |||
122 | assert_eq!(desc.name, "foo".to_string()); | ||
123 | assert_eq!(desc.params, vec!("x".to_string(), "y".to_string())); | ||
124 | assert_eq!(desc.ret_type, Some("-> u32".into())); | ||
125 | assert_eq!(param, Some(1)); | ||
126 | } | ||
127 | |||
128 | #[test] | ||
129 | fn test_fn_signature_for_impl() { | ||
130 | let (desc, param) = get_signature( | ||
131 | r#"struct F; impl F { pub fn new() { F{}} } | ||
132 | fn bar() {let _ : F = F::new(<|>);}"#, | ||
133 | ); | ||
134 | |||
135 | assert_eq!(desc.name, "new".to_string()); | ||
136 | assert_eq!(desc.params, Vec::<String>::new()); | ||
137 | assert_eq!(desc.ret_type, None); | ||
138 | assert_eq!(param, None); | ||
139 | } | ||
140 | |||
141 | #[test] | ||
142 | fn test_fn_signature_for_method_self() { | ||
143 | let (desc, param) = get_signature( | ||
144 | r#"struct F; | ||
145 | impl F { | ||
146 | pub fn new() -> F{ | ||
147 | F{} | ||
148 | } | ||
149 | |||
150 | pub fn do_it(&self) {} | ||
151 | } | ||
152 | |||
153 | fn bar() { | ||
154 | let f : F = F::new(); | ||
155 | f.do_it(<|>); | ||
156 | }"#, | ||
157 | ); | ||
158 | |||
159 | assert_eq!(desc.name, "do_it".to_string()); | ||
160 | assert_eq!(desc.params, vec!["&self".to_string()]); | ||
161 | assert_eq!(desc.ret_type, None); | ||
162 | assert_eq!(param, None); | ||
163 | } | ||
164 | |||
165 | #[test] | ||
166 | fn test_fn_signature_for_method_with_arg() { | ||
167 | let (desc, param) = get_signature( | ||
168 | r#"struct F; | ||
169 | impl F { | ||
170 | pub fn new() -> F{ | ||
171 | F{} | ||
172 | } | ||
173 | |||
174 | pub fn do_it(&self, x: i32) {} | ||
175 | } | ||
176 | |||
177 | fn bar() { | ||
178 | let f : F = F::new(); | ||
179 | f.do_it(<|>); | ||
180 | }"#, | ||
181 | ); | ||
182 | |||
183 | assert_eq!(desc.name, "do_it".to_string()); | ||
184 | assert_eq!(desc.params, vec!["&self".to_string(), "x".to_string()]); | ||
185 | assert_eq!(desc.ret_type, None); | ||
186 | assert_eq!(param, Some(1)); | ||
187 | } | ||
188 | |||
189 | #[test] | ||
190 | fn test_fn_signature_with_docs_simple() { | ||
191 | let (desc, param) = get_signature( | ||
192 | r#" | ||
193 | /// test | ||
194 | // non-doc-comment | ||
195 | fn foo(j: u32) -> u32 { | ||
196 | j | ||
197 | } | ||
198 | |||
199 | fn bar() { | ||
200 | let _ = foo(<|>); | ||
201 | } | ||
202 | "#, | ||
203 | ); | ||
204 | |||
205 | assert_eq!(desc.name, "foo".to_string()); | ||
206 | assert_eq!(desc.params, vec!["j".to_string()]); | ||
207 | assert_eq!(desc.ret_type, Some("-> u32".to_string())); | ||
208 | assert_eq!(param, Some(0)); | ||
209 | assert_eq!(desc.label, "fn foo(j: u32) -> u32".to_string()); | ||
210 | assert_eq!(desc.doc, Some("test".into())); | ||
211 | } | ||
212 | |||
213 | #[test] | ||
214 | fn test_fn_signature_with_docs() { | ||
215 | let (desc, param) = get_signature( | ||
216 | r#" | ||
217 | /// Adds one to the number given. | ||
218 | /// | ||
219 | /// # Examples | ||
220 | /// | ||
221 | /// ``` | ||
222 | /// let five = 5; | ||
223 | /// | ||
224 | /// assert_eq!(6, my_crate::add_one(5)); | ||
225 | /// ``` | ||
226 | pub fn add_one(x: i32) -> i32 { | ||
227 | x + 1 | ||
228 | } | ||
229 | |||
230 | pub fn do() { | ||
231 | add_one(<|> | ||
232 | }"#, | ||
233 | ); | ||
234 | |||
235 | assert_eq!(desc.name, "add_one".to_string()); | ||
236 | assert_eq!(desc.params, vec!["x".to_string()]); | ||
237 | assert_eq!(desc.ret_type, Some("-> i32".to_string())); | ||
238 | assert_eq!(param, Some(0)); | ||
239 | assert_eq!(desc.label, "pub fn add_one(x: i32) -> i32".to_string()); | ||
240 | assert_eq!( | ||
241 | desc.doc, | ||
242 | Some( | ||
243 | r#"Adds one to the number given. | ||
244 | |||
245 | # Examples | ||
246 | |||
247 | ```rust | ||
248 | let five = 5; | ||
249 | |||
250 | assert_eq!(6, my_crate::add_one(5)); | ||
251 | ```"# | ||
252 | .into() | ||
253 | ) | ||
254 | ); | ||
255 | } | ||
256 | |||
257 | #[test] | ||
258 | fn test_fn_signature_with_docs_impl() { | ||
259 | let (desc, param) = get_signature( | ||
260 | r#" | ||
261 | struct addr; | ||
262 | impl addr { | ||
263 | /// Adds one to the number given. | ||
264 | /// | ||
265 | /// # Examples | ||
266 | /// | ||
267 | /// ``` | ||
268 | /// let five = 5; | ||
269 | /// | ||
270 | /// assert_eq!(6, my_crate::add_one(5)); | ||
271 | /// ``` | ||
272 | pub fn add_one(x: i32) -> i32 { | ||
273 | x + 1 | ||
274 | } | ||
275 | } | ||
276 | |||
277 | pub fn do_it() { | ||
278 | addr {}; | ||
279 | addr::add_one(<|>); | ||
280 | }"#, | ||
281 | ); | ||
282 | |||
283 | assert_eq!(desc.name, "add_one".to_string()); | ||
284 | assert_eq!(desc.params, vec!["x".to_string()]); | ||
285 | assert_eq!(desc.ret_type, Some("-> i32".to_string())); | ||
286 | assert_eq!(param, Some(0)); | ||
287 | assert_eq!(desc.label, "pub fn add_one(x: i32) -> i32".to_string()); | ||
288 | assert_eq!( | ||
289 | desc.doc, | ||
290 | Some( | ||
291 | r#"Adds one to the number given. | ||
292 | |||
293 | # Examples | ||
294 | |||
295 | ```rust | ||
296 | let five = 5; | ||
297 | |||
298 | assert_eq!(6, my_crate::add_one(5)); | ||
299 | ```"# | ||
300 | .into() | ||
301 | ) | ||
302 | ); | ||
303 | } | ||
304 | |||
305 | #[test] | ||
306 | fn test_fn_signature_with_docs_from_actix() { | ||
307 | let (desc, param) = get_signature( | ||
308 | r#" | ||
309 | pub trait WriteHandler<E> | ||
310 | where | ||
311 | Self: Actor, | ||
312 | Self::Context: ActorContext, | ||
313 | { | ||
314 | /// Method is called when writer emits error. | ||
315 | /// | ||
316 | /// If this method returns `ErrorAction::Continue` writer processing | ||
317 | /// continues otherwise stream processing stops. | ||
318 | fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running { | ||
319 | Running::Stop | ||
320 | } | ||
321 | |||
322 | /// Method is called when writer finishes. | ||
323 | /// | ||
324 | /// By default this method stops actor's `Context`. | ||
325 | fn finished(&mut self, ctx: &mut Self::Context) { | ||
326 | ctx.stop() | ||
327 | } | ||
328 | } | ||
329 | |||
330 | pub fn foo() { | ||
331 | WriteHandler r; | ||
332 | r.finished(<|>); | ||
333 | } | ||
334 | |||
335 | "#, | ||
336 | ); | ||
337 | |||
338 | assert_eq!(desc.name, "finished".to_string()); | ||
339 | assert_eq!( | ||
340 | desc.params, | ||
341 | vec!["&mut self".to_string(), "ctx".to_string()] | ||
342 | ); | ||
343 | assert_eq!(desc.ret_type, None); | ||
344 | assert_eq!(param, Some(1)); | ||
345 | assert_eq!( | ||
346 | desc.doc, | ||
347 | Some( | ||
348 | r#"Method is called when writer finishes. | ||
349 | |||
350 | By default this method stops actor's `Context`."# | ||
351 | .into() | ||
352 | ) | ||
353 | ); | ||
354 | } | ||
355 | |||
356 | fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { | 97 | fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { |
357 | let (analysis, position) = single_file_with_position(text); | 98 | let (analysis, position) = single_file_with_position(text); |
358 | analysis.find_all_refs(position).unwrap() | 99 | analysis.find_all_refs(position).unwrap() |
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index 81b790c5f..2cfc4caa4 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs | |||
@@ -1,14 +1,11 @@ | |||
1 | mod scope; | 1 | mod scope; |
2 | 2 | ||
3 | use std::{ | 3 | use std::sync::Arc; |
4 | cmp::{max, min}, | ||
5 | sync::Arc, | ||
6 | }; | ||
7 | 4 | ||
8 | use ra_db::Cancelable; | 5 | use ra_db::Cancelable; |
9 | use ra_syntax::{ | 6 | use ra_syntax::{ |
10 | TextRange, TextUnit, TreePtr, | 7 | TreePtr, |
11 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, | 8 | ast::{self, AstNode}, |
12 | }; | 9 | }; |
13 | 10 | ||
14 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}, type_ref::{TypeRef, Mutability}, Name}; | 11 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}, type_ref::{TypeRef, Mutability}, Name}; |
@@ -57,11 +54,6 @@ impl Function { | |||
57 | db.fn_signature(self.def_id) | 54 | db.fn_signature(self.def_id) |
58 | } | 55 | } |
59 | 56 | ||
60 | pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> { | ||
61 | let syntax = self.syntax(db); | ||
62 | FnSignatureInfo::new(&syntax) | ||
63 | } | ||
64 | |||
65 | pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> { | 57 | pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> { |
66 | db.infer(self.def_id) | 58 | db.infer(self.def_id) |
67 | } | 59 | } |
@@ -132,116 +124,3 @@ pub(crate) fn fn_signature(db: &impl HirDatabase, def_id: DefId) -> Arc<FnSignat | |||
132 | let sig = FnSignature { args, ret_type }; | 124 | let sig = FnSignature { args, ret_type }; |
133 | Arc::new(sig) | 125 | Arc::new(sig) |
134 | } | 126 | } |
135 | |||
136 | #[derive(Debug, Clone)] | ||
137 | pub struct FnSignatureInfo { | ||
138 | pub name: String, | ||
139 | pub label: String, | ||
140 | pub ret_type: Option<String>, | ||
141 | pub params: Vec<String>, | ||
142 | pub doc: Option<String>, | ||
143 | } | ||
144 | |||
145 | impl FnSignatureInfo { | ||
146 | fn new(node: &ast::FnDef) -> Option<Self> { | ||
147 | let name = node.name()?.text().to_string(); | ||
148 | |||
149 | let mut doc = None; | ||
150 | |||
151 | // Strip the body out for the label. | ||
152 | let mut label: String = if let Some(body) = node.body() { | ||
153 | let body_range = body.syntax().range(); | ||
154 | let label: String = node | ||
155 | .syntax() | ||
156 | .children() | ||
157 | .filter(|child| !child.range().is_subrange(&body_range)) | ||
158 | .map(|node| node.text().to_string()) | ||
159 | .collect(); | ||
160 | label | ||
161 | } else { | ||
162 | node.syntax().text().to_string() | ||
163 | }; | ||
164 | |||
165 | if let Some((comment_range, docs)) = FnSignatureInfo::extract_doc_comments(node) { | ||
166 | let comment_range = comment_range | ||
167 | .checked_sub(node.syntax().range().start()) | ||
168 | .unwrap(); | ||
169 | let start = comment_range.start().to_usize(); | ||
170 | let end = comment_range.end().to_usize(); | ||
171 | |||
172 | // Remove the comment from the label | ||
173 | label.replace_range(start..end, ""); | ||
174 | |||
175 | // Massage markdown | ||
176 | let mut processed_lines = Vec::new(); | ||
177 | let mut in_code_block = false; | ||
178 | for line in docs.lines() { | ||
179 | if line.starts_with("```") { | ||
180 | in_code_block = !in_code_block; | ||
181 | } | ||
182 | |||
183 | let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { | ||
184 | "```rust".into() | ||
185 | } else { | ||
186 | line.to_string() | ||
187 | }; | ||
188 | |||
189 | processed_lines.push(line); | ||
190 | } | ||
191 | |||
192 | if !processed_lines.is_empty() { | ||
193 | doc = Some(processed_lines.join("\n")); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | let params = FnSignatureInfo::param_list(node); | ||
198 | let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); | ||
199 | |||
200 | Some(FnSignatureInfo { | ||
201 | name, | ||
202 | ret_type, | ||
203 | params, | ||
204 | label: label.trim().to_owned(), | ||
205 | doc, | ||
206 | }) | ||
207 | } | ||
208 | |||
209 | fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> { | ||
210 | if node.doc_comments().count() == 0 { | ||
211 | return None; | ||
212 | } | ||
213 | |||
214 | let comment_text = node.doc_comment_text(); | ||
215 | |||
216 | let (begin, end) = node | ||
217 | .doc_comments() | ||
218 | .map(|comment| comment.syntax().range()) | ||
219 | .map(|range| (range.start().to_usize(), range.end().to_usize())) | ||
220 | .fold((std::usize::MAX, std::usize::MIN), |acc, range| { | ||
221 | (min(acc.0, range.0), max(acc.1, range.1)) | ||
222 | }); | ||
223 | |||
224 | let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end)); | ||
225 | |||
226 | Some((range, comment_text)) | ||
227 | } | ||
228 | |||
229 | fn param_list(node: &ast::FnDef) -> Vec<String> { | ||
230 | let mut res = vec![]; | ||
231 | if let Some(param_list) = node.param_list() { | ||
232 | if let Some(self_param) = param_list.self_param() { | ||
233 | res.push(self_param.syntax().text().to_string()) | ||
234 | } | ||
235 | |||
236 | // Maybe use param.pat here? See if we can just extract the name? | ||
237 | //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); | ||
238 | res.extend( | ||
239 | param_list | ||
240 | .params() | ||
241 | .filter_map(|p| p.pat()) | ||
242 | .map(|pat| pat.syntax().text().to_string()), | ||
243 | ); | ||
244 | } | ||
245 | res | ||
246 | } | ||
247 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f8ac28cf7..197d8c4fd 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -53,8 +53,6 @@ pub use self::{ | |||
53 | impl_block::{ImplBlock, ImplItem}, | 53 | impl_block::{ImplBlock, ImplItem}, |
54 | }; | 54 | }; |
55 | 55 | ||
56 | pub use self::function::FnSignatureInfo; | ||
57 | |||
58 | pub use self::code_model_api::{ | 56 | pub use self::code_model_api::{ |
59 | Crate, CrateDependency, | 57 | Crate, CrateDependency, |
60 | Module, ModuleSource, Problem, | 58 | Module, ModuleSource, Problem, |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 99f15354f..b9b42f1b3 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -475,36 +475,30 @@ pub fn handle_signature_help( | |||
475 | params: req::TextDocumentPositionParams, | 475 | params: req::TextDocumentPositionParams, |
476 | ) -> Result<Option<req::SignatureHelp>> { | 476 | ) -> Result<Option<req::SignatureHelp>> { |
477 | let position = params.try_conv_with(&world)?; | 477 | let position = params.try_conv_with(&world)?; |
478 | 478 | if let Some(call_info) = world.analysis().call_info(position)? { | |
479 | if let Some((descriptor, active_param)) = world.analysis().resolve_callable(position)? { | 479 | let parameters: Vec<ParameterInformation> = call_info |
480 | let parameters: Vec<ParameterInformation> = descriptor | 480 | .parameters |
481 | .params | 481 | .into_iter() |
482 | .iter() | ||
483 | .map(|param| ParameterInformation { | 482 | .map(|param| ParameterInformation { |
484 | label: ParameterLabel::Simple(param.clone()), | 483 | label: ParameterLabel::Simple(param.clone()), |
485 | documentation: None, | 484 | documentation: None, |
486 | }) | 485 | }) |
487 | .collect(); | 486 | .collect(); |
488 | 487 | let documentation = call_info.doc.map(|value| { | |
489 | let documentation = if let Some(doc) = descriptor.doc { | 488 | Documentation::MarkupContent(MarkupContent { |
490 | Some(Documentation::MarkupContent(MarkupContent { | ||
491 | kind: MarkupKind::Markdown, | 489 | kind: MarkupKind::Markdown, |
492 | value: doc, | 490 | value, |
493 | })) | 491 | }) |
494 | } else { | 492 | }); |
495 | None | ||
496 | }; | ||
497 | |||
498 | let sig_info = SignatureInformation { | 493 | let sig_info = SignatureInformation { |
499 | label: descriptor.label, | 494 | label: call_info.label, |
500 | documentation, | 495 | documentation, |
501 | parameters: Some(parameters), | 496 | parameters: Some(parameters), |
502 | }; | 497 | }; |
503 | |||
504 | Ok(Some(req::SignatureHelp { | 498 | Ok(Some(req::SignatureHelp { |
505 | signatures: vec![sig_info], | 499 | signatures: vec![sig_info], |
506 | active_signature: Some(0), | 500 | active_signature: Some(0), |
507 | active_parameter: active_param.map(|a| a as u64), | 501 | active_parameter: call_info.active_parameter.map(|it| it as u64), |
508 | })) | 502 | })) |
509 | } else { | 503 | } else { |
510 | Ok(None) | 504 | Ok(None) |