diff options
author | Aleksey Kladov <[email protected]> | 2018-08-13 13:10:20 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-13 13:10:20 +0100 |
commit | 133d001d8296e51bcb4d0dc0982671f55c2c77d9 (patch) | |
tree | b0c17dddc00a9d05f5f7a0b2e14fcf2564459533 /crates | |
parent | ed2ac1713326df6b926062efcc6109a20cdf7c37 (diff) |
world symbols
Diffstat (limited to 'crates')
-rw-r--r-- | crates/libanalysis/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/libanalysis/idx.rs | 84 | ||||
-rw-r--r-- | crates/libanalysis/src/lib.rs | 47 | ||||
-rw-r--r-- | crates/libanalysis/src/symbol_index.rs | 74 |
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" | |||
8 | failure = "0.1.2" | 8 | failure = "0.1.2" |
9 | parking_lot = "0.6.3" | 9 | parking_lot = "0.6.3" |
10 | once_cell = "0.1.4" | 10 | once_cell = "0.1.4" |
11 | fst = { git = "https://github.com/matklad/fst", branch = "subsequence"} | ||
11 | libsyntax2 = { path = "../libsyntax2" } | 12 | libsyntax2 = { path = "../libsyntax2" } |
12 | libeditor = { path = "../libeditor" } | 13 | libeditor = { 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 @@ | |||
1 | use std::path::PathBuf; | ||
2 | |||
3 | use fst; | ||
4 | use fst::IntoStreamer; | ||
5 | use file; | ||
6 | |||
7 | use fall_tree::{TextRange, NodeType}; | ||
8 | use indxr::{FileIndex, IndexableFileSet}; | ||
9 | |||
10 | use editor::line_index::{LineCol, LineIndex}; | ||
11 | use editor::fst_subseq::FstSubSeq; | ||
12 | use editor::file_symbols::process_symbols; | ||
13 | |||
14 | use syntax::{STRUCT_DEF, ENUM_DEF, TRAIT_DEF, TYPE_DEF}; | ||
15 | |||
16 | |||
17 | pub struct SymbolIndex { | ||
18 | index: FileIndex<FileSymbols>, | ||
19 | } | ||
20 | |||
21 | impl 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 | |||
52 | struct Query { | ||
53 | query: String, | ||
54 | all_symbols: bool, | ||
55 | } | ||
56 | |||
57 | impl 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; | |||
6 | extern crate once_cell; | 6 | extern crate once_cell; |
7 | extern crate libsyntax2; | 7 | extern crate libsyntax2; |
8 | extern crate libeditor; | 8 | extern crate libeditor; |
9 | extern crate fst; | ||
10 | |||
11 | mod symbol_index; | ||
9 | 12 | ||
10 | use once_cell::sync::OnceCell; | 13 | use 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 | |||
17 | use libsyntax2::ast; | 21 | use libsyntax2::ast; |
18 | use libeditor::LineIndex; | 22 | use libeditor::{LineIndex, FileSymbol}; |
23 | |||
24 | use self::symbol_index::{FileSymbols, Query}; | ||
19 | 25 | ||
20 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 26 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
21 | 27 | ||
@@ -70,12 +76,7 @@ impl WorldState { | |||
70 | impl World { | 76 | impl 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)] | ||
111 | pub enum Search { | ||
112 | Continue, | ||
113 | Break, | ||
114 | } | ||
115 | |||
99 | 116 | ||
100 | #[derive(Default, Debug)] | 117 | #[derive(Default, Debug)] |
101 | struct WorldData { | 118 | struct WorldData { |
@@ -105,6 +122,7 @@ struct WorldData { | |||
105 | #[derive(Debug)] | 122 | #[derive(Debug)] |
106 | struct FileData { | 123 | struct 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 @@ | |||
1 | use libeditor::{FileSymbol, file_symbols}; | ||
2 | use libsyntax2::{ | ||
3 | ast, | ||
4 | SyntaxKind::{self, *}, | ||
5 | }; | ||
6 | use fst::{self, IntoStreamer}; | ||
7 | |||
8 | use Search; | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | pub(crate) struct FileSymbols { | ||
12 | symbols: Vec<FileSymbol>, | ||
13 | map: fst::Map, | ||
14 | } | ||
15 | |||
16 | impl 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 | |||
35 | pub(crate) struct Query { | ||
36 | query: String, | ||
37 | all_symbols: bool, | ||
38 | } | ||
39 | |||
40 | impl 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 | |||