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.rs122
1 files changed, 29 insertions, 93 deletions
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index ddcf3d052..10d8e8059 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -1,3 +1,24 @@
1//! This module handles fuzzy-searching of functions, structs and other symbols
2//! by name across the whole workspace and dependencies.
3//!
4//! It works by building an incrementally-updated text-search index of all
5//! symbols. The backbone of the index is the **awesome** `fst` crate by
6//! @BurntSushi.
7//!
8//! In a nutshell, you give a set of strings to the `fst`, and it builds a
9//! finite state machine describing this set of strtings. The strings which
10//! could fuzzy-match a pattern can also be described by a finite state machine.
11//! What is freakingly cool is that you can now traverse both state machines in
12//! lock-step to enumerate the strings which are both in the input set and
13//! fuzz-match the query. Or, more formally, given two langauges described by
14//! fsts, one can build an product fst which describes the intersection of the
15//! languages.
16//!
17//! `fst` does not support cheap updating of the index, but it supports unioning
18//! of state machines. So, to account for changing source code, we build an fst
19//! for each library (which is assumed to never change) and an fst for each rust
20//! file in the current workspace, and run a query aginst the union of all
21//! thouse fsts.
1use std::{ 22use std::{
2 hash::{Hash, Hasher}, 23 hash::{Hash, Hasher},
3 sync::Arc, 24 sync::Arc,
@@ -5,12 +26,12 @@ use std::{
5 26
6use fst::{self, Streamer}; 27use fst::{self, Streamer};
7use ra_syntax::{ 28use ra_syntax::{
8 AstNode, SyntaxNodeRef, SourceFileNode, SmolStr, TextRange, 29 SyntaxNodeRef, SourceFileNode, SmolStr,
9 algo::visit::{visitor, Visitor}, 30 algo::visit::{visitor, Visitor},
10 SyntaxKind::{self, *}, 31 SyntaxKind::{self, *},
11 ast::{self, NameOwner, DocCommentsOwner}, 32 ast::{self, NameOwner},
12}; 33};
13use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase}; 34use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase, LocalSyntaxPtr};
14use salsa::ParallelDatabase; 35use salsa::ParallelDatabase;
15use rayon::prelude::*; 36use rayon::prelude::*;
16 37
@@ -140,7 +161,7 @@ impl Query {
140 let idx = indexed_value.value as usize; 161 let idx = indexed_value.value as usize;
141 162
142 let (file_id, symbol) = &file_symbols.symbols[idx]; 163 let (file_id, symbol) = &file_symbols.symbols[idx];
143 if self.only_types && !is_type(symbol.kind) { 164 if self.only_types && !is_type(symbol.ptr.kind()) {
144 continue; 165 continue;
145 } 166 }
146 if self.exact && symbol.name != self.query { 167 if self.exact && symbol.name != self.query {
@@ -160,96 +181,12 @@ fn is_type(kind: SyntaxKind) -> bool {
160 } 181 }
161} 182}
162 183
184/// The actual data that is stored in the index. It should be as compact as
185/// possible.
163#[derive(Debug, Clone, PartialEq, Eq, Hash)] 186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
164pub(crate) struct FileSymbol { 187pub(crate) struct FileSymbol {
165 pub(crate) name: SmolStr, 188 pub(crate) name: SmolStr,
166 pub(crate) node_range: TextRange, 189 pub(crate) ptr: LocalSyntaxPtr,
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} 190}
254 191
255fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { 192fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
@@ -257,8 +194,7 @@ fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
257 let name = node.name()?; 194 let name = node.name()?;
258 Some(FileSymbol { 195 Some(FileSymbol {
259 name: name.text(), 196 name: name.text(),
260 node_range: node.syntax().range(), 197 ptr: LocalSyntaxPtr::new(node.syntax()),
261 kind: node.syntax().kind(),
262 }) 198 })
263 } 199 }
264 visitor() 200 visitor()