aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/libanalysis/Cargo.toml1
-rw-r--r--crates/libanalysis/idx.rs84
-rw-r--r--crates/libanalysis/src/lib.rs47
-rw-r--r--crates/libanalysis/src/symbol_index.rs74
4 files changed, 199 insertions, 7 deletions
diff --git a/crates/libanalysis/Cargo.toml b/crates/libanalysis/Cargo.toml
index c773f4211..88b1099f2 100644
--- a/crates/libanalysis/Cargo.toml
+++ b/crates/libanalysis/Cargo.toml
@@ -8,5 +8,6 @@ log = "0.4.2"
8failure = "0.1.2" 8failure = "0.1.2"
9parking_lot = "0.6.3" 9parking_lot = "0.6.3"
10once_cell = "0.1.4" 10once_cell = "0.1.4"
11fst = { git = "https://github.com/matklad/fst", branch = "subsequence"}
11libsyntax2 = { path = "../libsyntax2" } 12libsyntax2 = { path = "../libsyntax2" }
12libeditor = { path = "../libeditor" } 13libeditor = { path = "../libeditor" }
diff --git a/crates/libanalysis/idx.rs b/crates/libanalysis/idx.rs
new file mode 100644
index 000000000..69a635aef
--- /dev/null
+++ b/crates/libanalysis/idx.rs
@@ -0,0 +1,84 @@
1use std::path::PathBuf;
2
3use fst;
4use fst::IntoStreamer;
5use file;
6
7use fall_tree::{TextRange, NodeType};
8use indxr::{FileIndex, IndexableFileSet};
9
10use editor::line_index::{LineCol, LineIndex};
11use editor::fst_subseq::FstSubSeq;
12use editor::file_symbols::process_symbols;
13
14use syntax::{STRUCT_DEF, ENUM_DEF, TRAIT_DEF, TYPE_DEF};
15
16
17pub struct SymbolIndex {
18 index: FileIndex<FileSymbols>,
19}
20
21impl SymbolIndex {
22 pub fn new(roots: Vec<PathBuf>) -> SymbolIndex {
23 let file_set = IndexableFileSet::new(roots, "rs");
24 let index = FileIndex::new(file_set, Box::new(|path| {
25 let text = file::get_text(path).ok()?;
26 Some(FileSymbols::new(text))
27 }));
28 SymbolIndex { index }
29 }
30
31 pub fn query(&self, query: &str) -> Vec<(PathBuf, Symbol)> {
32 let mut query = Query::new(query);
33 let mut result = Vec::new();
34 self.process_query(&query, &mut result);
35 if result.is_empty() && !query.all_symbols {
36 query.all_symbols = true;
37 self.process_query(&query, &mut result);
38 }
39 result
40 }
41
42 fn process_query(&self, query: &Query, acc: &mut Vec<(PathBuf, Symbol)>) {
43 self.index.process_files(&mut |file| {
44 query.process(&file.value, &mut |symbol| {
45 acc.push((file.path.clone(), symbol))
46 });
47 acc.len() > 512
48 });
49 }
50}
51
52struct Query {
53 query: String,
54 all_symbols: bool,
55}
56
57impl Query {
58 fn new(query: &str) -> Query {
59 let all_symbols = query.contains("#");
60 let query: String = query.chars()
61 .filter(|&c| c != '#')
62 .flat_map(char::to_lowercase)
63 .collect();
64 Query { query, all_symbols }
65 }
66
67 fn process(&self, file: &FileSymbols, acc: &mut FnMut(Symbol)) {
68 fn is_type(ty: NodeType) -> bool {
69 match ty {
70 STRUCT_DEF | ENUM_DEF | TRAIT_DEF| TYPE_DEF => true,
71 _ => false,
72 }
73 }
74
75 let a = FstSubSeq::new(&self.query);
76 for idx in file.map.search(a).into_stream().into_values() {
77 let idx = idx as usize;
78 let symbol = file.symbols[idx].clone();
79 if self.all_symbols || is_type(symbol.ty) {
80 acc(symbol)
81 }
82 }
83 }
84}
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index e4df3de2e..f0d0cf0a4 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -6,6 +6,9 @@ extern crate log;
6extern crate once_cell; 6extern crate once_cell;
7extern crate libsyntax2; 7extern crate libsyntax2;
8extern crate libeditor; 8extern crate libeditor;
9extern crate fst;
10
11mod symbol_index;
9 12
10use once_cell::sync::OnceCell; 13use once_cell::sync::OnceCell;
11 14
@@ -14,8 +17,11 @@ use std::{
14 collections::hash_map::HashMap, 17 collections::hash_map::HashMap,
15 path::{PathBuf, Path}, 18 path::{PathBuf, Path},
16}; 19};
20
17use libsyntax2::ast; 21use libsyntax2::ast;
18use libeditor::LineIndex; 22use libeditor::{LineIndex, FileSymbol};
23
24use self::symbol_index::{FileSymbols, Query};
19 25
20pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 26pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
21 27
@@ -70,12 +76,7 @@ impl WorldState {
70impl World { 76impl World {
71 pub fn file_syntax(&self, path: &Path) -> Result<ast::File> { 77 pub fn file_syntax(&self, path: &Path) -> Result<ast::File> {
72 let data = self.file_data(path)?; 78 let data = self.file_data(path)?;
73 let syntax = data.syntax 79 Ok(data.syntax(path).clone())
74 .get_or_init(|| {
75 trace!("parsing: {}", path.display());
76 ast::File::parse(&data.text)
77 }).clone();
78 Ok(syntax)
79 } 80 }
80 81
81 pub fn file_line_index(&self, path: &Path) -> Result<LineIndex> { 82 pub fn file_line_index(&self, path: &Path) -> Result<LineIndex> {
@@ -88,6 +89,16 @@ impl World {
88 Ok(index.clone()) 89 Ok(index.clone())
89 } 90 }
90 91
92 pub fn world_symbols(&self, query: &str, f: &mut FnMut(&Path, &FileSymbol) -> Search) {
93 let q = Query::new(query);
94 for (path, data) in self.data.file_map.iter() {
95 let symbols = data.symbols(path.as_path());
96 if q.process(symbols, &mut |symbol| f(path, symbol)) == Search::Break {
97 break;
98 }
99 }
100 }
101
91 fn file_data(&self, path: &Path) -> Result<Arc<FileData>> { 102 fn file_data(&self, path: &Path) -> Result<Arc<FileData>> {
92 match self.data.file_map.get(path) { 103 match self.data.file_map.get(path) {
93 Some(data) => Ok(data.clone()), 104 Some(data) => Ok(data.clone()),
@@ -96,6 +107,12 @@ impl World {
96 } 107 }
97} 108}
98 109
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum Search {
112 Continue,
113 Break,
114}
115
99 116
100#[derive(Default, Debug)] 117#[derive(Default, Debug)]
101struct WorldData { 118struct WorldData {
@@ -105,6 +122,7 @@ struct WorldData {
105#[derive(Debug)] 122#[derive(Debug)]
106struct FileData { 123struct FileData {
107 text: String, 124 text: String,
125 symbols: OnceCell<FileSymbols>,
108 syntax: OnceCell<ast::File>, 126 syntax: OnceCell<ast::File>,
109 lines: OnceCell<LineIndex>, 127 lines: OnceCell<LineIndex>,
110} 128}
@@ -113,8 +131,23 @@ impl FileData {
113 fn new(text: String) -> FileData { 131 fn new(text: String) -> FileData {
114 FileData { 132 FileData {
115 text, 133 text,
134 symbols: OnceCell::new(),
116 syntax: OnceCell::new(), 135 syntax: OnceCell::new(),
117 lines: OnceCell::new(), 136 lines: OnceCell::new(),
118 } 137 }
119 } 138 }
139
140 fn syntax(&self, path: &Path) -> &ast::File {
141 self.syntax
142 .get_or_init(|| {
143 trace!("parsing: {}", path.display());
144 ast::File::parse(&self.text)
145 })
146 }
147
148 fn symbols(&self, path: &Path) -> &FileSymbols {
149 let syntax = self.syntax(path);
150 self.symbols
151 .get_or_init(|| FileSymbols::new(syntax))
152 }
120} 153}
diff --git a/crates/libanalysis/src/symbol_index.rs b/crates/libanalysis/src/symbol_index.rs
new file mode 100644
index 000000000..1878fae99
--- /dev/null
+++ b/crates/libanalysis/src/symbol_index.rs
@@ -0,0 +1,74 @@
1use libeditor::{FileSymbol, file_symbols};
2use libsyntax2::{
3 ast,
4 SyntaxKind::{self, *},
5};
6use fst::{self, IntoStreamer};
7
8use Search;
9
10#[derive(Debug)]
11pub(crate) struct FileSymbols {
12 symbols: Vec<FileSymbol>,
13 map: fst::Map,
14}
15
16impl FileSymbols {
17 pub(crate) fn new(file: &ast::File) -> FileSymbols {
18 let mut symbols = file_symbols(file)
19 .into_iter()
20 .map(|s| (s.name.as_str().to_lowercase(), s))
21 .collect::<Vec<_>>();
22
23 symbols.sort_by(|s1, s2| s1.0.cmp(&s2.0));
24 symbols.dedup_by(|s1, s2| s1.0 == s2.0);
25 let (names, symbols): (Vec<String>, Vec<FileSymbol>) =
26 symbols.into_iter().unzip();
27
28 let map = fst::Map::from_iter(
29 names.into_iter().zip(0u64..)
30 ).unwrap();
31 FileSymbols { symbols, map }
32 }
33}
34
35pub(crate) struct Query {
36 query: String,
37 all_symbols: bool,
38}
39
40impl Query {
41 pub(crate) fn new(query: &str) -> Query {
42 let all_symbols = query.contains("#");
43 let query: String = query.chars()
44 .filter(|&c| c != '#')
45 .flat_map(char::to_lowercase)
46 .collect();
47 Query { query, all_symbols }
48 }
49
50 pub(crate) fn process(
51 &self,
52 file: &FileSymbols,
53 acc: &mut FnMut(&FileSymbol) -> Search,
54 ) -> Search {
55 fn is_type(kind: SyntaxKind) -> bool {
56 match kind {
57 STRUCT | ENUM | TRAIT | TYPE_ITEM => true,
58 _ => false,
59 }
60 }
61 let automaton = fst::automaton::Subsequence::new(&self.query);
62 for idx in file.map.search(automaton).into_stream().into_values() {
63 let idx = idx as usize;
64 let symbol = &file.symbols[idx];
65 if self.all_symbols || is_type(symbol.kind) {
66 if acc(&symbol) == Search::Break {
67 return Search::Break;
68 }
69 }
70 }
71 Search::Continue
72 }
73}
74