aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-04-09 18:55:44 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-04-09 18:55:44 +0100
commit2fc2d4373b2c4e96bebf320a84270eee3afe34aa (patch)
tree5bdb33ae377b4004f0b28ec5e2edff71b41e8c4e /crates
parent5f700179fc7ed16d2848a6dbc7cf23da3b8df6c7 (diff)
parent45a2b9252401cc580dfa2e0e761313cc8334d47c (diff)
Merge #1110
1110: Introduce display module and implement new FunctionSignature for CallInfo's r=matklad a=vipentti This introduces a new module `display` in `ra_ide_api` that contains UI-related things, in addition this refactors CallInfo's function signatures into a new `FunctionSignature` type, which implements `Display` and can be converted into `lsp_types::SignatureInformation` in the `conv` layer. Currently only `CallInfo` uses the `FunctionSignature` directly, but `function_label` now uses the same signature and returns it as a string, using the `Display` implementation. This also fixes #960 I think this similar structure could be applied to other UI-displayable items, so instead of the `ra_ide_api` returning `Strings` we could return some intermediate structures that can be converted into a UI-displayable `String` easily, but that could also provide some additional information. Co-authored-by: Ville Penttinen <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide_api/src/call_info.rs114
-rw-r--r--crates/ra_ide_api/src/completion.rs41
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs2
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs5
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap14
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap6
-rw-r--r--crates/ra_ide_api/src/display.rs82
-rw-r--r--crates/ra_ide_api/src/display/function_signature.rs101
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs (renamed from crates/ra_ide_api/src/navigation_target.rs)82
-rw-r--r--crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap (renamed from crates/ra_ide_api/src/snapshots/tests__file_structure.snap)6
-rw-r--r--crates/ra_ide_api/src/display/structure.rs (renamed from crates/ra_ide_api/src/structure.rs)0
-rw-r--r--crates/ra_ide_api/src/hover.rs110
-rw-r--r--crates/ra_ide_api/src/lib.rs12
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs2
-rw-r--r--crates/ra_lsp_server/src/conv.rs22
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs23
16 files changed, 391 insertions, 231 deletions
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index 29fa7d30b..dbb3853d0 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -6,9 +6,8 @@ use ra_syntax::{
6 ast::{self, ArgListOwner}, 6 ast::{self, ArgListOwner},
7 algo::find_node_at_offset, 7 algo::find_node_at_offset,
8}; 8};
9use hir::Docs;
10 9
11use crate::{FilePosition, CallInfo, db::RootDatabase}; 10use crate::{FilePosition, CallInfo, FunctionSignature, db::RootDatabase};
12 11
13/// Computes parameter information for the given call expression. 12/// Computes parameter information for the given call expression.
14pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 13pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
@@ -27,10 +26,10 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
27 let fn_def = ast::FnDef::cast(fn_def).unwrap(); 26 let fn_def = ast::FnDef::cast(fn_def).unwrap();
28 let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?; 27 let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?;
29 28
30 let mut call_info = CallInfo::new(db, function, fn_def)?; 29 let mut call_info = CallInfo::new(db, function);
31 30
32 // If we have a calling expression let's find which argument we are on 31 // If we have a calling expression let's find which argument we are on
33 let num_params = call_info.parameters.len(); 32 let num_params = call_info.parameters().len();
34 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 33 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
35 34
36 if num_params == 1 { 35 if num_params == 1 {
@@ -107,28 +106,15 @@ impl<'a> FnCallNode<'a> {
107} 106}
108 107
109impl CallInfo { 108impl CallInfo {
110 fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option<Self> { 109 fn new(db: &RootDatabase, function: hir::Function) -> Self {
111 let label = crate::completion::function_label(node)?; 110 let signature = FunctionSignature::from_hir(db, function);
112 let doc = function.docs(db);
113 111
114 Some(CallInfo { parameters: param_list(node), label, doc, active_parameter: None }) 112 CallInfo { signature, active_parameter: None }
115 } 113 }
116}
117
118fn param_list(node: &ast::FnDef) -> Vec<String> {
119 let mut res = vec![];
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 114
125 // Maybe use param.pat here? See if we can just extract the name? 115 fn parameters(&self) -> &[String] {
126 //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); 116 &self.signature.parameters
127 res.extend(
128 param_list.params().filter_map(|p| p.pat()).map(|pat| pat.syntax().text().to_string()),
129 );
130 } 117 }
131 res
132} 118}
133 119
134#[cfg(test)] 120#[cfg(test)]
@@ -139,6 +125,17 @@ mod tests {
139 125
140 use super::*; 126 use super::*;
141 127
128 // These are only used when testing
129 impl CallInfo {
130 fn doc(&self) -> Option<hir::Documentation> {
131 self.signature.doc.clone()
132 }
133
134 fn label(&self) -> String {
135 self.signature.to_string()
136 }
137 }
138
142 fn call_info(text: &str) -> CallInfo { 139 fn call_info(text: &str) -> CallInfo {
143 let (analysis, position) = single_file_with_position(text); 140 let (analysis, position) = single_file_with_position(text);
144 analysis.call_info(position).unwrap().unwrap() 141 analysis.call_info(position).unwrap().unwrap()
@@ -151,7 +148,7 @@ mod tests {
151fn bar() { foo(<|>3, ); }"#, 148fn bar() { foo(<|>3, ); }"#,
152 ); 149 );
153 150
154 assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); 151 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
155 assert_eq!(info.active_parameter, Some(0)); 152 assert_eq!(info.active_parameter, Some(0));
156 } 153 }
157 154
@@ -162,7 +159,7 @@ fn bar() { foo(<|>3, ); }"#,
162fn bar() { foo(3, <|>); }"#, 159fn bar() { foo(3, <|>); }"#,
163 ); 160 );
164 161
165 assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); 162 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
166 assert_eq!(info.active_parameter, Some(1)); 163 assert_eq!(info.active_parameter, Some(1));
167 } 164 }
168 165
@@ -173,18 +170,57 @@ fn bar() { foo(3, <|>); }"#,
173fn bar() { foo(<|>); }"#, 170fn bar() { foo(<|>); }"#,
174 ); 171 );
175 172
176 assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); 173 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
174 assert_eq!(info.active_parameter, Some(0));
175 }
176
177 #[test]
178 fn test_fn_signature_two_args_first_generics() {
179 let info = call_info(
180 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
181fn bar() { foo(<|>3, ); }"#,
182 );
183
184 assert_eq!(info.parameters(), ["x: T", "y: U"]);
185 assert_eq!(
186 info.label(),
187 r#"
188fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
189where T: Copy + Display,
190 U: Debug
191 "#
192 .trim()
193 );
177 assert_eq!(info.active_parameter, Some(0)); 194 assert_eq!(info.active_parameter, Some(0));
178 } 195 }
179 196
180 #[test] 197 #[test]
198 fn test_fn_signature_no_params() {
199 let info = call_info(
200 r#"fn foo<T>() -> T where T: Copy + Display {}
201fn bar() { foo(<|>); }"#,
202 );
203
204 assert!(info.parameters().is_empty());
205 assert_eq!(
206 info.label(),
207 r#"
208fn foo<T>() -> T
209where T: Copy + Display
210 "#
211 .trim()
212 );
213 assert!(info.active_parameter.is_none());
214 }
215
216 #[test]
181 fn test_fn_signature_for_impl() { 217 fn test_fn_signature_for_impl() {
182 let info = call_info( 218 let info = call_info(
183 r#"struct F; impl F { pub fn new() { F{}} } 219 r#"struct F; impl F { pub fn new() { F{}} }
184fn bar() {let _ : F = F::new(<|>);}"#, 220fn bar() {let _ : F = F::new(<|>);}"#,
185 ); 221 );
186 222
187 assert_eq!(info.parameters, Vec::<String>::new()); 223 assert!(info.parameters().is_empty());
188 assert_eq!(info.active_parameter, None); 224 assert_eq!(info.active_parameter, None);
189 } 225 }
190 226
@@ -206,7 +242,7 @@ fn bar() {
206}"#, 242}"#,
207 ); 243 );
208 244
209 assert_eq!(info.parameters, vec!["&self".to_string()]); 245 assert_eq!(info.parameters(), ["&self"]);
210 assert_eq!(info.active_parameter, None); 246 assert_eq!(info.active_parameter, None);
211 } 247 }
212 248
@@ -228,7 +264,7 @@ fn bar() {
228}"#, 264}"#,
229 ); 265 );
230 266
231 assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]); 267 assert_eq!(info.parameters(), ["&self", "x: i32"]);
232 assert_eq!(info.active_parameter, Some(1)); 268 assert_eq!(info.active_parameter, Some(1));
233 } 269 }
234 270
@@ -248,10 +284,10 @@ fn bar() {
248"#, 284"#,
249 ); 285 );
250 286
251 assert_eq!(info.parameters, vec!["j".to_string()]); 287 assert_eq!(info.parameters(), ["j: u32"]);
252 assert_eq!(info.active_parameter, Some(0)); 288 assert_eq!(info.active_parameter, Some(0));
253 assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); 289 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
254 assert_eq!(info.doc.map(|it| it.into()), Some("test".to_string())); 290 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
255 } 291 }
256 292
257 #[test] 293 #[test]
@@ -276,11 +312,11 @@ pub fn do() {
276}"#, 312}"#,
277 ); 313 );
278 314
279 assert_eq!(info.parameters, vec!["x".to_string()]); 315 assert_eq!(info.parameters(), ["x: i32"]);
280 assert_eq!(info.active_parameter, Some(0)); 316 assert_eq!(info.active_parameter, Some(0));
281 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 317 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
282 assert_eq!( 318 assert_eq!(
283 info.doc.map(|it| it.into()), 319 info.doc().map(|it| it.into()),
284 Some( 320 Some(
285 r#"Adds one to the number given. 321 r#"Adds one to the number given.
286 322
@@ -322,11 +358,11 @@ pub fn do_it() {
322}"#, 358}"#,
323 ); 359 );
324 360
325 assert_eq!(info.parameters, vec!["x".to_string()]); 361 assert_eq!(info.parameters(), ["x: i32"]);
326 assert_eq!(info.active_parameter, Some(0)); 362 assert_eq!(info.active_parameter, Some(0));
327 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 363 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
328 assert_eq!( 364 assert_eq!(
329 info.doc.map(|it| it.into()), 365 info.doc().map(|it| it.into()),
330 Some( 366 Some(
331 r#"Adds one to the number given. 367 r#"Adds one to the number given.
332 368
@@ -375,10 +411,10 @@ pub fn foo() {
375"#, 411"#,
376 ); 412 );
377 413
378 assert_eq!(info.parameters, vec!["&mut self".to_string(), "ctx".to_string()]); 414 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]);
379 assert_eq!(info.active_parameter, Some(1)); 415 assert_eq!(info.active_parameter, Some(1));
380 assert_eq!( 416 assert_eq!(
381 info.doc.map(|it| it.into()), 417 info.doc().map(|it| it.into()),
382 Some( 418 Some(
383 r#"Method is called when writer finishes. 419 r#"Method is called when writer finishes.
384 420
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs
index a846a7a3c..deff59cd3 100644
--- a/crates/ra_ide_api/src/completion.rs
+++ b/crates/ra_ide_api/src/completion.rs
@@ -13,7 +13,6 @@ mod complete_scope;
13mod complete_postfix; 13mod complete_postfix;
14 14
15use ra_db::SourceDatabase; 15use ra_db::SourceDatabase;
16use ra_syntax::{ast::{self, AstNode}, SyntaxKind::{ATTR, COMMENT}};
17 16
18use crate::{ 17use crate::{
19 db, 18 db,
@@ -70,43 +69,3 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti
70 complete_postfix::complete_postfix(&mut acc, &ctx); 69 complete_postfix::complete_postfix(&mut acc, &ctx);
71 Some(acc) 70 Some(acc)
72} 71}
73
74pub fn function_label(node: &ast::FnDef) -> Option<String> {
75 let label: String = if let Some(body) = node.body() {
76 let body_range = body.syntax().range();
77 let label: String = node
78 .syntax()
79 .children_with_tokens()
80 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
81 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) // Filter out comments and attrs
82 .map(|node| node.to_string())
83 .collect();
84 label
85 } else {
86 node.syntax().text().to_string()
87 };
88
89 Some(label.trim().to_owned())
90}
91
92pub fn const_label(node: &ast::ConstDef) -> String {
93 let label: String = node
94 .syntax()
95 .children_with_tokens()
96 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
97 .map(|node| node.to_string())
98 .collect();
99
100 label.trim().to_owned()
101}
102
103pub fn type_label(node: &ast::TypeAliasDef) -> String {
104 let label: String = node
105 .syntax()
106 .children_with_tokens()
107 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
108 .map(|node| node.to_string())
109 .collect();
110
111 label.trim().to_owned()
112}
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index 6146b7bb6..9d82f2270 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -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/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs
index 28c8f83ab..9aa346688 100644
--- a/crates/ra_ide_api/src/completion/presentation.rs
+++ b/crates/ra_ide_api/src/completion/presentation.rs
@@ -6,6 +6,9 @@ use ra_syntax::ast::NameOwner;
6 6
7use crate::completion::{ 7use crate::completion::{
8 Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem, 8 Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem,
9};
10
11use crate::display::{
9 function_label, const_label, type_label, 12 function_label, const_label, type_label,
10}; 13};
11 14
@@ -101,7 +104,7 @@ impl Completions {
101 CompletionItemKind::Function 104 CompletionItemKind::Function
102 }) 105 })
103 .set_documentation(func.docs(ctx.db)) 106 .set_documentation(func.docs(ctx.db))
104 .set_detail(detail); 107 .detail(detail);
105 // If not an import, add parenthesis automatically. 108 // If not an import, add parenthesis automatically.
106 if ctx.use_item_syntax.is_none() && !ctx.is_call { 109 if ctx.use_item_syntax.is_none() && !ctx.is_call {
107 tested_by!(inserts_parens_for_function_calls); 110 tested_by!(inserts_parens_for_function_calls);
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap
index 87691b304..34adcda6c 100644
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap
+++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap
@@ -1,23 +1,23 @@
1--- 1---
2created: "2019-02-18T09:22:24.188564584Z" 2created: "2019-04-04T14:52:24.531844100Z"
3creator: insta@0.6.2 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/completion/completion_item.rs 4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions 5expression: kind_completions
6--- 6---
7[ 7[
8 CompletionItem { 8 CompletionItem {
9 label: "bar", 9 label: "bar",
10 source_range: [129; 129), 10 source_range: [126; 126),
11 delete: [129; 129), 11 delete: [126; 126),
12 insert: "bar", 12 insert: "bar",
13 kind: Binding 13 kind: Binding
14 }, 14 },
15 CompletionItem { 15 CompletionItem {
16 label: "foo", 16 label: "foo",
17 source_range: [129; 129), 17 source_range: [126; 126),
18 delete: [129; 129), 18 delete: [126; 126),
19 insert: "foo()$0", 19 insert: "foo()$0",
20 kind: Function, 20 kind: Function,
21 detail: "fn foo() ->" 21 detail: "fn foo()"
22 } 22 }
23] 23]
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap
index 0738cf466..ff36df707 100644
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap
+++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap
@@ -1,6 +1,6 @@
1--- 1---
2created: "2019-02-18T09:22:24.182964414Z" 2created: "2019-04-04T14:52:24.525395600Z"
3creator: insta@0.6.2 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/completion/completion_item.rs 4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions 5expression: kind_completions
6--- 6---
@@ -18,6 +18,6 @@ expression: kind_completions
18 delete: [47; 47), 18 delete: [47; 47),
19 insert: "x()$0", 19 insert: "x()$0",
20 kind: Function, 20 kind: Function,
21 detail: "fn x() ->" 21 detail: "fn x()"
22 } 22 }
23] 23]
diff --git a/crates/ra_ide_api/src/display.rs b/crates/ra_ide_api/src/display.rs
new file mode 100644
index 000000000..1b06abf94
--- /dev/null
+++ b/crates/ra_ide_api/src/display.rs
@@ -0,0 +1,82 @@
1//! This module contains utilities for turning SyntaxNodes and HIR types
2//! into types that may be used to render in a UI.
3
4mod function_signature;
5mod navigation_target;
6mod structure;
7
8use crate::db::RootDatabase;
9use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}};
10
11pub use navigation_target::NavigationTarget;
12pub use structure::{StructureNode, file_structure};
13pub use function_signature::FunctionSignature;
14
15pub(crate) fn function_label(node: &ast::FnDef) -> String {
16 FunctionSignature::from(node).to_string()
17}
18
19pub(crate) fn const_label(node: &ast::ConstDef) -> String {
20 let label: String = node
21 .syntax()
22 .children_with_tokens()
23 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
24 .map(|node| node.to_string())
25 .collect();
26
27 label.trim().to_owned()
28}
29
30pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String {
31 let label: String = node
32 .syntax()
33 .children_with_tokens()
34 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
35 .map(|node| node.to_string())
36 .collect();
37
38 label.trim().to_owned()
39}
40
41pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> {
42 let mut res = vec![];
43 if let Some(type_params) = node.type_param_list() {
44 res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string()));
45 res.extend(type_params.type_params().map(|p| p.syntax().text().to_string()));
46 }
47 res
48}
49
50pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
51 let mut res = vec![];
52 if let Some(clause) = node.where_clause() {
53 res.extend(clause.predicates().map(|p| p.syntax().text().to_string()));
54 }
55 res
56}
57
58pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
59 rust_code_markup_with_doc::<_, &str>(val, None)
60}
61
62pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
63where
64 CODE: AsRef<str>,
65 DOC: AsRef<str>,
66{
67 if let Some(doc) = doc {
68 format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
69 } else {
70 format!("```rust\n{}\n```", val.as_ref())
71 }
72}
73
74// FIXME: this should not really use navigation target. Rather, approximately
75// resolved symbol should return a `DefId`.
76pub(crate) fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
77 match (nav.description(db), nav.docs(db)) {
78 (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
79 (None, Some(docs)) => Some(docs),
80 _ => None,
81 }
82}
diff --git a/crates/ra_ide_api/src/display/function_signature.rs b/crates/ra_ide_api/src/display/function_signature.rs
new file mode 100644
index 000000000..d09950bce
--- /dev/null
+++ b/crates/ra_ide_api/src/display/function_signature.rs
@@ -0,0 +1,101 @@
1use super::{where_predicates, generic_parameters};
2use crate::db;
3use std::fmt::{self, Display};
4use join_to_string::join;
5use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
6use std::convert::From;
7use hir::{Docs, Documentation};
8
9/// Contains information about a function signature
10#[derive(Debug)]
11pub struct FunctionSignature {
12 /// Optional visibility
13 pub visibility: Option<String>,
14 /// Name of the function
15 pub name: Option<String>,
16 /// Documentation for the function
17 pub doc: Option<Documentation>,
18 /// Generic parameters
19 pub generic_parameters: Vec<String>,
20 /// Parameters of the function
21 pub parameters: Vec<String>,
22 /// Optional return type
23 pub ret_type: Option<String>,
24 /// Where predicates
25 pub where_predicates: Vec<String>,
26}
27
28impl FunctionSignature {
29 pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
30 self.doc = doc;
31 self
32 }
33
34 pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self {
35 let doc = function.docs(db);
36 let (_, ast_node) = function.source(db);
37 FunctionSignature::from(&*ast_node).with_doc_opt(doc)
38 }
39}
40
41impl From<&'_ ast::FnDef> for FunctionSignature {
42 fn from(node: &ast::FnDef) -> FunctionSignature {
43 fn param_list(node: &ast::FnDef) -> Vec<String> {
44 let mut res = vec![];
45 if let Some(param_list) = node.param_list() {
46 if let Some(self_param) = param_list.self_param() {
47 res.push(self_param.syntax().text().to_string())
48 }
49
50 res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
51 }
52 res
53 }
54
55 FunctionSignature {
56 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
57 name: node.name().map(|n| n.text().to_string()),
58 ret_type: node
59 .ret_type()
60 .and_then(|r| r.type_ref())
61 .map(|n| n.syntax().text().to_string()),
62 parameters: param_list(node),
63 generic_parameters: generic_parameters(node),
64 where_predicates: where_predicates(node),
65 // docs are processed separately
66 doc: None,
67 }
68 }
69}
70
71impl Display for FunctionSignature {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 if let Some(t) = &self.visibility {
74 write!(f, "{} ", t)?;
75 }
76
77 if let Some(name) = &self.name {
78 write!(f, "fn {}", name)?;
79 }
80
81 if !self.generic_parameters.is_empty() {
82 join(self.generic_parameters.iter())
83 .separator(", ")
84 .surround_with("<", ">")
85 .to_fmt(f)?;
86 }
87
88 join(self.parameters.iter()).separator(", ").surround_with("(", ")").to_fmt(f)?;
89
90 if let Some(t) = &self.ret_type {
91 write!(f, " -> {}", t)?;
92 }
93
94 if !self.where_predicates.is_empty() {
95 write!(f, "\nwhere ")?;
96 join(self.where_predicates.iter()).separator(",\n ").to_fmt(f)?;
97 }
98
99 Ok(())
100 }
101}
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index f6d7f3192..3c518faf5 100644
--- a/crates/ra_ide_api/src/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -1,7 +1,9 @@
1use ra_db::FileId; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, 3 SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc,
4 SyntaxKind::{self, NAME}, 4 SyntaxKind::{self, NAME},
5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
6 algo::visit::{visitor, Visitor},
5}; 7};
6use hir::{ModuleSource, FieldSource, Name, ImplItem}; 8use hir::{ModuleSource, FieldSource, Name, ImplItem};
7 9
@@ -248,4 +250,80 @@ impl NavigationTarget {
248 container_name: None, 250 container_name: None,
249 } 251 }
250 } 252 }
253
254 pub(crate) fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> {
255 let source_file = db.parse(self.file_id());
256 let source_file = source_file.syntax();
257 let node = source_file
258 .descendants()
259 .find(|node| node.kind() == self.kind() && node.range() == self.full_range())?
260 .to_owned();
261 Some(node)
262 }
263
264 pub(crate) fn docs(&self, db: &RootDatabase) -> Option<String> {
265 let node = self.node(db)?;
266 fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> {
267 node.doc_comment_text()
268 }
269
270 visitor()
271 .visit(doc_comments::<ast::FnDef>)
272 .visit(doc_comments::<ast::StructDef>)
273 .visit(doc_comments::<ast::EnumDef>)
274 .visit(doc_comments::<ast::TraitDef>)
275 .visit(doc_comments::<ast::Module>)
276 .visit(doc_comments::<ast::TypeAliasDef>)
277 .visit(doc_comments::<ast::ConstDef>)
278 .visit(doc_comments::<ast::StaticDef>)
279 .visit(doc_comments::<ast::NamedFieldDef>)
280 .visit(doc_comments::<ast::EnumVariant>)
281 .accept(&node)?
282 }
283
284 /// Get a description of this node.
285 ///
286 /// e.g. `struct Name`, `enum Name`, `fn Name`
287 pub(crate) fn description(&self, db: &RootDatabase) -> Option<String> {
288 // FIXME: After type inference is done, add type information to improve the output
289 let node = self.node(db)?;
290
291 fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String>
292 where
293 T: NameOwner + VisibilityOwner + TypeAscriptionOwner,
294 {
295 let mut string = visit_node(node, prefix)?;
296
297 if let Some(type_ref) = node.ascribed_type() {
298 string.push_str(": ");
299 type_ref.syntax().text().push_to(&mut string);
300 }
301
302 Some(string)
303 }
304
305 fn visit_node<T>(node: &T, label: &str) -> Option<String>
306 where
307 T: NameOwner + VisibilityOwner,
308 {
309 let mut string =
310 node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default();
311 string.push_str(label);
312 string.push_str(node.name()?.text().as_str());
313 Some(string)
314 }
315
316 visitor()
317 .visit(|node: &ast::FnDef| Some(crate::display::function_label(node)))
318 .visit(|node: &ast::StructDef| visit_node(node, "struct "))
319 .visit(|node: &ast::EnumDef| visit_node(node, "enum "))
320 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
321 .visit(|node: &ast::Module| visit_node(node, "mod "))
322 .visit(|node: &ast::TypeAliasDef| visit_node(node, "type "))
323 .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const "))
324 .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static "))
325 .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, ""))
326 .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string()))
327 .accept(&node)?
328 }
251} 329}
diff --git a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap
index 2efa8e22c..32dd99484 100644
--- a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap
+++ b/crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap
@@ -1,7 +1,7 @@
1--- 1---
2created: "2019-02-05T22:03:50.763530100Z" 2created: "2019-04-08T09:44:50.196004400Z"
3creator: insta@0.6.1 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/structure.rs 4source: crates/ra_ide_api/src/display/structure.rs
5expression: structure 5expression: structure
6--- 6---
7[ 7[
diff --git a/crates/ra_ide_api/src/structure.rs b/crates/ra_ide_api/src/display/structure.rs
index ec2c9bbc6..ec2c9bbc6 100644
--- a/crates/ra_ide_api/src/structure.rs
+++ b/crates/ra_ide_api/src/display/structure.rs
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index bfa7cd67a..3a8c93b99 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -1,11 +1,11 @@
1use ra_db::SourceDatabase; 1use ra_db::SourceDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, 3 AstNode, ast,
4 algo::{find_covering_element, find_node_at_offset, find_token_at_offset, visit::{visitor, Visitor}}, 4 algo::{find_covering_element, find_node_at_offset, find_token_at_offset},
5}; 5};
6use hir::HirDisplay; 6use hir::HirDisplay;
7 7
8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; 8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, display::{rust_code_markup, doc_text_for}};
9 9
10/// Contains the results when hovering over an item 10/// Contains the results when hovering over an item
11#[derive(Debug, Clone)] 11#[derive(Debug, Clone)]
@@ -145,110 +145,6 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
145 } 145 }
146} 146}
147 147
148fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
149 rust_code_markup_with_doc::<_, &str>(val, None)
150}
151
152fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
153where
154 CODE: AsRef<str>,
155 DOC: AsRef<str>,
156{
157 if let Some(doc) = doc {
158 format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
159 } else {
160 format!("```rust\n{}\n```", val.as_ref())
161 }
162}
163
164// FIXME: this should not really use navigation target. Rather, approximately
165// resolved symbol should return a `DefId`.
166fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
167 match (nav.description(db), nav.docs(db)) {
168 (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
169 (None, Some(docs)) => Some(docs),
170 _ => None,
171 }
172}
173
174impl NavigationTarget {
175 fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> {
176 let source_file = db.parse(self.file_id());
177 let source_file = source_file.syntax();
178 let node = source_file
179 .descendants()
180 .find(|node| node.kind() == self.kind() && node.range() == self.full_range())?
181 .to_owned();
182 Some(node)
183 }
184
185 fn docs(&self, db: &RootDatabase) -> Option<String> {
186 let node = self.node(db)?;
187 fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> {
188 node.doc_comment_text()
189 }
190
191 visitor()
192 .visit(doc_comments::<ast::FnDef>)
193 .visit(doc_comments::<ast::StructDef>)
194 .visit(doc_comments::<ast::EnumDef>)
195 .visit(doc_comments::<ast::TraitDef>)
196 .visit(doc_comments::<ast::Module>)
197 .visit(doc_comments::<ast::TypeAliasDef>)
198 .visit(doc_comments::<ast::ConstDef>)
199 .visit(doc_comments::<ast::StaticDef>)
200 .visit(doc_comments::<ast::NamedFieldDef>)
201 .visit(doc_comments::<ast::EnumVariant>)
202 .accept(&node)?
203 }
204
205 /// Get a description of this node.
206 ///
207 /// e.g. `struct Name`, `enum Name`, `fn Name`
208 fn description(&self, db: &RootDatabase) -> Option<String> {
209 // FIXME: After type inference is done, add type information to improve the output
210 let node = self.node(db)?;
211
212 fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String>
213 where
214 T: NameOwner + VisibilityOwner + TypeAscriptionOwner,
215 {
216 let mut string = visit_node(node, prefix)?;
217
218 if let Some(type_ref) = node.ascribed_type() {
219 string.push_str(": ");
220 type_ref.syntax().text().push_to(&mut string);
221 }
222
223 Some(string)
224 }
225
226 fn visit_node<T>(node: &T, label: &str) -> Option<String>
227 where
228 T: NameOwner + VisibilityOwner,
229 {
230 let mut string =
231 node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default();
232 string.push_str(label);
233 string.push_str(node.name()?.text().as_str());
234 Some(string)
235 }
236
237 visitor()
238 .visit(crate::completion::function_label)
239 .visit(|node: &ast::StructDef| visit_node(node, "struct "))
240 .visit(|node: &ast::EnumDef| visit_node(node, "enum "))
241 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
242 .visit(|node: &ast::Module| visit_node(node, "mod "))
243 .visit(|node: &ast::TypeAliasDef| visit_node(node, "type "))
244 .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const "))
245 .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static "))
246 .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, ""))
247 .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string()))
248 .accept(&node)?
249 }
250}
251
252#[cfg(test)] 148#[cfg(test)]
253mod tests { 149mod tests {
254 use ra_syntax::TextRange; 150 use ra_syntax::TextRange;
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 9063f78a9..d25795adc 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -13,7 +13,6 @@
13mod db; 13mod db;
14pub mod mock_analysis; 14pub mod mock_analysis;
15mod symbol_index; 15mod symbol_index;
16mod navigation_target;
17mod change; 16mod change;
18 17
19mod status; 18mod status;
@@ -34,9 +33,9 @@ mod folding_ranges;
34mod line_index; 33mod line_index;
35mod line_index_utils; 34mod line_index_utils;
36mod join_lines; 35mod join_lines;
37mod structure;
38mod typing; 36mod typing;
39mod matching_brace; 37mod matching_brace;
38mod display;
40 39
41#[cfg(test)] 40#[cfg(test)]
42mod marks; 41mod marks;
@@ -62,7 +61,6 @@ pub use crate::{
62 change::{AnalysisChange, LibraryData}, 61 change::{AnalysisChange, LibraryData},
63 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 62 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
64 runnables::{Runnable, RunnableKind}, 63 runnables::{Runnable, RunnableKind},
65 navigation_target::NavigationTarget,
66 references::ReferenceSearchResult, 64 references::ReferenceSearchResult,
67 assists::{Assist, AssistId}, 65 assists::{Assist, AssistId},
68 hover::{HoverResult}, 66 hover::{HoverResult},
@@ -70,8 +68,8 @@ pub use crate::{
70 line_index_utils::translate_offset_with_edit, 68 line_index_utils::translate_offset_with_edit,
71 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
72 syntax_highlighting::HighlightedRange, 70 syntax_highlighting::HighlightedRange,
73 structure::{StructureNode, file_structure},
74 diagnostics::Severity, 71 diagnostics::Severity,
72 display::{FunctionSignature, NavigationTarget, StructureNode, file_structure},
75}; 73};
76 74
77pub use ra_db::{ 75pub use ra_db::{
@@ -243,9 +241,7 @@ impl<T> RangeInfo<T> {
243 241
244#[derive(Debug)] 242#[derive(Debug)]
245pub struct CallInfo { 243pub struct CallInfo {
246 pub label: String, 244 pub signature: FunctionSignature,
247 pub doc: Option<Documentation>,
248 pub parameters: Vec<String>,
249 pub active_parameter: Option<usize>, 245 pub active_parameter: Option<usize>,
250} 246}
251 247
@@ -387,7 +383,7 @@ impl Analysis {
387 /// file outline. 383 /// file outline.
388 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 384 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
389 let file = self.db.parse(file_id); 385 let file = self.db.parse(file_id);
390 structure::file_structure(&file) 386 file_structure(&file)
391 } 387 }
392 388
393 /// Returns the set of folding ranges. 389 /// Returns the set of folding ranges.
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs
index 0eadc4e71..914d3fc71 100644
--- a/crates/ra_ide_api/src/symbol_index.rs
+++ b/crates/ra_ide_api/src/symbol_index.rs
@@ -275,7 +275,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
275mod tests { 275mod tests {
276 use ra_syntax::SmolStr; 276 use ra_syntax::SmolStr;
277 use crate::{ 277 use crate::{
278 navigation_target::NavigationTarget, 278 display::NavigationTarget,
279 mock_analysis::single_file, 279 mock_analysis::single_file,
280 Query, 280 Query,
281}; 281};
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 74e91c236..4d6ede316 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -174,6 +174,28 @@ impl Conv for ra_ide_api::Documentation {
174 } 174 }
175} 175}
176 176
177impl Conv for ra_ide_api::FunctionSignature {
178 type Output = lsp_types::SignatureInformation;
179 fn conv(self) -> Self::Output {
180 use lsp_types::{ParameterInformation, ParameterLabel, SignatureInformation};
181
182 let label = self.to_string();
183
184 let documentation = self.doc.map(|it| it.conv());
185
186 let parameters: Vec<ParameterInformation> = self
187 .parameters
188 .into_iter()
189 .map(|param| ParameterInformation {
190 label: ParameterLabel::Simple(param),
191 documentation: None,
192 })
193 .collect();
194
195 SignatureInformation { label, documentation, parameters: Some(parameters) }
196 }
197}
198
177impl ConvWith for TextEdit { 199impl ConvWith for TextEdit {
178 type Ctx = LineIndex; 200 type Ctx = LineIndex;
179 type Output = Vec<lsp_types::TextEdit>; 201 type Output = Vec<lsp_types::TextEdit>;
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 89e96a33a..b96deb061 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -3,8 +3,8 @@ use lsp_types::{
3 CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, CodeAction, 3 CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, CodeAction,
4 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, 4 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
5 FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, 5 FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
6 MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, 6 MarkupKind, Position, PrepareRenameResponse, Range,
7 RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, 7 RenameParams,SymbolInformation, TextDocumentIdentifier, TextEdit,
8 WorkspaceEdit, 8 WorkspaceEdit,
9}; 9};
10use ra_ide_api::{ 10use ra_ide_api::{
@@ -403,26 +403,13 @@ pub fn handle_signature_help(
403) -> Result<Option<req::SignatureHelp>> { 403) -> Result<Option<req::SignatureHelp>> {
404 let position = params.try_conv_with(&world)?; 404 let position = params.try_conv_with(&world)?;
405 if let Some(call_info) = world.analysis().call_info(position)? { 405 if let Some(call_info) = world.analysis().call_info(position)? {
406 let parameters: Vec<ParameterInformation> = call_info 406 let active_parameter = call_info.active_parameter.map(|it| it as i64);
407 .parameters 407 let sig_info = call_info.signature.conv();
408 .into_iter()
409 .map(|param| ParameterInformation {
410 label: ParameterLabel::Simple(param.clone()),
411 documentation: None,
412 })
413 .collect();
414 408
415 let documentation = call_info.doc.map(|it| it.conv());
416
417 let sig_info = SignatureInformation {
418 label: call_info.label,
419 documentation,
420 parameters: Some(parameters),
421 };
422 Ok(Some(req::SignatureHelp { 409 Ok(Some(req::SignatureHelp {
423 signatures: vec![sig_info], 410 signatures: vec![sig_info],
424 active_signature: Some(0), 411 active_signature: Some(0),
425 active_parameter: call_info.active_parameter.map(|it| it as i64), 412 active_parameter,
426 })) 413 }))
427 } else { 414 } else {
428 Ok(None) 415 Ok(None)