diff options
Diffstat (limited to 'crates/ra_analysis/src/symbol_index.rs')
-rw-r--r-- | crates/ra_analysis/src/symbol_index.rs | 165 |
1 files changed, 158 insertions, 7 deletions
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs index e5bdf0aa1..ddcf3d052 100644 --- a/crates/ra_analysis/src/symbol_index.rs +++ b/crates/ra_analysis/src/symbol_index.rs | |||
@@ -4,17 +4,19 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use fst::{self, Streamer}; | 6 | use fst::{self, Streamer}; |
7 | use ra_editor::{self, FileSymbol}; | ||
8 | use ra_syntax::{ | 7 | use 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 | }; |
12 | use ra_db::{SyntaxDatabase, SourceRootId}; | 13 | use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase}; |
14 | use salsa::ParallelDatabase; | ||
13 | use rayon::prelude::*; | 15 | use rayon::prelude::*; |
14 | 16 | ||
15 | use crate::{ | 17 | use crate::{ |
16 | Cancelable, | 18 | Cancelable, FileId, Query, |
17 | FileId, Query, | 19 | db::RootDatabase, |
18 | }; | 20 | }; |
19 | 21 | ||
20 | salsa::query_group! { | 22 | salsa::query_group! { |
@@ -35,6 +37,41 @@ fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<Sym | |||
35 | Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) | 37 | Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) |
36 | } | 38 | } |
37 | 39 | ||
40 | pub(crate) fn world_symbols( | ||
41 | db: &RootDatabase, | ||
42 | query: Query, | ||
43 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | ||
44 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | ||
45 | struct Snap(salsa::Snapshot<RootDatabase>); | ||
46 | impl Clone for Snap { | ||
47 | fn clone(&self) -> Snap { | ||
48 | Snap(self.0.snapshot()) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | let buf: Vec<Arc<SymbolIndex>> = if query.libs { | ||
53 | let snap = Snap(db.snapshot()); | ||
54 | db.library_roots() | ||
55 | .par_iter() | ||
56 | .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id)) | ||
57 | .collect() | ||
58 | } else { | ||
59 | let mut files = Vec::new(); | ||
60 | for &root in db.local_roots().iter() { | ||
61 | let sr = db.source_root(root); | ||
62 | files.extend(sr.files.values().map(|&it| it)) | ||
63 | } | ||
64 | |||
65 | let snap = Snap(db.snapshot()); | ||
66 | files | ||
67 | .par_iter() | ||
68 | .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) | ||
69 | .filter_map(|it| it.ok()) | ||
70 | .collect() | ||
71 | }; | ||
72 | Ok(query.search(&buf)) | ||
73 | } | ||
74 | |||
38 | #[derive(Default, Debug)] | 75 | #[derive(Default, Debug)] |
39 | pub(crate) struct SymbolIndex { | 76 | pub(crate) struct SymbolIndex { |
40 | symbols: Vec<(FileId, FileSymbol)>, | 77 | symbols: Vec<(FileId, FileSymbol)>, |
@@ -65,8 +102,9 @@ impl SymbolIndex { | |||
65 | ) -> SymbolIndex { | 102 | ) -> SymbolIndex { |
66 | let mut symbols = files | 103 | let mut symbols = files |
67 | .flat_map(|(file_id, file)| { | 104 | .flat_map(|(file_id, file)| { |
68 | ra_editor::file_symbols(&file) | 105 | file.syntax() |
69 | .into_iter() | 106 | .descendants() |
107 | .filter_map(to_symbol) | ||
70 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) | 108 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) |
71 | .collect::<Vec<_>>() | 109 | .collect::<Vec<_>>() |
72 | }) | 110 | }) |
@@ -121,3 +159,116 @@ fn is_type(kind: SyntaxKind) -> bool { | |||
121 | _ => false, | 159 | _ => false, |
122 | } | 160 | } |
123 | } | 161 | } |
162 | |||
163 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
164 | pub(crate) struct FileSymbol { | ||
165 | pub(crate) name: SmolStr, | ||
166 | pub(crate) node_range: TextRange, | ||
167 | pub(crate) kind: SyntaxKind, | ||
168 | } | ||
169 | |||
170 | impl FileSymbol { | ||
171 | pub(crate) fn docs(&self, file: &SourceFileNode) -> Option<String> { | ||
172 | file.syntax() | ||
173 | .descendants() | ||
174 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
175 | .filter_map(|node: SyntaxNodeRef| { | ||
176 | fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
177 | let comments = node.doc_comment_text(); | ||
178 | if comments.is_empty() { | ||
179 | None | ||
180 | } else { | ||
181 | Some(comments) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | visitor() | ||
186 | .visit(doc_comments::<ast::FnDef>) | ||
187 | .visit(doc_comments::<ast::StructDef>) | ||
188 | .visit(doc_comments::<ast::EnumDef>) | ||
189 | .visit(doc_comments::<ast::TraitDef>) | ||
190 | .visit(doc_comments::<ast::Module>) | ||
191 | .visit(doc_comments::<ast::TypeDef>) | ||
192 | .visit(doc_comments::<ast::ConstDef>) | ||
193 | .visit(doc_comments::<ast::StaticDef>) | ||
194 | .accept(node)? | ||
195 | }) | ||
196 | .nth(0) | ||
197 | } | ||
198 | /// Get a description of this node. | ||
199 | /// | ||
200 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
201 | pub(crate) fn description(&self, file: &SourceFileNode) -> Option<String> { | ||
202 | // TODO: After type inference is done, add type information to improve the output | ||
203 | file.syntax() | ||
204 | .descendants() | ||
205 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
206 | .filter_map(|node: SyntaxNodeRef| { | ||
207 | // TODO: Refactor to be have less repetition | ||
208 | visitor() | ||
209 | .visit(|node: ast::FnDef| { | ||
210 | let mut string = "fn ".to_string(); | ||
211 | node.name()?.syntax().text().push_to(&mut string); | ||
212 | Some(string) | ||
213 | }) | ||
214 | .visit(|node: ast::StructDef| { | ||
215 | let mut string = "struct ".to_string(); | ||
216 | node.name()?.syntax().text().push_to(&mut string); | ||
217 | Some(string) | ||
218 | }) | ||
219 | .visit(|node: ast::EnumDef| { | ||
220 | let mut string = "enum ".to_string(); | ||
221 | node.name()?.syntax().text().push_to(&mut string); | ||
222 | Some(string) | ||
223 | }) | ||
224 | .visit(|node: ast::TraitDef| { | ||
225 | let mut string = "trait ".to_string(); | ||
226 | node.name()?.syntax().text().push_to(&mut string); | ||
227 | Some(string) | ||
228 | }) | ||
229 | .visit(|node: ast::Module| { | ||
230 | let mut string = "mod ".to_string(); | ||
231 | node.name()?.syntax().text().push_to(&mut string); | ||
232 | Some(string) | ||
233 | }) | ||
234 | .visit(|node: ast::TypeDef| { | ||
235 | let mut string = "type ".to_string(); | ||
236 | node.name()?.syntax().text().push_to(&mut string); | ||
237 | Some(string) | ||
238 | }) | ||
239 | .visit(|node: ast::ConstDef| { | ||
240 | let mut string = "const ".to_string(); | ||
241 | node.name()?.syntax().text().push_to(&mut string); | ||
242 | Some(string) | ||
243 | }) | ||
244 | .visit(|node: ast::StaticDef| { | ||
245 | let mut string = "static ".to_string(); | ||
246 | node.name()?.syntax().text().push_to(&mut string); | ||
247 | Some(string) | ||
248 | }) | ||
249 | .accept(node)? | ||
250 | }) | ||
251 | .nth(0) | ||
252 | } | ||
253 | } | ||
254 | |||
255 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | ||
256 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> { | ||
257 | let name = node.name()?; | ||
258 | Some(FileSymbol { | ||
259 | name: name.text(), | ||
260 | node_range: node.syntax().range(), | ||
261 | kind: node.syntax().kind(), | ||
262 | }) | ||
263 | } | ||
264 | visitor() | ||
265 | .visit(decl::<ast::FnDef>) | ||
266 | .visit(decl::<ast::StructDef>) | ||
267 | .visit(decl::<ast::EnumDef>) | ||
268 | .visit(decl::<ast::TraitDef>) | ||
269 | .visit(decl::<ast::Module>) | ||
270 | .visit(decl::<ast::TypeDef>) | ||
271 | .visit(decl::<ast::ConstDef>) | ||
272 | .visit(decl::<ast::StaticDef>) | ||
273 | .accept(node)? | ||
274 | } | ||