aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/call_info.rs66
-rw-r--r--crates/ra_ide_api/src/completion.rs19
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs35
-rw-r--r--crates/ra_ide_api/src/impls.rs120
-rw-r--r--crates/ra_ide_api/src/lib.rs11
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs10
6 files changed, 187 insertions, 74 deletions
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index ee1e13799..2eb388e0e 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -3,9 +3,10 @@ use ra_db::SourceDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SyntaxNode, TextUnit, TextRange, 4 AstNode, SyntaxNode, TextUnit, TextRange,
5 SyntaxKind::FN_DEF, 5 SyntaxKind::FN_DEF,
6 ast::{self, ArgListOwner, DocCommentsOwner}, 6 ast::{self, ArgListOwner},
7 algo::find_node_at_offset, 7 algo::find_node_at_offset,
8}; 8};
9use hir::Docs;
9 10
10use crate::{FilePosition, CallInfo, db::RootDatabase}; 11use crate::{FilePosition, CallInfo, db::RootDatabase};
11 12
@@ -26,7 +27,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
26 let fn_file = db.parse(symbol.file_id); 27 let fn_file = db.parse(symbol.file_id);
27 let fn_def = symbol.ptr.to_node(&fn_file); 28 let fn_def = symbol.ptr.to_node(&fn_file);
28 let fn_def = ast::FnDef::cast(fn_def).unwrap(); 29 let fn_def = ast::FnDef::cast(fn_def).unwrap();
29 let mut call_info = CallInfo::new(fn_def)?; 30 let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?;
31
32 let mut call_info = CallInfo::new(db, function, fn_def)?;
30 // If we have a calling expression let's find which argument we are on 33 // If we have a calling expression let's find which argument we are on
31 let num_params = call_info.parameters.len(); 34 let num_params = call_info.parameters.len();
32 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 35 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
@@ -110,46 +113,13 @@ impl<'a> FnCallNode<'a> {
110} 113}
111 114
112impl CallInfo { 115impl CallInfo {
113 fn new(node: &ast::FnDef) -> Option<Self> { 116 fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option<Self> {
114 let label: String = if let Some(body) = node.body() { 117 let label = crate::completion::function_label(node)?;
115 let body_range = body.syntax().range(); 118 let doc = function.docs(db);
116 let label: String = node
117 .syntax()
118 .children()
119 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
120 .filter(|child| ast::Comment::cast(child).is_none()) // Filter out doc comments
121 .map(|node| node.text().to_string())
122 .collect();
123 label
124 } else {
125 node.syntax().text().to_string()
126 };
127
128 let mut doc = None;
129 if let Some(docs) = node.doc_comment_text() {
130 // Massage markdown
131 let mut processed_lines = Vec::new();
132 let mut in_code_block = false;
133 for line in docs.lines() {
134 if line.starts_with("```") {
135 in_code_block = !in_code_block;
136 }
137
138 let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
139 "```rust".into()
140 } else {
141 line.to_string()
142 };
143
144 processed_lines.push(line);
145 }
146
147 doc = Some(processed_lines.join("\n"));
148 }
149 119
150 Some(CallInfo { 120 Some(CallInfo {
151 parameters: param_list(node), 121 parameters: param_list(node),
152 label: label.trim().to_owned(), 122 label,
153 doc, 123 doc,
154 active_parameter: None, 124 active_parameter: None,
155 }) 125 })
@@ -284,7 +254,7 @@ fn bar() {
284 assert_eq!(info.parameters, vec!["j".to_string()]); 254 assert_eq!(info.parameters, vec!["j".to_string()]);
285 assert_eq!(info.active_parameter, Some(0)); 255 assert_eq!(info.active_parameter, Some(0));
286 assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); 256 assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
287 assert_eq!(info.doc, Some("test".into())); 257 assert_eq!(info.doc.map(|it| it.into()), Some("test".to_string()));
288 } 258 }
289 259
290 #[test] 260 #[test]
@@ -313,18 +283,18 @@ pub fn do() {
313 assert_eq!(info.active_parameter, Some(0)); 283 assert_eq!(info.active_parameter, Some(0));
314 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 284 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
315 assert_eq!( 285 assert_eq!(
316 info.doc, 286 info.doc.map(|it| it.into()),
317 Some( 287 Some(
318 r#"Adds one to the number given. 288 r#"Adds one to the number given.
319 289
320# Examples 290# Examples
321 291
322```rust 292```
323let five = 5; 293let five = 5;
324 294
325assert_eq!(6, my_crate::add_one(5)); 295assert_eq!(6, my_crate::add_one(5));
326```"# 296```"#
327 .into() 297 .to_string()
328 ) 298 )
329 ); 299 );
330 } 300 }
@@ -359,18 +329,18 @@ pub fn do_it() {
359 assert_eq!(info.active_parameter, Some(0)); 329 assert_eq!(info.active_parameter, Some(0));
360 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 330 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
361 assert_eq!( 331 assert_eq!(
362 info.doc, 332 info.doc.map(|it| it.into()),
363 Some( 333 Some(
364 r#"Adds one to the number given. 334 r#"Adds one to the number given.
365 335
366# Examples 336# Examples
367 337
368```rust 338```
369let five = 5; 339let five = 5;
370 340
371assert_eq!(6, my_crate::add_one(5)); 341assert_eq!(6, my_crate::add_one(5));
372```"# 342```"#
373 .into() 343 .to_string()
374 ) 344 )
375 ); 345 );
376 } 346 }
@@ -414,12 +384,12 @@ pub fn foo() {
414 ); 384 );
415 assert_eq!(info.active_parameter, Some(1)); 385 assert_eq!(info.active_parameter, Some(1));
416 assert_eq!( 386 assert_eq!(
417 info.doc, 387 info.doc.map(|it| it.into()),
418 Some( 388 Some(
419 r#"Method is called when writer finishes. 389 r#"Method is called when writer finishes.
420 390
421By default this method stops actor's `Context`."# 391By default this method stops actor's `Context`."#
422 .into() 392 .to_string()
423 ) 393 )
424 ); 394 );
425 } 395 }
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs
index b1867de42..722d94f3a 100644
--- a/crates/ra_ide_api/src/completion.rs
+++ b/crates/ra_ide_api/src/completion.rs
@@ -10,6 +10,7 @@ mod complete_scope;
10mod complete_postfix; 10mod complete_postfix;
11 11
12use ra_db::SourceDatabase; 12use ra_db::SourceDatabase;
13use ra_syntax::ast::{self, AstNode};
13 14
14use crate::{ 15use crate::{
15 db, 16 db,
@@ -61,3 +62,21 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti
61 complete_postfix::complete_postfix(&mut acc, &ctx); 62 complete_postfix::complete_postfix(&mut acc, &ctx);
62 Some(acc) 63 Some(acc)
63} 64}
65
66pub fn function_label(node: &ast::FnDef) -> Option<String> {
67 let label: String = if let Some(body) = node.body() {
68 let body_range = body.syntax().range();
69 let label: String = node
70 .syntax()
71 .children()
72 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
73 .filter(|child| ast::Comment::cast(child).is_none()) // Filter out comments
74 .map(|node| node.text().to_string())
75 .collect();
76 label
77 } else {
78 node.syntax().text().to_string()
79 };
80
81 Some(label.trim().to_owned())
82}
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs
index 49bd636a5..d3bc14894 100644
--- a/crates/ra_ide_api/src/completion/completion_item.rs
+++ b/crates/ra_ide_api/src/completion/completion_item.rs
@@ -1,12 +1,12 @@
1use hir::{Docs, Documentation}; 1use hir::{Docs, Documentation};
2use ra_syntax::{ 2use ra_syntax::TextRange;
3 ast::{self, AstNode},
4 TextRange,
5};
6use ra_text_edit::TextEdit; 3use ra_text_edit::TextEdit;
7use test_utils::tested_by; 4use test_utils::tested_by;
8 5
9use crate::completion::completion_context::CompletionContext; 6use crate::completion::{
7 completion_context::CompletionContext,
8 function_label,
9};
10 10
11/// `CompletionItem` describes a single completion variant in the editor pop-up. 11/// `CompletionItem` describes a single completion variant in the editor pop-up.
12/// It is basically a POD with various properties. To construct a 12/// It is basically a POD with various properties. To construct a
@@ -97,8 +97,8 @@ impl CompletionItem {
97 self.detail.as_ref().map(|it| it.as_str()) 97 self.detail.as_ref().map(|it| it.as_str())
98 } 98 }
99 /// A doc-comment 99 /// A doc-comment
100 pub fn documentation(&self) -> Option<&str> { 100 pub fn documentation(&self) -> Option<Documentation> {
101 self.documentation.as_ref().map(|it| it.contents()) 101 self.documentation.clone()
102 } 102 }
103 /// What string is used for filtering. 103 /// What string is used for filtering.
104 pub fn lookup(&self) -> &str { 104 pub fn lookup(&self) -> &str {
@@ -252,7 +252,7 @@ impl Builder {
252 self.documentation = Some(docs); 252 self.documentation = Some(docs);
253 } 253 }
254 254
255 if let Some(label) = function_label(ctx, function) { 255 if let Some(label) = function_item_label(ctx, function) {
256 self.detail = Some(label); 256 self.detail = Some(label);
257 } 257 }
258 258
@@ -292,24 +292,9 @@ impl Into<Vec<CompletionItem>> for Completions {
292 } 292 }
293} 293}
294 294
295fn function_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> { 295fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> {
296 let node = function.source(ctx.db).1; 296 let node = function.source(ctx.db).1;
297 297 function_label(&node)
298 let label: String = if let Some(body) = node.body() {
299 let body_range = body.syntax().range();
300 let label: String = node
301 .syntax()
302 .children()
303 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
304 .filter(|child| ast::Comment::cast(child).is_none()) // Filter out comments
305 .map(|node| node.text().to_string())
306 .collect();
307 label
308 } else {
309 node.syntax().text().to_string()
310 };
311
312 Some(label.trim().to_owned())
313} 298}
314 299
315#[cfg(test)] 300#[cfg(test)]
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs
new file mode 100644
index 000000000..469d56d63
--- /dev/null
+++ b/crates/ra_ide_api/src/impls.rs
@@ -0,0 +1,120 @@
1use ra_db::{SourceDatabase};
2use ra_syntax::{
3 AstNode, ast,
4 algo::find_node_at_offset,
5};
6use hir::{db::HirDatabase, source_binder};
7
8use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
9
10pub(crate) fn goto_implementation(
11 db: &RootDatabase,
12 position: FilePosition,
13) -> Option<RangeInfo<Vec<NavigationTarget>>> {
14 let file = db.parse(position.file_id);
15 let syntax = file.syntax();
16
17 let module = source_binder::module_from_position(db, position)?;
18 let krate = module.krate(db)?;
19
20 let node = find_node_at_offset::<ast::NominalDef>(syntax, position.offset)?;
21 let ty = match node.kind() {
22 ast::NominalDefKind::StructDef(def) => {
23 source_binder::struct_from_module(db, module, &def).ty(db)
24 }
25 ast::NominalDefKind::EnumDef(def) => {
26 source_binder::enum_from_module(db, module, &def).ty(db)
27 }
28 };
29
30 let impls = db.impls_in_crate(krate);
31
32 let navs = impls
33 .lookup_impl_blocks(db, &ty)
34 .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp));
35
36 Some(RangeInfo::new(node.syntax().range(), navs.collect()))
37}
38
39#[cfg(test)]
40mod tests {
41 use crate::mock_analysis::analysis_and_position;
42
43 fn check_goto(fixuture: &str, expected: &[&str]) {
44 let (analysis, pos) = analysis_and_position(fixuture);
45
46 let navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
47 assert_eq!(navs.len(), expected.len());
48 navs.into_iter()
49 .enumerate()
50 .for_each(|(i, nav)| nav.assert_match(expected[i]));
51 }
52
53 #[test]
54 fn goto_implementation_works() {
55 check_goto(
56 "
57 //- /lib.rs
58 struct Foo<|>;
59 impl Foo {}
60 ",
61 &["impl IMPL_BLOCK FileId(1) [12; 23)"],
62 );
63 }
64
65 #[test]
66 fn goto_implementation_works_multiple_blocks() {
67 check_goto(
68 "
69 //- /lib.rs
70 struct Foo<|>;
71 impl Foo {}
72 impl Foo {}
73 ",
74 &[
75 "impl IMPL_BLOCK FileId(1) [12; 23)",
76 "impl IMPL_BLOCK FileId(1) [24; 35)",
77 ],
78 );
79 }
80
81 #[test]
82 fn goto_implementation_works_multiple_mods() {
83 check_goto(
84 "
85 //- /lib.rs
86 struct Foo<|>;
87 mod a {
88 impl super::Foo {}
89 }
90 mod b {
91 impl super::Foo {}
92 }
93 ",
94 &[
95 "impl IMPL_BLOCK FileId(1) [24; 42)",
96 "impl IMPL_BLOCK FileId(1) [57; 75)",
97 ],
98 );
99 }
100
101 #[test]
102 fn goto_implementation_works_multiple_files() {
103 check_goto(
104 "
105 //- /lib.rs
106 struct Foo<|>;
107 mod a;
108 mod b;
109 //- /a.rs
110 impl crate::Foo {}
111 //- /b.rs
112 impl crate::Foo {}
113 ",
114 &[
115 "impl IMPL_BLOCK FileId(2) [0; 18)",
116 "impl IMPL_BLOCK FileId(3) [0; 18)",
117 ],
118 );
119 }
120}
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 51947e4cc..5d8acf9df 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -25,6 +25,7 @@ mod call_info;
25mod syntax_highlighting; 25mod syntax_highlighting;
26mod parent_module; 26mod parent_module;
27mod rename; 27mod rename;
28mod impls;
28 29
29#[cfg(test)] 30#[cfg(test)]
30mod marks; 31mod marks;
@@ -58,6 +59,7 @@ pub use ra_ide_api_light::{
58pub use ra_db::{ 59pub use ra_db::{
59 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId 60 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId
60}; 61};
62pub use hir::Documentation;
61 63
62// We use jemalloc mainly to get heap usage statistics, actual performance 64// We use jemalloc mainly to get heap usage statistics, actual performance
63// differnece is not measures. 65// differnece is not measures.
@@ -266,7 +268,7 @@ impl<T> RangeInfo<T> {
266#[derive(Debug)] 268#[derive(Debug)]
267pub struct CallInfo { 269pub struct CallInfo {
268 pub label: String, 270 pub label: String,
269 pub doc: Option<String>, 271 pub doc: Option<Documentation>,
270 pub parameters: Vec<String>, 272 pub parameters: Vec<String>,
271 pub active_parameter: Option<usize>, 273 pub active_parameter: Option<usize>,
272} 274}
@@ -415,6 +417,13 @@ impl Analysis {
415 self.with_db(|db| goto_definition::goto_definition(db, position)) 417 self.with_db(|db| goto_definition::goto_definition(db, position))
416 } 418 }
417 419
420 pub fn goto_implementation(
421 &self,
422 position: FilePosition,
423 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
424 self.with_db(|db| impls::goto_implementation(db, position))
425 }
426
418 /// Finds all usages of the reference at point. 427 /// Finds all usages of the reference at point.
419 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 428 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
420 self.with_db(|db| db.find_all_refs(position)) 429 self.with_db(|db| db.find_all_refs(position))
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs
index d73d4afa7..5ccb5cc2e 100644
--- a/crates/ra_ide_api/src/navigation_target.rs
+++ b/crates/ra_ide_api/src/navigation_target.rs
@@ -147,6 +147,16 @@ impl NavigationTarget {
147 } 147 }
148 } 148 }
149 149
150 pub(crate) fn from_impl_block(
151 db: &RootDatabase,
152 module: hir::Module,
153 impl_block: &hir::ImplBlock,
154 ) -> NavigationTarget {
155 let (file_id, _) = module.definition_source(db);
156 let node = module.impl_source(db, impl_block.id());
157 NavigationTarget::from_syntax(file_id, "impl".into(), None, node.syntax())
158 }
159
150 #[cfg(test)] 160 #[cfg(test)]
151 pub(crate) fn assert_match(&self, expected: &str) { 161 pub(crate) fn assert_match(&self, expected: &str) {
152 let actual = self.debug_render(); 162 let actual = self.debug_render();