aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_ide/Cargo.toml1
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs337
-rw-r--r--crates/ra_ide/src/call_info.rs15
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs2
-rw-r--r--crates/ra_ide/src/lib.rs20
-rw-r--r--crates/ra_lsp_server/src/caps.rs6
-rw-r--r--crates/ra_lsp_server/src/conv.rs18
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs3
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs98
-rw-r--r--crates/ra_lsp_server/src/req.rs16
11 files changed, 502 insertions, 15 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 159b65d01..c570e0c3c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1032,6 +1032,7 @@ dependencies = [
1032 "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 1032 "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
1033 "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 1033 "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
1034 "fst 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 1034 "fst 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
1035 "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
1035 "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 1036 "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
1036 "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 1037 "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
1037 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1038 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index e3439ae31..2c9f9dce0 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -13,6 +13,7 @@ wasm = []
13[dependencies] 13[dependencies]
14either = "1.5" 14either = "1.5"
15format-buf = "1.0.0" 15format-buf = "1.0.0"
16indexmap = "1.3.0"
16itertools = "0.8.0" 17itertools = "0.8.0"
17join_to_string = "0.1.3" 18join_to_string = "0.1.3"
18log = "0.4.5" 19log = "0.4.5"
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
new file mode 100644
index 000000000..75658c20b
--- /dev/null
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -0,0 +1,337 @@
1//! Entry point for call-hierarchy
2
3use indexmap::IndexMap;
4
5use hir::db::AstDatabase;
6use ra_syntax::{
7 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, TextRange,
9};
10
11use crate::{
12 call_info::FnCallNode,
13 db::RootDatabase,
14 display::{ShortLabel, ToNav},
15 expand::descend_into_macros,
16 goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
17};
18
19#[derive(Default)]
20struct CallLocations {
21 funcs: IndexMap<NavigationTarget, Vec<TextRange>>,
22}
23
24impl CallLocations {
25 pub fn add(&mut self, target: &NavigationTarget, range: TextRange) {
26 self.funcs.entry(target.clone()).or_default().push(range);
27 }
28
29 pub fn into_items(self) -> Vec<CallItem> {
30 self.funcs.into_iter().map(|(target, ranges)| CallItem { target, ranges }).collect()
31 }
32}
33
34#[derive(Debug, Clone)]
35pub struct CallItem {
36 pub target: NavigationTarget,
37 pub ranges: Vec<TextRange>,
38}
39
40impl CallItem {
41 #[cfg(test)]
42 pub(crate) fn assert_match(&self, expected: &str) {
43 let actual = self.debug_render();
44 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
45 }
46
47 #[cfg(test)]
48 pub(crate) fn debug_render(&self) -> String {
49 format!("{} : {:?}", self.target.debug_render(), self.ranges)
50 }
51}
52
53pub(crate) fn call_hierarchy(
54 db: &RootDatabase,
55 position: FilePosition,
56) -> Option<RangeInfo<Vec<NavigationTarget>>> {
57 goto_definition::goto_definition(db, position)
58}
59
60pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
61 // 1. Find all refs
62 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
63 // 3. Add ranges relative to the start of the fndef.
64 let refs = references::find_all_refs(db, position, None)?;
65
66 let mut calls = CallLocations::default();
67
68 for reference in refs.info.references() {
69 let file_id = reference.file_range.file_id;
70 let file = db.parse_or_expand(file_id.into())?;
71 let token = file.token_at_offset(reference.file_range.range.start()).next()?;
72 let token = descend_into_macros(db, file_id, token);
73 let syntax = token.value.parent();
74
75 // This target is the containing function
76 if let Some(nav) = syntax.ancestors().find_map(|node| {
77 match_ast! {
78 match node {
79 ast::FnDef(it) => {
80 Some(NavigationTarget::from_named(
81 db,
82 token.with_value(&it),
83 it.doc_comment_text(),
84 it.short_label(),
85 ))
86 },
87 _ => { None },
88 }
89 }
90 }) {
91 let relative_range = reference.file_range.range;
92 calls.add(&nav, relative_range);
93 }
94 }
95
96 Some(calls.into_items())
97}
98
99pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
100 let file_id = position.file_id;
101 let file = db.parse_or_expand(file_id.into())?;
102 let token = file.token_at_offset(position.offset).next()?;
103 let token = descend_into_macros(db, file_id, token);
104 let syntax = token.value.parent();
105
106 let mut calls = CallLocations::default();
107
108 syntax
109 .descendants()
110 .filter_map(|node| FnCallNode::with_node_exact(&node))
111 .filter_map(|call_node| {
112 let name_ref = call_node.name_ref()?;
113 let name_ref = token.with_value(name_ref.syntax());
114
115 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
116
117 if let Some(func_target) = match &call_node {
118 FnCallNode::CallExpr(expr) => {
119 //FIXME: Type::as_callable is broken
120 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
121 match callable_def {
122 hir::CallableDef::FunctionId(it) => {
123 let fn_def: hir::Function = it.into();
124 let nav = fn_def.to_nav(db);
125 Some(nav)
126 }
127 _ => None,
128 }
129 }
130 FnCallNode::MethodCallExpr(expr) => {
131 let function = analyzer.resolve_method_call(&expr)?;
132 Some(function.to_nav(db))
133 }
134 FnCallNode::MacroCallExpr(expr) => {
135 let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
136 Some(macro_def.to_nav(db))
137 }
138 } {
139 Some((func_target.clone(), name_ref.value.text_range()))
140 } else {
141 None
142 }
143 })
144 .for_each(|(nav, range)| calls.add(&nav, range));
145
146 Some(calls.into_items())
147}
148
149#[cfg(test)]
150mod tests {
151 use ra_db::FilePosition;
152
153 use crate::mock_analysis::analysis_and_position;
154
155 fn check_hierarchy(
156 fixture: &str,
157 expected: &str,
158 expected_incoming: &[&str],
159 expected_outgoing: &[&str],
160 ) {
161 let (analysis, pos) = analysis_and_position(fixture);
162
163 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
164 assert_eq!(navs.len(), 1);
165 let nav = navs.pop().unwrap();
166 nav.assert_match(expected);
167
168 let item_pos = FilePosition { file_id: nav.file_id(), offset: nav.range().start() };
169 let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
170 assert_eq!(incoming_calls.len(), expected_incoming.len());
171
172 for call in 0..incoming_calls.len() {
173 incoming_calls[call].assert_match(expected_incoming[call]);
174 }
175
176 let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
177 assert_eq!(outgoing_calls.len(), expected_outgoing.len());
178
179 for call in 0..outgoing_calls.len() {
180 outgoing_calls[call].assert_match(expected_outgoing[call]);
181 }
182 }
183
184 #[test]
185 fn test_call_hierarchy_on_ref() {
186 check_hierarchy(
187 r#"
188 //- /lib.rs
189 fn callee() {}
190 fn caller() {
191 call<|>ee();
192 }
193 "#,
194 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
195 &["caller FN_DEF FileId(1) [15; 44) [18; 24) : [[33; 39)]"],
196 &[],
197 );
198 }
199
200 #[test]
201 fn test_call_hierarchy_on_def() {
202 check_hierarchy(
203 r#"
204 //- /lib.rs
205 fn call<|>ee() {}
206 fn caller() {
207 callee();
208 }
209 "#,
210 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
211 &["caller FN_DEF FileId(1) [15; 44) [18; 24) : [[33; 39)]"],
212 &[],
213 );
214 }
215
216 #[test]
217 fn test_call_hierarchy_in_same_fn() {
218 check_hierarchy(
219 r#"
220 //- /lib.rs
221 fn callee() {}
222 fn caller() {
223 call<|>ee();
224 callee();
225 }
226 "#,
227 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
228 &["caller FN_DEF FileId(1) [15; 58) [18; 24) : [[33; 39), [47; 53)]"],
229 &[],
230 );
231 }
232
233 #[test]
234 fn test_call_hierarchy_in_different_fn() {
235 check_hierarchy(
236 r#"
237 //- /lib.rs
238 fn callee() {}
239 fn caller1() {
240 call<|>ee();
241 }
242
243 fn caller2() {
244 callee();
245 }
246 "#,
247 "callee FN_DEF FileId(1) [0; 14) [3; 9)",
248 &[
249 "caller1 FN_DEF FileId(1) [15; 45) [18; 25) : [[34; 40)]",
250 "caller2 FN_DEF FileId(1) [46; 76) [49; 56) : [[65; 71)]",
251 ],
252 &[],
253 );
254 }
255
256 #[test]
257 fn test_call_hierarchy_in_different_files() {
258 check_hierarchy(
259 r#"
260 //- /lib.rs
261 mod foo;
262 use foo::callee;
263
264 fn caller() {
265 call<|>ee();
266 }
267
268 //- /foo/mod.rs
269 pub fn callee() {}
270 "#,
271 "callee FN_DEF FileId(2) [0; 18) [7; 13)",
272 &["caller FN_DEF FileId(1) [26; 55) [29; 35) : [[44; 50)]"],
273 &[],
274 );
275 }
276
277 #[test]
278 fn test_call_hierarchy_outgoing() {
279 check_hierarchy(
280 r#"
281 //- /lib.rs
282 fn callee() {}
283 fn call<|>er() {
284 callee();
285 callee();
286 }
287 "#,
288 "caller FN_DEF FileId(1) [15; 58) [18; 24)",
289 &[],
290 &["callee FN_DEF FileId(1) [0; 14) [3; 9) : [[33; 39), [47; 53)]"],
291 );
292 }
293
294 #[test]
295 fn test_call_hierarchy_outgoing_in_different_files() {
296 check_hierarchy(
297 r#"
298 //- /lib.rs
299 mod foo;
300 use foo::callee;
301
302 fn call<|>er() {
303 callee();
304 }
305
306 //- /foo/mod.rs
307 pub fn callee() {}
308 "#,
309 "caller FN_DEF FileId(1) [26; 55) [29; 35)",
310 &[],
311 &["callee FN_DEF FileId(2) [0; 18) [7; 13) : [[44; 50)]"],
312 );
313 }
314
315 #[test]
316 fn test_call_hierarchy_incoming_outgoing() {
317 check_hierarchy(
318 r#"
319 //- /lib.rs
320 fn caller1() {
321 call<|>er2();
322 }
323
324 fn caller2() {
325 caller3();
326 }
327
328 fn caller3() {
329
330 }
331 "#,
332 "caller2 FN_DEF FileId(1) [32; 63) [35; 42)",
333 &["caller1 FN_DEF FileId(1) [0; 31) [3; 10) : [[19; 26)]"],
334 &["caller3 FN_DEF FileId(1) [64; 80) [67; 74) : [[51; 58)]"],
335 );
336 }
337}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 2c2b6fa48..a7023529b 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -88,7 +88,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
88} 88}
89 89
90#[derive(Debug)] 90#[derive(Debug)]
91enum FnCallNode { 91pub(crate) enum FnCallNode {
92 CallExpr(ast::CallExpr), 92 CallExpr(ast::CallExpr),
93 MethodCallExpr(ast::MethodCallExpr), 93 MethodCallExpr(ast::MethodCallExpr),
94 MacroCallExpr(ast::MacroCall), 94 MacroCallExpr(ast::MacroCall),
@@ -108,7 +108,18 @@ impl FnCallNode {
108 }) 108 })
109 } 109 }
110 110
111 fn name_ref(&self) -> Option<ast::NameRef> { 111 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
112 match_ast! {
113 match node {
114 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
115 ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
116 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
117 _ => { None },
118 }
119 }
120 }
121
122 pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
112 match self { 123 match self {
113 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? { 124 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
114 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, 125 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index b9ae67828..f2e45fa31 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -19,7 +19,7 @@ use super::short_label::ShortLabel;
19/// 19///
20/// Typically, a `NavigationTarget` corresponds to some element in the source 20/// Typically, a `NavigationTarget` corresponds to some element in the source
21/// code, like a function or a struct, but this is not strictly required. 21/// code, like a function or a struct, but this is not strictly required.
22#[derive(Debug, Clone)] 22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub struct NavigationTarget { 23pub struct NavigationTarget {
24 file_id: FileId, 24 file_id: FileId,
25 name: SmolStr, 25 name: SmolStr,
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 779a81b2c..06497617b 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -24,6 +24,7 @@ mod goto_definition;
24mod goto_type_definition; 24mod goto_type_definition;
25mod extend_selection; 25mod extend_selection;
26mod hover; 26mod hover;
27mod call_hierarchy;
27mod call_info; 28mod call_info;
28mod syntax_highlighting; 29mod syntax_highlighting;
29mod parent_module; 30mod parent_module;
@@ -62,6 +63,7 @@ use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol};
62 63
63pub use crate::{ 64pub use crate::{
64 assists::{Assist, AssistId}, 65 assists::{Assist, AssistId},
66 call_hierarchy::CallItem,
65 change::{AnalysisChange, LibraryData}, 67 change::{AnalysisChange, LibraryData},
66 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 68 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
67 diagnostics::Severity, 69 diagnostics::Severity,
@@ -412,6 +414,24 @@ impl Analysis {
412 self.with_db(|db| call_info::call_info(db, position)) 414 self.with_db(|db| call_info::call_info(db, position))
413 } 415 }
414 416
417 /// Computes call hierarchy candidates for the given file position.
418 pub fn call_hierarchy(
419 &self,
420 position: FilePosition,
421 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
422 self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
423 }
424
425 /// Computes incoming calls for the given file position.
426 pub fn incoming_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> {
427 self.with_db(|db| call_hierarchy::incoming_calls(db, position))
428 }
429
430 /// Computes incoming calls for the given file position.
431 pub fn outgoing_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> {
432 self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
433 }
434
415 /// Returns a `mod name;` declaration which created the current module. 435 /// Returns a `mod name;` declaration which created the current module.
416 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 436 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
417 self.with_db(|db| parent_module::parent_module(db, position)) 437 self.with_db(|db| parent_module::parent_module(db, position))
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index db502c200..c4711076c 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -1,8 +1,8 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2 2
3use lsp_types::{ 3use lsp_types::{
4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions,
5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, 5 CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, 6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, 7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, 8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
@@ -56,7 +56,7 @@ pub fn server_capabilities() -> ServerCapabilities {
56 color_provider: None, 56 color_provider: None,
57 execute_command_provider: None, 57 execute_command_provider: None,
58 workspace: None, 58 workspace: None,
59 call_hierarchy_provider: None, 59 call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
60 experimental: Default::default(), 60 experimental: Default::default(),
61 } 61 }
62} 62}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index e93d4ea33..c260b51c4 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -490,6 +490,24 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>)
490 } 490 }
491} 491}
492 492
493pub fn to_call_hierarchy_item(
494 file_id: FileId,
495 range: TextRange,
496 world: &WorldSnapshot,
497 line_index: &LineIndex,
498 nav: NavigationTarget,
499) -> Result<lsp_types::CallHierarchyItem> {
500 Ok(lsp_types::CallHierarchyItem {
501 name: nav.name().to_string(),
502 kind: nav.kind().conv(),
503 tags: None,
504 detail: nav.description().map(|it| it.to_string()),
505 uri: file_id.try_conv_with(&world)?,
506 range: nav.range().conv_with(&line_index),
507 selection_range: range.conv_with(&line_index),
508 })
509}
510
493pub fn to_location( 511pub fn to_location(
494 file_id: FileId, 512 file_id: FileId,
495 range: TextRange, 513 range: TextRange,
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 4336583fe..047c86a3b 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -499,6 +499,9 @@ fn on_request(
499 .on::<req::Formatting>(handlers::handle_formatting)? 499 .on::<req::Formatting>(handlers::handle_formatting)?
500 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? 500 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)?
501 .on::<req::InlayHints>(handlers::handle_inlay_hints)? 501 .on::<req::InlayHints>(handlers::handle_inlay_hints)?
502 .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)?
503 .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)?
504 .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)?
502 .finish(); 505 .finish();
503 Ok(()) 506 Ok(())
504} 507}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index c8f52eb0e..a5b6f48af 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -5,13 +5,16 @@ use std::{fmt::Write as _, io::Write as _};
5 5
6use lsp_server::ErrorCode; 6use lsp_server::ErrorCode;
7use lsp_types::{ 7use lsp_types::{
8 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
9 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
8 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 10 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
9 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 11 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
10 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 12 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
11 Range, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, 13 Range, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit,
12}; 14};
13use ra_ide::{ 15use ra_ide::{
14 AssistId, FileId, FilePosition, FileRange, Query, Runnable, RunnableKind, SearchScope, 16 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
17 SearchScope,
15}; 18};
16use ra_prof::profile; 19use ra_prof::profile;
17use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; 20use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
@@ -21,7 +24,10 @@ use serde_json::to_value;
21 24
22use crate::{ 25use crate::{
23 cargo_target_spec::{runnable_args, CargoTargetSpec}, 26 cargo_target_spec::{runnable_args, CargoTargetSpec},
24 conv::{to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, TryConvWithToVec}, 27 conv::{
28 to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith,
29 TryConvWithToVec,
30 },
25 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, 31 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind},
26 world::WorldSnapshot, 32 world::WorldSnapshot,
27 LspError, Result, 33 LspError, Result,
@@ -936,3 +942,91 @@ pub fn handle_inlay_hints(
936 }) 942 })
937 .collect()) 943 .collect())
938} 944}
945
946pub fn handle_call_hierarchy_prepare(
947 world: WorldSnapshot,
948 params: CallHierarchyPrepareParams,
949) -> Result<Option<Vec<CallHierarchyItem>>> {
950 let _p = profile("handle_call_hierarchy_prepare");
951 let position = params.text_document_position_params.try_conv_with(&world)?;
952 let file_id = position.file_id;
953
954 let nav_info = match world.analysis().call_hierarchy(position)? {
955 None => return Ok(None),
956 Some(it) => it,
957 };
958
959 let line_index = world.analysis().file_line_index(file_id)?;
960 let RangeInfo { range, info: navs } = nav_info;
961 let res = navs
962 .into_iter()
963 .filter(|it| it.kind() == SyntaxKind::FN_DEF)
964 .filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok())
965 .collect();
966
967 Ok(Some(res))
968}
969
970pub fn handle_call_hierarchy_incoming(
971 world: WorldSnapshot,
972 params: CallHierarchyIncomingCallsParams,
973) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
974 let _p = profile("handle_call_hierarchy_incoming");
975 let item = params.item;
976
977 let doc = TextDocumentIdentifier::new(item.uri);
978 let frange: FileRange = (&doc, item.range).try_conv_with(&world)?;
979 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
980
981 let call_items = match world.analysis().incoming_calls(fpos)? {
982 None => return Ok(None),
983 Some(it) => it,
984 };
985
986 let mut res = vec![];
987
988 for call_item in call_items.into_iter() {
989 let file_id = call_item.target.file_id();
990 let line_index = world.analysis().file_line_index(file_id)?;
991 let range = call_item.target.range();
992 let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?;
993 res.push(CallHierarchyIncomingCall {
994 from: item,
995 from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(),
996 });
997 }
998
999 Ok(Some(res))
1000}
1001
1002pub fn handle_call_hierarchy_outgoing(
1003 world: WorldSnapshot,
1004 params: CallHierarchyOutgoingCallsParams,
1005) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1006 let _p = profile("handle_call_hierarchy_outgoing");
1007 let item = params.item;
1008
1009 let doc = TextDocumentIdentifier::new(item.uri);
1010 let frange: FileRange = (&doc, item.range).try_conv_with(&world)?;
1011 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1012
1013 let call_items = match world.analysis().outgoing_calls(fpos)? {
1014 None => return Ok(None),
1015 Some(it) => it,
1016 };
1017
1018 let mut res = vec![];
1019
1020 for call_item in call_items.into_iter() {
1021 let file_id = call_item.target.file_id();
1022 let line_index = world.analysis().file_line_index(file_id)?;
1023 let range = call_item.target.range();
1024 let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?;
1025 res.push(CallHierarchyOutgoingCall {
1026 to: item,
1027 from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(),
1028 });
1029 }
1030
1031 Ok(Some(res))
1032}
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 40edaf677..8098ff31d 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -6,13 +6,15 @@ use serde::{Deserialize, Serialize};
6 6
7pub use lsp_types::{ 7pub use lsp_types::{
8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, 8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
9 CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, 9 CodeLensParams, CompletionParams, CompletionResponse, DiagnosticTag,
10 DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, 10 DidChangeConfigurationParams, DidChangeWatchedFilesParams,
11 DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, 11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
12 FileSystemWatcher, Hover, InitializeResult, MessageType, ProgressParams, ProgressParamsValue, 12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
13 ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, 13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
14 SelectionRange, SelectionRangeParams, ShowMessageParams, SignatureHelp, TextDocumentEdit, 14 PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange,
15 TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, 15 SelectionRangeParams, ServerCapabilities, ShowMessageParams, SignatureHelp, SymbolKind,
16 TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkDoneProgressParams, WorkspaceEdit,
17 WorkspaceSymbolParams,
16}; 18};
17 19
18pub enum AnalyzerStatus {} 20pub enum AnalyzerStatus {}