aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/Cargo.toml1
-rw-r--r--crates/ra_ide/src/assists.rs10
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs2
-rw-r--r--crates/ra_ide/src/call_info.rs6
-rw-r--r--crates/ra_ide/src/change.rs386
-rw-r--r--crates/ra_ide/src/completion.rs5
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs7
-rw-r--r--crates/ra_ide/src/db.rs132
-rw-r--r--crates/ra_ide/src/diagnostics.rs3
-rw-r--r--crates/ra_ide/src/display/function_signature.rs17
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs3
-rw-r--r--crates/ra_ide/src/expand.rs3
-rw-r--r--crates/ra_ide/src/expand_macro.rs25
-rw-r--r--crates/ra_ide/src/extend_selection.rs12
-rw-r--r--crates/ra_ide/src/feature_flags.rs71
-rw-r--r--crates/ra_ide/src/goto_definition.rs4
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs4
-rw-r--r--crates/ra_ide/src/hover.rs2
-rw-r--r--crates/ra_ide/src/impls.rs3
-rw-r--r--crates/ra_ide/src/imports_locator.rs15
-rw-r--r--crates/ra_ide/src/inlay_hints.rs3
-rw-r--r--crates/ra_ide/src/lib.rs147
-rw-r--r--crates/ra_ide/src/line_index.rs283
-rw-r--r--crates/ra_ide/src/line_index_utils.rs331
-rw-r--r--crates/ra_ide/src/parent_module.rs3
-rw-r--r--crates/ra_ide/src/references.rs5
-rw-r--r--crates/ra_ide/src/references/classify.rs2
-rw-r--r--crates/ra_ide/src/references/name_definition.rs2
-rw-r--r--crates/ra_ide/src/references/rename.rs4
-rw-r--r--crates/ra_ide/src/references/search_scope.rs2
-rw-r--r--crates/ra_ide/src/runnables.rs3
-rw-r--r--crates/ra_ide/src/status.rs10
-rw-r--r--crates/ra_ide/src/symbol_index.rs405
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ra_ide/src/syntax_tree.rs2
-rw-r--r--crates/ra_ide/src/typing.rs3
-rw-r--r--crates/ra_ide/src/wasm_shims.rs19
37 files changed, 177 insertions, 1760 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 53817d1f7..9ace35229 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -28,6 +28,7 @@ once_cell = "1.2.0"
28ra_syntax = { path = "../ra_syntax" } 28ra_syntax = { path = "../ra_syntax" }
29ra_text_edit = { path = "../ra_text_edit" } 29ra_text_edit = { path = "../ra_text_edit" }
30ra_db = { path = "../ra_db" } 30ra_db = { path = "../ra_db" }
31ra_ide_db = { path = "../ra_ide_db" }
31ra_cfg = { path = "../ra_cfg" } 32ra_cfg = { path = "../ra_cfg" }
32ra_fmt = { path = "../ra_fmt" } 33ra_fmt = { path = "../ra_fmt" }
33ra_prof = { path = "../ra_prof" } 34ra_prof = { path = "../ra_prof" }
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index c43c45c65..f26047570 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -1,13 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use either::Either;
4use ra_assists::{AssistAction, AssistLabel};
3use ra_db::{FilePosition, FileRange}; 5use ra_db::{FilePosition, FileRange};
6use ra_ide_db::RootDatabase;
7
8use crate::{imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit};
4 9
5use crate::{
6 db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit,
7};
8use either::Either;
9pub use ra_assists::AssistId; 10pub use ra_assists::AssistId;
10use ra_assists::{AssistAction, AssistLabel};
11 11
12#[derive(Debug)] 12#[derive(Debug)]
13pub struct Assist { 13pub struct Assist {
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index aa5d60c7b..f984f40ad 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -3,6 +3,7 @@
3use indexmap::IndexMap; 3use indexmap::IndexMap;
4 4
5use hir::db::AstDatabase; 5use hir::db::AstDatabase;
6use ra_ide_db::RootDatabase;
6use ra_syntax::{ 7use ra_syntax::{
7 ast::{self, DocCommentsOwner}, 8 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, TextRange, 9 match_ast, AstNode, TextRange,
@@ -10,7 +11,6 @@ use ra_syntax::{
10 11
11use crate::{ 12use crate::{
12 call_info::FnCallNode, 13 call_info::FnCallNode,
13 db::RootDatabase,
14 display::{ShortLabel, ToNav}, 14 display::{ShortLabel, ToNav},
15 expand::descend_into_macros, 15 expand::descend_into_macros,
16 goto_definition, references, FilePosition, NavigationTarget, RangeInfo, 16 goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 72a68522e..f2b29306e 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -1,15 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use hir::db::AstDatabase; 2use hir::db::AstDatabase;
3use ra_ide_db::RootDatabase;
3use ra_syntax::{ 4use ra_syntax::{
4 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
5 match_ast, AstNode, SyntaxNode, 6 match_ast, AstNode, SyntaxNode,
6}; 7};
7
8use test_utils::tested_by; 8use test_utils::tested_by;
9 9
10use crate::{ 10use crate::{expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature};
11 db::RootDatabase, expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature,
12};
13 11
14/// Computes parameter information for the given call expression. 12/// Computes parameter information for the given call expression.
15pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 13pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs
deleted file mode 100644
index 18dad2ea3..000000000
--- a/crates/ra_ide/src/change.rs
+++ /dev/null
@@ -1,386 +0,0 @@
1//! FIXME: write short doc here
2
3use std::{fmt, sync::Arc, time};
4
5use ra_db::{
6 salsa::{Database, Durability, SweepStrategy},
7 CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot,
8 SourceRootId,
9};
10use ra_prof::{memory_usage, profile, Bytes};
11use ra_syntax::SourceFile;
12#[cfg(not(feature = "wasm"))]
13use rayon::prelude::*;
14use rustc_hash::FxHashMap;
15
16use crate::{
17 db::{DebugData, RootDatabase},
18 symbol_index::{SymbolIndex, SymbolsDatabase},
19};
20
21#[derive(Default)]
22pub struct AnalysisChange {
23 new_roots: Vec<(SourceRootId, bool)>,
24 roots_changed: FxHashMap<SourceRootId, RootChange>,
25 files_changed: Vec<(FileId, Arc<String>)>,
26 libraries_added: Vec<LibraryData>,
27 crate_graph: Option<CrateGraph>,
28 debug_data: DebugData,
29}
30
31impl fmt::Debug for AnalysisChange {
32 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
33 let mut d = fmt.debug_struct("AnalysisChange");
34 if !self.new_roots.is_empty() {
35 d.field("new_roots", &self.new_roots);
36 }
37 if !self.roots_changed.is_empty() {
38 d.field("roots_changed", &self.roots_changed);
39 }
40 if !self.files_changed.is_empty() {
41 d.field("files_changed", &self.files_changed.len());
42 }
43 if !self.libraries_added.is_empty() {
44 d.field("libraries_added", &self.libraries_added.len());
45 }
46 if !self.crate_graph.is_none() {
47 d.field("crate_graph", &self.crate_graph);
48 }
49 d.finish()
50 }
51}
52
53impl AnalysisChange {
54 pub fn new() -> AnalysisChange {
55 AnalysisChange::default()
56 }
57
58 pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) {
59 self.new_roots.push((root_id, is_local));
60 }
61
62 pub fn add_file(
63 &mut self,
64 root_id: SourceRootId,
65 file_id: FileId,
66 path: RelativePathBuf,
67 text: Arc<String>,
68 ) {
69 let file = AddFile { file_id, path, text };
70 self.roots_changed.entry(root_id).or_default().added.push(file);
71 }
72
73 pub fn change_file(&mut self, file_id: FileId, new_text: Arc<String>) {
74 self.files_changed.push((file_id, new_text))
75 }
76
77 pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) {
78 let file = RemoveFile { file_id, path };
79 self.roots_changed.entry(root_id).or_default().removed.push(file);
80 }
81
82 pub fn add_library(&mut self, data: LibraryData) {
83 self.libraries_added.push(data)
84 }
85
86 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
87 self.crate_graph = Some(graph);
88 }
89
90 pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) {
91 self.debug_data.crate_names.insert(crate_id, name);
92 }
93
94 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
95 self.debug_data.root_paths.insert(source_root_id, path);
96 }
97}
98
99#[derive(Debug)]
100struct AddFile {
101 file_id: FileId,
102 path: RelativePathBuf,
103 text: Arc<String>,
104}
105
106#[derive(Debug)]
107struct RemoveFile {
108 file_id: FileId,
109 path: RelativePathBuf,
110}
111
112#[derive(Default)]
113struct RootChange {
114 added: Vec<AddFile>,
115 removed: Vec<RemoveFile>,
116}
117
118impl fmt::Debug for RootChange {
119 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
120 fmt.debug_struct("AnalysisChange")
121 .field("added", &self.added.len())
122 .field("removed", &self.removed.len())
123 .finish()
124 }
125}
126
127pub struct LibraryData {
128 root_id: SourceRootId,
129 root_change: RootChange,
130 symbol_index: SymbolIndex,
131}
132
133impl fmt::Debug for LibraryData {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 f.debug_struct("LibraryData")
136 .field("root_id", &self.root_id)
137 .field("root_change", &self.root_change)
138 .field("n_symbols", &self.symbol_index.len())
139 .finish()
140 }
141}
142
143impl LibraryData {
144 pub fn prepare(
145 root_id: SourceRootId,
146 files: Vec<(FileId, RelativePathBuf, Arc<String>)>,
147 ) -> LibraryData {
148 let _p = profile("LibraryData::prepare");
149
150 #[cfg(not(feature = "wasm"))]
151 let iter = files.par_iter();
152 #[cfg(feature = "wasm")]
153 let iter = files.iter();
154
155 let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| {
156 let parse = SourceFile::parse(text);
157 (*file_id, parse)
158 }));
159 let mut root_change = RootChange::default();
160 root_change.added = files
161 .into_iter()
162 .map(|(file_id, path, text)| AddFile { file_id, path, text })
163 .collect();
164 LibraryData { root_id, root_change, symbol_index }
165 }
166}
167
168const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
169
170impl RootDatabase {
171 pub(crate) fn request_cancellation(&mut self) {
172 let _p = profile("RootDatabase::request_cancellation");
173 self.salsa_runtime_mut().synthetic_write(Durability::LOW);
174 }
175
176 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
177 let _p = profile("RootDatabase::apply_change");
178 self.request_cancellation();
179 log::info!("apply_change {:?}", change);
180 if !change.new_roots.is_empty() {
181 let mut local_roots = Vec::clone(&self.local_roots());
182 for (root_id, is_local) in change.new_roots {
183 let root =
184 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() };
185 let durability = durability(&root);
186 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
187 if is_local {
188 local_roots.push(root_id);
189 }
190 }
191 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
192 }
193
194 for (root_id, root_change) in change.roots_changed {
195 self.apply_root_change(root_id, root_change);
196 }
197 for (file_id, text) in change.files_changed {
198 let source_root_id = self.file_source_root(file_id);
199 let source_root = self.source_root(source_root_id);
200 let durability = durability(&source_root);
201 self.set_file_text_with_durability(file_id, text, durability)
202 }
203 if !change.libraries_added.is_empty() {
204 let mut libraries = Vec::clone(&self.library_roots());
205 for library in change.libraries_added {
206 libraries.push(library.root_id);
207 self.set_source_root_with_durability(
208 library.root_id,
209 Arc::new(SourceRoot::new_library()),
210 Durability::HIGH,
211 );
212 self.set_library_symbols_with_durability(
213 library.root_id,
214 Arc::new(library.symbol_index),
215 Durability::HIGH,
216 );
217 self.apply_root_change(library.root_id, library.root_change);
218 }
219 self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH);
220 }
221 if let Some(crate_graph) = change.crate_graph {
222 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
223 }
224
225 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
226 }
227
228 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
229 let mut source_root = SourceRoot::clone(&self.source_root(root_id));
230 let durability = durability(&source_root);
231 for add_file in root_change.added {
232 self.set_file_text_with_durability(add_file.file_id, add_file.text, durability);
233 self.set_file_relative_path_with_durability(
234 add_file.file_id,
235 add_file.path.clone(),
236 durability,
237 );
238 self.set_file_source_root_with_durability(add_file.file_id, root_id, durability);
239 source_root.insert_file(add_file.path, add_file.file_id);
240 }
241 for remove_file in root_change.removed {
242 self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability);
243 source_root.remove_file(&remove_file.path);
244 }
245 self.set_source_root_with_durability(root_id, Arc::new(source_root), durability);
246 }
247
248 pub(crate) fn maybe_collect_garbage(&mut self) {
249 if cfg!(feature = "wasm") {
250 return;
251 }
252
253 if self.last_gc_check.elapsed() > GC_COOLDOWN {
254 self.last_gc_check = crate::wasm_shims::Instant::now();
255 }
256 }
257
258 pub(crate) fn collect_garbage(&mut self) {
259 if cfg!(feature = "wasm") {
260 return;
261 }
262
263 let _p = profile("RootDatabase::collect_garbage");
264 self.last_gc = crate::wasm_shims::Instant::now();
265
266 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
267
268 self.query(ra_db::ParseQuery).sweep(sweep);
269 self.query(hir::db::ParseMacroQuery).sweep(sweep);
270
271 // Macros do take significant space, but less then the syntax trees
272 // self.query(hir::db::MacroDefQuery).sweep(sweep);
273 // self.query(hir::db::MacroArgQuery).sweep(sweep);
274 // self.query(hir::db::MacroExpandQuery).sweep(sweep);
275
276 self.query(hir::db::AstIdMapQuery).sweep(sweep);
277
278 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
279
280 self.query(hir::db::ExprScopesQuery).sweep(sweep);
281 self.query(hir::db::DoInferQuery).sweep(sweep);
282 self.query(hir::db::BodyQuery).sweep(sweep);
283 }
284
285 pub(crate) fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
286 let mut acc: Vec<(String, Bytes)> = vec![];
287 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
288 macro_rules! sweep_each_query {
289 ($($q:path)*) => {$(
290 let before = memory_usage().allocated;
291 self.query($q).sweep(sweep);
292 let after = memory_usage().allocated;
293 let q: $q = Default::default();
294 let name = format!("{:?}", q);
295 acc.push((name, before - after));
296
297 let before = memory_usage().allocated;
298 self.query($q).sweep(sweep.discard_everything());
299 let after = memory_usage().allocated;
300 let q: $q = Default::default();
301 let name = format!("{:?} (deps)", q);
302 acc.push((name, before - after));
303 )*}
304 }
305 sweep_each_query![
306 // SourceDatabase
307 ra_db::ParseQuery
308 ra_db::SourceRootCratesQuery
309
310 // AstDatabase
311 hir::db::AstIdMapQuery
312 hir::db::InternMacroQuery
313 hir::db::MacroArgQuery
314 hir::db::MacroDefQuery
315 hir::db::ParseMacroQuery
316 hir::db::MacroExpandQuery
317
318 // DefDatabase
319 hir::db::RawItemsQuery
320 hir::db::ComputeCrateDefMapQuery
321 hir::db::StructDataQuery
322 hir::db::UnionDataQuery
323 hir::db::EnumDataQuery
324 hir::db::ImplDataQuery
325 hir::db::TraitDataQuery
326 hir::db::TypeAliasDataQuery
327 hir::db::FunctionDataQuery
328 hir::db::ConstDataQuery
329 hir::db::StaticDataQuery
330 hir::db::BodyWithSourceMapQuery
331 hir::db::BodyQuery
332 hir::db::ExprScopesQuery
333 hir::db::GenericParamsQuery
334 hir::db::AttrsQuery
335 hir::db::ModuleLangItemsQuery
336 hir::db::CrateLangItemsQuery
337 hir::db::LangItemQuery
338 hir::db::DocumentationQuery
339
340 // InternDatabase
341 hir::db::InternFunctionQuery
342 hir::db::InternStructQuery
343 hir::db::InternUnionQuery
344 hir::db::InternEnumQuery
345 hir::db::InternConstQuery
346 hir::db::InternStaticQuery
347 hir::db::InternTraitQuery
348 hir::db::InternTypeAliasQuery
349 hir::db::InternImplQuery
350
351 // HirDatabase
352 hir::db::DoInferQuery
353 hir::db::TyQuery
354 hir::db::ValueTyQuery
355 hir::db::ImplSelfTyQuery
356 hir::db::ImplTraitQuery
357 hir::db::FieldTypesQuery
358 hir::db::CallableItemSignatureQuery
359 hir::db::GenericPredicatesForParamQuery
360 hir::db::GenericPredicatesQuery
361 hir::db::GenericDefaultsQuery
362 hir::db::ImplsInCrateQuery
363 hir::db::ImplsForTraitQuery
364 hir::db::TraitSolverQuery
365 hir::db::InternTypeCtorQuery
366 hir::db::InternChalkImplQuery
367 hir::db::InternAssocTyValueQuery
368 hir::db::AssociatedTyDataQuery
369 hir::db::AssociatedTyValueQuery
370 hir::db::TraitSolveQuery
371 hir::db::TraitDatumQuery
372 hir::db::StructDatumQuery
373 hir::db::ImplDatumQuery
374 ];
375 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
376 acc
377 }
378}
379
380fn durability(source_root: &SourceRoot) -> Durability {
381 if source_root.is_library {
382 Durability::HIGH
383 } else {
384 Durability::LOW
385 }
386}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index abe1f36ce..fedc02e14 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -17,6 +17,7 @@ mod complete_postfix;
17mod complete_macro_in_item_position; 17mod complete_macro_in_item_position;
18 18
19use ra_db::SourceDatabase; 19use ra_db::SourceDatabase;
20use ra_ide_db::RootDatabase;
20 21
21#[cfg(test)] 22#[cfg(test)]
22use crate::completion::completion_item::do_completion; 23use crate::completion::completion_item::do_completion;
@@ -25,7 +26,7 @@ use crate::{
25 completion_context::CompletionContext, 26 completion_context::CompletionContext,
26 completion_item::{CompletionKind, Completions}, 27 completion_item::{CompletionKind, Completions},
27 }, 28 },
28 db, FilePosition, 29 FilePosition,
29}; 30};
30 31
31pub use crate::completion::completion_item::{ 32pub use crate::completion::completion_item::{
@@ -54,7 +55,7 @@ pub use crate::completion::completion_item::{
54/// `foo` *should* be present among the completion variants. Filtering by 55/// `foo` *should* be present among the completion variants. Filtering by
55/// identifier prefix/fuzzy match should be done higher in the stack, together 56/// identifier prefix/fuzzy match should be done higher in the stack, together
56/// with ordering of completions (currently this is done by the client). 57/// with ordering of completions (currently this is done by the client).
57pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Option<Completions> { 58pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> {
58 let original_parse = db.parse(position.file_id); 59 let original_parse = db.parse(position.file_id);
59 let ctx = CompletionContext::new(db, &original_parse, position)?; 60 let ctx = CompletionContext::new(db, &original_parse, position)?;
60 61
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index deaacda6c..5a0407fd7 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -1,5 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_ide_db::RootDatabase;
3use ra_syntax::{ 4use ra_syntax::{
4 algo::{find_covering_element, find_node_at_offset}, 5 algo::{find_covering_element, find_node_at_offset},
5 ast, AstNode, Parse, SourceFile, 6 ast, AstNode, Parse, SourceFile,
@@ -8,13 +9,13 @@ use ra_syntax::{
8}; 9};
9use ra_text_edit::AtomTextEdit; 10use ra_text_edit::AtomTextEdit;
10 11
11use crate::{db, FilePosition}; 12use crate::FilePosition;
12 13
13/// `CompletionContext` is created early during completion to figure out, where 14/// `CompletionContext` is created early during completion to figure out, where
14/// exactly is the cursor, syntax-wise. 15/// exactly is the cursor, syntax-wise.
15#[derive(Debug)] 16#[derive(Debug)]
16pub(crate) struct CompletionContext<'a> { 17pub(crate) struct CompletionContext<'a> {
17 pub(super) db: &'a db::RootDatabase, 18 pub(super) db: &'a RootDatabase,
18 pub(super) analyzer: hir::SourceAnalyzer, 19 pub(super) analyzer: hir::SourceAnalyzer,
19 pub(super) offset: TextUnit, 20 pub(super) offset: TextUnit,
20 pub(super) token: SyntaxToken, 21 pub(super) token: SyntaxToken,
@@ -48,7 +49,7 @@ pub(crate) struct CompletionContext<'a> {
48 49
49impl<'a> CompletionContext<'a> { 50impl<'a> CompletionContext<'a> {
50 pub(super) fn new( 51 pub(super) fn new(
51 db: &'a db::RootDatabase, 52 db: &'a RootDatabase,
52 original_parse: &'a Parse<ast::SourceFile>, 53 original_parse: &'a Parse<ast::SourceFile>,
53 position: FilePosition, 54 position: FilePosition,
54 ) -> Option<CompletionContext<'a>> { 55 ) -> Option<CompletionContext<'a>> {
diff --git a/crates/ra_ide/src/db.rs b/crates/ra_ide/src/db.rs
deleted file mode 100644
index 47d0aed6f..000000000
--- a/crates/ra_ide/src/db.rs
+++ /dev/null
@@ -1,132 +0,0 @@
1//! FIXME: write short doc here
2
3use std::sync::Arc;
4
5use ra_db::{
6 salsa::{self, Database, Durability},
7 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath,
8 SourceDatabase, SourceRootId,
9};
10use rustc_hash::FxHashMap;
11
12use crate::{
13 symbol_index::{self, SymbolsDatabase},
14 FeatureFlags, LineIndex,
15};
16
17#[salsa::database(
18 ra_db::SourceDatabaseStorage,
19 ra_db::SourceDatabaseExtStorage,
20 LineIndexDatabaseStorage,
21 symbol_index::SymbolsDatabaseStorage,
22 hir::db::InternDatabaseStorage,
23 hir::db::AstDatabaseStorage,
24 hir::db::DefDatabaseStorage,
25 hir::db::HirDatabaseStorage
26)]
27#[derive(Debug)]
28pub(crate) struct RootDatabase {
29 runtime: salsa::Runtime<RootDatabase>,
30 pub(crate) feature_flags: Arc<FeatureFlags>,
31 pub(crate) debug_data: Arc<DebugData>,
32 pub(crate) last_gc: crate::wasm_shims::Instant,
33 pub(crate) last_gc_check: crate::wasm_shims::Instant,
34}
35
36impl FileLoader for RootDatabase {
37 fn file_text(&self, file_id: FileId) -> Arc<String> {
38 FileLoaderDelegate(self).file_text(file_id)
39 }
40 fn resolve_relative_path(
41 &self,
42 anchor: FileId,
43 relative_path: &RelativePath,
44 ) -> Option<FileId> {
45 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
46 }
47 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
48 FileLoaderDelegate(self).relevant_crates(file_id)
49 }
50}
51
52impl salsa::Database for RootDatabase {
53 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> {
54 &self.runtime
55 }
56 fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime<Self> {
57 &mut self.runtime
58 }
59 fn on_propagated_panic(&self) -> ! {
60 Canceled::throw()
61 }
62 fn salsa_event(&self, event: impl Fn() -> salsa::Event<RootDatabase>) {
63 match event().kind {
64 salsa::EventKind::DidValidateMemoizedValue { .. }
65 | salsa::EventKind::WillExecute { .. } => {
66 self.check_canceled();
67 }
68 _ => (),
69 }
70 }
71}
72
73impl Default for RootDatabase {
74 fn default() -> RootDatabase {
75 RootDatabase::new(None, FeatureFlags::default())
76 }
77}
78
79impl RootDatabase {
80 pub fn new(lru_capacity: Option<usize>, feature_flags: FeatureFlags) -> RootDatabase {
81 let mut db = RootDatabase {
82 runtime: salsa::Runtime::default(),
83 last_gc: crate::wasm_shims::Instant::now(),
84 last_gc_check: crate::wasm_shims::Instant::now(),
85 feature_flags: Arc::new(feature_flags),
86 debug_data: Default::default(),
87 };
88 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
89 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
90 db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
91 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
92 db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
93 db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
94 db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
95 db
96 }
97}
98
99impl salsa::ParallelDatabase for RootDatabase {
100 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
101 salsa::Snapshot::new(RootDatabase {
102 runtime: self.runtime.snapshot(self),
103 last_gc: self.last_gc,
104 last_gc_check: self.last_gc_check,
105 feature_flags: Arc::clone(&self.feature_flags),
106 debug_data: Arc::clone(&self.debug_data),
107 })
108 }
109}
110
111#[salsa::query_group(LineIndexDatabaseStorage)]
112pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled {
113 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
114}
115
116fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
117 let text = db.file_text(file_id);
118 Arc::new(LineIndex::new(&*text))
119}
120
121#[derive(Debug, Default, Clone)]
122pub(crate) struct DebugData {
123 pub(crate) root_paths: FxHashMap<SourceRootId, String>,
124 pub(crate) crate_names: FxHashMap<CrateId, String>,
125}
126
127impl DebugData {
128 pub(crate) fn merge(&mut self, other: DebugData) {
129 self.root_paths.extend(other.root_paths.into_iter());
130 self.crate_names.extend(other.crate_names.into_iter());
131 }
132}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index f403b3bcf..22bd49723 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -5,6 +5,7 @@ use std::cell::RefCell;
5use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; 5use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink};
6use itertools::Itertools; 6use itertools::Itertools;
7use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; 7use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt};
8use ra_ide_db::RootDatabase;
8use ra_prof::profile; 9use ra_prof::profile;
9use ra_syntax::{ 10use ra_syntax::{
10 algo, 11 algo,
@@ -13,7 +14,7 @@ use ra_syntax::{
13}; 14};
14use ra_text_edit::{TextEdit, TextEditBuilder}; 15use ra_text_edit::{TextEdit, TextEditBuilder};
15 16
16use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; 17use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit};
17 18
18#[derive(Debug, Copy, Clone)] 19#[derive(Debug, Copy, Clone)]
19pub enum Severity { 20pub enum Severity {
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 1e4a472b4..c23e08e9a 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -4,13 +4,11 @@ use std::fmt::{self, Display};
4 4
5use hir::{Docs, Documentation, HasSource, HirDisplay}; 5use hir::{Docs, Documentation, HasSource, HirDisplay};
6use join_to_string::join; 6use join_to_string::join;
7use ra_ide_db::RootDatabase;
7use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 8use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
8use std::convert::From; 9use std::convert::From;
9 10
10use crate::{ 11use crate::display::{generic_parameters, where_predicates};
11 db,
12 display::{generic_parameters, where_predicates},
13};
14 12
15#[derive(Debug)] 13#[derive(Debug)]
16pub enum CallableKind { 14pub enum CallableKind {
@@ -48,13 +46,13 @@ impl FunctionSignature {
48 self 46 self
49 } 47 }
50 48
51 pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self { 49 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
52 let doc = function.docs(db); 50 let doc = function.docs(db);
53 let ast_node = function.source(db).value; 51 let ast_node = function.source(db).value;
54 FunctionSignature::from(&ast_node).with_doc_opt(doc) 52 FunctionSignature::from(&ast_node).with_doc_opt(doc)
55 } 53 }
56 54
57 pub(crate) fn from_struct(db: &db::RootDatabase, st: hir::Struct) -> Option<Self> { 55 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
58 let node: ast::StructDef = st.source(db).value; 56 let node: ast::StructDef = st.source(db).value;
59 match node.kind() { 57 match node.kind() {
60 ast::StructKind::Record(_) => return None, 58 ast::StructKind::Record(_) => return None,
@@ -86,10 +84,7 @@ impl FunctionSignature {
86 ) 84 )
87 } 85 }
88 86
89 pub(crate) fn from_enum_variant( 87 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
90 db: &db::RootDatabase,
91 variant: hir::EnumVariant,
92 ) -> Option<Self> {
93 let node: ast::EnumVariant = variant.source(db).value; 88 let node: ast::EnumVariant = variant.source(db).value;
94 match node.kind() { 89 match node.kind() {
95 ast::StructKind::Record(_) | ast::StructKind::Unit => return None, 90 ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
@@ -126,7 +121,7 @@ impl FunctionSignature {
126 ) 121 )
127 } 122 }
128 123
129 pub(crate) fn from_macro(db: &db::RootDatabase, macro_def: hir::MacroDef) -> Option<Self> { 124 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
130 let node: ast::MacroCall = macro_def.source(db).value; 125 let node: ast::MacroCall = macro_def.source(db).value;
131 126
132 let params = vec![]; 127 let params = vec![];
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index b2af3479c..906aab1eb 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -3,6 +3,7 @@
3use either::Either; 3use either::Either;
4use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; 4use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource};
5use ra_db::{FileId, SourceDatabase}; 5use ra_db::{FileId, SourceDatabase};
6use ra_ide_db::RootDatabase;
6use ra_syntax::{ 7use ra_syntax::{
7 ast::{self, DocCommentsOwner, NameOwner}, 8 ast::{self, DocCommentsOwner, NameOwner},
8 match_ast, AstNode, SmolStr, 9 match_ast, AstNode, SmolStr,
@@ -10,7 +11,7 @@ use ra_syntax::{
10 TextRange, 11 TextRange,
11}; 12};
12 13
13use crate::{db::RootDatabase, expand::original_range, FileSymbol}; 14use crate::{expand::original_range, FileSymbol};
14 15
15use super::short_label::ShortLabel; 16use super::short_label::ShortLabel;
16 17
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs
index 831438c09..9f3aaa3a3 100644
--- a/crates/ra_ide/src/expand.rs
+++ b/crates/ra_ide/src/expand.rs
@@ -3,9 +3,10 @@ use std::iter::successors;
3 3
4use hir::{InFile, Origin}; 4use hir::{InFile, Origin};
5use ra_db::FileId; 5use ra_db::FileId;
6use ra_ide_db::RootDatabase;
6use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; 7use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange};
7 8
8use crate::{db::RootDatabase, FileRange}; 9use crate::FileRange;
9 10
10pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { 11pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange {
11 if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { 12 if let Some((range, Origin::Call)) = original_range_and_origin(db, node) {
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index 0f7b6e875..af2783bef 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -1,14 +1,15 @@
1//! This modules implements "expand macro" functionality in the IDE 1//! This modules implements "expand macro" functionality in the IDE
2 2
3use crate::{db::RootDatabase, FilePosition};
4use hir::db::AstDatabase; 3use hir::db::AstDatabase;
5use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
6use rustc_hash::FxHashMap; 5use ra_ide_db::RootDatabase;
7
8use ra_syntax::{ 6use ra_syntax::{
9 algo::{find_node_at_offset, replace_descendants}, 7 algo::{find_node_at_offset, replace_descendants},
10 ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, WalkEvent, T, 8 ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, WalkEvent, T,
11}; 9};
10use rustc_hash::FxHashMap;
11
12use crate::FilePosition;
12 13
13pub struct ExpandedMacro { 14pub struct ExpandedMacro {
14 pub name: String, 15 pub name: String,
@@ -185,7 +186,7 @@ fn some_thing() -> u32 {
185 //- /lib.rs 186 //- /lib.rs
186 macro_rules! match_ast { 187 macro_rules! match_ast {
187 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 188 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
188 189
189 (match ($node:expr) { 190 (match ($node:expr) {
190 $( ast::$ast:ident($it:ident) => $res:block, )* 191 $( ast::$ast:ident($it:ident) => $res:block, )*
191 _ => $catch_all:expr $(,)? 192 _ => $catch_all:expr $(,)?
@@ -193,7 +194,7 @@ fn some_thing() -> u32 {
193 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* 194 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
194 { $catch_all } 195 { $catch_all }
195 }}; 196 }};
196 } 197 }
197 198
198 fn main() { 199 fn main() {
199 mat<|>ch_ast! { 200 mat<|>ch_ast! {
@@ -227,11 +228,11 @@ fn some_thing() -> u32 {
227 r#" 228 r#"
228 //- /lib.rs 229 //- /lib.rs
229 macro_rules! match_ast { 230 macro_rules! match_ast {
230 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 231 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
231 (match ($node:expr) {}) => {{}}; 232 (match ($node:expr) {}) => {{}};
232 } 233 }
233 234
234 fn main() { 235 fn main() {
235 let p = f(|it| { 236 let p = f(|it| {
236 let res = mat<|>ch_ast! { match c {}}; 237 let res = mat<|>ch_ast! { match c {}};
237 Some(res) 238 Some(res)
@@ -254,9 +255,9 @@ fn some_thing() -> u32 {
254 } 255 }
255 macro_rules! foo { 256 macro_rules! foo {
256 () => {bar!()}; 257 () => {bar!()};
257 } 258 }
258 259
259 fn main() { 260 fn main() {
260 let res = fo<|>o!(); 261 let res = fo<|>o!();
261 } 262 }
262 "#, 263 "#,
@@ -277,9 +278,9 @@ fn some_thing() -> u32 {
277 } 278 }
278 macro_rules! foo { 279 macro_rules! foo {
279 () => {$crate::bar!()}; 280 () => {$crate::bar!()};
280 } 281 }
281 282
282 fn main() { 283 fn main() {
283 let res = fo<|>o!(); 284 let res = fo<|>o!();
284 } 285 }
285 "#, 286 "#,
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 930e0c4c2..726963a33 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -1,6 +1,10 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::iter::successors;
4
5use hir::db::AstDatabase;
3use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
7use ra_ide_db::RootDatabase;
4use ra_syntax::{ 8use ra_syntax::{
5 algo::find_covering_element, 9 algo::find_covering_element,
6 ast::{self, AstNode, AstToken}, 10 ast::{self, AstNode, AstToken},
@@ -9,9 +13,7 @@ use ra_syntax::{
9 SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, 13 SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T,
10}; 14};
11 15
12use crate::{db::RootDatabase, expand::descend_into_macros, FileId, FileRange}; 16use crate::{expand::descend_into_macros, FileId, FileRange};
13use hir::db::AstDatabase;
14use std::iter::successors;
15 17
16pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 18pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
17 let src = db.parse(frange.file_id).tree(); 19 let src = db.parse(frange.file_id).tree();
@@ -512,8 +514,8 @@ fn bar(){}
512 fn test_extend_trait_bounds_list_in_where_clause() { 514 fn test_extend_trait_bounds_list_in_where_clause() {
513 do_check( 515 do_check(
514 r#" 516 r#"
515fn foo<R>() 517fn foo<R>()
516 where 518 where
517 R: req::Request + 'static, 519 R: req::Request + 'static,
518 R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static, 520 R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static,
519 R::Result: Serialize + 'static, 521 R::Result: Serialize + 'static,
diff --git a/crates/ra_ide/src/feature_flags.rs b/crates/ra_ide/src/feature_flags.rs
deleted file mode 100644
index 85617640d..000000000
--- a/crates/ra_ide/src/feature_flags.rs
+++ /dev/null
@@ -1,71 +0,0 @@
1//! FIXME: write short doc here
2
3use rustc_hash::FxHashMap;
4
5/// Feature flags hold fine-grained toggles for all *user-visible* features of
6/// rust-analyzer.
7///
8/// The exists such that users are able to disable any annoying feature (and,
9/// with many users and many features, some features are bound to be annoying
10/// for some users)
11///
12/// Note that we purposefully use run-time checked strings, and not something
13/// checked at compile time, to keep things simple and flexible.
14///
15/// Also note that, at the moment, `FeatureFlags` also store features for
16/// `ra_lsp_server`. This should be benign layering violation.
17#[derive(Debug)]
18pub struct FeatureFlags {
19 flags: FxHashMap<String, bool>,
20}
21
22impl FeatureFlags {
23 fn new(flags: &[(&str, bool)]) -> FeatureFlags {
24 let flags = flags
25 .iter()
26 .map(|&(name, value)| {
27 check_flag_name(name);
28 (name.to_string(), value)
29 })
30 .collect();
31 FeatureFlags { flags }
32 }
33
34 pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> {
35 match self.flags.get_mut(flag) {
36 None => Err(()),
37 Some(slot) => {
38 *slot = value;
39 Ok(())
40 }
41 }
42 }
43
44 pub fn get(&self, flag: &str) -> bool {
45 match self.flags.get(flag) {
46 None => panic!("unknown flag: {:?}", flag),
47 Some(value) => *value,
48 }
49 }
50}
51
52impl Default for FeatureFlags {
53 fn default() -> FeatureFlags {
54 FeatureFlags::new(&[
55 ("lsp.diagnostics", true),
56 ("completion.insertion.add-call-parenthesis", true),
57 ("completion.enable-postfix", true),
58 ("notifications.workspace-loaded", true),
59 ("notifications.cargo-toml-not-found", true),
60 ])
61 }
62}
63
64fn check_flag_name(flag: &str) {
65 for c in flag.bytes() {
66 match c {
67 b'a'..=b'z' | b'-' | b'.' => (),
68 _ => panic!("flag name does not match conventions: {:?}", flag),
69 }
70 }
71}
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 5a12a619c..e9329a72c 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{db::AstDatabase, InFile, SourceBinder}; 3use hir::{db::AstDatabase, InFile, SourceBinder};
4use ra_ide_db::{symbol_index, RootDatabase};
4use ra_syntax::{ 5use ra_syntax::{
5 ast::{self, DocCommentsOwner}, 6 ast::{self, DocCommentsOwner},
6 match_ast, AstNode, 7 match_ast, AstNode,
@@ -9,7 +10,6 @@ use ra_syntax::{
9}; 10};
10 11
11use crate::{ 12use crate::{
12 db::RootDatabase,
13 display::{ShortLabel, ToNav}, 13 display::{ShortLabel, ToNav},
14 expand::descend_into_macros, 14 expand::descend_into_macros,
15 references::{classify_name_ref, NameKind::*}, 15 references::{classify_name_ref, NameKind::*},
@@ -94,7 +94,7 @@ pub(crate) fn reference_definition(
94 }; 94 };
95 95
96 // Fallback index based approach: 96 // Fallback index based approach:
97 let navs = crate::symbol_index::index_resolve(sb.db, name_ref.value) 97 let navs = symbol_index::index_resolve(sb.db, name_ref.value)
98 .into_iter() 98 .into_iter()
99 .map(|s| s.to_nav(sb.db)) 99 .map(|s| s.to_nav(sb.db))
100 .collect(); 100 .collect();
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index ce8b6c72a..11ad6d137 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,11 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::db::AstDatabase; 3use hir::db::AstDatabase;
4use ra_ide_db::RootDatabase;
4use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 5use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
5 6
6use crate::{ 7use crate::{
7 db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, 8 display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, RangeInfo,
8 RangeInfo,
9}; 9};
10 10
11pub(crate) fn goto_type_definition( 11pub(crate) fn goto_type_definition(
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 6661e5cb2..315b88190 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -2,6 +2,7 @@
2 2
3use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; 3use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase;
5use ra_syntax::{ 6use ra_syntax::{
6 algo::find_covering_element, 7 algo::find_covering_element,
7 ast::{self, DocCommentsOwner}, 8 ast::{self, DocCommentsOwner},
@@ -11,7 +12,6 @@ use ra_syntax::{
11}; 12};
12 13
13use crate::{ 14use crate::{
14 db::RootDatabase,
15 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 15 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
16 expand::descend_into_macros, 16 expand::descend_into_macros,
17 references::{classify_name, classify_name_ref, NameKind, NameKind::*}, 17 references::{classify_name, classify_name_ref, NameKind, NameKind::*},
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs
index 9834025d3..64a2dadc8 100644
--- a/crates/ra_ide/src/impls.rs
+++ b/crates/ra_ide/src/impls.rs
@@ -2,9 +2,10 @@
2 2
3use hir::{Crate, ImplBlock, SourceBinder}; 3use hir::{Crate, ImplBlock, SourceBinder};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase;
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 6use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
6 7
7use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 8use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
8 9
9pub(crate) fn goto_implementation( 10pub(crate) fn goto_implementation(
10 db: &RootDatabase, 11 db: &RootDatabase,
diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs
index 9e1a1c1ec..cfd58aafe 100644
--- a/crates/ra_ide/src/imports_locator.rs
+++ b/crates/ra_ide/src/imports_locator.rs
@@ -1,17 +1,20 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use crate::{
5 db::RootDatabase,
6 references::{classify_name, NameDefinition, NameKind},
7 symbol_index::{self, FileSymbol},
8 Query,
9};
10use hir::{db::HirDatabase, ModuleDef, SourceBinder}; 4use hir::{db::HirDatabase, ModuleDef, SourceBinder};
11use ra_assists::ImportsLocator; 5use ra_assists::ImportsLocator;
6use ra_ide_db::{
7 symbol_index::{self, FileSymbol},
8 RootDatabase,
9};
12use ra_prof::profile; 10use ra_prof::profile;
13use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 11use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
14 12
13use crate::{
14 references::{classify_name, NameDefinition, NameKind},
15 Query,
16};
17
15pub(crate) struct ImportsLocatorIde<'a> { 18pub(crate) struct ImportsLocatorIde<'a> {
16 source_binder: SourceBinder<'a, RootDatabase>, 19 source_binder: SourceBinder<'a, RootDatabase>,
17} 20}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index de447a5aa..6b0d3d996 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -2,13 +2,14 @@
2 2
3use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; 3use hir::{HirDisplay, SourceAnalyzer, SourceBinder};
4use once_cell::unsync::Lazy; 4use once_cell::unsync::Lazy;
5use ra_ide_db::RootDatabase;
5use ra_prof::profile; 6use ra_prof::profile;
6use ra_syntax::{ 7use ra_syntax::{
7 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 8 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
8 match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, 9 match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange,
9}; 10};
10 11
11use crate::{db::RootDatabase, FileId, FunctionSignature}; 12use crate::{FileId, FunctionSignature};
12 13
13#[derive(Debug, PartialEq, Eq)] 14#[derive(Debug, PartialEq, Eq)]
14pub enum InlayKind { 15pub enum InlayKind {
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 03ad6b2c1..5fb111a90 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -10,12 +10,8 @@
10// For proving that RootDatabase is RefUnwindSafe. 10// For proving that RootDatabase is RefUnwindSafe.
11#![recursion_limit = "128"] 11#![recursion_limit = "128"]
12 12
13mod db;
14pub mod mock_analysis; 13pub mod mock_analysis;
15mod symbol_index;
16mod change;
17mod source_change; 14mod source_change;
18mod feature_flags;
19 15
20mod status; 16mod status;
21mod completion; 17mod completion;
@@ -35,14 +31,11 @@ mod assists;
35mod diagnostics; 31mod diagnostics;
36mod syntax_tree; 32mod syntax_tree;
37mod folding_ranges; 33mod folding_ranges;
38mod line_index;
39mod line_index_utils;
40mod join_lines; 34mod join_lines;
41mod typing; 35mod typing;
42mod matching_brace; 36mod matching_brace;
43mod display; 37mod display;
44mod inlay_hints; 38mod inlay_hints;
45mod wasm_shims;
46mod expand; 39mod expand;
47mod expand_macro; 40mod expand_macro;
48 41
@@ -58,24 +51,24 @@ use ra_db::{
58 salsa::{self, ParallelDatabase}, 51 salsa::{self, ParallelDatabase},
59 CheckCanceled, Env, FileLoader, SourceDatabase, 52 CheckCanceled, Env, FileLoader, SourceDatabase,
60}; 53};
54use ra_ide_db::{
55 symbol_index::{self, FileSymbol},
56 LineIndexDatabase,
57};
61use ra_syntax::{SourceFile, TextRange, TextUnit}; 58use ra_syntax::{SourceFile, TextRange, TextUnit};
62 59
63use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol}; 60use crate::display::ToNav;
64 61
65pub use crate::{ 62pub use crate::{
66 assists::{Assist, AssistId}, 63 assists::{Assist, AssistId},
67 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
68 change::{AnalysisChange, LibraryData},
69 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 65 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
70 diagnostics::Severity, 66 diagnostics::Severity,
71 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
72 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
73 feature_flags::FeatureFlags,
74 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
75 hover::HoverResult, 70 hover::HoverResult,
76 inlay_hints::{InlayHint, InlayKind}, 71 inlay_hints::{InlayHint, InlayKind},
77 line_index::{LineCol, LineIndex},
78 line_index_utils::translate_offset_with_edit,
79 references::{ 72 references::{
80 Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, 73 Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope,
81 }, 74 },
@@ -88,6 +81,14 @@ pub use hir::Documentation;
88pub use ra_db::{ 81pub use ra_db::{
89 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
90}; 83};
84pub use ra_ide_db::{
85 change::{AnalysisChange, LibraryData},
86 feature_flags::FeatureFlags,
87 line_index::{LineCol, LineIndex},
88 line_index_utils::translate_offset_with_edit,
89 symbol_index::Query,
90 RootDatabase,
91};
91 92
92pub type Cancelable<T> = Result<T, Canceled>; 93pub type Cancelable<T> = Result<T, Canceled>;
93 94
@@ -99,46 +100,6 @@ pub struct Diagnostic {
99 pub severity: Severity, 100 pub severity: Severity,
100} 101}
101 102
102#[derive(Debug)]
103pub struct Query {
104 query: String,
105 lowercased: String,
106 only_types: bool,
107 libs: bool,
108 exact: bool,
109 limit: usize,
110}
111
112impl Query {
113 pub fn new(query: String) -> Query {
114 let lowercased = query.to_lowercase();
115 Query {
116 query,
117 lowercased,
118 only_types: false,
119 libs: false,
120 exact: false,
121 limit: usize::max_value(),
122 }
123 }
124
125 pub fn only_types(&mut self) {
126 self.only_types = true;
127 }
128
129 pub fn libs(&mut self) {
130 self.libs = true;
131 }
132
133 pub fn exact(&mut self) {
134 self.exact = true;
135 }
136
137 pub fn limit(&mut self, limit: usize) {
138 self.limit = limit
139 }
140}
141
142/// Info associated with a text range. 103/// Info associated with a text range.
143#[derive(Debug)] 104#[derive(Debug)]
144pub struct RangeInfo<T> { 105pub struct RangeInfo<T> {
@@ -163,7 +124,7 @@ pub struct CallInfo {
163/// `AnalysisHost` stores the current state of the world. 124/// `AnalysisHost` stores the current state of the world.
164#[derive(Debug)] 125#[derive(Debug)]
165pub struct AnalysisHost { 126pub struct AnalysisHost {
166 db: db::RootDatabase, 127 db: RootDatabase,
167} 128}
168 129
169impl Default for AnalysisHost { 130impl Default for AnalysisHost {
@@ -174,7 +135,7 @@ impl Default for AnalysisHost {
174 135
175impl AnalysisHost { 136impl AnalysisHost {
176 pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost { 137 pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost {
177 AnalysisHost { db: db::RootDatabase::new(lru_capcity, feature_flags) } 138 AnalysisHost { db: RootDatabase::new(lru_capcity, feature_flags) }
178 } 139 }
179 /// Returns a snapshot of the current state, which you can query for 140 /// Returns a snapshot of the current state, which you can query for
180 /// semantic information. 141 /// semantic information.
@@ -224,7 +185,7 @@ impl AnalysisHost {
224/// `Analysis` are canceled (most method return `Err(Canceled)`). 185/// `Analysis` are canceled (most method return `Err(Canceled)`).
225#[derive(Debug)] 186#[derive(Debug)]
226pub struct Analysis { 187pub struct Analysis {
227 db: salsa::Snapshot<db::RootDatabase>, 188 db: salsa::Snapshot<RootDatabase>,
228} 189}
229 190
230// As a general design guideline, `Analysis` API are intended to be independent 191// As a general design guideline, `Analysis` API are intended to be independent
@@ -505,7 +466,7 @@ impl Analysis {
505 } 466 }
506 467
507 /// Performs an operation on that may be Canceled. 468 /// Performs an operation on that may be Canceled.
508 fn with_db<F: FnOnce(&db::RootDatabase) -> T + std::panic::UnwindSafe, T>( 469 fn with_db<F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, T>(
509 &self, 470 &self,
510 f: F, 471 f: F,
511 ) -> Cancelable<T> { 472 ) -> Cancelable<T> {
@@ -518,3 +479,77 @@ fn analysis_is_send() {
518 fn is_send<T: Send>() {} 479 fn is_send<T: Send>() {}
519 is_send::<Analysis>(); 480 is_send::<Analysis>();
520} 481}
482
483#[cfg(test)]
484mod tests {
485 use crate::{display::NavigationTarget, mock_analysis::single_file, Query};
486 use ra_syntax::{
487 SmolStr,
488 SyntaxKind::{FN_DEF, STRUCT_DEF},
489 };
490
491 #[test]
492 fn test_world_symbols_with_no_container() {
493 let code = r#"
494 enum FooInner { }
495 "#;
496
497 let mut symbols = get_symbols_matching(code, "FooInner");
498
499 let s = symbols.pop().unwrap();
500
501 assert_eq!(s.name(), "FooInner");
502 assert!(s.container_name().is_none());
503 }
504
505 #[test]
506 fn test_world_symbols_include_container_name() {
507 let code = r#"
508fn foo() {
509 enum FooInner { }
510}
511 "#;
512
513 let mut symbols = get_symbols_matching(code, "FooInner");
514
515 let s = symbols.pop().unwrap();
516
517 assert_eq!(s.name(), "FooInner");
518 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
519
520 let code = r#"
521mod foo {
522 struct FooInner;
523}
524 "#;
525
526 let mut symbols = get_symbols_matching(code, "FooInner");
527
528 let s = symbols.pop().unwrap();
529
530 assert_eq!(s.name(), "FooInner");
531 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
532 }
533
534 #[test]
535 fn test_world_symbols_are_case_sensitive() {
536 let code = r#"
537fn foo() {}
538
539struct Foo;
540 "#;
541
542 let symbols = get_symbols_matching(code, "Foo");
543
544 let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind());
545 let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind());
546
547 assert_eq!(fn_match, Some(FN_DEF));
548 assert_eq!(struct_match, Some(STRUCT_DEF));
549 }
550
551 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
552 let (analysis, _) = single_file(text);
553 analysis.symbol_search(Query::new(query.into())).unwrap()
554 }
555}
diff --git a/crates/ra_ide/src/line_index.rs b/crates/ra_ide/src/line_index.rs
deleted file mode 100644
index 710890d27..000000000
--- a/crates/ra_ide/src/line_index.rs
+++ /dev/null
@@ -1,283 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::TextUnit;
4use rustc_hash::FxHashMap;
5use superslice::Ext;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct LineIndex {
9 pub(crate) newlines: Vec<TextUnit>,
10 pub(crate) utf16_lines: FxHashMap<u32, Vec<Utf16Char>>,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct LineCol {
15 /// Zero-based
16 pub line: u32,
17 /// Zero-based
18 pub col_utf16: u32,
19}
20
21#[derive(Clone, Debug, Hash, PartialEq, Eq)]
22pub(crate) struct Utf16Char {
23 pub(crate) start: TextUnit,
24 pub(crate) end: TextUnit,
25}
26
27impl Utf16Char {
28 fn len(&self) -> TextUnit {
29 self.end - self.start
30 }
31}
32
33impl LineIndex {
34 pub fn new(text: &str) -> LineIndex {
35 let mut utf16_lines = FxHashMap::default();
36 let mut utf16_chars = Vec::new();
37
38 let mut newlines = vec![0.into()];
39 let mut curr_row = 0.into();
40 let mut curr_col = 0.into();
41 let mut line = 0;
42 for c in text.chars() {
43 curr_row += TextUnit::of_char(c);
44 if c == '\n' {
45 newlines.push(curr_row);
46
47 // Save any utf-16 characters seen in the previous line
48 if !utf16_chars.is_empty() {
49 utf16_lines.insert(line, utf16_chars);
50 utf16_chars = Vec::new();
51 }
52
53 // Prepare for processing the next line
54 curr_col = 0.into();
55 line += 1;
56 continue;
57 }
58
59 let char_len = TextUnit::of_char(c);
60 if char_len.to_usize() > 1 {
61 utf16_chars.push(Utf16Char { start: curr_col, end: curr_col + char_len });
62 }
63
64 curr_col += char_len;
65 }
66
67 // Save any utf-16 characters seen in the last line
68 if !utf16_chars.is_empty() {
69 utf16_lines.insert(line, utf16_chars);
70 }
71
72 LineIndex { newlines, utf16_lines }
73 }
74
75 pub fn line_col(&self, offset: TextUnit) -> LineCol {
76 let line = self.newlines.upper_bound(&offset) - 1;
77 let line_start_offset = self.newlines[line];
78 let col = offset - line_start_offset;
79
80 LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 }
81 }
82
83 pub fn offset(&self, line_col: LineCol) -> TextUnit {
84 //FIXME: return Result
85 let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16);
86 self.newlines[line_col.line as usize] + col
87 }
88
89 fn utf8_to_utf16_col(&self, line: u32, mut col: TextUnit) -> usize {
90 if let Some(utf16_chars) = self.utf16_lines.get(&line) {
91 let mut correction = TextUnit::from_usize(0);
92 for c in utf16_chars {
93 if col >= c.end {
94 correction += c.len() - TextUnit::from_usize(1);
95 } else {
96 // From here on, all utf16 characters come *after* the character we are mapping,
97 // so we don't need to take them into account
98 break;
99 }
100 }
101
102 col -= correction;
103 }
104
105 col.to_usize()
106 }
107
108 fn utf16_to_utf8_col(&self, line: u32, col: u32) -> TextUnit {
109 let mut col: TextUnit = col.into();
110 if let Some(utf16_chars) = self.utf16_lines.get(&line) {
111 for c in utf16_chars {
112 if col >= c.start {
113 col += c.len() - TextUnit::from_usize(1);
114 } else {
115 // From here on, all utf16 characters come *after* the character we are mapping,
116 // so we don't need to take them into account
117 break;
118 }
119 }
120 }
121
122 col
123 }
124}
125
126#[cfg(test)]
127/// Simple reference implementation to use in proptests
128pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol {
129 let mut res = LineCol { line: 0, col_utf16: 0 };
130 for (i, c) in text.char_indices() {
131 if i + c.len_utf8() > offset.to_usize() {
132 // if it's an invalid offset, inside a multibyte char
133 // return as if it was at the start of the char
134 break;
135 }
136 if c == '\n' {
137 res.line += 1;
138 res.col_utf16 = 0;
139 } else {
140 res.col_utf16 += 1;
141 }
142 }
143 res
144}
145
146#[cfg(test)]
147mod test_line_index {
148 use super::*;
149 use proptest::{prelude::*, proptest};
150 use ra_text_edit::test_utils::{arb_offset, arb_text};
151
152 #[test]
153 fn test_line_index() {
154 let text = "hello\nworld";
155 let index = LineIndex::new(text);
156 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
157 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
158 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
159 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
160 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
161 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
162 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
163 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
164 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
165
166 let text = "\nhello\nworld";
167 let index = LineIndex::new(text);
168 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
169 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
170 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
171 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
172 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
173 }
174
175 fn arb_text_with_offset() -> BoxedStrategy<(TextUnit, String)> {
176 arb_text().prop_flat_map(|text| (arb_offset(&text), Just(text))).boxed()
177 }
178
179 fn to_line_col(text: &str, offset: TextUnit) -> LineCol {
180 let mut res = LineCol { line: 0, col_utf16: 0 };
181 for (i, c) in text.char_indices() {
182 if i + c.len_utf8() > offset.to_usize() {
183 // if it's an invalid offset, inside a multibyte char
184 // return as if it was at the start of the char
185 break;
186 }
187 if c == '\n' {
188 res.line += 1;
189 res.col_utf16 = 0;
190 } else {
191 res.col_utf16 += 1;
192 }
193 }
194 res
195 }
196
197 proptest! {
198 #[test]
199 fn test_line_index_proptest((offset, text) in arb_text_with_offset()) {
200 let expected = to_line_col(&text, offset);
201 let line_index = LineIndex::new(&text);
202 let actual = line_index.line_col(offset);
203
204 assert_eq!(actual, expected);
205 }
206 }
207}
208
209#[cfg(test)]
210mod test_utf8_utf16_conv {
211 use super::*;
212
213 #[test]
214 fn test_char_len() {
215 assert_eq!('メ'.len_utf8(), 3);
216 assert_eq!('メ'.len_utf16(), 1);
217 }
218
219 #[test]
220 fn test_empty_index() {
221 let col_index = LineIndex::new(
222 "
223const C: char = 'x';
224",
225 );
226 assert_eq!(col_index.utf16_lines.len(), 0);
227 }
228
229 #[test]
230 fn test_single_char() {
231 let col_index = LineIndex::new(
232 "
233const C: char = 'メ';
234",
235 );
236
237 assert_eq!(col_index.utf16_lines.len(), 1);
238 assert_eq!(col_index.utf16_lines[&1].len(), 1);
239 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
240
241 // UTF-8 to UTF-16, no changes
242 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
243
244 // UTF-8 to UTF-16
245 assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
246
247 // UTF-16 to UTF-8, no changes
248 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from(15));
249
250 // UTF-16 to UTF-8
251 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from(21));
252 }
253
254 #[test]
255 fn test_string() {
256 let col_index = LineIndex::new(
257 "
258const C: char = \"メ メ\";
259",
260 );
261
262 assert_eq!(col_index.utf16_lines.len(), 1);
263 assert_eq!(col_index.utf16_lines[&1].len(), 2);
264 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
265 assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
266
267 // UTF-8 to UTF-16
268 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
269
270 assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
271 assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
272
273 assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
274
275 // UTF-16 to UTF-8
276 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from_usize(15));
277
278 assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextUnit::from_usize(20));
279 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from_usize(23));
280
281 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15));
282 }
283}
diff --git a/crates/ra_ide/src/line_index_utils.rs b/crates/ra_ide/src/line_index_utils.rs
deleted file mode 100644
index bd1e08feb..000000000
--- a/crates/ra_ide/src/line_index_utils.rs
+++ /dev/null
@@ -1,331 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::{line_index::Utf16Char, LineCol, LineIndex};
4use ra_syntax::{TextRange, TextUnit};
5use ra_text_edit::{AtomTextEdit, TextEdit};
6
7#[derive(Debug, Clone)]
8enum Step {
9 Newline(TextUnit),
10 Utf16Char(TextRange),
11}
12
13#[derive(Debug)]
14struct LineIndexStepIter<'a> {
15 line_index: &'a LineIndex,
16 next_newline_idx: usize,
17 utf16_chars: Option<(TextUnit, std::slice::Iter<'a, Utf16Char>)>,
18}
19
20impl<'a> LineIndexStepIter<'a> {
21 fn from(line_index: &LineIndex) -> LineIndexStepIter {
22 let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None };
23 // skip first newline since it's not real
24 x.next();
25 x
26 }
27}
28
29impl<'a> Iterator for LineIndexStepIter<'a> {
30 type Item = Step;
31 fn next(&mut self) -> Option<Step> {
32 self.utf16_chars
33 .as_mut()
34 .and_then(|(newline, x)| {
35 let x = x.next()?;
36 Some(Step::Utf16Char(TextRange::from_to(*newline + x.start, *newline + x.end)))
37 })
38 .or_else(|| {
39 let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?;
40 self.utf16_chars = self
41 .line_index
42 .utf16_lines
43 .get(&(self.next_newline_idx as u32))
44 .map(|x| (next_newline, x.iter()));
45 self.next_newline_idx += 1;
46 Some(Step::Newline(next_newline))
47 })
48 }
49}
50
51#[derive(Debug)]
52struct OffsetStepIter<'a> {
53 text: &'a str,
54 offset: TextUnit,
55}
56
57impl<'a> Iterator for OffsetStepIter<'a> {
58 type Item = Step;
59 fn next(&mut self) -> Option<Step> {
60 let (next, next_offset) = self
61 .text
62 .char_indices()
63 .filter_map(|(i, c)| {
64 if c == '\n' {
65 let next_offset = self.offset + TextUnit::from_usize(i + 1);
66 let next = Step::Newline(next_offset);
67 Some((next, next_offset))
68 } else {
69 let char_len = TextUnit::of_char(c);
70 if char_len.to_usize() > 1 {
71 let start = self.offset + TextUnit::from_usize(i);
72 let end = start + char_len;
73 let next = Step::Utf16Char(TextRange::from_to(start, end));
74 let next_offset = end;
75 Some((next, next_offset))
76 } else {
77 None
78 }
79 }
80 })
81 .next()?;
82 let next_idx = (next_offset - self.offset).to_usize();
83 self.text = &self.text[next_idx..];
84 self.offset = next_offset;
85 Some(next)
86 }
87}
88
89#[derive(Debug)]
90enum NextSteps<'a> {
91 Use,
92 ReplaceMany(OffsetStepIter<'a>),
93 AddMany(OffsetStepIter<'a>),
94}
95
96#[derive(Debug)]
97struct TranslatedEdit<'a> {
98 delete: TextRange,
99 insert: &'a str,
100 diff: i64,
101}
102
103struct Edits<'a> {
104 edits: &'a [AtomTextEdit],
105 current: Option<TranslatedEdit<'a>>,
106 acc_diff: i64,
107}
108
109impl<'a> Edits<'a> {
110 fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> {
111 let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 };
112 x.advance_edit();
113 x
114 }
115 fn advance_edit(&mut self) {
116 self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff);
117 match self.edits.split_first() {
118 Some((next, rest)) => {
119 let delete = self.translate_range(next.delete);
120 let diff = next.insert.len() as i64 - next.delete.len().to_usize() as i64;
121 self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff });
122 self.edits = rest;
123 }
124 None => {
125 self.current = None;
126 }
127 }
128 }
129
130 fn next_inserted_steps(&mut self) -> Option<OffsetStepIter<'a>> {
131 let cur = self.current.as_ref()?;
132 let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert });
133 self.advance_edit();
134 res
135 }
136
137 fn next_steps(&mut self, step: &Step) -> NextSteps {
138 let step_pos = match *step {
139 Step::Newline(n) => n,
140 Step::Utf16Char(r) => r.end(),
141 };
142 match &mut self.current {
143 Some(edit) => {
144 if step_pos <= edit.delete.start() {
145 NextSteps::Use
146 } else if step_pos <= edit.delete.end() {
147 let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert };
148 // empty slice to avoid returning steps again
149 edit.insert = &edit.insert[edit.insert.len()..];
150 NextSteps::ReplaceMany(iter)
151 } else {
152 let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert };
153 // empty slice to avoid returning steps again
154 edit.insert = &edit.insert[edit.insert.len()..];
155 self.advance_edit();
156 NextSteps::AddMany(iter)
157 }
158 }
159 None => NextSteps::Use,
160 }
161 }
162
163 fn translate_range(&self, range: TextRange) -> TextRange {
164 if self.acc_diff == 0 {
165 range
166 } else {
167 let start = self.translate(range.start());
168 let end = self.translate(range.end());
169 TextRange::from_to(start, end)
170 }
171 }
172
173 fn translate(&self, x: TextUnit) -> TextUnit {
174 if self.acc_diff == 0 {
175 x
176 } else {
177 TextUnit::from((x.to_usize() as i64 + self.acc_diff) as u32)
178 }
179 }
180
181 fn translate_step(&self, x: &Step) -> Step {
182 if self.acc_diff == 0 {
183 x.clone()
184 } else {
185 match *x {
186 Step::Newline(n) => Step::Newline(self.translate(n)),
187 Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)),
188 }
189 }
190 }
191}
192
193#[derive(Debug)]
194struct RunningLineCol {
195 line: u32,
196 last_newline: TextUnit,
197 col_adjust: TextUnit,
198}
199
200impl RunningLineCol {
201 fn new() -> RunningLineCol {
202 RunningLineCol { line: 0, last_newline: TextUnit::from(0), col_adjust: TextUnit::from(0) }
203 }
204
205 fn to_line_col(&self, offset: TextUnit) -> LineCol {
206 LineCol {
207 line: self.line,
208 col_utf16: ((offset - self.last_newline) - self.col_adjust).into(),
209 }
210 }
211
212 fn add_line(&mut self, newline: TextUnit) {
213 self.line += 1;
214 self.last_newline = newline;
215 self.col_adjust = TextUnit::from(0);
216 }
217
218 fn adjust_col(&mut self, range: TextRange) {
219 self.col_adjust += range.len() - TextUnit::from(1);
220 }
221}
222
223pub fn translate_offset_with_edit(
224 line_index: &LineIndex,
225 offset: TextUnit,
226 text_edit: &TextEdit,
227) -> LineCol {
228 let mut state = Edits::from_text_edit(&text_edit);
229
230 let mut res = RunningLineCol::new();
231
232 macro_rules! test_step {
233 ($x:ident) => {
234 match &$x {
235 Step::Newline(n) => {
236 if offset < *n {
237 return res.to_line_col(offset);
238 } else {
239 res.add_line(*n);
240 }
241 }
242 Step::Utf16Char(x) => {
243 if offset < x.end() {
244 // if the offset is inside a multibyte char it's invalid
245 // clamp it to the start of the char
246 let clamp = offset.min(x.start());
247 return res.to_line_col(clamp);
248 } else {
249 res.adjust_col(*x);
250 }
251 }
252 }
253 };
254 }
255
256 for orig_step in LineIndexStepIter::from(line_index) {
257 loop {
258 let translated_step = state.translate_step(&orig_step);
259 match state.next_steps(&translated_step) {
260 NextSteps::Use => {
261 test_step!(translated_step);
262 break;
263 }
264 NextSteps::ReplaceMany(ns) => {
265 for n in ns {
266 test_step!(n);
267 }
268 break;
269 }
270 NextSteps::AddMany(ns) => {
271 for n in ns {
272 test_step!(n);
273 }
274 }
275 }
276 }
277 }
278
279 loop {
280 match state.next_inserted_steps() {
281 None => break,
282 Some(ns) => {
283 for n in ns {
284 test_step!(n);
285 }
286 }
287 }
288 }
289
290 res.to_line_col(offset)
291}
292
293#[cfg(test)]
294mod test {
295 use super::*;
296 use crate::line_index;
297 use proptest::{prelude::*, proptest};
298 use ra_text_edit::test_utils::{arb_offset, arb_text_with_edit};
299 use ra_text_edit::TextEdit;
300
301 #[derive(Debug)]
302 struct ArbTextWithEditAndOffset {
303 text: String,
304 edit: TextEdit,
305 edited_text: String,
306 offset: TextUnit,
307 }
308
309 fn arb_text_with_edit_and_offset() -> BoxedStrategy<ArbTextWithEditAndOffset> {
310 arb_text_with_edit()
311 .prop_flat_map(|x| {
312 let edited_text = x.edit.apply(&x.text);
313 let arb_offset = arb_offset(&edited_text);
314 (Just(x), Just(edited_text), arb_offset).prop_map(|(x, edited_text, offset)| {
315 ArbTextWithEditAndOffset { text: x.text, edit: x.edit, edited_text, offset }
316 })
317 })
318 .boxed()
319 }
320
321 proptest! {
322 #[test]
323 fn test_translate_offset_with_edit(x in arb_text_with_edit_and_offset()) {
324 let expected = line_index::to_line_col(&x.edited_text, x.offset);
325 let line_index = LineIndex::new(&x.text);
326 let actual = translate_offset_with_edit(&line_index, x.offset, &x.edit);
327
328 assert_eq!(actual, expected);
329 }
330 }
331}
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index 2dbccfc3b..e0332da88 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -1,12 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; 3use ra_db::{CrateId, FileId, FilePosition, SourceDatabase};
4use ra_ide_db::RootDatabase;
4use ra_syntax::{ 5use ra_syntax::{
5 algo::find_node_at_offset, 6 algo::find_node_at_offset,
6 ast::{self, AstNode}, 7 ast::{self, AstNode},
7}; 8};
8 9
9use crate::{db::RootDatabase, NavigationTarget}; 10use crate::NavigationTarget;
10 11
11/// This returns `Vec` because a module may be included from several places. We 12/// This returns `Vec` because a module may be included from several places. We
12/// don't handle this case yet though, so the Vec has length at most one. 13/// don't handle this case yet though, so the Vec has length at most one.
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index ebded715d..b47f8bcd9 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -17,6 +17,7 @@ mod search_scope;
17use hir::{InFile, SourceBinder}; 17use hir::{InFile, SourceBinder};
18use once_cell::unsync::Lazy; 18use once_cell::unsync::Lazy;
19use ra_db::{SourceDatabase, SourceDatabaseExt}; 19use ra_db::{SourceDatabase, SourceDatabaseExt};
20use ra_ide_db::RootDatabase;
20use ra_prof::profile; 21use ra_prof::profile;
21use ra_syntax::{ 22use ra_syntax::{
22 algo::find_node_at_offset, 23 algo::find_node_at_offset,
@@ -24,9 +25,7 @@ use ra_syntax::{
24 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, 25 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
25}; 26};
26 27
27use crate::{ 28use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
28 db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
29};
30 29
31pub(crate) use self::{ 30pub(crate) use self::{
32 classify::{classify_name, classify_name_ref}, 31 classify::{classify_name, classify_name_ref},
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs
index 46cba30a3..758ea4e8b 100644
--- a/crates/ra_ide/src/references/classify.rs
+++ b/crates/ra_ide/src/references/classify.rs
@@ -9,7 +9,7 @@ use super::{
9 name_definition::{from_assoc_item, from_module_def, from_struct_field}, 9 name_definition::{from_assoc_item, from_module_def, from_struct_field},
10 NameDefinition, NameKind, 10 NameDefinition, NameKind,
11}; 11};
12use crate::db::RootDatabase; 12use ra_ide_db::RootDatabase;
13 13
14pub(crate) fn classify_name( 14pub(crate) fn classify_name(
15 sb: &mut SourceBinder<RootDatabase>, 15 sb: &mut SourceBinder<RootDatabase>,
diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs
index 1e4226ab9..71565e6d3 100644
--- a/crates/ra_ide/src/references/name_definition.rs
+++ b/crates/ra_ide/src/references/name_definition.rs
@@ -9,7 +9,7 @@ use hir::{
9}; 9};
10use ra_syntax::{ast, ast::VisibilityOwner}; 10use ra_syntax::{ast, ast::VisibilityOwner};
11 11
12use crate::db::RootDatabase; 12use ra_ide_db::RootDatabase;
13 13
14#[derive(Debug, PartialEq, Eq)] 14#[derive(Debug, PartialEq, Eq)]
15pub enum NameKind { 15pub enum NameKind {
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 9a84c1c88..08e77c01f 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -2,14 +2,14 @@
2 2
3use hir::ModuleSource; 3use hir::ModuleSource;
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; 4use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::RootDatabase;
5use ra_syntax::{ 6use ra_syntax::{
6 algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, 7 algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode,
7}; 8};
8use ra_text_edit::TextEdit; 9use ra_text_edit::TextEdit;
9 10
10use crate::{ 11use crate::{
11 db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, 12 FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, SourceFileEdit, TextRange,
12 SourceFileEdit, TextRange,
13}; 13};
14 14
15use super::find_all_refs; 15use super::find_all_refs;
diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs
index f8211a746..97c65c2cd 100644
--- a/crates/ra_ide/src/references/search_scope.rs
+++ b/crates/ra_ide/src/references/search_scope.rs
@@ -10,7 +10,7 @@ use ra_prof::profile;
10use ra_syntax::{AstNode, TextRange}; 10use ra_syntax::{AstNode, TextRange};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
12 12
13use crate::db::RootDatabase; 13use ra_ide_db::RootDatabase;
14 14
15use super::{NameDefinition, NameKind}; 15use super::{NameDefinition, NameKind};
16 16
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 8622dd956..b6b0c70f9 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -3,12 +3,13 @@
3use hir::InFile; 3use hir::InFile;
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_db::SourceDatabase; 5use ra_db::SourceDatabase;
6use ra_ide_db::RootDatabase;
6use ra_syntax::{ 7use ra_syntax::{
7 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
8 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode, TextRange,
9}; 10};
10 11
11use crate::{db::RootDatabase, FileId}; 12use crate::FileId;
12 13
13#[derive(Debug)] 14#[derive(Debug)]
14pub struct Runnable { 15pub struct Runnable {
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 1bb27eb85..30eb5c995 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -10,14 +10,14 @@ use ra_db::{
10 }, 10 },
11 FileTextQuery, SourceRootId, 11 FileTextQuery, SourceRootId,
12}; 12};
13use ra_ide_db::{
14 symbol_index::{LibrarySymbolsQuery, SymbolIndex},
15 RootDatabase,
16};
13use ra_prof::{memory_usage, Bytes}; 17use ra_prof::{memory_usage, Bytes};
14use ra_syntax::{ast, Parse, SyntaxNode}; 18use ra_syntax::{ast, Parse, SyntaxNode};
15 19
16use crate::{ 20use crate::FileId;
17 db::RootDatabase,
18 symbol_index::{LibrarySymbolsQuery, SymbolIndex},
19 FileId,
20};
21 21
22fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 22fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
23 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>() 23 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>()
diff --git a/crates/ra_ide/src/symbol_index.rs b/crates/ra_ide/src/symbol_index.rs
deleted file mode 100644
index 5729eb5b3..000000000
--- a/crates/ra_ide/src/symbol_index.rs
+++ /dev/null
@@ -1,405 +0,0 @@
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 `fst`, and it builds a
9//! finite state machine describing this set of strings. The strings which
10//! could fuzzy-match a pattern can also be described by a finite state machine.
11//! What is freaking 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 languages described by
14//! FSTs, one can build a 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 against the union of all
21//! those FSTs.
22use std::{
23 fmt,
24 hash::{Hash, Hasher},
25 mem,
26 sync::Arc,
27};
28
29use fst::{self, Streamer};
30use ra_db::{
31 salsa::{self, ParallelDatabase},
32 SourceDatabaseExt, SourceRootId,
33};
34use ra_syntax::{
35 ast::{self, NameOwner},
36 match_ast, AstNode, Parse, SmolStr, SourceFile,
37 SyntaxKind::{self, *},
38 SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
39};
40#[cfg(not(feature = "wasm"))]
41use rayon::prelude::*;
42
43use crate::{db::RootDatabase, FileId, Query};
44
45#[salsa::query_group(SymbolsDatabaseStorage)]
46pub(crate) trait SymbolsDatabase: hir::db::HirDatabase {
47 fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
48 #[salsa::input]
49 fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
50 /// The set of "local" (that is, from the current workspace) roots.
51 /// Files in local roots are assumed to change frequently.
52 #[salsa::input]
53 fn local_roots(&self) -> Arc<Vec<SourceRootId>>;
54 /// The set of roots for crates.io libraries.
55 /// Files in libraries are assumed to never change.
56 #[salsa::input]
57 fn library_roots(&self) -> Arc<Vec<SourceRootId>>;
58}
59
60fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
61 db.check_canceled();
62 let parse = db.parse(file_id);
63
64 let symbols = source_file_to_file_symbols(&parse.tree(), file_id);
65
66 // FIXME: add macros here
67
68 Arc::new(SymbolIndex::new(symbols))
69}
70
71pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
72 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
73 struct Snap(salsa::Snapshot<RootDatabase>);
74 impl Clone for Snap {
75 fn clone(&self) -> Snap {
76 Snap(self.0.snapshot())
77 }
78 }
79
80 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
81 let snap = Snap(db.snapshot());
82 #[cfg(not(feature = "wasm"))]
83 let buf = db
84 .library_roots()
85 .par_iter()
86 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
87 .collect();
88
89 #[cfg(feature = "wasm")]
90 let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();
91
92 buf
93 } else {
94 let mut files = Vec::new();
95 for &root in db.local_roots().iter() {
96 let sr = db.source_root(root);
97 files.extend(sr.walk())
98 }
99
100 let snap = Snap(db.snapshot());
101 #[cfg(not(feature = "wasm"))]
102 let buf =
103 files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect();
104
105 #[cfg(feature = "wasm")]
106 let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect();
107
108 buf
109 };
110 query.search(&buf)
111}
112
113pub(crate) fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
114 let name = name_ref.text();
115 let mut query = Query::new(name.to_string());
116 query.exact();
117 query.limit(4);
118 crate::symbol_index::world_symbols(db, query)
119}
120
121#[derive(Default)]
122pub(crate) struct SymbolIndex {
123 symbols: Vec<FileSymbol>,
124 map: fst::Map,
125}
126
127impl fmt::Debug for SymbolIndex {
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
130 }
131}
132
133impl PartialEq for SymbolIndex {
134 fn eq(&self, other: &SymbolIndex) -> bool {
135 self.symbols == other.symbols
136 }
137}
138
139impl Eq for SymbolIndex {}
140
141impl Hash for SymbolIndex {
142 fn hash<H: Hasher>(&self, hasher: &mut H) {
143 self.symbols.hash(hasher)
144 }
145}
146
147impl SymbolIndex {
148 fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
149 fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a {
150 unicase::Ascii::new(s1.name.as_str())
151 }
152 #[cfg(not(feature = "wasm"))]
153 symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2)));
154
155 #[cfg(feature = "wasm")]
156 symbols.sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2)));
157
158 let mut builder = fst::MapBuilder::memory();
159
160 let mut last_batch_start = 0;
161
162 for idx in 0..symbols.len() {
163 if symbols.get(last_batch_start).map(cmp_key) == symbols.get(idx + 1).map(cmp_key) {
164 continue;
165 }
166
167 let start = last_batch_start;
168 let end = idx + 1;
169 last_batch_start = end;
170
171 let key = symbols[start].name.as_str().to_lowercase();
172 let value = SymbolIndex::range_to_map_value(start, end);
173
174 builder.insert(key, value).unwrap();
175 }
176
177 let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap();
178 SymbolIndex { symbols, map }
179 }
180
181 pub(crate) fn len(&self) -> usize {
182 self.symbols.len()
183 }
184
185 pub(crate) fn memory_size(&self) -> usize {
186 self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
187 }
188
189 #[cfg(not(feature = "wasm"))]
190 pub(crate) fn for_files(
191 files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
192 ) -> SymbolIndex {
193 let symbols = files
194 .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
195 .collect::<Vec<_>>();
196 SymbolIndex::new(symbols)
197 }
198
199 #[cfg(feature = "wasm")]
200 pub(crate) fn for_files(
201 files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
202 ) -> SymbolIndex {
203 let symbols = files
204 .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
205 .collect::<Vec<_>>();
206 SymbolIndex::new(symbols)
207 }
208
209 fn range_to_map_value(start: usize, end: usize) -> u64 {
210 debug_assert![start <= (std::u32::MAX as usize)];
211 debug_assert![end <= (std::u32::MAX as usize)];
212
213 ((start as u64) << 32) | end as u64
214 }
215
216 fn map_value_to_range(value: u64) -> (usize, usize) {
217 let end = value as u32 as usize;
218 let start = (value >> 32) as usize;
219 (start, end)
220 }
221}
222
223impl Query {
224 pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<FileSymbol> {
225 let mut op = fst::map::OpBuilder::new();
226 for file_symbols in indices.iter() {
227 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
228 op = op.add(file_symbols.map.search(automaton))
229 }
230 let mut stream = op.union();
231 let mut res = Vec::new();
232 while let Some((_, indexed_values)) = stream.next() {
233 if res.len() >= self.limit {
234 break;
235 }
236 for indexed_value in indexed_values {
237 let symbol_index = &indices[indexed_value.index];
238 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
239
240 for symbol in &symbol_index.symbols[start..end] {
241 if self.only_types && !is_type(symbol.ptr.kind()) {
242 continue;
243 }
244 if self.exact && symbol.name != self.query {
245 continue;
246 }
247 res.push(symbol.clone());
248 }
249 }
250 }
251 res
252 }
253}
254
255fn is_type(kind: SyntaxKind) -> bool {
256 match kind {
257 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true,
258 _ => false,
259 }
260}
261
262/// The actual data that is stored in the index. It should be as compact as
263/// possible.
264#[derive(Debug, Clone, PartialEq, Eq, Hash)]
265pub(crate) struct FileSymbol {
266 pub(crate) file_id: FileId,
267 pub(crate) name: SmolStr,
268 pub(crate) ptr: SyntaxNodePtr,
269 pub(crate) name_range: Option<TextRange>,
270 pub(crate) container_name: Option<SmolStr>,
271}
272
273fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec<FileSymbol> {
274 let mut symbols = Vec::new();
275 let mut stack = Vec::new();
276
277 for event in source_file.syntax().preorder() {
278 match event {
279 WalkEvent::Enter(node) => {
280 if let Some(mut symbol) = to_file_symbol(&node, file_id) {
281 symbol.container_name = stack.last().cloned();
282
283 stack.push(symbol.name.clone());
284 symbols.push(symbol);
285 }
286 }
287
288 WalkEvent::Leave(node) => {
289 if to_symbol(&node).is_some() {
290 stack.pop();
291 }
292 }
293 }
294 }
295
296 symbols
297}
298
299fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
300 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
301 let name = node.name()?;
302 let name_range = name.syntax().text_range();
303 let name = name.text().clone();
304 let ptr = SyntaxNodePtr::new(node.syntax());
305
306 Some((name, ptr, name_range))
307 }
308 match_ast! {
309 match node {
310 ast::FnDef(it) => { decl(it) },
311 ast::StructDef(it) => { decl(it) },
312 ast::EnumDef(it) => { decl(it) },
313 ast::TraitDef(it) => { decl(it) },
314 ast::Module(it) => { decl(it) },
315 ast::TypeAliasDef(it) => { decl(it) },
316 ast::ConstDef(it) => { decl(it) },
317 ast::StaticDef(it) => { decl(it) },
318 _ => None,
319 }
320 }
321}
322
323fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
324 to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol {
325 name,
326 ptr,
327 file_id,
328 name_range: Some(name_range),
329 container_name: None,
330 })
331}
332
333#[cfg(test)]
334mod tests {
335 use crate::{display::NavigationTarget, mock_analysis::single_file, Query};
336 use ra_syntax::{
337 SmolStr,
338 SyntaxKind::{FN_DEF, STRUCT_DEF},
339 };
340
341 #[test]
342 fn test_world_symbols_with_no_container() {
343 let code = r#"
344 enum FooInner { }
345 "#;
346
347 let mut symbols = get_symbols_matching(code, "FooInner");
348
349 let s = symbols.pop().unwrap();
350
351 assert_eq!(s.name(), "FooInner");
352 assert!(s.container_name().is_none());
353 }
354
355 #[test]
356 fn test_world_symbols_include_container_name() {
357 let code = r#"
358fn foo() {
359 enum FooInner { }
360}
361 "#;
362
363 let mut symbols = get_symbols_matching(code, "FooInner");
364
365 let s = symbols.pop().unwrap();
366
367 assert_eq!(s.name(), "FooInner");
368 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
369
370 let code = r#"
371mod foo {
372 struct FooInner;
373}
374 "#;
375
376 let mut symbols = get_symbols_matching(code, "FooInner");
377
378 let s = symbols.pop().unwrap();
379
380 assert_eq!(s.name(), "FooInner");
381 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
382 }
383
384 #[test]
385 fn test_world_symbols_are_case_sensitive() {
386 let code = r#"
387fn foo() {}
388
389struct Foo;
390 "#;
391
392 let symbols = get_symbols_matching(code, "Foo");
393
394 let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind());
395 let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind());
396
397 assert_eq!(fn_match, Some(FN_DEF));
398 assert_eq!(struct_match, Some(STRUCT_DEF));
399 }
400
401 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
402 let (analysis, _) = single_file(text);
403 analysis.symbol_search(Query::new(query.into())).unwrap()
404 }
405}
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 530b984fc..c5d249fe8 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -4,6 +4,7 @@ use rustc_hash::FxHashMap;
4 4
5use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; 5use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder};
6use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
7use ra_ide_db::RootDatabase;
7use ra_prof::profile; 8use ra_prof::profile;
8use ra_syntax::{ 9use ra_syntax::{
9 ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, 10 ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange,
@@ -11,7 +12,6 @@ use ra_syntax::{
11}; 12};
12 13
13use crate::{ 14use crate::{
14 db::RootDatabase,
15 expand::descend_into_macros_with_analyzer, 15 expand::descend_into_macros_with_analyzer,
16 references::{ 16 references::{
17 classify_name, classify_name_ref, 17 classify_name, classify_name_ref,
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index 4d0f0fc47..55966daf3 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::db::RootDatabase;
4use ra_db::SourceDatabase; 3use ra_db::SourceDatabase;
4use ra_ide_db::RootDatabase;
5use ra_syntax::{ 5use ra_syntax::{
6 algo, AstNode, NodeOrToken, SourceFile, 6 algo, AstNode, NodeOrToken, SourceFile,
7 SyntaxKind::{RAW_STRING, STRING}, 7 SyntaxKind::{RAW_STRING, STRING},
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 21e5be9b3..e5d1779fd 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -15,6 +15,7 @@
15 15
16use ra_db::{FilePosition, SourceDatabase}; 16use ra_db::{FilePosition, SourceDatabase};
17use ra_fmt::leading_indent; 17use ra_fmt::leading_indent;
18use ra_ide_db::RootDatabase;
18use ra_syntax::{ 19use ra_syntax::{
19 algo::find_node_at_offset, 20 algo::find_node_at_offset,
20 ast::{self, AstToken}, 21 ast::{self, AstToken},
@@ -24,7 +25,7 @@ use ra_syntax::{
24}; 25};
25use ra_text_edit::TextEdit; 26use ra_text_edit::TextEdit;
26 27
27use crate::{db::RootDatabase, source_change::SingleFileChange, SourceChange, SourceFileEdit}; 28use crate::{source_change::SingleFileChange, SourceChange, SourceFileEdit};
28 29
29pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> { 30pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
30 let parse = db.parse(position.file_id); 31 let parse = db.parse(position.file_id);
diff --git a/crates/ra_ide/src/wasm_shims.rs b/crates/ra_ide/src/wasm_shims.rs
deleted file mode 100644
index 088cc9be4..000000000
--- a/crates/ra_ide/src/wasm_shims.rs
+++ /dev/null
@@ -1,19 +0,0 @@
1//! FIXME: write short doc here
2
3#[cfg(not(feature = "wasm"))]
4pub use std::time::Instant;
5
6#[cfg(feature = "wasm")]
7#[derive(Clone, Copy, Debug)]
8pub struct Instant;
9
10#[cfg(feature = "wasm")]
11impl Instant {
12 pub fn now() -> Self {
13 Self
14 }
15
16 pub fn elapsed(&self) -> std::time::Duration {
17 std::time::Duration::new(0, 0)
18 }
19}