aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_analysis/src/imp.rs18
-rw-r--r--crates/ra_analysis/src/lib.rs49
-rw-r--r--crates/ra_analysis/src/symbol_index.rs123
-rw-r--r--crates/ra_analysis/tests/tests.rs10
-rw-r--r--crates/ra_editor/src/lib.rs4
-rw-r--r--crates/ra_editor/src/structure.rs129
-rw-r--r--crates/ra_editor/src/symbols.rs246
-rw-r--r--crates/ra_lsp_server/src/conv.rs11
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs37
9 files changed, 329 insertions, 298 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index ec7da437a..8071554a7 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -10,7 +10,7 @@ use hir::{
10 self, FnSignatureInfo, Problem, source_binder, 10 self, FnSignatureInfo, Problem, source_binder,
11}; 11};
12use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; 12use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
13use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; 13use ra_editor::{self, find_node_at_offset, LineIndex, LocalEdit, Severity};
14use ra_syntax::{ 14use ra_syntax::{
15 algo::find_covering_node, 15 algo::find_covering_node,
16 ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, 16 ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
@@ -21,11 +21,11 @@ use ra_syntax::{
21 21
22use crate::{ 22use crate::{
23 AnalysisChange, 23 AnalysisChange,
24 Cancelable, 24 Cancelable, NavigationTarget,
25 completion::{CompletionItem, completions}, 25 completion::{CompletionItem, completions},
26 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, 26 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, 27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit,
28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, 28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase, FileSymbol},
29}; 29};
30 30
31#[derive(Debug, Default)] 31#[derive(Debug, Default)]
@@ -205,7 +205,7 @@ impl AnalysisImpl {
205 205
206 /// This returns `Vec` because a module may be included from several places. We 206 /// This returns `Vec` because a module may be included from several places. We
207 /// don't handle this case yet though, so the Vec has length at most one. 207 /// don't handle this case yet though, so the Vec has length at most one.
208 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 208 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
209 let descr = match source_binder::module_from_position(&*self.db, position)? { 209 let descr = match source_binder::module_from_position(&*self.db, position)? {
210 None => return Ok(Vec::new()), 210 None => return Ok(Vec::new()),
211 Some(it) => it, 211 Some(it) => it,
@@ -216,12 +216,12 @@ impl AnalysisImpl {
216 }; 216 };
217 let decl = decl.borrowed(); 217 let decl = decl.borrowed();
218 let decl_name = decl.name().unwrap(); 218 let decl_name = decl.name().unwrap();
219 let sym = FileSymbol { 219 let symbol = FileSymbol {
220 name: decl_name.text(), 220 name: decl_name.text(),
221 node_range: decl_name.syntax().range(), 221 node_range: decl_name.syntax().range(),
222 kind: MODULE, 222 kind: MODULE,
223 }; 223 };
224 Ok(vec![(file_id, sym)]) 224 Ok(vec![NavigationTarget { file_id, symbol }])
225 } 225 }
226 /// Returns `Vec` for the same reason as `parent_module` 226 /// Returns `Vec` for the same reason as `parent_module`
227 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 227 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
@@ -355,9 +355,9 @@ impl AnalysisImpl {
355 Ok(Some((binding, descr))) 355 Ok(Some((binding, descr)))
356 } 356 }
357 } 357 }
358 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 358 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
359 let file = self.db.source_file(file_id); 359 let file = self.db.source_file(nav.file_id);
360 let result = match (symbol.description(&file), symbol.docs(&file)) { 360 let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) {
361 (Some(desc), Some(docs)) => { 361 (Some(desc), Some(docs)) => {
362 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) 362 Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs)
363 } 363 }
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 08ecb125a..9576453ab 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -23,22 +23,22 @@ mod syntax_highlighting;
23use std::{fmt, sync::Arc}; 23use std::{fmt, sync::Arc};
24 24
25use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
26use ra_syntax::{SourceFileNode, TextRange, TextUnit}; 26use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
27use ra_text_edit::TextEdit; 27use ra_text_edit::TextEdit;
28use rayon::prelude::*; 28use rayon::prelude::*;
29use relative_path::RelativePathBuf; 29use relative_path::RelativePathBuf;
30 30
31use crate::{ 31use crate::{
32 imp::{AnalysisHostImpl, AnalysisImpl}, 32 imp::{AnalysisHostImpl, AnalysisImpl},
33 symbol_index::SymbolIndex, 33 symbol_index::{SymbolIndex, FileSymbol},
34}; 34};
35 35
36pub use crate::{ 36pub use crate::{
37 completion::{CompletionItem, CompletionItemKind, InsertText}, 37 completion::{CompletionItem, CompletionItemKind, InsertText},
38 runnables::{Runnable, RunnableKind} 38 runnables::{Runnable, RunnableKind},
39}; 39};
40pub use ra_editor::{ 40pub use ra_editor::{
41 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity 41 Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
42}; 42};
43pub use hir::FnSignatureInfo; 43pub use hir::FnSignatureInfo;
44 44
@@ -242,6 +242,27 @@ impl Query {
242 } 242 }
243} 243}
244 244
245#[derive(Debug)]
246pub struct NavigationTarget {
247 file_id: FileId,
248 symbol: FileSymbol,
249}
250
251impl NavigationTarget {
252 pub fn name(&self) -> SmolStr {
253 self.symbol.name.clone()
254 }
255 pub fn kind(&self) -> SyntaxKind {
256 self.symbol.kind
257 }
258 pub fn file_id(&self) -> FileId {
259 self.file_id
260 }
261 pub fn range(&self) -> TextRange {
262 self.symbol.node_range
263 }
264}
265
245/// Result of "goto def" query. 266/// Result of "goto def" query.
246#[derive(Debug)] 267#[derive(Debug)]
247pub struct ReferenceResolution { 268pub struct ReferenceResolution {
@@ -250,7 +271,7 @@ pub struct ReferenceResolution {
250 /// client where the reference was. 271 /// client where the reference was.
251 pub reference_range: TextRange, 272 pub reference_range: TextRange,
252 /// What this reference resolves to. 273 /// What this reference resolves to.
253 pub resolves_to: Vec<(FileId, FileSymbol)>, 274 pub resolves_to: Vec<NavigationTarget>,
254} 275}
255 276
256impl ReferenceResolution { 277impl ReferenceResolution {
@@ -262,7 +283,7 @@ impl ReferenceResolution {
262 } 283 }
263 284
264 fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { 285 fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) {
265 self.resolves_to.push((file_id, symbol)) 286 self.resolves_to.push(NavigationTarget { file_id, symbol })
266 } 287 }
267} 288}
268 289
@@ -320,8 +341,14 @@ impl Analysis {
320 let file = self.imp.file_syntax(file_id); 341 let file = self.imp.file_syntax(file_id);
321 ra_editor::folding_ranges(&file) 342 ra_editor::folding_ranges(&file)
322 } 343 }
323 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> { 344 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
324 self.imp.world_symbols(query) 345 let res = self
346 .imp
347 .world_symbols(query)?
348 .into_iter()
349 .map(|(file_id, symbol)| NavigationTarget { file_id, symbol })
350 .collect();
351 Ok(res)
325 } 352 }
326 pub fn approximately_resolve_symbol( 353 pub fn approximately_resolve_symbol(
327 &self, 354 &self,
@@ -332,10 +359,10 @@ impl Analysis {
332 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 359 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
333 self.imp.find_all_refs(position) 360 self.imp.find_all_refs(position)
334 } 361 }
335 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 362 pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
336 self.imp.doc_text_for(file_id, symbol) 363 self.imp.doc_text_for(nav)
337 } 364 }
338 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 365 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
339 self.imp.parent_module(position) 366 self.imp.parent_module(position)
340 } 367 }
341 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { 368 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index e5bdf0aa1..56a84a850 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -4,10 +4,11 @@ use std::{
4}; 4};
5 5
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use ra_editor::{self, FileSymbol};
8use ra_syntax::{ 7use ra_syntax::{
9 SourceFileNode, 8 AstNode, SyntaxNodeRef, SourceFileNode, SmolStr, TextRange,
9 algo::visit::{visitor, Visitor},
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
11 ast::{self, NameOwner, DocCommentsOwner},
11}; 12};
12use ra_db::{SyntaxDatabase, SourceRootId}; 13use ra_db::{SyntaxDatabase, SourceRootId};
13use rayon::prelude::*; 14use rayon::prelude::*;
@@ -65,8 +66,9 @@ impl SymbolIndex {
65 ) -> SymbolIndex { 66 ) -> SymbolIndex {
66 let mut symbols = files 67 let mut symbols = files
67 .flat_map(|(file_id, file)| { 68 .flat_map(|(file_id, file)| {
68 ra_editor::file_symbols(&file) 69 file.syntax()
69 .into_iter() 70 .descendants()
71 .filter_map(to_symbol)
70 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) 72 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol)))
71 .collect::<Vec<_>>() 73 .collect::<Vec<_>>()
72 }) 74 })
@@ -121,3 +123,116 @@ fn is_type(kind: SyntaxKind) -> bool {
121 _ => false, 123 _ => false,
122 } 124 }
123} 125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash)]
128pub(crate) struct FileSymbol {
129 pub(crate) name: SmolStr,
130 pub(crate) node_range: TextRange,
131 pub(crate) kind: SyntaxKind,
132}
133
134impl FileSymbol {
135 pub(crate) fn docs(&self, file: &SourceFileNode) -> Option<String> {
136 file.syntax()
137 .descendants()
138 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
139 .filter_map(|node: SyntaxNodeRef| {
140 fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> {
141 let comments = node.doc_comment_text();
142 if comments.is_empty() {
143 None
144 } else {
145 Some(comments)
146 }
147 }
148
149 visitor()
150 .visit(doc_comments::<ast::FnDef>)
151 .visit(doc_comments::<ast::StructDef>)
152 .visit(doc_comments::<ast::EnumDef>)
153 .visit(doc_comments::<ast::TraitDef>)
154 .visit(doc_comments::<ast::Module>)
155 .visit(doc_comments::<ast::TypeDef>)
156 .visit(doc_comments::<ast::ConstDef>)
157 .visit(doc_comments::<ast::StaticDef>)
158 .accept(node)?
159 })
160 .nth(0)
161 }
162 /// Get a description of this node.
163 ///
164 /// e.g. `struct Name`, `enum Name`, `fn Name`
165 pub(crate) fn description(&self, file: &SourceFileNode) -> Option<String> {
166 // TODO: After type inference is done, add type information to improve the output
167 file.syntax()
168 .descendants()
169 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
170 .filter_map(|node: SyntaxNodeRef| {
171 // TODO: Refactor to be have less repetition
172 visitor()
173 .visit(|node: ast::FnDef| {
174 let mut string = "fn ".to_string();
175 node.name()?.syntax().text().push_to(&mut string);
176 Some(string)
177 })
178 .visit(|node: ast::StructDef| {
179 let mut string = "struct ".to_string();
180 node.name()?.syntax().text().push_to(&mut string);
181 Some(string)
182 })
183 .visit(|node: ast::EnumDef| {
184 let mut string = "enum ".to_string();
185 node.name()?.syntax().text().push_to(&mut string);
186 Some(string)
187 })
188 .visit(|node: ast::TraitDef| {
189 let mut string = "trait ".to_string();
190 node.name()?.syntax().text().push_to(&mut string);
191 Some(string)
192 })
193 .visit(|node: ast::Module| {
194 let mut string = "mod ".to_string();
195 node.name()?.syntax().text().push_to(&mut string);
196 Some(string)
197 })
198 .visit(|node: ast::TypeDef| {
199 let mut string = "type ".to_string();
200 node.name()?.syntax().text().push_to(&mut string);
201 Some(string)
202 })
203 .visit(|node: ast::ConstDef| {
204 let mut string = "const ".to_string();
205 node.name()?.syntax().text().push_to(&mut string);
206 Some(string)
207 })
208 .visit(|node: ast::StaticDef| {
209 let mut string = "static ".to_string();
210 node.name()?.syntax().text().push_to(&mut string);
211 Some(string)
212 })
213 .accept(node)?
214 })
215 .nth(0)
216 }
217}
218
219fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
220 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> {
221 let name = node.name()?;
222 Some(FileSymbol {
223 name: name.text(),
224 node_range: node.syntax().range(),
225 kind: node.syntax().kind(),
226 })
227 }
228 visitor()
229 .visit(decl::<ast::FnDef>)
230 .visit(decl::<ast::StructDef>)
231 .visit(decl::<ast::EnumDef>)
232 .visit(decl::<ast::TraitDef>)
233 .visit(decl::<ast::Module>)
234 .visit(decl::<ast::TypeDef>)
235 .visit(decl::<ast::ConstDef>)
236 .visit(decl::<ast::StaticDef>)
237 .accept(node)?
238}
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index b61ead752..845fff3c6 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -25,7 +25,7 @@ fn approximate_resolve_works_in_items() {
25 assert_eq_dbg( 25 assert_eq_dbg(
26 r#"ReferenceResolution { 26 r#"ReferenceResolution {
27 reference_range: [23; 26), 27 reference_range: [23; 26),
28 resolves_to: [(FileId(1), FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF })] 28 resolves_to: [NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF } }]
29 }"#, 29 }"#,
30 &symbols, 30 &symbols,
31 ); 31 );
@@ -46,7 +46,7 @@ fn test_resolve_module() {
46 assert_eq_dbg( 46 assert_eq_dbg(
47 r#"ReferenceResolution { 47 r#"ReferenceResolution {
48 reference_range: [4; 7), 48 reference_range: [4; 7),
49 resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] 49 resolves_to: [NavigationTarget { file_id: FileId(2), symbol: FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE } }]
50 }"#, 50 }"#,
51 &symbols, 51 &symbols,
52 ); 52 );
@@ -64,7 +64,7 @@ fn test_resolve_module() {
64 assert_eq_dbg( 64 assert_eq_dbg(
65 r#"ReferenceResolution { 65 r#"ReferenceResolution {
66 reference_range: [4; 7), 66 reference_range: [4; 7),
67 resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] 67 resolves_to: [NavigationTarget { file_id: FileId(2), symbol: FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE } }]
68 }"#, 68 }"#,
69 &symbols, 69 &symbols,
70 ); 70 );
@@ -107,7 +107,7 @@ fn test_resolve_parent_module() {
107 ); 107 );
108 let symbols = analysis.parent_module(pos).unwrap(); 108 let symbols = analysis.parent_module(pos).unwrap();
109 assert_eq_dbg( 109 assert_eq_dbg(
110 r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#, 110 r#"[NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE } }]"#,
111 &symbols, 111 &symbols,
112 ); 112 );
113} 113}
@@ -126,7 +126,7 @@ fn test_resolve_parent_module_for_inline() {
126 ); 126 );
127 let symbols = analysis.parent_module(pos).unwrap(); 127 let symbols = analysis.parent_module(pos).unwrap();
128 assert_eq_dbg( 128 assert_eq_dbg(
129 r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#, 129 r#"[NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE } }]"#,
130 &symbols, 130 &symbols,
131 ); 131 );
132} 132}
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index b03f9ea54..bfc745e58 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -3,7 +3,7 @@ mod extend_selection;
3mod folding_ranges; 3mod folding_ranges;
4mod line_index; 4mod line_index;
5mod line_index_utils; 5mod line_index_utils;
6mod symbols; 6mod structure;
7#[cfg(test)] 7#[cfg(test)]
8mod test_utils; 8mod test_utils;
9mod typing; 9mod typing;
@@ -15,7 +15,7 @@ pub use self::{
15 folding_ranges::{folding_ranges, Fold, FoldKind}, 15 folding_ranges::{folding_ranges, Fold, FoldKind},
16 line_index::{LineCol, LineIndex}, 16 line_index::{LineCol, LineIndex},
17 line_index_utils::translate_offset_with_edit, 17 line_index_utils::translate_offset_with_edit,
18 symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, 18 structure::{file_structure, StructureNode},
19 typing::{join_lines, on_enter, on_eq_typed}, 19 typing::{join_lines, on_enter, on_eq_typed},
20 diagnostics::diagnostics 20 diagnostics::diagnostics
21}; 21};
diff --git a/crates/ra_editor/src/structure.rs b/crates/ra_editor/src/structure.rs
new file mode 100644
index 000000000..2292b1ddf
--- /dev/null
+++ b/crates/ra_editor/src/structure.rs
@@ -0,0 +1,129 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, NameOwner},
6 AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
19 let mut res = Vec::new();
20 let mut stack = Vec::new();
21
22 for event in file.syntax().preorder() {
23 match event {
24 WalkEvent::Enter(node) => {
25 if let Some(mut symbol) = structure_node(node) {
26 symbol.parent = stack.last().map(|&n| n);
27 stack.push(res.len());
28 res.push(symbol);
29 }
30 }
31 WalkEvent::Leave(node) => {
32 if structure_node(node).is_some() {
33 stack.pop().unwrap();
34 }
35 }
36 }
37 }
38 res
39}
40
41fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
42 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
43 let name = node.name()?;
44 Some(StructureNode {
45 parent: None,
46 label: name.text().to_string(),
47 navigation_range: name.syntax().range(),
48 node_range: node.syntax().range(),
49 kind: node.syntax().kind(),
50 })
51 }
52
53 visitor()
54 .visit(decl::<ast::FnDef>)
55 .visit(decl::<ast::StructDef>)
56 .visit(decl::<ast::NamedFieldDef>)
57 .visit(decl::<ast::EnumDef>)
58 .visit(decl::<ast::TraitDef>)
59 .visit(decl::<ast::Module>)
60 .visit(decl::<ast::TypeDef>)
61 .visit(decl::<ast::ConstDef>)
62 .visit(decl::<ast::StaticDef>)
63 .visit(|im: ast::ImplItem| {
64 let target_type = im.target_type()?;
65 let target_trait = im.target_trait();
66 let label = match target_trait {
67 None => format!("impl {}", target_type.syntax().text()),
68 Some(t) => format!(
69 "impl {} for {}",
70 t.syntax().text(),
71 target_type.syntax().text(),
72 ),
73 };
74
75 let node = StructureNode {
76 parent: None,
77 label,
78 navigation_range: target_type.syntax().range(),
79 node_range: im.syntax().range(),
80 kind: im.syntax().kind(),
81 };
82 Some(node)
83 })
84 .accept(node)?
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use test_utils::assert_eq_dbg;
91
92 #[test]
93 fn test_file_structure() {
94 let file = SourceFileNode::parse(
95 r#"
96struct Foo {
97 x: i32
98}
99
100mod m {
101 fn bar() {}
102}
103
104enum E { X, Y(i32) }
105type T = ();
106static S: i32 = 92;
107const C: i32 = 92;
108
109impl E {}
110
111impl fmt::Debug for E {}
112"#,
113 );
114 let structure = file_structure(&file);
115 assert_eq_dbg(
116 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
117 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
118 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
119 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
120 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
121 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
122 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
123 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
124 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
125 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
126 &structure,
127 )
128 }
129}
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs
deleted file mode 100644
index 9e25decfb..000000000
--- a/crates/ra_editor/src/symbols.rs
+++ /dev/null
@@ -1,246 +0,0 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, DocCommentsOwner, NameOwner},
6 AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct FileSymbol {
20 pub name: SmolStr,
21 pub node_range: TextRange,
22 pub kind: SyntaxKind,
23}
24
25impl FileSymbol {
26 pub fn docs(&self, file: &SourceFileNode) -> Option<String> {
27 file.syntax()
28 .descendants()
29 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
30 .filter_map(|node: SyntaxNodeRef| {
31 fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> {
32 let comments = node.doc_comment_text();
33 if comments.is_empty() {
34 None
35 } else {
36 Some(comments)
37 }
38 }
39
40 visitor()
41 .visit(doc_comments::<ast::FnDef>)
42 .visit(doc_comments::<ast::StructDef>)
43 .visit(doc_comments::<ast::EnumDef>)
44 .visit(doc_comments::<ast::TraitDef>)
45 .visit(doc_comments::<ast::Module>)
46 .visit(doc_comments::<ast::TypeDef>)
47 .visit(doc_comments::<ast::ConstDef>)
48 .visit(doc_comments::<ast::StaticDef>)
49 .accept(node)?
50 })
51 .nth(0)
52 }
53 /// Get a description of this node.
54 ///
55 /// e.g. `struct Name`, `enum Name`, `fn Name`
56 pub fn description(&self, file: &SourceFileNode) -> Option<String> {
57 // TODO: After type inference is done, add type information to improve the output
58 file.syntax()
59 .descendants()
60 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
61 .filter_map(|node: SyntaxNodeRef| {
62 // TODO: Refactor to be have less repetition
63 visitor()
64 .visit(|node: ast::FnDef| {
65 let mut string = "fn ".to_string();
66 node.name()?.syntax().text().push_to(&mut string);
67 Some(string)
68 })
69 .visit(|node: ast::StructDef| {
70 let mut string = "struct ".to_string();
71 node.name()?.syntax().text().push_to(&mut string);
72 Some(string)
73 })
74 .visit(|node: ast::EnumDef| {
75 let mut string = "enum ".to_string();
76 node.name()?.syntax().text().push_to(&mut string);
77 Some(string)
78 })
79 .visit(|node: ast::TraitDef| {
80 let mut string = "trait ".to_string();
81 node.name()?.syntax().text().push_to(&mut string);
82 Some(string)
83 })
84 .visit(|node: ast::Module| {
85 let mut string = "mod ".to_string();
86 node.name()?.syntax().text().push_to(&mut string);
87 Some(string)
88 })
89 .visit(|node: ast::TypeDef| {
90 let mut string = "type ".to_string();
91 node.name()?.syntax().text().push_to(&mut string);
92 Some(string)
93 })
94 .visit(|node: ast::ConstDef| {
95 let mut string = "const ".to_string();
96 node.name()?.syntax().text().push_to(&mut string);
97 Some(string)
98 })
99 .visit(|node: ast::StaticDef| {
100 let mut string = "static ".to_string();
101 node.name()?.syntax().text().push_to(&mut string);
102 Some(string)
103 })
104 .accept(node)?
105 })
106 .nth(0)
107 }
108}
109
110pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> {
111 file.syntax().descendants().filter_map(to_symbol).collect()
112}
113
114fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
115 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> {
116 let name = node.name()?;
117 Some(FileSymbol {
118 name: name.text(),
119 node_range: node.syntax().range(),
120 kind: node.syntax().kind(),
121 })
122 }
123 visitor()
124 .visit(decl::<ast::FnDef>)
125 .visit(decl::<ast::StructDef>)
126 .visit(decl::<ast::EnumDef>)
127 .visit(decl::<ast::TraitDef>)
128 .visit(decl::<ast::Module>)
129 .visit(decl::<ast::TypeDef>)
130 .visit(decl::<ast::ConstDef>)
131 .visit(decl::<ast::StaticDef>)
132 .accept(node)?
133}
134
135pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
136 let mut res = Vec::new();
137 let mut stack = Vec::new();
138
139 for event in file.syntax().preorder() {
140 match event {
141 WalkEvent::Enter(node) => {
142 if let Some(mut symbol) = structure_node(node) {
143 symbol.parent = stack.last().map(|&n| n);
144 stack.push(res.len());
145 res.push(symbol);
146 }
147 }
148 WalkEvent::Leave(node) => {
149 if structure_node(node).is_some() {
150 stack.pop().unwrap();
151 }
152 }
153 }
154 }
155 res
156}
157
158fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
159 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
160 let name = node.name()?;
161 Some(StructureNode {
162 parent: None,
163 label: name.text().to_string(),
164 navigation_range: name.syntax().range(),
165 node_range: node.syntax().range(),
166 kind: node.syntax().kind(),
167 })
168 }
169
170 visitor()
171 .visit(decl::<ast::FnDef>)
172 .visit(decl::<ast::StructDef>)
173 .visit(decl::<ast::NamedFieldDef>)
174 .visit(decl::<ast::EnumDef>)
175 .visit(decl::<ast::TraitDef>)
176 .visit(decl::<ast::Module>)
177 .visit(decl::<ast::TypeDef>)
178 .visit(decl::<ast::ConstDef>)
179 .visit(decl::<ast::StaticDef>)
180 .visit(|im: ast::ImplItem| {
181 let target_type = im.target_type()?;
182 let target_trait = im.target_trait();
183 let label = match target_trait {
184 None => format!("impl {}", target_type.syntax().text()),
185 Some(t) => format!(
186 "impl {} for {}",
187 t.syntax().text(),
188 target_type.syntax().text(),
189 ),
190 };
191
192 let node = StructureNode {
193 parent: None,
194 label,
195 navigation_range: target_type.syntax().range(),
196 node_range: im.syntax().range(),
197 kind: im.syntax().kind(),
198 };
199 Some(node)
200 })
201 .accept(node)?
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use test_utils::assert_eq_dbg;
208
209 #[test]
210 fn test_file_structure() {
211 let file = SourceFileNode::parse(
212 r#"
213struct Foo {
214 x: i32
215}
216
217mod m {
218 fn bar() {}
219}
220
221enum E { X, Y(i32) }
222type T = ();
223static S: i32 = 92;
224const C: i32 = 92;
225
226impl E {}
227
228impl fmt::Debug for E {}
229"#,
230 );
231 let symbols = file_structure(&file);
232 assert_eq_dbg(
233 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
234 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
235 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
236 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
237 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
238 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
239 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
240 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
241 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
242 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
243 &symbols,
244 )
245 }
246}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 618486250..1107ffc8b 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -2,7 +2,7 @@ use languageserver_types::{
2 self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, 2 self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
3 TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, 3 TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat,
4}; 4};
5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText}; 5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText, NavigationTarget};
6use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; 6use ra_editor::{LineCol, LineIndex, translate_offset_with_edit};
7use ra_text_edit::{AtomTextEdit, TextEdit}; 7use ra_text_edit::{AtomTextEdit, TextEdit};
8use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 8use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@@ -322,6 +322,15 @@ impl TryConvWith for FileSystemEdit {
322 } 322 }
323} 323}
324 324
325impl TryConvWith for &NavigationTarget {
326 type Ctx = ServerWorld;
327 type Output = Location;
328 fn try_conv_with(self, world: &ServerWorld) -> Result<Location> {
329 let line_index = world.analysis().file_line_index(self.file_id());
330 to_location(self.file_id(), self.range(), &world, &line_index)
331 }
332}
333
325pub fn to_location( 334pub fn to_location(
326 file_id: FileId, 335 file_id: FileId,
327 range: TextRange, 336 range: TextRange,
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 11825d74e..26b6c7d8a 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -188,12 +188,11 @@ pub fn handle_workspace_symbol(
188 188
189 fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { 189 fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> {
190 let mut res = Vec::new(); 190 let mut res = Vec::new();
191 for (file_id, symbol) in world.analysis().symbol_search(query)? { 191 for nav in world.analysis().symbol_search(query)? {
192 let line_index = world.analysis().file_line_index(file_id);
193 let info = SymbolInformation { 192 let info = SymbolInformation {
194 name: symbol.name.to_string(), 193 name: nav.name().into(),
195 kind: symbol.kind.conv(), 194 kind: nav.kind().conv(),
196 location: to_location(file_id, symbol.node_range, world, &line_index)?, 195 location: nav.try_conv_with(world)?,
197 container_name: None, 196 container_name: None,
198 deprecated: None, 197 deprecated: None,
199 }; 198 };
@@ -212,12 +211,11 @@ pub fn handle_goto_definition(
212 None => return Ok(None), 211 None => return Ok(None),
213 Some(it) => it, 212 Some(it) => it,
214 }; 213 };
215 let mut res = Vec::new(); 214 let res = rr
216 for (file_id, symbol) in rr.resolves_to { 215 .resolves_to
217 let line_index = world.analysis().file_line_index(file_id); 216 .into_iter()
218 let location = to_location(file_id, symbol.node_range, &world, &line_index)?; 217 .map(|nav| nav.try_conv_with(&world))
219 res.push(location) 218 .collect::<Result<Vec<_>>>()?;
220 }
221 Ok(Some(req::GotoDefinitionResponse::Array(res))) 219 Ok(Some(req::GotoDefinitionResponse::Array(res)))
222} 220}
223 221
@@ -226,13 +224,12 @@ pub fn handle_parent_module(
226 params: req::TextDocumentPositionParams, 224 params: req::TextDocumentPositionParams,
227) -> Result<Vec<Location>> { 225) -> Result<Vec<Location>> {
228 let position = params.try_conv_with(&world)?; 226 let position = params.try_conv_with(&world)?;
229 let mut res = Vec::new(); 227 world
230 for (file_id, symbol) in world.analysis().parent_module(position)? { 228 .analysis()
231 let line_index = world.analysis().file_line_index(file_id); 229 .parent_module(position)?
232 let location = to_location(file_id, symbol.node_range, &world, &line_index)?; 230 .into_iter()
233 res.push(location); 231 .map(|nav| nav.try_conv_with(&world))
234 } 232 .collect::<Result<Vec<_>>>()
235 Ok(res)
236} 233}
237 234
238pub fn handle_runnables( 235pub fn handle_runnables(
@@ -517,8 +514,8 @@ pub fn handle_hover(
517 Some(it) => it, 514 Some(it) => it,
518 }; 515 };
519 let mut result = Vec::new(); 516 let mut result = Vec::new();
520 for (file_id, symbol) in rr.resolves_to { 517 for nav in rr.resolves_to {
521 if let Some(docs) = world.analysis().doc_text_for(file_id, symbol)? { 518 if let Some(docs) = world.analysis().doc_text_for(nav)? {
522 result.push(docs); 519 result.push(docs);
523 } 520 }
524 } 521 }