aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--README.md1
-rw-r--r--crates/ra_db/src/fixture.rs6
-rw-r--r--crates/ra_db/src/input.rs8
-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/change.rs3
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs2
-rw-r--r--crates/ra_ide/src/feature_flags.rs1
-rw-r--r--crates/ra_ide/src/lib.rs22
-rw-r--r--crates/ra_ide/src/references.rs112
-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.rs42
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs98
-rw-r--r--crates/ra_lsp_server/src/req.rs16
-rw-r--r--crates/ra_parser/src/grammar/items/traits.rs10
-rw-r--r--crates/ra_project_model/src/lib.rs14
-rw-r--r--crates/ra_project_model/src/sysroot.rs5
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/algo.rs3
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs3
-rw-r--r--crates/ra_syntax/src/syntax_node.rs8
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt128
-rw-r--r--docs/dev/lsp-features.md4
-rw-r--r--docs/user/README.md2
-rw-r--r--xtask/src/codegen.rs24
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs2
-rw-r--r--xtask/src/codegen/gen_syntax.rs4
-rw-r--r--xtask/src/lib.rs37
32 files changed, 836 insertions, 108 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 159b65d01..ba9a201b9 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)",
@@ -1136,7 +1137,7 @@ dependencies = [
1136 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1137 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
1137 "ra_parser 0.1.0", 1138 "ra_parser 0.1.0",
1138 "ra_text_edit 0.1.0", 1139 "ra_text_edit 0.1.0",
1139 "rowan 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 1140 "rowan 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
1140 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1141 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
1141 "rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1142 "rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1142 "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", 1143 "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1392,7 +1393,7 @@ dependencies = [
1392 1393
1393[[package]] 1394[[package]]
1394name = "rowan" 1395name = "rowan"
1395version = "0.8.2" 1396version = "0.9.0"
1396source = "registry+https://github.com/rust-lang/crates.io-index" 1397source = "registry+https://github.com/rust-lang/crates.io-index"
1397dependencies = [ 1398dependencies = [
1398 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1399 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1894,7 +1895,7 @@ dependencies = [
1894"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" 1895"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
1895"checksum relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bedde000f40f2921ce439ea165c9c53fd629bfa115140c72e22aceacb4a21954" 1896"checksum relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bedde000f40f2921ce439ea165c9c53fd629bfa115140c72e22aceacb4a21954"
1896"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 1897"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
1897"checksum rowan 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3eb10a10a48f0f809a217bcf074b85a03dcf79831bae80e7f1a043d0897463e2" 1898"checksum rowan 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d03d4eff7a4e8dcc362e4c06bb2b1b33af4bcd64336c7f40a31a05850336b6c"
1898"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 1899"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
1899"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" 1900"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8"
1900"checksum rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" 1901"checksum rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5"
diff --git a/README.md b/README.md
index deaa147ba..89d495aab 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ Work on the Rust Analyzer is sponsored by
11 11
12[<img src="https://user-images.githubusercontent.com/1711539/58105231-cf306900-7bee-11e9-83d8-9f1102e59d29.png" alt="Ferrous Systems" width="300">](https://ferrous-systems.com/) 12[<img src="https://user-images.githubusercontent.com/1711539/58105231-cf306900-7bee-11e9-83d8-9f1102e59d29.png" alt="Ferrous Systems" width="300">](https://ferrous-systems.com/)
13- [Mozilla](https://www.mozilla.org/en-US/) 13- [Mozilla](https://www.mozilla.org/en-US/)
14- [freiheit.com](https://www.freiheit.com)
14 15
15## Language Server Quick Start 16## Language Server Quick Start
16 17
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index e8f335e33..30b598e9a 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -49,7 +49,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId {
49 let file_id = FileId(0); 49 let file_id = FileId(0);
50 let rel_path: RelativePathBuf = "/main.rs".into(); 50 let rel_path: RelativePathBuf = "/main.rs".into();
51 51
52 let mut source_root = SourceRoot::default(); 52 let mut source_root = SourceRoot::new_local();
53 source_root.insert_file(rel_path.clone(), file_id); 53 source_root.insert_file(rel_path.clone(), file_id);
54 54
55 let mut crate_graph = CrateGraph::default(); 55 let mut crate_graph = CrateGraph::default();
@@ -77,7 +77,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
77 let mut crate_deps = Vec::new(); 77 let mut crate_deps = Vec::new();
78 let mut default_crate_root: Option<FileId> = None; 78 let mut default_crate_root: Option<FileId> = None;
79 79
80 let mut source_root = SourceRoot::default(); 80 let mut source_root = SourceRoot::new_local();
81 let mut source_root_id = WORKSPACE; 81 let mut source_root_id = WORKSPACE;
82 let mut source_root_prefix: RelativePathBuf = "/".into(); 82 let mut source_root_prefix: RelativePathBuf = "/".into();
83 let mut file_id = FileId(0); 83 let mut file_id = FileId(0);
@@ -87,7 +87,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
87 for entry in fixture.iter() { 87 for entry in fixture.iter() {
88 let meta = match parse_meta(&entry.meta) { 88 let meta = match parse_meta(&entry.meta) {
89 ParsedMeta::Root { path } => { 89 ParsedMeta::Root { path } => {
90 let source_root = std::mem::replace(&mut source_root, SourceRoot::default()); 90 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local());
91 db.set_source_root(source_root_id, Arc::new(source_root)); 91 db.set_source_root(source_root_id, Arc::new(source_root));
92 source_root_id.0 += 1; 92 source_root_id.0 += 1;
93 source_root_prefix = path; 93 source_root_prefix = path;
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 2a7ed20d1..07269237a 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -33,7 +33,7 @@ pub struct FileId(pub u32);
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
34pub struct SourceRootId(pub u32); 34pub struct SourceRootId(pub u32);
35 35
36#[derive(Default, Clone, Debug, PartialEq, Eq)] 36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct SourceRoot { 37pub struct SourceRoot {
38 /// Sysroot or crates.io library. 38 /// Sysroot or crates.io library.
39 /// 39 ///
@@ -44,11 +44,11 @@ pub struct SourceRoot {
44} 44}
45 45
46impl SourceRoot { 46impl SourceRoot {
47 pub fn new() -> SourceRoot { 47 pub fn new_local() -> SourceRoot {
48 Default::default() 48 SourceRoot { is_library: false, files: Default::default() }
49 } 49 }
50 pub fn new_library() -> SourceRoot { 50 pub fn new_library() -> SourceRoot {
51 SourceRoot { is_library: true, ..SourceRoot::new() } 51 SourceRoot { is_library: true, files: Default::default() }
52 } 52 }
53 pub fn insert_file(&mut self, path: RelativePathBuf, file_id: FileId) { 53 pub fn insert_file(&mut self, path: RelativePathBuf, file_id: FileId) {
54 self.files.insert(path, file_id); 54 self.files.insert(path, file_id);
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..1cb712e32
--- /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(Debug, Clone)]
20pub struct CallItem {
21 pub target: NavigationTarget,
22 pub ranges: Vec<TextRange>,
23}
24
25impl CallItem {
26 #[cfg(test)]
27 pub(crate) fn assert_match(&self, expected: &str) {
28 let actual = self.debug_render();
29 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
30 }
31
32 #[cfg(test)]
33 pub(crate) fn debug_render(&self) -> String {
34 format!("{} : {:?}", self.target.debug_render(), self.ranges)
35 }
36}
37
38pub(crate) fn call_hierarchy(
39 db: &RootDatabase,
40 position: FilePosition,
41) -> Option<RangeInfo<Vec<NavigationTarget>>> {
42 goto_definition::goto_definition(db, position)
43}
44
45pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
46 // 1. Find all refs
47 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
48 // 3. Add ranges relative to the start of the fndef.
49 let refs = references::find_all_refs(db, position, None)?;
50
51 let mut calls = CallLocations::default();
52
53 for reference in refs.info.references() {
54 let file_id = reference.file_range.file_id;
55 let file = db.parse_or_expand(file_id.into())?;
56 let token = file.token_at_offset(reference.file_range.range.start()).next()?;
57 let token = descend_into_macros(db, file_id, token);
58 let syntax = token.value.parent();
59
60 // This target is the containing function
61 if let Some(nav) = syntax.ancestors().find_map(|node| {
62 match_ast! {
63 match node {
64 ast::FnDef(it) => {
65 Some(NavigationTarget::from_named(
66 db,
67 token.with_value(&it),
68 it.doc_comment_text(),
69 it.short_label(),
70 ))
71 },
72 _ => { None },
73 }
74 }
75 }) {
76 let relative_range = reference.file_range.range;
77 calls.add(&nav, relative_range);
78 }
79 }
80
81 Some(calls.into_items())
82}
83
84pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
85 let file_id = position.file_id;
86 let file = db.parse_or_expand(file_id.into())?;
87 let token = file.token_at_offset(position.offset).next()?;
88 let token = descend_into_macros(db, file_id, token);
89 let syntax = token.value.parent();
90
91 let mut calls = CallLocations::default();
92
93 syntax
94 .descendants()
95 .filter_map(|node| FnCallNode::with_node_exact(&node))
96 .filter_map(|call_node| {
97 let name_ref = call_node.name_ref()?;
98 let name_ref = token.with_value(name_ref.syntax());
99
100 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
101
102 if let Some(func_target) = match &call_node {
103 FnCallNode::CallExpr(expr) => {
104 //FIXME: Type::as_callable is broken
105 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
106 match callable_def {
107 hir::CallableDef::FunctionId(it) => {
108 let fn_def: hir::Function = it.into();
109 let nav = fn_def.to_nav(db);
110 Some(nav)
111 }
112 _ => None,
113 }
114 }
115 FnCallNode::MethodCallExpr(expr) => {
116 let function = analyzer.resolve_method_call(&expr)?;
117 Some(function.to_nav(db))
118 }
119 FnCallNode::MacroCallExpr(expr) => {
120 let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
121 Some(macro_def.to_nav(db))
122 }
123 } {
124 Some((func_target.clone(), name_ref.value.text_range()))
125 } else {
126 None
127 }
128 })
129 .for_each(|(nav, range)| calls.add(&nav, range));
130
131 Some(calls.into_items())
132}
133
134#[derive(Default)]
135struct CallLocations {
136 funcs: IndexMap<NavigationTarget, Vec<TextRange>>,
137}
138
139impl CallLocations {
140 fn add(&mut self, target: &NavigationTarget, range: TextRange) {
141 self.funcs.entry(target.clone()).or_default().push(range);
142 }
143
144 fn into_items(self) -> Vec<CallItem> {
145 self.funcs.into_iter().map(|(target, ranges)| CallItem { target, ranges }).collect()
146 }
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/change.rs b/crates/ra_ide/src/change.rs
index 8b197d642..b0aa2c8e0 100644
--- a/crates/ra_ide/src/change.rs
+++ b/crates/ra_ide/src/change.rs
@@ -176,7 +176,8 @@ impl RootDatabase {
176 if !change.new_roots.is_empty() { 176 if !change.new_roots.is_empty() {
177 let mut local_roots = Vec::clone(&self.local_roots()); 177 let mut local_roots = Vec::clone(&self.local_roots());
178 for (root_id, is_local) in change.new_roots { 178 for (root_id, is_local) in change.new_roots {
179 let root = if is_local { SourceRoot::new() } else { SourceRoot::new_library() }; 179 let root =
180 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() };
180 let durability = durability(&root); 181 let durability = durability(&root);
181 self.set_source_root_with_durability(root_id, Arc::new(root), durability); 182 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
182 if is_local { 183 if is_local {
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/feature_flags.rs b/crates/ra_ide/src/feature_flags.rs
index de4ae513d..85617640d 100644
--- a/crates/ra_ide/src/feature_flags.rs
+++ b/crates/ra_ide/src/feature_flags.rs
@@ -56,6 +56,7 @@ impl Default for FeatureFlags {
56 ("completion.insertion.add-call-parenthesis", true), 56 ("completion.insertion.add-call-parenthesis", true),
57 ("completion.enable-postfix", true), 57 ("completion.enable-postfix", true),
58 ("notifications.workspace-loaded", true), 58 ("notifications.workspace-loaded", true),
59 ("notifications.cargo-toml-not-found", true),
59 ]) 60 ])
60 } 61 }
61} 62}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 779a81b2c..7b187eba3 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,
@@ -73,7 +75,7 @@ pub use crate::{
73 inlay_hints::{InlayHint, InlayKind}, 75 inlay_hints::{InlayHint, InlayKind},
74 line_index::{LineCol, LineIndex}, 76 line_index::{LineCol, LineIndex},
75 line_index_utils::translate_offset_with_edit, 77 line_index_utils::translate_offset_with_edit,
76 references::{ReferenceSearchResult, SearchScope}, 78 references::{Reference, ReferenceKind, ReferenceSearchResult, SearchScope},
77 runnables::{Runnable, RunnableKind}, 79 runnables::{Runnable, RunnableKind},
78 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 80 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
79 syntax_highlighting::HighlightedRange, 81 syntax_highlighting::HighlightedRange,
@@ -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_ide/src/references.rs b/crates/ra_ide/src/references.rs
index a0226b1bc..5a3ec4eb9 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -214,7 +214,7 @@ fn process_definition(
214mod tests { 214mod tests {
215 use crate::{ 215 use crate::{
216 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, 216 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis},
217 ReferenceSearchResult, SearchScope, 217 Reference, ReferenceKind, ReferenceSearchResult, SearchScope,
218 }; 218 };
219 219
220 #[test] 220 #[test]
@@ -232,7 +232,12 @@ mod tests {
232 }"#; 232 }"#;
233 233
234 let refs = get_all_refs(code); 234 let refs = get_all_refs(code);
235 assert_eq!(refs.len(), 2); 235 check_result(
236 refs,
237 "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15)",
238 ReferenceKind::Other,
239 &["FileId(1) [142; 145) StructLiteral"],
240 );
236 } 241 }
237 242
238 #[test] 243 #[test]
@@ -251,7 +256,17 @@ mod tests {
251 }"#; 256 }"#;
252 257
253 let refs = get_all_refs(code); 258 let refs = get_all_refs(code);
254 assert_eq!(refs.len(), 5); 259 check_result(
260 refs,
261 "i BIND_PAT FileId(1) [33; 34)",
262 ReferenceKind::Other,
263 &[
264 "FileId(1) [67; 68) Other",
265 "FileId(1) [71; 72) Other",
266 "FileId(1) [101; 102) Other",
267 "FileId(1) [127; 128) Other",
268 ],
269 );
255 } 270 }
256 271
257 #[test] 272 #[test]
@@ -262,7 +277,12 @@ mod tests {
262 }"#; 277 }"#;
263 278
264 let refs = get_all_refs(code); 279 let refs = get_all_refs(code);
265 assert_eq!(refs.len(), 2); 280 check_result(
281 refs,
282 "i BIND_PAT FileId(1) [12; 13)",
283 ReferenceKind::Other,
284 &["FileId(1) [38; 39) Other"],
285 );
266 } 286 }
267 287
268 #[test] 288 #[test]
@@ -273,7 +293,12 @@ mod tests {
273 }"#; 293 }"#;
274 294
275 let refs = get_all_refs(code); 295 let refs = get_all_refs(code);
276 assert_eq!(refs.len(), 2); 296 check_result(
297 refs,
298 "i BIND_PAT FileId(1) [12; 13)",
299 ReferenceKind::Other,
300 &["FileId(1) [38; 39) Other"],
301 );
277 } 302 }
278 303
279 #[test] 304 #[test]
@@ -290,7 +315,12 @@ mod tests {
290 "#; 315 "#;
291 316
292 let refs = get_all_refs(code); 317 let refs = get_all_refs(code);
293 assert_eq!(refs.len(), 2); 318 check_result(
319 refs,
320 "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74)",
321 ReferenceKind::Other,
322 &["FileId(1) [152; 156) Other"],
323 );
294 } 324 }
295 325
296 #[test] 326 #[test]
@@ -304,7 +334,7 @@ mod tests {
304 "#; 334 "#;
305 335
306 let refs = get_all_refs(code); 336 let refs = get_all_refs(code);
307 assert_eq!(refs.len(), 1); 337 check_result(refs, "f FN_DEF FileId(1) [88; 104) [91; 92)", ReferenceKind::Other, &[]);
308 } 338 }
309 339
310 #[test] 340 #[test]
@@ -319,7 +349,7 @@ mod tests {
319 "#; 349 "#;
320 350
321 let refs = get_all_refs(code); 351 let refs = get_all_refs(code);
322 assert_eq!(refs.len(), 1); 352 check_result(refs, "B ENUM_VARIANT FileId(1) [83; 84) [83; 84)", ReferenceKind::Other, &[]);
323 } 353 }
324 354
325 #[test] 355 #[test]
@@ -358,7 +388,12 @@ mod tests {
358 388
359 let (analysis, pos) = analysis_and_position(code); 389 let (analysis, pos) = analysis_and_position(code);
360 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 390 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
361 assert_eq!(refs.len(), 3); 391 check_result(
392 refs,
393 "Foo STRUCT_DEF FileId(2) [16; 50) [27; 30)",
394 ReferenceKind::Other,
395 &["FileId(1) [52; 55) StructLiteral", "FileId(3) [77; 80) StructLiteral"],
396 );
362 } 397 }
363 398
364 // `mod foo;` is not in the results because `foo` is an `ast::Name`. 399 // `mod foo;` is not in the results because `foo` is an `ast::Name`.
@@ -384,7 +419,12 @@ mod tests {
384 419
385 let (analysis, pos) = analysis_and_position(code); 420 let (analysis, pos) = analysis_and_position(code);
386 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 421 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
387 assert_eq!(refs.len(), 2); 422 check_result(
423 refs,
424 "foo SOURCE_FILE FileId(2) [0; 35)",
425 ReferenceKind::Other,
426 &["FileId(1) [13; 16) Other"],
427 );
388 } 428 }
389 429
390 #[test] 430 #[test]
@@ -409,7 +449,12 @@ mod tests {
409 449
410 let (analysis, pos) = analysis_and_position(code); 450 let (analysis, pos) = analysis_and_position(code);
411 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 451 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
412 assert_eq!(refs.len(), 3); 452 check_result(
453 refs,
454 "Foo STRUCT_DEF FileId(3) [0; 41) [18; 21)",
455 ReferenceKind::Other,
456 &["FileId(2) [20; 23) Other", "FileId(2) [46; 49) StructLiteral"],
457 );
413 } 458 }
414 459
415 #[test] 460 #[test]
@@ -433,11 +478,21 @@ mod tests {
433 let analysis = mock.analysis(); 478 let analysis = mock.analysis();
434 479
435 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 480 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
436 assert_eq!(refs.len(), 3); 481 check_result(
482 refs,
483 "quux FN_DEF FileId(1) [18; 34) [25; 29)",
484 ReferenceKind::Other,
485 &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"],
486 );
437 487
438 let refs = 488 let refs =
439 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); 489 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap();
440 assert_eq!(refs.len(), 2); 490 check_result(
491 refs,
492 "quux FN_DEF FileId(1) [18; 34) [25; 29)",
493 ReferenceKind::Other,
494 &["FileId(3) [16; 20) Other"],
495 );
441 } 496 }
442 497
443 #[test] 498 #[test]
@@ -452,11 +507,40 @@ mod tests {
452 }"#; 507 }"#;
453 508
454 let refs = get_all_refs(code); 509 let refs = get_all_refs(code);
455 assert_eq!(refs.len(), 3); 510 check_result(
511 refs,
512 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48)",
513 ReferenceKind::Other,
514 &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"],
515 );
456 } 516 }
457 517
458 fn get_all_refs(text: &str) -> ReferenceSearchResult { 518 fn get_all_refs(text: &str) -> ReferenceSearchResult {
459 let (analysis, position) = single_file_with_position(text); 519 let (analysis, position) = single_file_with_position(text);
460 analysis.find_all_refs(position, None).unwrap().unwrap() 520 analysis.find_all_refs(position, None).unwrap().unwrap()
461 } 521 }
522
523 fn check_result(
524 res: ReferenceSearchResult,
525 expected_decl: &str,
526 decl_kind: ReferenceKind,
527 expected_refs: &[&str],
528 ) {
529 res.declaration().assert_match(expected_decl);
530 assert_eq!(res.declaration_kind, decl_kind);
531
532 assert_eq!(res.references.len(), expected_refs.len());
533 res.references().iter().enumerate().for_each(|(i, r)| r.assert_match(expected_refs[i]));
534 }
535
536 impl Reference {
537 fn debug_render(&self) -> String {
538 format!("{:?} {:?} {:?}", self.file_range.file_id, self.file_range.range, self.kind)
539 }
540
541 fn assert_match(&self, expected: &str) {
542 let actual = self.debug_render();
543 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
544 }
545 }
462} 546}
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..7a49cad86 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -62,6 +62,22 @@ pub fn main_loop(
62 62
63 let mut loop_state = LoopState::default(); 63 let mut loop_state = LoopState::default();
64 let mut world_state = { 64 let mut world_state = {
65 let feature_flags = {
66 let mut ff = FeatureFlags::default();
67 for (flag, value) in config.feature_flags {
68 if ff.set(flag.as_str(), value).is_err() {
69 log::error!("unknown feature flag: {:?}", flag);
70 show_message(
71 req::MessageType::Error,
72 format!("unknown feature flag: {:?}", flag),
73 &connection.sender,
74 );
75 }
76 }
77 ff
78 };
79 log::info!("feature_flags: {:#?}", feature_flags);
80
65 // FIXME: support dynamic workspace loading. 81 // FIXME: support dynamic workspace loading.
66 let workspaces = { 82 let workspaces = {
67 let mut loaded_workspaces = Vec::new(); 83 let mut loaded_workspaces = Vec::new();
@@ -75,7 +91,12 @@ pub fn main_loop(
75 Ok(workspace) => loaded_workspaces.push(workspace), 91 Ok(workspace) => loaded_workspaces.push(workspace),
76 Err(e) => { 92 Err(e) => {
77 log::error!("loading workspace failed: {}", e); 93 log::error!("loading workspace failed: {}", e);
78 94 if let Some(ra_project_model::CargoTomlNotFoundError(_)) = e.downcast_ref()
95 {
96 if !feature_flags.get("notifications.cargo-toml-not-found") {
97 continue;
98 }
99 }
79 show_message( 100 show_message(
80 req::MessageType::Error, 101 req::MessageType::Error,
81 format!("rust-analyzer failed to load workspace: {}", e), 102 format!("rust-analyzer failed to load workspace: {}", e),
@@ -136,22 +157,6 @@ pub fn main_loop(
136 } 157 }
137 }; 158 };
138 159
139 let feature_flags = {
140 let mut ff = FeatureFlags::default();
141 for (flag, value) in config.feature_flags {
142 if ff.set(flag.as_str(), value).is_err() {
143 log::error!("unknown feature flag: {:?}", flag);
144 show_message(
145 req::MessageType::Error,
146 format!("unknown feature flag: {:?}", flag),
147 &connection.sender,
148 );
149 }
150 }
151 ff
152 };
153 log::info!("feature_flags: {:#?}", feature_flags);
154
155 WorldState::new( 160 WorldState::new(
156 ws_roots, 161 ws_roots,
157 workspaces, 162 workspaces,
@@ -499,6 +504,9 @@ fn on_request(
499 .on::<req::Formatting>(handlers::handle_formatting)? 504 .on::<req::Formatting>(handlers::handle_formatting)?
500 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? 505 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)?
501 .on::<req::InlayHints>(handlers::handle_inlay_hints)? 506 .on::<req::InlayHints>(handlers::handle_inlay_hints)?
507 .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)?
508 .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)?
509 .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)?
502 .finish(); 510 .finish();
503 Ok(()) 511 Ok(())
504} 512}
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 {}
diff --git a/crates/ra_parser/src/grammar/items/traits.rs b/crates/ra_parser/src/grammar/items/traits.rs
index 964fd3041..03dae3cdb 100644
--- a/crates/ra_parser/src/grammar/items/traits.rs
+++ b/crates/ra_parser/src/grammar/items/traits.rs
@@ -10,6 +10,16 @@ pub(super) fn trait_def(p: &mut Parser) {
10 p.bump(T![trait]); 10 p.bump(T![trait]);
11 name_r(p, ITEM_RECOVERY_SET); 11 name_r(p, ITEM_RECOVERY_SET);
12 type_params::opt_type_param_list(p); 12 type_params::opt_type_param_list(p);
13 // test trait_alias
14 // trait Z<U> = T<U>;
15 // trait Z<U> = T<U> where U: Copy;
16 // trait Z<U> = where Self: T<U>;
17 if p.eat(T![=]) {
18 type_params::bounds_without_colon(p);
19 type_params::opt_where_clause(p);
20 p.expect(T![;]);
21 return;
22 }
13 if p.at(T![:]) { 23 if p.at(T![:]) {
14 type_params::bounds(p); 24 type_params::bounds(p);
15 } 25 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index d71b7031a..b7f6a9b57 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -23,9 +23,19 @@ pub use crate::{
23 sysroot::Sysroot, 23 sysroot::Sysroot,
24}; 24};
25 25
26// FIXME use proper error enum
27pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>; 26pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>;
28 27
28#[derive(Clone, PartialEq, Eq, Hash, Debug)]
29pub struct CargoTomlNotFoundError(pub PathBuf);
30
31impl std::fmt::Display for CargoTomlNotFoundError {
32 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(fmt, "can't find Cargo.toml at {}", self.0.display())
34 }
35}
36
37impl Error for CargoTomlNotFoundError {}
38
29#[derive(Debug, Clone)] 39#[derive(Debug, Clone)]
30pub enum ProjectWorkspace { 40pub enum ProjectWorkspace {
31 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 41 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@@ -362,7 +372,7 @@ fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
362 } 372 }
363 curr = path.parent(); 373 curr = path.parent();
364 } 374 }
365 Err(format!("can't find Cargo.toml at {}", path.display()))? 375 Err(CargoTomlNotFoundError(path.to_path_buf()))?
366} 376}
367 377
368pub fn get_rustc_cfg_options() -> CfgOptions { 378pub fn get_rustc_cfg_options() -> CfgOptions {
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 10ca391b6..34d066b1e 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -53,9 +53,10 @@ impl Sysroot {
53 if !src.exists() { 53 if !src.exists() {
54 Err(format!( 54 Err(format!(
55 "can't load standard library from sysroot\n\ 55 "can't load standard library from sysroot\n\
56 {:?}\n\ 56 {}\n\
57 (discovered via `rustc --print sysroot`)\n\
57 try running `rustup component add rust-src` or set `RUST_SRC_PATH`", 58 try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
58 src, 59 src.display(),
59 ))?; 60 ))?;
60 } 61 }
61 62
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index b6ebb129d..c5a3d1999 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.8.0" 14itertools = "0.8.0"
15rowan = "0.8.0" 15rowan = "0.9.0"
16rustc_lexer = "0.1.0" 16rustc_lexer = "0.1.0"
17rustc-hash = "1.0.1" 17rustc-hash = "1.0.1"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index e4061e994..2b2b295f9 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -214,8 +214,7 @@ fn with_children(
214 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, 214 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
215) -> SyntaxNode { 215) -> SyntaxNode {
216 let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>(); 216 let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
217 let new_node = 217 let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children);
218 rowan::GreenNode::new(rowan::cursor::SyntaxKind(parent.kind() as u16), new_children);
219 let new_root_node = parent.replace_with(new_node); 218 let new_root_node = parent.replace_with(new_node);
220 let new_root_node = SyntaxNode::new_root(new_root_node); 219 let new_root_node = SyntaxNode::new_root(new_root_node);
221 220
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index 65b8aa10d..06bdda11d 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -70,8 +70,7 @@ fn reparse_token<'node>(
70 } 70 }
71 } 71 }
72 72
73 let new_token = 73 let new_token = GreenToken::new(rowan::SyntaxKind(token.kind().into()), text.into());
74 GreenToken::new(rowan::cursor::SyntaxKind(token.kind().into()), text.into());
75 Some((token.replace_with(new_token), token.text_range())) 74 Some((token.replace_with(new_token), token.text_range()))
76 } 75 }
77 _ => None, 76 _ => None,
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index 041c6ea8d..b3eb5da63 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -21,18 +21,18 @@ pub enum RustLanguage {}
21impl Language for RustLanguage { 21impl Language for RustLanguage {
22 type Kind = SyntaxKind; 22 type Kind = SyntaxKind;
23 23
24 fn kind_from_raw(raw: rowan::cursor::SyntaxKind) -> SyntaxKind { 24 fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
25 SyntaxKind::from(raw.0) 25 SyntaxKind::from(raw.0)
26 } 26 }
27 27
28 fn kind_to_raw(kind: SyntaxKind) -> rowan::cursor::SyntaxKind { 28 fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
29 rowan::cursor::SyntaxKind(kind.into()) 29 rowan::SyntaxKind(kind.into())
30 } 30 }
31} 31}
32 32
33pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>; 33pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>;
34pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>; 34pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>;
35pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>; 35pub type SyntaxElement = rowan::SyntaxElement<RustLanguage>;
36pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>; 36pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>;
37pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>; 37pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>;
38 38
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs
new file mode 100644
index 000000000..4bd428ee4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.rs
@@ -0,0 +1,3 @@
1trait Z<U> = T<U>;
2trait Z<U> = T<U> where U: Copy;
3trait Z<U> = where Self: T<U>;
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt
new file mode 100644
index 000000000..de1536ef0
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_trait_alias.txt
@@ -0,0 +1,128 @@
1SOURCE_FILE@[0; 83)
2 TRAIT_DEF@[0; 18)
3 TRAIT_KW@[0; 5) "trait"
4 WHITESPACE@[5; 6) " "
5 NAME@[6; 7)
6 IDENT@[6; 7) "Z"
7 TYPE_PARAM_LIST@[7; 10)
8 L_ANGLE@[7; 8) "<"
9 TYPE_PARAM@[8; 9)
10 NAME@[8; 9)
11 IDENT@[8; 9) "U"
12 R_ANGLE@[9; 10) ">"
13 WHITESPACE@[10; 11) " "
14 EQ@[11; 12) "="
15 WHITESPACE@[12; 13) " "
16 TYPE_BOUND_LIST@[13; 17)
17 TYPE_BOUND@[13; 17)
18 PATH_TYPE@[13; 17)
19 PATH@[13; 17)
20 PATH_SEGMENT@[13; 17)
21 NAME_REF@[13; 14)
22 IDENT@[13; 14) "T"
23 TYPE_ARG_LIST@[14; 17)
24 L_ANGLE@[14; 15) "<"
25 TYPE_ARG@[15; 16)
26 PATH_TYPE@[15; 16)
27 PATH@[15; 16)
28 PATH_SEGMENT@[15; 16)
29 NAME_REF@[15; 16)
30 IDENT@[15; 16) "U"
31 R_ANGLE@[16; 17) ">"
32 SEMI@[17; 18) ";"
33 WHITESPACE@[18; 19) "\n"
34 TRAIT_DEF@[19; 51)
35 TRAIT_KW@[19; 24) "trait"
36 WHITESPACE@[24; 25) " "
37 NAME@[25; 26)
38 IDENT@[25; 26) "Z"
39 TYPE_PARAM_LIST@[26; 29)
40 L_ANGLE@[26; 27) "<"
41 TYPE_PARAM@[27; 28)
42 NAME@[27; 28)
43 IDENT@[27; 28) "U"
44 R_ANGLE@[28; 29) ">"
45 WHITESPACE@[29; 30) " "
46 EQ@[30; 31) "="
47 WHITESPACE@[31; 32) " "
48 TYPE_BOUND_LIST@[32; 36)
49 TYPE_BOUND@[32; 36)
50 PATH_TYPE@[32; 36)
51 PATH@[32; 36)
52 PATH_SEGMENT@[32; 36)
53 NAME_REF@[32; 33)
54 IDENT@[32; 33) "T"
55 TYPE_ARG_LIST@[33; 36)
56 L_ANGLE@[33; 34) "<"
57 TYPE_ARG@[34; 35)
58 PATH_TYPE@[34; 35)
59 PATH@[34; 35)
60 PATH_SEGMENT@[34; 35)
61 NAME_REF@[34; 35)
62 IDENT@[34; 35) "U"
63 R_ANGLE@[35; 36) ">"
64 WHITESPACE@[36; 37) " "
65 WHERE_CLAUSE@[37; 50)
66 WHERE_KW@[37; 42) "where"
67 WHITESPACE@[42; 43) " "
68 WHERE_PRED@[43; 50)
69 PATH_TYPE@[43; 44)
70 PATH@[43; 44)
71 PATH_SEGMENT@[43; 44)
72 NAME_REF@[43; 44)
73 IDENT@[43; 44) "U"
74 COLON@[44; 45) ":"
75 WHITESPACE@[45; 46) " "
76 TYPE_BOUND_LIST@[46; 50)
77 TYPE_BOUND@[46; 50)
78 PATH_TYPE@[46; 50)
79 PATH@[46; 50)
80 PATH_SEGMENT@[46; 50)
81 NAME_REF@[46; 50)
82 IDENT@[46; 50) "Copy"
83 SEMI@[50; 51) ";"
84 WHITESPACE@[51; 52) "\n"
85 TRAIT_DEF@[52; 82)
86 TRAIT_KW@[52; 57) "trait"
87 WHITESPACE@[57; 58) " "
88 NAME@[58; 59)
89 IDENT@[58; 59) "Z"
90 TYPE_PARAM_LIST@[59; 62)
91 L_ANGLE@[59; 60) "<"
92 TYPE_PARAM@[60; 61)
93 NAME@[60; 61)
94 IDENT@[60; 61) "U"
95 R_ANGLE@[61; 62) ">"
96 WHITESPACE@[62; 63) " "
97 EQ@[63; 64) "="
98 WHITESPACE@[64; 65) " "
99 TYPE_BOUND_LIST@[65; 65)
100 WHERE_CLAUSE@[65; 81)
101 WHERE_KW@[65; 70) "where"
102 WHITESPACE@[70; 71) " "
103 WHERE_PRED@[71; 81)
104 PATH_TYPE@[71; 75)
105 PATH@[71; 75)
106 PATH_SEGMENT@[71; 75)
107 NAME_REF@[71; 75)
108 IDENT@[71; 75) "Self"
109 COLON@[75; 76) ":"
110 WHITESPACE@[76; 77) " "
111 TYPE_BOUND_LIST@[77; 81)
112 TYPE_BOUND@[77; 81)
113 PATH_TYPE@[77; 81)
114 PATH@[77; 81)
115 PATH_SEGMENT@[77; 81)
116 NAME_REF@[77; 78)
117 IDENT@[77; 78) "T"
118 TYPE_ARG_LIST@[78; 81)
119 L_ANGLE@[78; 79) "<"
120 TYPE_ARG@[79; 80)
121 PATH_TYPE@[79; 80)
122 PATH@[79; 80)
123 PATH_SEGMENT@[79; 80)
124 NAME_REF@[79; 80)
125 IDENT@[79; 80) "U"
126 R_ANGLE@[80; 81) ">"
127 SEMI@[81; 82) ";"
128 WHITESPACE@[82; 83) "\n"
diff --git a/docs/dev/lsp-features.md b/docs/dev/lsp-features.md
index d3e79b8be..00b0867d7 100644
--- a/docs/dev/lsp-features.md
+++ b/docs/dev/lsp-features.md
@@ -41,7 +41,7 @@ This list documents LSP features, supported by rust-analyzer.
41 - trigger characters: `:`, `.` 41 - trigger characters: `:`, `.`
42- [x] [textDocument/hover](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover) 42- [x] [textDocument/hover](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
43- [x] [textDocument/signatureHelp](https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp) 43- [x] [textDocument/signatureHelp](https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp)
44 - trigger characters: `(`, `,`, `)` 44 - trigger characters: `(`, `,`
45- [ ] [textDocument/declaration](https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration) 45- [ ] [textDocument/declaration](https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration)
46- [x] [textDocument/definition](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) 46- [x] [textDocument/definition](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition)
47- [x] [textDocument/typeDefinition](https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition) 47- [x] [textDocument/typeDefinition](https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition)
@@ -58,7 +58,7 @@ This list documents LSP features, supported by rust-analyzer.
58 - rust-analyzer.run 58 - rust-analyzer.run
59 - rust-analyzer.analyzerStatus 59 - rust-analyzer.analyzerStatus
60- [x] [textDocument/codeLens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens) 60- [x] [textDocument/codeLens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
61- [ ] [textDocument/documentLink](https://microsoft.github.io/language-server-protocol/specification#codeLens_resolve) 61- [x] [codeLens/resolve](https://microsoft.github.io/language-server-protocol/specification#codeLens_resolve)
62- [ ] [documentLink/resolve](https://microsoft.github.io/language-server-protocol/specification#documentLink_resolve) 62- [ ] [documentLink/resolve](https://microsoft.github.io/language-server-protocol/specification#documentLink_resolve)
63- [ ] [textDocument/documentColor](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor) 63- [ ] [textDocument/documentColor](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor)
64- [ ] [textDocument/colorPresentation](https://microsoft.github.io/language-server-protocol/specification#textDocument_colorPresentation) 64- [ ] [textDocument/colorPresentation](https://microsoft.github.io/language-server-protocol/specification#textDocument_colorPresentation)
diff --git a/docs/user/README.md b/docs/user/README.md
index 282445722..fa202f06c 100644
--- a/docs/user/README.md
+++ b/docs/user/README.md
@@ -122,6 +122,8 @@ host.
122 "completion.enable-postfix": true, 122 "completion.enable-postfix": true,
123 // Show notification when workspace is fully loaded 123 // Show notification when workspace is fully loaded
124 "notifications.workspace-loaded": true, 124 "notifications.workspace-loaded": true,
125 // Show error when no Cargo.toml was found
126 "notifications.cargo-toml-not-found": true,
125 } 127 }
126 ``` 128 ```
127 129
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 158cfc2d6..efa638e06 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -9,15 +9,9 @@ mod gen_syntax;
9mod gen_parser_tests; 9mod gen_parser_tests;
10mod gen_assists_docs; 10mod gen_assists_docs;
11 11
12use std::{ 12use std::{fs, mem, path::Path};
13 fs,
14 io::Write,
15 mem,
16 path::Path,
17 process::{Command, Stdio},
18};
19 13
20use crate::{project_root, Result}; 14use crate::Result;
21 15
22pub use self::{ 16pub use self::{
23 gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, 17 gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
@@ -62,20 +56,6 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
62 } 56 }
63} 57}
64 58
65fn reformat(text: impl std::fmt::Display) -> Result<String> {
66 let mut rustfmt = Command::new("rustfmt")
67 .arg("--config-path")
68 .arg(project_root().join("rustfmt.toml"))
69 .stdin(Stdio::piped())
70 .stdout(Stdio::piped())
71 .spawn()?;
72 write!(rustfmt.stdin.take().unwrap(), "{}", text)?;
73 let output = rustfmt.wait_with_output()?;
74 let stdout = String::from_utf8(output.stdout)?;
75 let preamble = "Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`";
76 Ok(format!("//! {}\n\n{}", preamble, stdout))
77}
78
79fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { 59fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
80 do_extract_comment_blocks(text, false) 60 do_extract_comment_blocks(text, false)
81} 61}
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 05afda8f1..69f9b4872 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -102,7 +102,7 @@ r#####"
102 102
103 buf.push_str(&test) 103 buf.push_str(&test)
104 } 104 }
105 let buf = codegen::reformat(buf)?; 105 let buf = crate::reformat(buf)?;
106 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) 106 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode)
107} 107}
108 108
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index 0f50ca569..19fea67d8 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -152,7 +152,7 @@ fn generate_ast(grammar: AstSrc<'_>) -> Result<String> {
152 #(#enums)* 152 #(#enums)*
153 }; 153 };
154 154
155 let pretty = codegen::reformat(ast)?; 155 let pretty = crate::reformat(ast)?;
156 Ok(pretty) 156 Ok(pretty)
157} 157}
158 158
@@ -265,7 +265,7 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> {
265 } 265 }
266 }; 266 };
267 267
268 codegen::reformat(ast) 268 crate::reformat(ast)
269} 269}
270 270
271fn to_upper_snake_case(s: &str) -> String { 271fn to_upper_snake_case(s: &str) -> String {
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index e46c21db7..0a569cf5d 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -10,6 +10,7 @@ mod ast_src;
10use anyhow::Context; 10use anyhow::Context;
11use std::{ 11use std::{
12 env, fs, 12 env, fs,
13 io::Write,
13 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
14 process::{Command, Stdio}, 15 process::{Command, Stdio},
15}; 16};
@@ -31,15 +32,7 @@ pub fn project_root() -> PathBuf {
31} 32}
32 33
33pub fn run_rustfmt(mode: Mode) -> Result<()> { 34pub fn run_rustfmt(mode: Mode) -> Result<()> {
34 match Command::new("rustup") 35 ensure_rustfmt()?;
35 .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
36 .stderr(Stdio::null())
37 .stdout(Stdio::null())
38 .status()
39 {
40 Ok(status) if status.success() => (),
41 _ => install_rustfmt().context("install rustfmt")?,
42 };
43 36
44 if mode == Mode::Verify { 37 if mode == Mode::Verify {
45 run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?; 38 run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?;
@@ -49,7 +42,31 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
49 Ok(()) 42 Ok(())
50} 43}
51 44
52fn install_rustfmt() -> Result<()> { 45fn reformat(text: impl std::fmt::Display) -> Result<String> {
46 ensure_rustfmt()?;
47 let mut rustfmt = Command::new("rustup")
48 .args(&["run", TOOLCHAIN, "--", "rustfmt", "--config-path"])
49 .arg(project_root().join("rustfmt.toml"))
50 .stdin(Stdio::piped())
51 .stdout(Stdio::piped())
52 .spawn()?;
53 write!(rustfmt.stdin.take().unwrap(), "{}", text)?;
54 let output = rustfmt.wait_with_output()?;
55 let stdout = String::from_utf8(output.stdout)?;
56 let preamble = "Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`";
57 Ok(format!("//! {}\n\n{}", preamble, stdout))
58}
59
60fn ensure_rustfmt() -> Result<()> {
61 match Command::new("rustup")
62 .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
63 .stderr(Stdio::null())
64 .stdout(Stdio::null())
65 .status()
66 {
67 Ok(status) if status.success() => return Ok(()),
68 _ => (),
69 };
53 run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; 70 run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?;
54 run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") 71 run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".")
55} 72}