aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/Cargo.toml1
-rw-r--r--crates/libanalysis/src/api.rs142
-rw-r--r--crates/libanalysis/src/imp.rs46
-rw-r--r--crates/libanalysis/src/lib.rs174
-rw-r--r--crates/libanalysis/src/symbol_index.rs32
-rw-r--r--crates/libanalysis/tests/tests.rs10
6 files changed, 193 insertions, 212 deletions
diff --git a/crates/libanalysis/Cargo.toml b/crates/libanalysis/Cargo.toml
index 5aca84f0e..1f072533c 100644
--- a/crates/libanalysis/Cargo.toml
+++ b/crates/libanalysis/Cargo.toml
@@ -6,7 +6,6 @@ authors = ["Aleksey Kladov <[email protected]>"]
6[dependencies] 6[dependencies]
7relative-path = "0.3.7" 7relative-path = "0.3.7"
8log = "0.4.2" 8log = "0.4.2"
9failure = "0.1.2"
10parking_lot = "0.6.3" 9parking_lot = "0.6.3"
11once_cell = "0.1.4" 10once_cell = "0.1.4"
12rayon = "1.0.2" 11rayon = "1.0.2"
diff --git a/crates/libanalysis/src/api.rs b/crates/libanalysis/src/api.rs
deleted file mode 100644
index ded88cd15..000000000
--- a/crates/libanalysis/src/api.rs
+++ /dev/null
@@ -1,142 +0,0 @@
1use relative_path::{RelativePath, RelativePathBuf};
2use libsyntax2::{File, TextRange, TextUnit, AtomEdit};
3use libeditor;
4use {imp::{AnalysisImpl, AnalysisHostImpl}, Query};
5
6pub use libeditor::{
7 LocalEdit, StructureNode, LineIndex, FileSymbol,
8 Runnable, RunnableKind, HighlightedRange, CompletionItem
9};
10
11#[derive(Debug)]
12pub struct SourceChange {
13 pub label: String,
14 pub source_file_edits: Vec<SourceFileEdit>,
15 pub file_system_edits: Vec<FileSystemEdit>,
16 pub cursor_position: Option<Position>,
17}
18
19#[derive(Debug)]
20pub struct Position {
21 pub file_id: FileId,
22 pub offset: TextUnit,
23}
24
25#[derive(Debug)]
26pub struct SourceFileEdit {
27 pub file_id: FileId,
28 pub edits: Vec<AtomEdit>,
29}
30
31#[derive(Debug)]
32pub enum FileSystemEdit {
33 CreateFile {
34 anchor: FileId,
35 path: RelativePathBuf,
36 },
37 MoveFile {
38 file: FileId,
39 path: RelativePathBuf,
40 }
41}
42
43#[derive(Debug)]
44pub struct Diagnostic {
45 pub message: String,
46 pub range: TextRange,
47 pub fix: Option<SourceChange>,
48}
49
50#[derive(Clone, Debug)]
51pub struct Analysis {
52 pub(crate) imp: AnalysisImpl
53}
54
55impl Analysis {
56 pub fn file_syntax(&self, file_id: FileId) -> File {
57 self.imp.file_syntax(file_id)
58 }
59 pub fn file_line_index(&self, file_id: FileId) -> LineIndex {
60 self.imp.file_line_index(file_id)
61 }
62 pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange {
63 libeditor::extend_selection(file, range).unwrap_or(range)
64 }
65 pub fn matching_brace(&self, file: &File, offset: TextUnit) -> Option<TextUnit> {
66 libeditor::matching_brace(file, offset)
67 }
68 pub fn syntax_tree(&self, file_id: FileId) -> String {
69 let file = self.file_syntax(file_id);
70 libeditor::syntax_tree(&file)
71 }
72 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange {
73 let file = self.file_syntax(file_id);
74 SourceChange::from_local_edit(file_id, "join lines", libeditor::join_lines(&file, range))
75 }
76 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
77 let file = self.file_syntax(file_id);
78 Some(SourceChange::from_local_edit(file_id, "add semicolon", libeditor::on_eq_typed(&file, offset)?))
79 }
80 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
81 let file = self.file_syntax(file_id);
82 libeditor::file_structure(&file)
83 }
84 pub fn symbol_search(&self, query: Query) -> Vec<(FileId, FileSymbol)> {
85 self.imp.world_symbols(query)
86 }
87 pub fn approximately_resolve_symbol(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, FileSymbol)> {
88 self.imp.approximately_resolve_symbol(file_id, offset)
89 }
90 pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> {
91 self.imp.parent_module(file_id)
92 }
93 pub fn runnables(&self, file_id: FileId) -> Vec<Runnable> {
94 let file = self.file_syntax(file_id);
95 libeditor::runnables(&file)
96 }
97 pub fn highlight(&self, file_id: FileId) -> Vec<HighlightedRange> {
98 let file = self.file_syntax(file_id);
99 libeditor::highlight(&file)
100 }
101 pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option<Vec<CompletionItem>> {
102 let file = self.file_syntax(file_id);
103 libeditor::scope_completion(&file, offset)
104 }
105 pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
106 self.imp.assists(file_id, offset)
107 }
108 pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
109 self.imp.diagnostics(file_id)
110 }
111}
112
113pub trait FileResolver: Send + Sync + 'static {
114 fn file_stem(&self, id: FileId) -> String;
115 fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
119pub struct FileId(pub u32);
120
121#[derive(Debug)]
122pub struct AnalysisHost {
123 pub(crate) imp: AnalysisHostImpl
124}
125
126impl AnalysisHost {
127 pub fn new() -> AnalysisHost {
128 AnalysisHost { imp: AnalysisHostImpl::new() }
129 }
130
131 pub fn analysis(&self, file_resolver: impl FileResolver) -> Analysis {
132 Analysis { imp: self.imp.analysis(file_resolver) }
133 }
134
135 pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
136 self.change_files(::std::iter::once((file_id, text)));
137 }
138
139 pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) {
140 self.imp.change_files(changes)
141 }
142}
diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs
index 06bbc7cf2..004942e72 100644
--- a/crates/libanalysis/src/imp.rs
+++ b/crates/libanalysis/src/imp.rs
@@ -9,14 +9,14 @@ use std::{
9 panic, 9 panic,
10}; 10};
11 11
12use rayon::prelude::*;
13use once_cell::sync::OnceCell;
14use libeditor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit};
12use libsyntax2::{ 15use libsyntax2::{
13 TextUnit, TextRange, SmolStr, File, AstNode, 16 TextUnit, TextRange, SmolStr, File, AstNode,
14 SyntaxKind::*, 17 SyntaxKind::*,
15 ast::{self, NameOwner}, 18 ast::{self, NameOwner},
16}; 19};
17use rayon::prelude::*;
18use once_cell::sync::OnceCell;
19use libeditor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit};
20 20
21use { 21use {
22 FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, 22 FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit,
@@ -44,11 +44,11 @@ impl AnalysisHostImpl {
44 AnalysisImpl { 44 AnalysisImpl {
45 needs_reindex: AtomicBool::new(false), 45 needs_reindex: AtomicBool::new(false),
46 file_resolver: Arc::new(file_resolver), 46 file_resolver: Arc::new(file_resolver),
47 data: self.data.clone() 47 data: self.data.clone(),
48 } 48 }
49 } 49 }
50 50
51 pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) { 51 pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) {
52 let data = self.data_mut(); 52 let data = self.data_mut();
53 for (file_id, text) in changes { 53 for (file_id, text) in changes {
54 let change_kind = if data.file_map.remove(&file_id).is_some() { 54 let change_kind = if data.file_map.remove(&file_id).is_some() {
@@ -72,20 +72,14 @@ impl AnalysisHostImpl {
72 } 72 }
73 73
74 fn data_mut(&mut self) -> &mut WorldData { 74 fn data_mut(&mut self) -> &mut WorldData {
75 if Arc::get_mut(&mut self.data).is_none() { 75 Arc::make_mut(&mut self.data)
76 self.data = Arc::new(WorldData {
77 file_map: self.data.file_map.clone(),
78 module_map: self.data.module_map.clone(),
79 });
80 }
81 Arc::get_mut(&mut self.data).unwrap()
82 } 76 }
83} 77}
84 78
85pub(crate) struct AnalysisImpl { 79pub(crate) struct AnalysisImpl {
86 pub(crate) needs_reindex: AtomicBool, 80 needs_reindex: AtomicBool,
87 pub(crate) file_resolver: Arc<FileResolver>, 81 file_resolver: Arc<FileResolver>,
88 pub(crate) data: Arc<WorldData>, 82 data: Arc<WorldData>,
89} 83}
90 84
91impl fmt::Debug for AnalysisImpl { 85impl fmt::Debug for AnalysisImpl {
@@ -280,7 +274,7 @@ impl AnalysisImpl {
280 } 274 }
281 275
282 fn reindex(&self) { 276 fn reindex(&self) {
283 if self.needs_reindex.compare_and_swap(false, true, SeqCst) { 277 if self.needs_reindex.compare_and_swap(true, false, SeqCst) {
284 let now = Instant::now(); 278 let now = Instant::now();
285 let data = &*self.data; 279 let data = &*self.data;
286 data.file_map 280 data.file_map
@@ -298,22 +292,22 @@ impl AnalysisImpl {
298 } 292 }
299} 293}
300 294
301#[derive(Default, Debug)] 295#[derive(Clone, Default, Debug)]
302pub(crate) struct WorldData { 296struct WorldData {
303 pub(crate) file_map: HashMap<FileId, Arc<FileData>>, 297 file_map: HashMap<FileId, Arc<FileData>>,
304 pub(crate) module_map: ModuleMap, 298 module_map: ModuleMap,
305} 299}
306 300
307#[derive(Debug)] 301#[derive(Debug)]
308pub(crate) struct FileData { 302struct FileData {
309 pub(crate) text: String, 303 text: String,
310 pub(crate) symbols: OnceCell<FileSymbols>, 304 symbols: OnceCell<FileSymbols>,
311 pub(crate) syntax: OnceCell<File>, 305 syntax: OnceCell<File>,
312 pub(crate) lines: OnceCell<LineIndex>, 306 lines: OnceCell<LineIndex>,
313} 307}
314 308
315impl FileData { 309impl FileData {
316 pub(crate) fn new(text: String) -> FileData { 310 fn new(text: String) -> FileData {
317 FileData { 311 FileData {
318 text, 312 text,
319 symbols: OnceCell::new(), 313 symbols: OnceCell::new(),
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index a39141941..810228632 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -1,4 +1,3 @@
1extern crate failure;
2extern crate parking_lot; 1extern crate parking_lot;
3#[macro_use] 2#[macro_use]
4extern crate log; 3extern crate log;
@@ -11,13 +10,174 @@ extern crate relative_path;
11 10
12mod symbol_index; 11mod symbol_index;
13mod module_map; 12mod module_map;
14mod api;
15mod imp; 13mod imp;
16 14
17pub use self::symbol_index::Query; 15use relative_path::{RelativePath, RelativePathBuf};
18pub use self::api::{ 16use libsyntax2::{File, TextRange, TextUnit, AtomEdit};
19 AnalysisHost, Analysis, SourceChange, SourceFileEdit, FileSystemEdit, Position, Diagnostic, Runnable, RunnableKind, 17use imp::{AnalysisImpl, AnalysisHostImpl};
20 FileId, FileResolver, 18
19pub use libeditor::{
20 StructureNode, LineIndex, FileSymbol,
21 Runnable, RunnableKind, HighlightedRange, CompletionItem,
21}; 22};
22 23
23pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct FileId(pub u32);
26
27pub trait FileResolver: Send + Sync + 'static {
28 fn file_stem(&self, id: FileId) -> String;
29 fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
30}
31
32#[derive(Debug)]
33pub struct AnalysisHost {
34 pub(crate) imp: AnalysisHostImpl
35}
36
37impl AnalysisHost {
38 pub fn new() -> AnalysisHost {
39 AnalysisHost { imp: AnalysisHostImpl::new() }
40 }
41 pub fn analysis(&self, file_resolver: impl FileResolver) -> Analysis {
42 Analysis { imp: self.imp.analysis(file_resolver) }
43 }
44 pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
45 self.change_files(::std::iter::once((file_id, text)));
46 }
47 pub fn change_files(&mut self, mut changes: impl Iterator<Item=(FileId, Option<String>)>) {
48 self.imp.change_files(&mut changes)
49 }
50}
51
52#[derive(Debug)]
53pub struct SourceChange {
54 pub label: String,
55 pub source_file_edits: Vec<SourceFileEdit>,
56 pub file_system_edits: Vec<FileSystemEdit>,
57 pub cursor_position: Option<Position>,
58}
59
60#[derive(Debug)]
61pub struct Position {
62 pub file_id: FileId,
63 pub offset: TextUnit,
64}
65
66#[derive(Debug)]
67pub struct SourceFileEdit {
68 pub file_id: FileId,
69 pub edits: Vec<AtomEdit>,
70}
71
72#[derive(Debug)]
73pub enum FileSystemEdit {
74 CreateFile {
75 anchor: FileId,
76 path: RelativePathBuf,
77 },
78 MoveFile {
79 file: FileId,
80 path: RelativePathBuf,
81 }
82}
83
84#[derive(Debug)]
85pub struct Diagnostic {
86 pub message: String,
87 pub range: TextRange,
88 pub fix: Option<SourceChange>,
89}
90
91#[derive(Debug)]
92pub struct Query {
93 query: String,
94 lowercased: String,
95 only_types: bool,
96 exact: bool,
97 limit: usize,
98}
99
100impl Query {
101 pub fn new(query: String) -> Query {
102 let lowercased = query.to_lowercase();
103 Query {
104 query,
105 lowercased,
106 only_types: false,
107 exact: false,
108 limit: usize::max_value()
109 }
110 }
111 pub fn only_types(&mut self) {
112 self.only_types = true;
113 }
114 pub fn exact(&mut self) {
115 self.exact = true;
116 }
117 pub fn limit(&mut self, limit: usize) {
118 self.limit = limit
119 }
120}
121
122#[derive(Clone, Debug)]
123pub struct Analysis {
124 pub(crate) imp: AnalysisImpl
125}
126
127impl Analysis {
128 pub fn file_syntax(&self, file_id: FileId) -> File {
129 self.imp.file_syntax(file_id)
130 }
131 pub fn file_line_index(&self, file_id: FileId) -> LineIndex {
132 self.imp.file_line_index(file_id)
133 }
134 pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange {
135 libeditor::extend_selection(file, range).unwrap_or(range)
136 }
137 pub fn matching_brace(&self, file: &File, offset: TextUnit) -> Option<TextUnit> {
138 libeditor::matching_brace(file, offset)
139 }
140 pub fn syntax_tree(&self, file_id: FileId) -> String {
141 let file = self.file_syntax(file_id);
142 libeditor::syntax_tree(&file)
143 }
144 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange {
145 let file = self.file_syntax(file_id);
146 SourceChange::from_local_edit(file_id, "join lines", libeditor::join_lines(&file, range))
147 }
148 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
149 let file = self.file_syntax(file_id);
150 Some(SourceChange::from_local_edit(file_id, "add semicolon", libeditor::on_eq_typed(&file, offset)?))
151 }
152 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
153 let file = self.file_syntax(file_id);
154 libeditor::file_structure(&file)
155 }
156 pub fn symbol_search(&self, query: Query) -> Vec<(FileId, FileSymbol)> {
157 self.imp.world_symbols(query)
158 }
159 pub fn approximately_resolve_symbol(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, FileSymbol)> {
160 self.imp.approximately_resolve_symbol(file_id, offset)
161 }
162 pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> {
163 self.imp.parent_module(file_id)
164 }
165 pub fn runnables(&self, file_id: FileId) -> Vec<Runnable> {
166 let file = self.file_syntax(file_id);
167 libeditor::runnables(&file)
168 }
169 pub fn highlight(&self, file_id: FileId) -> Vec<HighlightedRange> {
170 let file = self.file_syntax(file_id);
171 libeditor::highlight(&file)
172 }
173 pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option<Vec<CompletionItem>> {
174 let file = self.file_syntax(file_id);
175 libeditor::scope_completion(&file, offset)
176 }
177 pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
178 self.imp.assists(file_id, offset)
179 }
180 pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
181 self.imp.diagnostics(file_id)
182 }
183}
diff --git a/crates/libanalysis/src/symbol_index.rs b/crates/libanalysis/src/symbol_index.rs
index 73cbf5702..cb35ab1d1 100644
--- a/crates/libanalysis/src/symbol_index.rs
+++ b/crates/libanalysis/src/symbol_index.rs
@@ -4,6 +4,7 @@ use libsyntax2::{
4 SyntaxKind::{self, *}, 4 SyntaxKind::{self, *},
5}; 5};
6use fst::{self, IntoStreamer, Streamer}; 6use fst::{self, IntoStreamer, Streamer};
7use Query;
7 8
8#[derive(Debug)] 9#[derive(Debug)]
9pub(crate) struct FileSymbols { 10pub(crate) struct FileSymbols {
@@ -30,38 +31,7 @@ impl FileSymbols {
30 } 31 }
31} 32}
32 33
33pub struct Query {
34 query: String,
35 lowercased: String,
36 only_types: bool,
37 exact: bool,
38 limit: usize,
39}
40
41impl Query { 34impl Query {
42 pub fn new(query: String) -> Query {
43 let lowercased = query.to_lowercase();
44 Query {
45 query,
46 lowercased,
47 only_types: false,
48 exact: false,
49 limit: usize::max_value()
50 }
51 }
52
53 pub fn only_types(&mut self) {
54 self.only_types = true;
55 }
56
57 pub fn exact(&mut self) {
58 self.exact = true;
59 }
60
61 pub fn limit(&mut self, limit: usize) {
62 self.limit = limit
63 }
64
65 pub(crate) fn process( 35 pub(crate) fn process(
66 &mut self, 36 &mut self,
67 file: &FileSymbols, 37 file: &FileSymbols,
diff --git a/crates/libanalysis/tests/tests.rs b/crates/libanalysis/tests/tests.rs
index 5893efaf6..2f1299463 100644
--- a/crates/libanalysis/tests/tests.rs
+++ b/crates/libanalysis/tests/tests.rs
@@ -5,7 +5,7 @@ extern crate test_utils;
5use std::path::{Path}; 5use std::path::{Path};
6 6
7use relative_path::RelativePath; 7use relative_path::RelativePath;
8use libanalysis::{WorldState, FileId, FileResolver}; 8use libanalysis::{AnalysisHost, FileId, FileResolver};
9use test_utils::assert_eq_dbg; 9use test_utils::assert_eq_dbg;
10 10
11struct FileMap(&'static [(u32, &'static str)]); 11struct FileMap(&'static [(u32, &'static str)]);
@@ -37,7 +37,7 @@ impl FileResolver for FileMap {
37 37
38#[test] 38#[test]
39fn test_resolve_module() { 39fn test_resolve_module() {
40 let mut world = WorldState::new(); 40 let mut world = AnalysisHost::new();
41 world.change_file(FileId(1), Some("mod foo;".to_string())); 41 world.change_file(FileId(1), Some("mod foo;".to_string()));
42 world.change_file(FileId(2), Some("".to_string())); 42 world.change_file(FileId(2), Some("".to_string()));
43 43
@@ -64,7 +64,7 @@ fn test_resolve_module() {
64 64
65#[test] 65#[test]
66fn test_unresolved_module_diagnostic() { 66fn test_unresolved_module_diagnostic() {
67 let mut world = WorldState::new(); 67 let mut world = AnalysisHost::new();
68 world.change_file(FileId(1), Some("mod foo;".to_string())); 68 world.change_file(FileId(1), Some("mod foo;".to_string()));
69 69
70 let snap = world.analysis(FileMap(&[(1, "/lib.rs")])); 70 let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
@@ -84,7 +84,7 @@ fn test_unresolved_module_diagnostic() {
84 84
85#[test] 85#[test]
86fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { 86fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
87 let mut world = WorldState::new(); 87 let mut world = AnalysisHost::new();
88 world.change_file(FileId(1), Some("mod foo {}".to_string())); 88 world.change_file(FileId(1), Some("mod foo {}".to_string()));
89 89
90 let snap = world.analysis(FileMap(&[(1, "/lib.rs")])); 90 let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
@@ -97,7 +97,7 @@ fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
97 97
98#[test] 98#[test]
99fn test_resolve_parent_module() { 99fn test_resolve_parent_module() {
100 let mut world = WorldState::new(); 100 let mut world = AnalysisHost::new();
101 world.change_file(FileId(1), Some("mod foo;".to_string())); 101 world.change_file(FileId(1), Some("mod foo;".to_string()));
102 world.change_file(FileId(2), Some("".to_string())); 102 world.change_file(FileId(2), Some("".to_string()));
103 103