aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/symbol_index.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/symbol_index.rs')
-rw-r--r--crates/ra_analysis/src/symbol_index.rs165
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
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, FilesDatabase};
14use salsa::ParallelDatabase;
13use rayon::prelude::*; 15use rayon::prelude::*;
14 16
15use crate::{ 17use crate::{
16 Cancelable, 18 Cancelable, FileId, Query,
17 FileId, Query, 19 db::RootDatabase,
18}; 20};
19 21
20salsa::query_group! { 22salsa::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
40pub(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)]
39pub(crate) struct SymbolIndex { 76pub(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)]
164pub(crate) struct FileSymbol {
165 pub(crate) name: SmolStr,
166 pub(crate) node_range: TextRange,
167 pub(crate) kind: SyntaxKind,
168}
169
170impl 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
255fn 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}