aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
authorKevin DeLorey <[email protected]>2020-02-09 16:25:47 +0000
committerKevin DeLorey <[email protected]>2020-02-09 16:37:43 +0000
commita957c473fdb79880c39b73dc9e0c923093cf16ac (patch)
treef998b548f530ce604651e0e6af314ed2ec74b3b5 /crates/ra_ide/src
parent22caf982b99c54058e2e9200aeea0e61cada284a (diff)
parent1b9b13b4b4a75b5531c3f046ce6bf72d681f2732 (diff)
Merge branch 'master' into kdelorey/complete-trait-impl
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/assists.rs31
-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.rs353
-rw-r--r--crates/ra_ide/src/completion.rs5
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs117
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs2
-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.rs11
-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.rs9
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs4
-rw-r--r--crates/ra_ide/src/hover.rs11
-rw-r--r--crates/ra_ide/src/impls.rs3
-rw-r--r--crates/ra_ide/src/inlay_hints.rs19
-rw-r--r--crates/ra_ide/src/lib.rs150
-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/marks.rs1
-rw-r--r--crates/ra_ide/src/mock_analysis.rs6
-rw-r--r--crates/ra_ide/src/parent_module.rs40
-rw-r--r--crates/ra_ide/src/references.rs157
-rw-r--r--crates/ra_ide/src/references/classify.rs140
-rw-r--r--crates/ra_ide/src/references/name_definition.rs85
-rw-r--r--crates/ra_ide/src/references/rename.rs16
-rw-r--r--crates/ra_ide/src/references/search_scope.rs104
-rw-r--r--crates/ra_ide/src/runnables.rs19
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html10
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html12
-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.rs314
-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
41 files changed, 657 insertions, 2293 deletions
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index a936900da..40d56a4f7 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -1,22 +1,23 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_assists::{resolved_assists, AssistAction, AssistLabel};
3use ra_db::{FilePosition, FileRange}; 4use ra_db::{FilePosition, FileRange};
5use ra_ide_db::RootDatabase;
4 6
5use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; 7use crate::{FileId, SourceChange, SourceFileEdit};
6 8
7use either::Either;
8pub use ra_assists::AssistId; 9pub use ra_assists::AssistId;
9use ra_assists::{AssistAction, AssistLabel};
10 10
11#[derive(Debug)] 11#[derive(Debug)]
12pub struct Assist { 12pub struct Assist {
13 pub id: AssistId, 13 pub id: AssistId,
14 pub label: String, 14 pub label: String,
15 pub change_data: Either<SourceChange, Vec<SourceChange>>, 15 pub group_label: Option<String>,
16 pub source_change: SourceChange,
16} 17}
17 18
18pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { 19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
19 ra_assists::assists(db, frange) 20 resolved_assists(db, frange)
20 .into_iter() 21 .into_iter()
21 .map(|assist| { 22 .map(|assist| {
22 let file_id = frange.file_id; 23 let file_id = frange.file_id;
@@ -24,17 +25,8 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
24 Assist { 25 Assist {
25 id: assist_label.id, 26 id: assist_label.id,
26 label: assist_label.label.clone(), 27 label: assist_label.label.clone(),
27 change_data: match assist.action_data { 28 group_label: assist.group_label.map(|it| it.0),
28 Either::Left(action) => { 29 source_change: action_to_edit(assist.action, file_id, assist_label),
29 Either::Left(action_to_edit(action, file_id, assist_label))
30 }
31 Either::Right(actions) => Either::Right(
32 actions
33 .into_iter()
34 .map(|action| action_to_edit(action, file_id, assist_label))
35 .collect(),
36 ),
37 },
38 } 30 }
39 }) 31 })
40 .collect() 32 .collect()
@@ -46,9 +38,6 @@ fn action_to_edit(
46 assist_label: &AssistLabel, 38 assist_label: &AssistLabel,
47) -> SourceChange { 39) -> SourceChange {
48 let file_edit = SourceFileEdit { file_id, edit: action.edit }; 40 let file_edit = SourceFileEdit { file_id, edit: action.edit };
49 SourceChange::source_file_edit( 41 SourceChange::source_file_edit(assist_label.label.clone(), file_edit)
50 action.label.unwrap_or_else(|| assist_label.label.clone()), 42 .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
51 file_edit,
52 )
53 .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
54} 43}
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 b0aa2c8e0..000000000
--- a/crates/ra_ide/src/change.rs
+++ /dev/null
@@ -1,353 +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 #[cfg(not(feature = "wasm"))]
149 let iter = files.par_iter();
150 #[cfg(feature = "wasm")]
151 let iter = files.iter();
152
153 let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| {
154 let parse = SourceFile::parse(text);
155 (*file_id, parse)
156 }));
157 let mut root_change = RootChange::default();
158 root_change.added = files
159 .into_iter()
160 .map(|(file_id, path, text)| AddFile { file_id, path, text })
161 .collect();
162 LibraryData { root_id, root_change, symbol_index }
163 }
164}
165
166const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
167
168impl RootDatabase {
169 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
170 let _p = profile("RootDatabase::apply_change");
171 log::info!("apply_change {:?}", change);
172 {
173 let _p = profile("RootDatabase::apply_change/cancellation");
174 self.salsa_runtime_mut().synthetic_write(Durability::LOW);
175 }
176 if !change.new_roots.is_empty() {
177 let mut local_roots = Vec::clone(&self.local_roots());
178 for (root_id, is_local) in change.new_roots {
179 let root =
180 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() };
181 let durability = durability(&root);
182 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
183 if is_local {
184 local_roots.push(root_id);
185 }
186 }
187 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
188 }
189
190 for (root_id, root_change) in change.roots_changed {
191 self.apply_root_change(root_id, root_change);
192 }
193 for (file_id, text) in change.files_changed {
194 let source_root_id = self.file_source_root(file_id);
195 let source_root = self.source_root(source_root_id);
196 let durability = durability(&source_root);
197 self.set_file_text_with_durability(file_id, text, durability)
198 }
199 if !change.libraries_added.is_empty() {
200 let mut libraries = Vec::clone(&self.library_roots());
201 for library in change.libraries_added {
202 libraries.push(library.root_id);
203 self.set_source_root_with_durability(
204 library.root_id,
205 Arc::new(SourceRoot::new_library()),
206 Durability::HIGH,
207 );
208 self.set_library_symbols_with_durability(
209 library.root_id,
210 Arc::new(library.symbol_index),
211 Durability::HIGH,
212 );
213 self.apply_root_change(library.root_id, library.root_change);
214 }
215 self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH);
216 }
217 if let Some(crate_graph) = change.crate_graph {
218 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
219 }
220
221 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
222 }
223
224 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
225 let mut source_root = SourceRoot::clone(&self.source_root(root_id));
226 let durability = durability(&source_root);
227 for add_file in root_change.added {
228 self.set_file_text_with_durability(add_file.file_id, add_file.text, durability);
229 self.set_file_relative_path_with_durability(
230 add_file.file_id,
231 add_file.path.clone(),
232 durability,
233 );
234 self.set_file_source_root_with_durability(add_file.file_id, root_id, durability);
235 source_root.insert_file(add_file.path, add_file.file_id);
236 }
237 for remove_file in root_change.removed {
238 self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability);
239 source_root.remove_file(&remove_file.path);
240 }
241 self.set_source_root_with_durability(root_id, Arc::new(source_root), durability);
242 }
243
244 pub(crate) fn maybe_collect_garbage(&mut self) {
245 if cfg!(feature = "wasm") {
246 return;
247 }
248
249 if self.last_gc_check.elapsed() > GC_COOLDOWN {
250 self.last_gc_check = crate::wasm_shims::Instant::now();
251 }
252 }
253
254 pub(crate) fn collect_garbage(&mut self) {
255 if cfg!(feature = "wasm") {
256 return;
257 }
258
259 let _p = profile("RootDatabase::collect_garbage");
260 self.last_gc = crate::wasm_shims::Instant::now();
261
262 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
263
264 self.query(ra_db::ParseQuery).sweep(sweep);
265 self.query(hir::db::ParseMacroQuery).sweep(sweep);
266
267 // Macros do take significant space, but less then the syntax trees
268 // self.query(hir::db::MacroDefQuery).sweep(sweep);
269 // self.query(hir::db::MacroArgQuery).sweep(sweep);
270 // self.query(hir::db::MacroExpandQuery).sweep(sweep);
271
272 self.query(hir::db::AstIdMapQuery).sweep(sweep);
273
274 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
275
276 self.query(hir::db::ExprScopesQuery).sweep(sweep);
277 self.query(hir::db::DoInferQuery).sweep(sweep);
278 self.query(hir::db::BodyQuery).sweep(sweep);
279 }
280
281 pub(crate) fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
282 let mut acc: Vec<(String, Bytes)> = vec![];
283 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
284 macro_rules! sweep_each_query {
285 ($($q:path)*) => {$(
286 let before = memory_usage().allocated;
287 self.query($q).sweep(sweep);
288 let after = memory_usage().allocated;
289 let q: $q = Default::default();
290 let name = format!("{:?}", q);
291 acc.push((name, before - after));
292
293 let before = memory_usage().allocated;
294 self.query($q).sweep(sweep.discard_everything());
295 let after = memory_usage().allocated;
296 let q: $q = Default::default();
297 let name = format!("{:?} (deps)", q);
298 acc.push((name, before - after));
299 )*}
300 }
301 sweep_each_query![
302 ra_db::ParseQuery
303 ra_db::SourceRootCratesQuery
304 hir::db::AstIdMapQuery
305 hir::db::ParseMacroQuery
306 hir::db::MacroDefQuery
307 hir::db::MacroArgQuery
308 hir::db::MacroExpandQuery
309 hir::db::StructDataQuery
310 hir::db::EnumDataQuery
311 hir::db::TraitDataQuery
312 hir::db::RawItemsQuery
313 hir::db::ComputeCrateDefMapQuery
314 hir::db::GenericParamsQuery
315 hir::db::FunctionDataQuery
316 hir::db::TypeAliasDataQuery
317 hir::db::ConstDataQuery
318 hir::db::StaticDataQuery
319 hir::db::ModuleLangItemsQuery
320 hir::db::CrateLangItemsQuery
321 hir::db::LangItemQuery
322 hir::db::DocumentationQuery
323 hir::db::ExprScopesQuery
324 hir::db::DoInferQuery
325 hir::db::TyQuery
326 hir::db::ValueTyQuery
327 hir::db::FieldTypesQuery
328 hir::db::CallableItemSignatureQuery
329 hir::db::GenericPredicatesQuery
330 hir::db::GenericDefaultsQuery
331 hir::db::BodyWithSourceMapQuery
332 hir::db::BodyQuery
333 hir::db::ImplsInCrateQuery
334 hir::db::ImplsForTraitQuery
335 hir::db::AssociatedTyDataQuery
336 hir::db::TraitDatumQuery
337 hir::db::StructDatumQuery
338 hir::db::ImplDatumQuery
339 hir::db::ImplDataQuery
340 hir::db::TraitSolveQuery
341 ];
342 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
343 acc
344 }
345}
346
347fn durability(source_root: &SourceRoot) -> Durability {
348 if source_root.is_library {
349 Durability::HIGH
350 } else {
351 Durability::LOW
352 }
353}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index ad7f6ef26..4f24cd1f9 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -18,6 +18,7 @@ mod complete_macro_in_item_position;
18mod complete_trait_impl; 18mod complete_trait_impl;
19 19
20use ra_db::SourceDatabase; 20use ra_db::SourceDatabase;
21use ra_ide_db::RootDatabase;
21 22
22#[cfg(test)] 23#[cfg(test)]
23use crate::completion::completion_item::do_completion; 24use crate::completion::completion_item::do_completion;
@@ -26,7 +27,7 @@ use crate::{
26 completion_context::CompletionContext, 27 completion_context::CompletionContext,
27 completion_item::{CompletionKind, Completions}, 28 completion_item::{CompletionKind, Completions},
28 }, 29 },
29 db, FilePosition, 30 FilePosition,
30}; 31};
31 32
32pub use crate::completion::completion_item::{ 33pub use crate::completion::completion_item::{
@@ -55,7 +56,7 @@ pub use crate::completion::completion_item::{
55/// `foo` *should* be present among the completion variants. Filtering by 56/// `foo` *should* be present among the completion variants. Filtering by
56/// identifier prefix/fuzzy match should be done higher in the stack, together 57/// identifier prefix/fuzzy match should be done higher in the stack, together
57/// with ordering of completions (currently this is done by the client). 58/// with ordering of completions (currently this is done by the client).
58pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Option<Completions> { 59pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> {
59 let original_parse = db.parse(position.file_id); 60 let original_parse = db.parse(position.file_id);
60 let ctx = CompletionContext::new(db, &original_parse, position)?; 61 let ctx = CompletionContext::new(db, &original_parse, position)?;
61 62
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index 458d7525e..e2ee86dd1 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -1,11 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_assists::auto_import_text_edit; 3use crate::completion::{CompletionContext, Completions};
4use ra_syntax::{ast, AstNode, SmolStr};
5use ra_text_edit::TextEditBuilder;
6use rustc_hash::FxHashMap;
7
8use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
9 4
10pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
11 if !ctx.is_trivial_path { 6 if !ctx.is_trivial_path {
@@ -15,120 +10,14 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
15 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { 10 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
16 acc.add_resolution(ctx, name.to_string(), &res) 11 acc.add_resolution(ctx, name.to_string(), &res)
17 }); 12 });
18
19 // auto-import
20 // We fetch ident from the original file, because we need to pre-filter auto-imports
21 if ast::NameRef::cast(ctx.token.parent()).is_some() {
22 let import_resolver = ImportResolver::new();
23 let import_names = import_resolver.all_names(ctx.token.text());
24 import_names.into_iter().for_each(|(name, path)| {
25 let edit = {
26 let mut builder = TextEditBuilder::default();
27 builder.replace(ctx.source_range(), name.to_string());
28 auto_import_text_edit(
29 &ctx.token.parent(),
30 &ctx.token.parent(),
31 &path,
32 &mut builder,
33 );
34 builder.finish()
35 };
36
37 // Hack: copied this check form conv.rs beacause auto import can produce edits
38 // that invalidate assert in conv_with.
39 if edit
40 .as_atoms()
41 .iter()
42 .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
43 .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
44 {
45 CompletionItem::new(
46 CompletionKind::Reference,
47 ctx.source_range(),
48 build_import_label(&name, &path),
49 )
50 .text_edit(edit)
51 .add_to(acc);
52 }
53 });
54 }
55}
56
57fn build_import_label(name: &str, path: &[SmolStr]) -> String {
58 let mut buf = String::with_capacity(64);
59 buf.push_str(name);
60 buf.push_str(" (");
61 fmt_import_path(path, &mut buf);
62 buf.push_str(")");
63 buf
64}
65
66fn fmt_import_path(path: &[SmolStr], buf: &mut String) {
67 let mut segments = path.iter();
68 if let Some(s) = segments.next() {
69 buf.push_str(&s);
70 }
71 for s in segments {
72 buf.push_str("::");
73 buf.push_str(&s);
74 }
75}
76
77#[derive(Debug, Clone, Default)]
78pub(crate) struct ImportResolver {
79 // todo: use fst crate or something like that
80 dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
81}
82
83impl ImportResolver {
84 pub(crate) fn new() -> Self {
85 let dummy_names = vec![
86 (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
87 (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
88 (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
89 (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
90 (
91 SmolStr::new("Debug"),
92 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
93 ),
94 (
95 SmolStr::new("Display"),
96 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
97 ),
98 (
99 SmolStr::new("Hash"),
100 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
101 ),
102 (
103 SmolStr::new("Hasher"),
104 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
105 ),
106 (
107 SmolStr::new("Iterator"),
108 vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
109 ),
110 ];
111
112 ImportResolver { dummy_names }
113 }
114
115 // Returns a map of importable items filtered by name.
116 // The map associates item name with its full path.
117 // todo: should return Resolutions
118 pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
119 if name.len() > 1 {
120 self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
121 } else {
122 FxHashMap::default()
123 }
124 }
125} 13}
126 14
127#[cfg(test)] 15#[cfg(test)]
128mod tests { 16mod tests {
129 use crate::completion::{do_completion, CompletionItem, CompletionKind};
130 use insta::assert_debug_snapshot; 17 use insta::assert_debug_snapshot;
131 18
19 use crate::completion::{do_completion, CompletionItem, CompletionKind};
20
132 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 21 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
133 do_completion(code, CompletionKind::Reference) 22 do_completion(code, CompletionKind::Reference)
134 } 23 }
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 528655fbc..0175f5e55 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -2,7 +2,7 @@ use crate::completion::{CompletionContext, Completions, CompletionItem, Completi
2 2
3use ra_syntax::ast::{self, NameOwner, AstNode}; 3use ra_syntax::ast::{self, NameOwner, AstNode};
4 4
5use hir::{self, db::HirDatabase}; 5use hir::{self, db::HirDatabase, Docs};
6 6
7 7
8pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 8pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 18c91a840..8678a3234 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,
@@ -49,7 +50,7 @@ pub(crate) struct CompletionContext<'a> {
49 50
50impl<'a> CompletionContext<'a> { 51impl<'a> CompletionContext<'a> {
51 pub(super) fn new( 52 pub(super) fn new(
52 db: &'a db::RootDatabase, 53 db: &'a RootDatabase,
53 original_parse: &'a Parse<ast::SourceFile>, 54 original_parse: &'a Parse<ast::SourceFile>,
54 position: FilePosition, 55 position: FilePosition,
55 ) -> Option<CompletionContext<'a>> { 56 ) -> 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 b82259a3d..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) {
@@ -79,6 +80,14 @@ pub(crate) fn descend_into_macros(
79 let source_analyzer = 80 let source_analyzer =
80 hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None); 81 hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None);
81 82
83 descend_into_macros_with_analyzer(db, &source_analyzer, src)
84}
85
86pub(crate) fn descend_into_macros_with_analyzer(
87 db: &RootDatabase,
88 source_analyzer: &hir::SourceAnalyzer,
89 src: InFile<SyntaxToken>,
90) -> InFile<SyntaxToken> {
82 successors(Some(src), |token| { 91 successors(Some(src), |token| {
83 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 92 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
84 let tt = macro_call.token_tree()?; 93 let tt = macro_call.token_tree()?;
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..de5551a4c 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::*},
@@ -76,11 +76,10 @@ pub(crate) fn reference_definition(
76 let name_kind = classify_name_ref(sb, name_ref).map(|d| d.kind); 76 let name_kind = classify_name_ref(sb, name_ref).map(|d| d.kind);
77 match name_kind { 77 match name_kind {
78 Some(Macro(it)) => return Exact(it.to_nav(sb.db)), 78 Some(Macro(it)) => return Exact(it.to_nav(sb.db)),
79 Some(Field(it)) => return Exact(it.to_nav(sb.db)), 79 Some(StructField(it)) => return Exact(it.to_nav(sb.db)),
80 Some(TypeParam(it)) => return Exact(it.to_nav(sb.db)), 80 Some(TypeParam(it)) => return Exact(it.to_nav(sb.db)),
81 Some(AssocItem(it)) => return Exact(it.to_nav(sb.db)),
82 Some(Local(it)) => return Exact(it.to_nav(sb.db)), 81 Some(Local(it)) => return Exact(it.to_nav(sb.db)),
83 Some(Def(def)) => match NavigationTarget::from_def(sb.db, def) { 82 Some(ModuleDef(def)) => match NavigationTarget::from_def(sb.db, def) {
84 Some(nav) => return Exact(nav), 83 Some(nav) => return Exact(nav),
85 None => return Approximate(vec![]), 84 None => return Approximate(vec![]),
86 }, 85 },
@@ -94,7 +93,7 @@ pub(crate) fn reference_definition(
94 }; 93 };
95 94
96 // Fallback index based approach: 95 // Fallback index based approach:
97 let navs = crate::symbol_index::index_resolve(sb.db, name_ref.value) 96 let navs = symbol_index::index_resolve(sb.db, name_ref.value)
98 .into_iter() 97 .into_iter()
99 .map(|s| s.to_nav(sb.db)) 98 .map(|s| s.to_nav(sb.db))
100 .collect(); 99 .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..3f88bb260 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::*},
@@ -98,19 +98,14 @@ fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option<S
98 let src = it.source(db); 98 let src = it.source(db);
99 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value))) 99 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)))
100 } 100 }
101 Field(it) => { 101 StructField(it) => {
102 let src = it.source(db); 102 let src = it.source(db);
103 match src.value { 103 match src.value {
104 hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()), 104 hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()),
105 _ => None, 105 _ => None,
106 } 106 }
107 } 107 }
108 AssocItem(it) => match it { 108 ModuleDef(it) => match it {
109 hir::AssocItem::Function(it) => from_def_source(db, it),
110 hir::AssocItem::Const(it) => from_def_source(db, it),
111 hir::AssocItem::TypeAlias(it) => from_def_source(db, it),
112 },
113 Def(it) => match it {
114 hir::ModuleDef::Module(it) => match it.definition_source(db).value { 109 hir::ModuleDef::Module(it) => match it.definition_source(db).value {
115 hir::ModuleSource::Module(it) => { 110 hir::ModuleSource::Module(it) => {
116 hover_text(it.doc_comment_text(), it.short_label()) 111 hover_text(it.doc_comment_text(), it.short_label())
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/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 393ca9447..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 {
@@ -376,7 +377,7 @@ fn main() {
376 let mut start = 0; 377 let mut start = 0;
377 (0..2).for_each(|increment| { 378 (0..2).for_each(|increment| {
378 start += increment; 379 start += increment;
379 }) 380 });
380 381
381 let multiply = |a, b, c, d| a * b * c * d; 382 let multiply = |a, b, c, d| a * b * c * d;
382 let _: i32 = multiply(1, 2, 3, 4); 383 let _: i32 = multiply(1, 2, 3, 4);
@@ -399,37 +400,37 @@ fn main() {
399 label: "i32", 400 label: "i32",
400 }, 401 },
401 InlayHint { 402 InlayHint {
402 range: [114; 122), 403 range: [115; 123),
403 kind: TypeHint, 404 kind: TypeHint,
404 label: "|…| -> i32", 405 label: "|…| -> i32",
405 }, 406 },
406 InlayHint { 407 InlayHint {
407 range: [126; 127), 408 range: [127; 128),
408 kind: TypeHint, 409 kind: TypeHint,
409 label: "i32", 410 label: "i32",
410 }, 411 },
411 InlayHint { 412 InlayHint {
412 range: [129; 130), 413 range: [130; 131),
413 kind: TypeHint, 414 kind: TypeHint,
414 label: "i32", 415 label: "i32",
415 }, 416 },
416 InlayHint { 417 InlayHint {
417 range: [132; 133), 418 range: [133; 134),
418 kind: TypeHint, 419 kind: TypeHint,
419 label: "i32", 420 label: "i32",
420 }, 421 },
421 InlayHint { 422 InlayHint {
422 range: [135; 136), 423 range: [136; 137),
423 kind: TypeHint, 424 kind: TypeHint,
424 label: "i32", 425 label: "i32",
425 }, 426 },
426 InlayHint { 427 InlayHint {
427 range: [200; 212), 428 range: [201; 213),
428 kind: TypeHint, 429 kind: TypeHint,
429 label: "&|…| -> i32", 430 label: "&|…| -> i32",
430 }, 431 },
431 InlayHint { 432 InlayHint {
432 range: [235; 244), 433 range: [236; 245),
433 kind: TypeHint, 434 kind: TypeHint,
434 label: "|| -> i32", 435 label: "|| -> i32",
435 }, 436 },
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 4d8deb21c..689921f3f 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;
@@ -34,14 +30,11 @@ mod assists;
34mod diagnostics; 30mod diagnostics;
35mod syntax_tree; 31mod syntax_tree;
36mod folding_ranges; 32mod folding_ranges;
37mod line_index;
38mod line_index_utils;
39mod join_lines; 33mod join_lines;
40mod typing; 34mod typing;
41mod matching_brace; 35mod matching_brace;
42mod display; 36mod display;
43mod inlay_hints; 37mod inlay_hints;
44mod wasm_shims;
45mod expand; 38mod expand;
46mod expand_macro; 39mod expand_macro;
47 40
@@ -57,24 +50,24 @@ use ra_db::{
57 salsa::{self, ParallelDatabase}, 50 salsa::{self, ParallelDatabase},
58 CheckCanceled, Env, FileLoader, SourceDatabase, 51 CheckCanceled, Env, FileLoader, SourceDatabase,
59}; 52};
53use ra_ide_db::{
54 symbol_index::{self, FileSymbol},
55 LineIndexDatabase,
56};
60use ra_syntax::{SourceFile, TextRange, TextUnit}; 57use ra_syntax::{SourceFile, TextRange, TextUnit};
61 58
62use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol}; 59use crate::display::ToNav;
63 60
64pub use crate::{ 61pub use crate::{
65 assists::{Assist, AssistId}, 62 assists::{Assist, AssistId},
66 call_hierarchy::CallItem, 63 call_hierarchy::CallItem,
67 change::{AnalysisChange, LibraryData},
68 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 64 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
69 diagnostics::Severity, 65 diagnostics::Severity,
70 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
71 expand_macro::ExpandedMacro, 67 expand_macro::ExpandedMacro,
72 feature_flags::FeatureFlags,
73 folding_ranges::{Fold, FoldKind}, 68 folding_ranges::{Fold, FoldKind},
74 hover::HoverResult, 69 hover::HoverResult,
75 inlay_hints::{InlayHint, InlayKind}, 70 inlay_hints::{InlayHint, InlayKind},
76 line_index::{LineCol, LineIndex},
77 line_index_utils::translate_offset_with_edit,
78 references::{ 71 references::{
79 Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, 72 Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope,
80 }, 73 },
@@ -87,6 +80,14 @@ pub use hir::Documentation;
87pub use ra_db::{ 80pub use ra_db::{
88 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 81 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
89}; 82};
83pub use ra_ide_db::{
84 change::{AnalysisChange, LibraryData},
85 feature_flags::FeatureFlags,
86 line_index::{LineCol, LineIndex},
87 line_index_utils::translate_offset_with_edit,
88 symbol_index::Query,
89 RootDatabase,
90};
90 91
91pub type Cancelable<T> = Result<T, Canceled>; 92pub type Cancelable<T> = Result<T, Canceled>;
92 93
@@ -98,46 +99,6 @@ pub struct Diagnostic {
98 pub severity: Severity, 99 pub severity: Severity,
99} 100}
100 101
101#[derive(Debug)]
102pub struct Query {
103 query: String,
104 lowercased: String,
105 only_types: bool,
106 libs: bool,
107 exact: bool,
108 limit: usize,
109}
110
111impl Query {
112 pub fn new(query: String) -> Query {
113 let lowercased = query.to_lowercase();
114 Query {
115 query,
116 lowercased,
117 only_types: false,
118 libs: false,
119 exact: false,
120 limit: usize::max_value(),
121 }
122 }
123
124 pub fn only_types(&mut self) {
125 self.only_types = true;
126 }
127
128 pub fn libs(&mut self) {
129 self.libs = true;
130 }
131
132 pub fn exact(&mut self) {
133 self.exact = true;
134 }
135
136 pub fn limit(&mut self, limit: usize) {
137 self.limit = limit
138 }
139}
140
141/// Info associated with a text range. 102/// Info associated with a text range.
142#[derive(Debug)] 103#[derive(Debug)]
143pub struct RangeInfo<T> { 104pub struct RangeInfo<T> {
@@ -162,7 +123,7 @@ pub struct CallInfo {
162/// `AnalysisHost` stores the current state of the world. 123/// `AnalysisHost` stores the current state of the world.
163#[derive(Debug)] 124#[derive(Debug)]
164pub struct AnalysisHost { 125pub struct AnalysisHost {
165 db: db::RootDatabase, 126 db: RootDatabase,
166} 127}
167 128
168impl Default for AnalysisHost { 129impl Default for AnalysisHost {
@@ -173,7 +134,7 @@ impl Default for AnalysisHost {
173 134
174impl AnalysisHost { 135impl AnalysisHost {
175 pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost { 136 pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost {
176 AnalysisHost { db: db::RootDatabase::new(lru_capcity, feature_flags) } 137 AnalysisHost { db: RootDatabase::new(lru_capcity, feature_flags) }
177 } 138 }
178 /// Returns a snapshot of the current state, which you can query for 139 /// Returns a snapshot of the current state, which you can query for
179 /// semantic information. 140 /// semantic information.
@@ -202,6 +163,9 @@ impl AnalysisHost {
202 pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { 163 pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> {
203 self.db.per_query_memory_usage() 164 self.db.per_query_memory_usage()
204 } 165 }
166 pub fn request_cancellation(&mut self) {
167 self.db.request_cancellation();
168 }
205 pub fn raw_database( 169 pub fn raw_database(
206 &self, 170 &self,
207 ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { 171 ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) {
@@ -220,7 +184,7 @@ impl AnalysisHost {
220/// `Analysis` are canceled (most method return `Err(Canceled)`). 184/// `Analysis` are canceled (most method return `Err(Canceled)`).
221#[derive(Debug)] 185#[derive(Debug)]
222pub struct Analysis { 186pub struct Analysis {
223 db: salsa::Snapshot<db::RootDatabase>, 187 db: salsa::Snapshot<RootDatabase>,
224} 188}
225 189
226// As a general design guideline, `Analysis` API are intended to be independent 190// As a general design guideline, `Analysis` API are intended to be independent
@@ -501,7 +465,7 @@ impl Analysis {
501 } 465 }
502 466
503 /// Performs an operation on that may be Canceled. 467 /// Performs an operation on that may be Canceled.
504 fn with_db<F: FnOnce(&db::RootDatabase) -> T + std::panic::UnwindSafe, T>( 468 fn with_db<F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, T>(
505 &self, 469 &self,
506 f: F, 470 f: F,
507 ) -> Cancelable<T> { 471 ) -> Cancelable<T> {
@@ -514,3 +478,77 @@ fn analysis_is_send() {
514 fn is_send<T: Send>() {} 478 fn is_send<T: Send>() {}
515 is_send::<Analysis>(); 479 is_send::<Analysis>();
516} 480}
481
482#[cfg(test)]
483mod tests {
484 use crate::{display::NavigationTarget, mock_analysis::single_file, Query};
485 use ra_syntax::{
486 SmolStr,
487 SyntaxKind::{FN_DEF, STRUCT_DEF},
488 };
489
490 #[test]
491 fn test_world_symbols_with_no_container() {
492 let code = r#"
493 enum FooInner { }
494 "#;
495
496 let mut symbols = get_symbols_matching(code, "FooInner");
497
498 let s = symbols.pop().unwrap();
499
500 assert_eq!(s.name(), "FooInner");
501 assert!(s.container_name().is_none());
502 }
503
504 #[test]
505 fn test_world_symbols_include_container_name() {
506 let code = r#"
507fn foo() {
508 enum FooInner { }
509}
510 "#;
511
512 let mut symbols = get_symbols_matching(code, "FooInner");
513
514 let s = symbols.pop().unwrap();
515
516 assert_eq!(s.name(), "FooInner");
517 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
518
519 let code = r#"
520mod foo {
521 struct FooInner;
522}
523 "#;
524
525 let mut symbols = get_symbols_matching(code, "FooInner");
526
527 let s = symbols.pop().unwrap();
528
529 assert_eq!(s.name(), "FooInner");
530 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
531 }
532
533 #[test]
534 fn test_world_symbols_are_case_sensitive() {
535 let code = r#"
536fn foo() {}
537
538struct Foo;
539 "#;
540
541 let symbols = get_symbols_matching(code, "Foo");
542
543 let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind());
544 let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind());
545
546 assert_eq!(fn_match, Some(FN_DEF));
547 assert_eq!(struct_match, Some(STRUCT_DEF));
548 }
549
550 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
551 let (analysis, _) = single_file(text);
552 analysis.symbol_search(Query::new(query.into())).unwrap()
553 }
554}
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/marks.rs b/crates/ra_ide/src/marks.rs
index 077a44473..5bf4d2062 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -11,4 +11,5 @@ test_utils::marks!(
11 call_info_bad_offset 11 call_info_bad_offset
12 dont_complete_current_use 12 dont_complete_current_use
13 dont_complete_primitive_in_use 13 dont_complete_primitive_in_use
14 test_resolve_parent_module_on_module_decl
14); 15);
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index bf8a54932..081aaee8c 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -3,7 +3,7 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use ra_cfg::CfgOptions; 5use ra_cfg::CfgOptions;
6use ra_db::{Env, RelativePathBuf}; 6use ra_db::{CrateName, Env, RelativePathBuf};
7use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; 7use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
8 8
9use crate::{ 9use crate::{
@@ -107,7 +107,9 @@ impl MockAnalysis {
107 crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); 107 crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default());
108 let crate_name = path.parent().unwrap().file_name().unwrap(); 108 let crate_name = path.parent().unwrap().file_name().unwrap();
109 if let Some(root_crate) = root_crate { 109 if let Some(root_crate) = root_crate {
110 crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); 110 crate_graph
111 .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate)
112 .unwrap();
111 } 113 }
112 } 114 }
113 change.add_file(source_root, file_id, path, Arc::new(contents)); 115 change.add_file(source_root, file_id, path, Arc::new(contents));
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index 2dbccfc3b..af14d6ab3 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -1,19 +1,35 @@
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};
9use test_utils::tested_by;
8 10
9use crate::{db::RootDatabase, NavigationTarget}; 11use crate::NavigationTarget;
10 12
11/// This returns `Vec` because a module may be included from several places. We 13/// 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. 14/// don't handle this case yet though, so the Vec has length at most one.
13pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 15pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
14 let mut sb = hir::SourceBinder::new(db); 16 let mut sb = hir::SourceBinder::new(db);
15 let parse = db.parse(position.file_id); 17 let parse = db.parse(position.file_id);
16 let module = match find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) { 18
19 let mut module = find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset);
20
21 // If cursor is literally on `mod foo`, go to the grandpa.
22 if let Some(m) = &module {
23 if !m
24 .item_list()
25 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset))
26 {
27 tested_by!(test_resolve_parent_module_on_module_decl);
28 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast);
29 }
30 }
31
32 let module = match module {
17 Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)), 33 Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)),
18 None => sb.to_module_def(position.file_id), 34 None => sb.to_module_def(position.file_id),
19 }; 35 };
@@ -40,6 +56,7 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
40mod tests { 56mod tests {
41 use ra_cfg::CfgOptions; 57 use ra_cfg::CfgOptions;
42 use ra_db::Env; 58 use ra_db::Env;
59 use test_utils::covers;
43 60
44 use crate::{ 61 use crate::{
45 mock_analysis::{analysis_and_position, MockAnalysis}, 62 mock_analysis::{analysis_and_position, MockAnalysis},
@@ -62,6 +79,25 @@ mod tests {
62 } 79 }
63 80
64 #[test] 81 #[test]
82 fn test_resolve_parent_module_on_module_decl() {
83 covers!(test_resolve_parent_module_on_module_decl);
84 let (analysis, pos) = analysis_and_position(
85 "
86 //- /lib.rs
87 mod foo;
88
89 //- /foo.rs
90 mod <|>bar;
91
92 //- /foo/bar.rs
93 // empty
94 ",
95 );
96 let nav = analysis.parent_module(pos).unwrap().pop().unwrap();
97 nav.assert_match("foo MODULE FileId(1) [0; 8)");
98 }
99
100 #[test]
65 fn test_resolve_parent_module_for_inline() { 101 fn test_resolve_parent_module_for_inline() {
66 let (analysis, pos) = analysis_and_position( 102 let (analysis, pos) = analysis_and_position(
67 " 103 "
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 5e2fe1905..a6320bd2f 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -10,13 +10,13 @@
10//! resolved to the search element definition, we get a reference. 10//! resolved to the search element definition, we get a reference.
11 11
12mod classify; 12mod classify;
13mod name_definition;
14mod rename; 13mod rename;
15mod search_scope; 14mod search_scope;
16 15
17use hir::{InFile, SourceBinder}; 16use hir::{InFile, SourceBinder};
18use once_cell::unsync::Lazy; 17use once_cell::unsync::Lazy;
19use ra_db::{SourceDatabase, SourceDatabaseExt}; 18use ra_db::{SourceDatabase, SourceDatabaseExt};
19use ra_ide_db::RootDatabase;
20use ra_prof::profile; 20use ra_prof::profile;
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
@@ -24,15 +24,13 @@ use ra_syntax::{
24 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, 24 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
25}; 25};
26 26
27use crate::{ 27use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
28 db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
29};
30 28
31pub(crate) use self::{ 29pub(crate) use self::{
32 classify::{classify_name, classify_name_ref}, 30 classify::{classify_name, classify_name_ref},
33 name_definition::{NameDefinition, NameKind},
34 rename::rename, 31 rename::rename,
35}; 32};
33pub(crate) use ra_ide_db::defs::{NameDefinition, NameKind};
36 34
37pub use self::search_scope::SearchScope; 35pub use self::search_scope::SearchScope;
38 36
@@ -112,38 +110,32 @@ impl IntoIterator for ReferenceSearchResult {
112 110
113pub(crate) fn find_all_refs( 111pub(crate) fn find_all_refs(
114 db: &RootDatabase, 112 db: &RootDatabase,
115 mut position: FilePosition, 113 position: FilePosition,
116 search_scope: Option<SearchScope>, 114 search_scope: Option<SearchScope>,
117) -> Option<RangeInfo<ReferenceSearchResult>> { 115) -> Option<RangeInfo<ReferenceSearchResult>> {
118 let parse = db.parse(position.file_id); 116 let parse = db.parse(position.file_id);
119 let syntax = parse.tree().syntax().clone(); 117 let syntax = parse.tree().syntax().clone();
120 118
121 let token = syntax.token_at_offset(position.offset); 119 let (opt_name, search_kind) =
122 let mut search_kind = ReferenceKind::Other; 120 if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) {
121 (Some(name), ReferenceKind::StructLiteral)
122 } else {
123 (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other)
124 };
123 125
124 if let TokenAtOffset::Between(ref left, ref right) = token { 126 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
125 if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN)
126 && left.kind() != SyntaxKind::IDENT
127 {
128 position = FilePosition { offset: left.text_range().start(), ..position };
129 search_kind = ReferenceKind::StructLiteral;
130 }
131 }
132
133 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
134 127
135 let declaration = match def.kind { 128 let declaration = match def.kind {
136 NameKind::Macro(mac) => mac.to_nav(db), 129 NameKind::Macro(mac) => mac.to_nav(db),
137 NameKind::Field(field) => field.to_nav(db), 130 NameKind::StructField(field) => field.to_nav(db),
138 NameKind::AssocItem(assoc) => assoc.to_nav(db), 131 NameKind::ModuleDef(def) => NavigationTarget::from_def(db, def)?,
139 NameKind::Def(def) => NavigationTarget::from_def(db, def)?,
140 NameKind::SelfType(imp) => imp.to_nav(db), 132 NameKind::SelfType(imp) => imp.to_nav(db),
141 NameKind::Local(local) => local.to_nav(db), 133 NameKind::Local(local) => local.to_nav(db),
142 NameKind::TypeParam(_) => return None, 134 NameKind::TypeParam(_) => return None,
143 }; 135 };
144 136
145 let search_scope = { 137 let search_scope = {
146 let base = def.search_scope(db); 138 let base = SearchScope::for_def(&def, db);
147 match search_scope { 139 match search_scope {
148 None => base, 140 None => base,
149 Some(scope) => base.intersection(&scope), 141 Some(scope) => base.intersection(&scope),
@@ -170,9 +162,10 @@ fn find_name(
170 db: &RootDatabase, 162 db: &RootDatabase,
171 syntax: &SyntaxNode, 163 syntax: &SyntaxNode,
172 position: FilePosition, 164 position: FilePosition,
165 opt_name: Option<ast::Name>,
173) -> Option<RangeInfo<(String, NameDefinition)>> { 166) -> Option<RangeInfo<(String, NameDefinition)>> {
174 let mut sb = SourceBinder::new(db); 167 let mut sb = SourceBinder::new(db);
175 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { 168 if let Some(name) = opt_name {
176 let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; 169 let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?;
177 let range = name.syntax().text_range(); 170 let range = name.syntax().text_range();
178 return Some(RangeInfo::new(range, (name.text().to_string(), def))); 171 return Some(RangeInfo::new(range, (name.text().to_string(), def)));
@@ -218,15 +211,8 @@ fn process_definition(
218 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) 211 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref))
219 { 212 {
220 if d == def { 213 if d == def {
221 let kind = if name_ref 214 let kind = if is_record_lit_name_ref(&name_ref)
222 .syntax() 215 || is_call_expr_name_ref(&name_ref)
223 .ancestors()
224 .find_map(ast::RecordLit::cast)
225 .and_then(|l| l.path())
226 .and_then(|p| p.segment())
227 .and_then(|p| p.name_ref())
228 .map(|n| n == name_ref)
229 .unwrap_or(false)
230 { 216 {
231 ReferenceKind::StructLiteral 217 ReferenceKind::StructLiteral
232 } else { 218 } else {
@@ -253,7 +239,7 @@ fn decl_access(
253 range: TextRange, 239 range: TextRange,
254) -> Option<ReferenceAccess> { 240) -> Option<ReferenceAccess> {
255 match kind { 241 match kind {
256 NameKind::Local(_) | NameKind::Field(_) => {} 242 NameKind::Local(_) | NameKind::StructField(_) => {}
257 _ => return None, 243 _ => return None,
258 }; 244 };
259 245
@@ -273,7 +259,7 @@ fn decl_access(
273fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { 259fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
274 // Only Locals and Fields have accesses for now. 260 // Only Locals and Fields have accesses for now.
275 match kind { 261 match kind {
276 NameKind::Local(_) | NameKind::Field(_) => {} 262 NameKind::Local(_) | NameKind::StructField(_) => {}
277 _ => return None, 263 _ => return None,
278 }; 264 };
279 265
@@ -301,6 +287,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc
301 mode.or(Some(ReferenceAccess::Read)) 287 mode.or(Some(ReferenceAccess::Read))
302} 288}
303 289
290fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
291 name_ref
292 .syntax()
293 .ancestors()
294 .find_map(ast::RecordLit::cast)
295 .and_then(|l| l.path())
296 .and_then(|p| p.segment())
297 .map(|p| p.name_ref().as_ref() == Some(name_ref))
298 .unwrap_or(false)
299}
300
301fn get_struct_def_name_for_struc_litetal_search(
302 syntax: &SyntaxNode,
303 position: FilePosition,
304) -> Option<ast::Name> {
305 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
306 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN {
307 return None;
308 }
309 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) {
310 return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
311 }
312 if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() {
313 return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
314 }
315 }
316 None
317}
318
319fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
320 name_ref
321 .syntax()
322 .ancestors()
323 .find_map(ast::CallExpr::cast)
324 .and_then(|c| match c.expr()? {
325 ast::Expr::PathExpr(p) => {
326 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
327 }
328 _ => None,
329 })
330 .unwrap_or(false)
331}
332
304#[cfg(test)] 333#[cfg(test)]
305mod tests { 334mod tests {
306 use crate::{ 335 use crate::{
@@ -309,7 +338,7 @@ mod tests {
309 }; 338 };
310 339
311 #[test] 340 #[test]
312 fn test_struct_literal() { 341 fn test_struct_literal_after_space() {
313 let code = r#" 342 let code = r#"
314 struct Foo <|>{ 343 struct Foo <|>{
315 a: i32, 344 a: i32,
@@ -331,6 +360,58 @@ mod tests {
331 } 360 }
332 361
333 #[test] 362 #[test]
363 fn test_struct_literal_befor_space() {
364 let code = r#"
365 struct Foo<|> {}
366 fn main() {
367 let f: Foo;
368 f = Foo {};
369 }"#;
370
371 let refs = get_all_refs(code);
372 check_result(
373 refs,
374 "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other",
375 &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"],
376 );
377 }
378
379 #[test]
380 fn test_struct_literal_with_generic_type() {
381 let code = r#"
382 struct Foo<T> <|>{}
383 fn main() {
384 let f: Foo::<i32>;
385 f = Foo {};
386 }"#;
387
388 let refs = get_all_refs(code);
389 check_result(
390 refs,
391 "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
392 &["FileId(1) [81; 84) StructLiteral"],
393 );
394 }
395
396 #[test]
397 fn test_struct_literal_for_tuple() {
398 let code = r#"
399 struct Foo<|>(i32);
400
401 fn main() {
402 let f: Foo;
403 f = Foo(1);
404 }"#;
405
406 let refs = get_all_refs(code);
407 check_result(
408 refs,
409 "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
410 &["FileId(1) [71; 74) StructLiteral"],
411 );
412 }
413
414 #[test]
334 fn test_find_all_refs_for_local() { 415 fn test_find_all_refs_for_local() {
335 let code = r#" 416 let code = r#"
336 fn main() { 417 fn main() {
@@ -564,7 +645,7 @@ mod tests {
564 check_result( 645 check_result(
565 refs, 646 refs,
566 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", 647 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
567 &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], 648 &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"],
568 ); 649 );
569 650
570 let refs = 651 let refs =
@@ -572,7 +653,7 @@ mod tests {
572 check_result( 653 check_result(
573 refs, 654 refs,
574 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", 655 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
575 &["FileId(3) [16; 20) Other"], 656 &["FileId(3) [16; 20) StructLiteral"],
576 ); 657 );
577 } 658 }
578 659
@@ -591,7 +672,7 @@ mod tests {
591 check_result( 672 check_result(
592 refs, 673 refs,
593 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", 674 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other",
594 &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], 675 &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"],
595 ); 676 );
596 } 677 }
597 678
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs
index 46cba30a3..d0f03d8a8 100644
--- a/crates/ra_ide/src/references/classify.rs
+++ b/crates/ra_ide/src/references/classify.rs
@@ -2,119 +2,13 @@
2 2
3use hir::{InFile, PathResolution, SourceBinder}; 3use hir::{InFile, PathResolution, SourceBinder};
4use ra_prof::profile; 4use ra_prof::profile;
5use ra_syntax::{ast, match_ast, AstNode}; 5use ra_syntax::{ast, AstNode};
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use super::{ 8use super::{NameDefinition, NameKind};
9 name_definition::{from_assoc_item, from_module_def, from_struct_field}, 9use ra_ide_db::RootDatabase;
10 NameDefinition, NameKind,
11};
12use crate::db::RootDatabase;
13 10
14pub(crate) fn classify_name( 11pub use ra_ide_db::defs::{classify_name, from_module_def, from_struct_field};
15 sb: &mut SourceBinder<RootDatabase>,
16 name: InFile<&ast::Name>,
17) -> Option<NameDefinition> {
18 let _p = profile("classify_name");
19 let parent = name.value.syntax().parent()?;
20
21 match_ast! {
22 match parent {
23 ast::BindPat(it) => {
24 let src = name.with_value(it);
25 let local = sb.to_def(src)?;
26 Some(NameDefinition {
27 visibility: None,
28 container: local.module(sb.db),
29 kind: NameKind::Local(local),
30 })
31 },
32 ast::RecordFieldDef(it) => {
33 let src = name.with_value(it);
34 let field: hir::StructField = sb.to_def(src)?;
35 Some(from_struct_field(sb.db, field))
36 },
37 ast::Module(it) => {
38 let def = sb.to_def(name.with_value(it))?;
39 Some(from_module_def(sb.db, def.into(), None))
40 },
41 ast::StructDef(it) => {
42 let src = name.with_value(it);
43 let def: hir::Struct = sb.to_def(src)?;
44 Some(from_module_def(sb.db, def.into(), None))
45 },
46 ast::EnumDef(it) => {
47 let src = name.with_value(it);
48 let def: hir::Enum = sb.to_def(src)?;
49 Some(from_module_def(sb.db, def.into(), None))
50 },
51 ast::TraitDef(it) => {
52 let src = name.with_value(it);
53 let def: hir::Trait = sb.to_def(src)?;
54 Some(from_module_def(sb.db, def.into(), None))
55 },
56 ast::StaticDef(it) => {
57 let src = name.with_value(it);
58 let def: hir::Static = sb.to_def(src)?;
59 Some(from_module_def(sb.db, def.into(), None))
60 },
61 ast::EnumVariant(it) => {
62 let src = name.with_value(it);
63 let def: hir::EnumVariant = sb.to_def(src)?;
64 Some(from_module_def(sb.db, def.into(), None))
65 },
66 ast::FnDef(it) => {
67 let src = name.with_value(it);
68 let def: hir::Function = sb.to_def(src)?;
69 if parent.parent().and_then(ast::ItemList::cast).is_some() {
70 Some(from_assoc_item(sb.db, def.into()))
71 } else {
72 Some(from_module_def(sb.db, def.into(), None))
73 }
74 },
75 ast::ConstDef(it) => {
76 let src = name.with_value(it);
77 let def: hir::Const = sb.to_def(src)?;
78 if parent.parent().and_then(ast::ItemList::cast).is_some() {
79 Some(from_assoc_item(sb.db, def.into()))
80 } else {
81 Some(from_module_def(sb.db, def.into(), None))
82 }
83 },
84 ast::TypeAliasDef(it) => {
85 let src = name.with_value(it);
86 let def: hir::TypeAlias = sb.to_def(src)?;
87 if parent.parent().and_then(ast::ItemList::cast).is_some() {
88 Some(from_assoc_item(sb.db, def.into()))
89 } else {
90 Some(from_module_def(sb.db, def.into(), None))
91 }
92 },
93 ast::MacroCall(it) => {
94 let src = name.with_value(it);
95 let def = sb.to_def(src.clone())?;
96
97 let module = sb.to_module_def(src.file_id.original_file(sb.db))?;
98
99 Some(NameDefinition {
100 visibility: None,
101 container: module,
102 kind: NameKind::Macro(def),
103 })
104 },
105 ast::TypeParam(it) => {
106 let src = name.with_value(it);
107 let def = sb.to_def(src)?;
108 Some(NameDefinition {
109 visibility: None,
110 container: def.module(sb.db),
111 kind: NameKind::TypeParam(def),
112 })
113 },
114 _ => None,
115 }
116 }
117}
118 12
119pub(crate) fn classify_name_ref( 13pub(crate) fn classify_name_ref(
120 sb: &mut SourceBinder<RootDatabase>, 14 sb: &mut SourceBinder<RootDatabase>,
@@ -128,7 +22,7 @@ pub(crate) fn classify_name_ref(
128 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { 22 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
129 tested_by!(goto_def_for_methods); 23 tested_by!(goto_def_for_methods);
130 if let Some(func) = analyzer.resolve_method_call(&method_call) { 24 if let Some(func) = analyzer.resolve_method_call(&method_call) {
131 return Some(from_assoc_item(sb.db, func.into())); 25 return Some(from_module_def(sb.db, func.into(), None));
132 } 26 }
133 } 27 }
134 28
@@ -163,27 +57,35 @@ pub(crate) fn classify_name_ref(
163 57
164 let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?; 58 let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?;
165 let resolved = analyzer.resolve_path(sb.db, &path)?; 59 let resolved = analyzer.resolve_path(sb.db, &path)?;
166 match resolved { 60 let res = match resolved {
167 PathResolution::Def(def) => Some(from_module_def(sb.db, def, Some(container))), 61 PathResolution::Def(def) => from_module_def(sb.db, def, Some(container)),
168 PathResolution::AssocItem(item) => Some(from_assoc_item(sb.db, item)), 62 PathResolution::AssocItem(item) => {
63 let def = match item {
64 hir::AssocItem::Function(it) => it.into(),
65 hir::AssocItem::Const(it) => it.into(),
66 hir::AssocItem::TypeAlias(it) => it.into(),
67 };
68 from_module_def(sb.db, def, Some(container))
69 }
169 PathResolution::Local(local) => { 70 PathResolution::Local(local) => {
170 let kind = NameKind::Local(local); 71 let kind = NameKind::Local(local);
171 let container = local.module(sb.db); 72 let container = local.module(sb.db);
172 Some(NameDefinition { kind, container, visibility: None }) 73 NameDefinition { kind, container, visibility: None }
173 } 74 }
174 PathResolution::TypeParam(par) => { 75 PathResolution::TypeParam(par) => {
175 let kind = NameKind::TypeParam(par); 76 let kind = NameKind::TypeParam(par);
176 let container = par.module(sb.db); 77 let container = par.module(sb.db);
177 Some(NameDefinition { kind, container, visibility }) 78 NameDefinition { kind, container, visibility }
178 } 79 }
179 PathResolution::Macro(def) => { 80 PathResolution::Macro(def) => {
180 let kind = NameKind::Macro(def); 81 let kind = NameKind::Macro(def);
181 Some(NameDefinition { kind, container, visibility }) 82 NameDefinition { kind, container, visibility }
182 } 83 }
183 PathResolution::SelfType(impl_block) => { 84 PathResolution::SelfType(impl_block) => {
184 let kind = NameKind::SelfType(impl_block); 85 let kind = NameKind::SelfType(impl_block);
185 let container = impl_block.module(sb.db); 86 let container = impl_block.module(sb.db);
186 Some(NameDefinition { kind, container, visibility }) 87 NameDefinition { kind, container, visibility }
187 } 88 }
188 } 89 };
90 Some(res)
189} 91}
diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs
deleted file mode 100644
index 1e4226ab9..000000000
--- a/crates/ra_ide/src/references/name_definition.rs
+++ /dev/null
@@ -1,85 +0,0 @@
1//! `NameDefinition` keeps information about the element we want to search references for.
2//! The element is represented by `NameKind`. It's located inside some `container` and
3//! has a `visibility`, which defines a search scope.
4//! Note that the reference search is possible for not all of the classified items.
5
6use hir::{
7 Adt, AssocItem, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, StructField,
8 TypeParam, VariantDef,
9};
10use ra_syntax::{ast, ast::VisibilityOwner};
11
12use crate::db::RootDatabase;
13
14#[derive(Debug, PartialEq, Eq)]
15pub enum NameKind {
16 Macro(MacroDef),
17 Field(StructField),
18 AssocItem(AssocItem),
19 Def(ModuleDef),
20 SelfType(ImplBlock),
21 Local(Local),
22 TypeParam(TypeParam),
23}
24
25#[derive(PartialEq, Eq)]
26pub(crate) struct NameDefinition {
27 pub visibility: Option<ast::Visibility>,
28 /// FIXME: this doesn't really make sense. For example, builtin types don't
29 /// really have a module.
30 pub container: Module,
31 pub kind: NameKind,
32}
33
34pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition {
35 let container = item.module(db);
36 let visibility = match item {
37 AssocItem::Function(f) => f.source(db).value.visibility(),
38 AssocItem::Const(c) => c.source(db).value.visibility(),
39 AssocItem::TypeAlias(a) => a.source(db).value.visibility(),
40 };
41 let kind = NameKind::AssocItem(item);
42 NameDefinition { kind, container, visibility }
43}
44
45pub(super) fn from_struct_field(db: &RootDatabase, field: StructField) -> NameDefinition {
46 let kind = NameKind::Field(field);
47 let parent = field.parent_def(db);
48 let container = parent.module(db);
49 let visibility = match parent {
50 VariantDef::Struct(s) => s.source(db).value.visibility(),
51 VariantDef::Union(e) => e.source(db).value.visibility(),
52 VariantDef::EnumVariant(e) => e.source(db).value.parent_enum().visibility(),
53 };
54 NameDefinition { kind, container, visibility }
55}
56
57pub(super) fn from_module_def(
58 db: &RootDatabase,
59 def: ModuleDef,
60 module: Option<Module>,
61) -> NameDefinition {
62 let kind = NameKind::Def(def);
63 let (container, visibility) = match def {
64 ModuleDef::Module(it) => {
65 let container = it.parent(db).or_else(|| Some(it)).unwrap();
66 let visibility = it.declaration_source(db).and_then(|s| s.value.visibility());
67 (container, visibility)
68 }
69 ModuleDef::EnumVariant(it) => {
70 let container = it.module(db);
71 let visibility = it.source(db).value.parent_enum().visibility();
72 (container, visibility)
73 }
74 ModuleDef::Function(it) => (it.module(db), it.source(db).value.visibility()),
75 ModuleDef::Const(it) => (it.module(db), it.source(db).value.visibility()),
76 ModuleDef::Static(it) => (it.module(db), it.source(db).value.visibility()),
77 ModuleDef::Trait(it) => (it.module(db), it.source(db).value.visibility()),
78 ModuleDef::TypeAlias(it) => (it.module(db), it.source(db).value.visibility()),
79 ModuleDef::Adt(Adt::Struct(it)) => (it.module(db), it.source(db).value.visibility()),
80 ModuleDef::Adt(Adt::Union(it)) => (it.module(db), it.source(db).value.visibility()),
81 ModuleDef::Adt(Adt::Enum(it)) => (it.module(db), it.source(db).value.visibility()),
82 ModuleDef::BuiltinType(..) => (module.unwrap(), None),
83 };
84 NameDefinition { kind, container, visibility }
85}
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 626efb603..08e77c01f 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -2,12 +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_syntax::{algo::find_node_at_offset, ast, tokenize, AstNode, SyntaxKind, SyntaxNode}; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{
7 algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode,
8};
6use ra_text_edit::TextEdit; 9use ra_text_edit::TextEdit;
7 10
8use crate::{ 11use crate::{
9 db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, 12 FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, SourceFileEdit, TextRange,
10 SourceFileEdit, TextRange,
11}; 13};
12 14
13use super::find_all_refs; 15use super::find_all_refs;
@@ -17,11 +19,9 @@ pub(crate) fn rename(
17 position: FilePosition, 19 position: FilePosition,
18 new_name: &str, 20 new_name: &str,
19) -> Option<RangeInfo<SourceChange>> { 21) -> Option<RangeInfo<SourceChange>> {
20 let tokens = tokenize(new_name); 22 match lex_single_valid_syntax_kind(new_name)? {
21 if tokens.len() != 1 23 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
22 || (tokens[0].kind != SyntaxKind::IDENT && tokens[0].kind != SyntaxKind::UNDERSCORE) 24 _ => return None,
23 {
24 return None;
25 } 25 }
26 26
27 let parse = db.parse(position.file_id); 27 let parse = db.parse(position.file_id);
diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs
index f8211a746..279f57be0 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
@@ -19,59 +19,13 @@ pub struct SearchScope {
19} 19}
20 20
21impl SearchScope { 21impl SearchScope {
22 fn new(entries: FxHashMap<FileId, Option<TextRange>>) -> SearchScope { 22 pub(crate) fn for_def(def: &NameDefinition, db: &RootDatabase) -> SearchScope {
23 SearchScope { entries }
24 }
25 pub fn single_file(file: FileId) -> SearchScope {
26 SearchScope::new(std::iter::once((file, None)).collect())
27 }
28 pub(crate) fn intersection(&self, other: &SearchScope) -> SearchScope {
29 let (mut small, mut large) = (&self.entries, &other.entries);
30 if small.len() > large.len() {
31 mem::swap(&mut small, &mut large)
32 }
33
34 let res = small
35 .iter()
36 .filter_map(|(file_id, r1)| {
37 let r2 = large.get(file_id)?;
38 let r = intersect_ranges(*r1, *r2)?;
39 Some((*file_id, r))
40 })
41 .collect();
42 return SearchScope::new(res);
43
44 fn intersect_ranges(
45 r1: Option<TextRange>,
46 r2: Option<TextRange>,
47 ) -> Option<Option<TextRange>> {
48 match (r1, r2) {
49 (None, r) | (r, None) => Some(r),
50 (Some(r1), Some(r2)) => {
51 let r = r1.intersection(&r2)?;
52 Some(Some(r))
53 }
54 }
55 }
56 }
57}
58
59impl IntoIterator for SearchScope {
60 type Item = (FileId, Option<TextRange>);
61 type IntoIter = std::collections::hash_map::IntoIter<FileId, Option<TextRange>>;
62 fn into_iter(self) -> Self::IntoIter {
63 self.entries.into_iter()
64 }
65}
66
67impl NameDefinition {
68 pub(crate) fn search_scope(&self, db: &RootDatabase) -> SearchScope {
69 let _p = profile("search_scope"); 23 let _p = profile("search_scope");
70 24
71 let module_src = self.container.definition_source(db); 25 let module_src = def.container.definition_source(db);
72 let file_id = module_src.file_id.original_file(db); 26 let file_id = module_src.file_id.original_file(db);
73 27
74 if let NameKind::Local(var) = self.kind { 28 if let NameKind::Local(var) = def.kind {
75 let range = match var.parent(db) { 29 let range = match var.parent(db) {
76 DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), 30 DefWithBody::Function(f) => f.source(db).value.syntax().text_range(),
77 DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), 31 DefWithBody::Const(c) => c.source(db).value.syntax().text_range(),
@@ -82,10 +36,10 @@ impl NameDefinition {
82 return SearchScope::new(res); 36 return SearchScope::new(res);
83 } 37 }
84 38
85 let vis = self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); 39 let vis = def.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or_default();
86 40
87 if vis.as_str() == "pub(super)" { 41 if vis.as_str() == "pub(super)" {
88 if let Some(parent_module) = self.container.parent(db) { 42 if let Some(parent_module) = def.container.parent(db) {
89 let mut res = FxHashMap::default(); 43 let mut res = FxHashMap::default();
90 let parent_src = parent_module.definition_source(db); 44 let parent_src = parent_module.definition_source(db);
91 let file_id = parent_src.file_id.original_file(db); 45 let file_id = parent_src.file_id.original_file(db);
@@ -118,7 +72,7 @@ impl NameDefinition {
118 return SearchScope::new(res); 72 return SearchScope::new(res);
119 } 73 }
120 if vis.as_str() == "pub" { 74 if vis.as_str() == "pub" {
121 let krate = self.container.krate(); 75 let krate = def.container.krate();
122 for rev_dep in krate.reverse_dependencies(db) { 76 for rev_dep in krate.reverse_dependencies(db) {
123 let root_file = rev_dep.root_file(db); 77 let root_file = rev_dep.root_file(db);
124 let source_root_id = db.file_source_root(root_file); 78 let source_root_id = db.file_source_root(root_file);
@@ -137,4 +91,48 @@ impl NameDefinition {
137 res.insert(file_id, range); 91 res.insert(file_id, range);
138 SearchScope::new(res) 92 SearchScope::new(res)
139 } 93 }
94
95 fn new(entries: FxHashMap<FileId, Option<TextRange>>) -> SearchScope {
96 SearchScope { entries }
97 }
98 pub fn single_file(file: FileId) -> SearchScope {
99 SearchScope::new(std::iter::once((file, None)).collect())
100 }
101 pub(crate) fn intersection(&self, other: &SearchScope) -> SearchScope {
102 let (mut small, mut large) = (&self.entries, &other.entries);
103 if small.len() > large.len() {
104 mem::swap(&mut small, &mut large)
105 }
106
107 let res = small
108 .iter()
109 .filter_map(|(file_id, r1)| {
110 let r2 = large.get(file_id)?;
111 let r = intersect_ranges(*r1, *r2)?;
112 Some((*file_id, r))
113 })
114 .collect();
115 return SearchScope::new(res);
116
117 fn intersect_ranges(
118 r1: Option<TextRange>,
119 r2: Option<TextRange>,
120 ) -> Option<Option<TextRange>> {
121 match (r1, r2) {
122 (None, r) | (r, None) => Some(r),
123 (Some(r1), Some(r2)) => {
124 let r = r1.intersection(&r2)?;
125 Some(Some(r))
126 }
127 }
128 }
129 }
130}
131
132impl IntoIterator for SearchScope {
133 type Item = (FileId, Option<TextRange>);
134 type IntoIter = std::collections::hash_map::IntoIter<FileId, Option<TextRange>>;
135 fn into_iter(self) -> Self::IntoIter {
136 self.entries.into_iter()
137 }
140} 138}
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 7533692f6..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 {
@@ -43,7 +44,7 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
43 let name = fn_def.name()?.text().clone(); 44 let name = fn_def.name()?.text().clone();
44 let kind = if name == "main" { 45 let kind = if name == "main" {
45 RunnableKind::Bin 46 RunnableKind::Bin
46 } else if fn_def.has_atom_attr("test") { 47 } else if has_test_related_attribute(&fn_def) {
47 RunnableKind::Test { name: name.to_string() } 48 RunnableKind::Test { name: name.to_string() }
48 } else if fn_def.has_atom_attr("bench") { 49 } else if fn_def.has_atom_attr("bench") {
49 RunnableKind::Bench { name: name.to_string() } 50 RunnableKind::Bench { name: name.to_string() }
@@ -53,6 +54,20 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
53 Some(Runnable { range: fn_def.syntax().text_range(), kind }) 54 Some(Runnable { range: fn_def.syntax().text_range(), kind })
54} 55}
55 56
57/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
58/// `#[test_case(...)]`, `#[tokio::test]` and similar.
59/// Also a regular `#[test]` annotation is supported.
60///
61/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
62/// but it's better than not to have the runnables for the tests at all.
63fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
64 fn_def
65 .attrs()
66 .filter_map(|attr| attr.path())
67 .map(|path| path.syntax().to_string().to_lowercase())
68 .any(|attribute_text| attribute_text.contains("test"))
69}
70
56fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { 71fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
57 let has_test_function = module 72 let has_test_function = module
58 .item_list()? 73 .item_list()?
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 1d130544f..1cc55e78b 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -34,6 +34,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
34 <span class="function">foo</span>::&lt;<span class="type.builtin">i32</span>&gt;(); 34 <span class="function">foo</span>::&lt;<span class="type.builtin">i32</span>&gt;();
35} 35}
36 36
37<span class="macro">macro_rules</span><span class="macro">!</span> def_fn {
38 ($($tt:tt)*) =&gt; {$($tt)*}
39}
40
41<span class="macro">def_fn</span><span class="macro">!</span>{
42 <span class="keyword">fn</span> <span class="function">bar</span>() -&gt; <span class="type.builtin">u32</span> {
43 <span class="literal.numeric">100</span>
44 }
45}
46
37<span class="comment">// comment</span> 47<span class="comment">// comment</span>
38<span class="keyword">fn</span> <span class="function">main</span>() { 48<span class="keyword">fn</span> <span class="function">main</span>() {
39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); 49 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>);
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index d90ee8540..918fd4b97 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -24,14 +24,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
24.keyword\.control { color: #F0DFAF; font-weight: bold; } 24.keyword\.control { color: #F0DFAF; font-weight: bold; }
25</style> 25</style>
26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() { 26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
27 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>; 27 <span class="keyword">let</span> <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string(); 28 <span class="keyword">let</span> <span class="variable" data-binding-hash="4303609361109701698" style="color: hsl(242,75%,88%);">x</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
29 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string(); 29 <span class="keyword">let</span> <span class="variable" data-binding-hash="13865792086344377029" style="color: hsl(340,64%,86%);">y</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
30 30
31 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>; 31 <span class="keyword">let</span> <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span> = <span class="string">"other color please!"</span>;
32 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.to_string(); 32 <span class="keyword">let</span> <span class="variable" data-binding-hash="12461245066629867975" style="color: hsl(132,91%,68%);">y</span> = <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span>.to_string();
33} 33}
34 34
35<span class="keyword">fn</span> <span class="function">bar</span>() { 35<span class="keyword">fn</span> <span class="function">bar</span>() {
36 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>; 36 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
37}</code></pre> \ No newline at end of file 37}</code></pre> \ No newline at end of file
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 0411977b9..174e13595 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,14 +1,18 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::FxHashMap;
4 4
5use hir::{InFile, Name, 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::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; 9use ra_syntax::{
10 ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange,
11 WalkEvent, T,
12};
9 13
10use crate::{ 14use crate::{
11 db::RootDatabase, 15 expand::descend_into_macros_with_analyzer,
12 references::{ 16 references::{
13 classify_name, classify_name_ref, 17 classify_name, classify_name_ref,
14 NameKind::{self, *}, 18 NameKind::{self, *},
@@ -72,121 +76,186 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
72 let parse = db.parse(file_id); 76 let parse = db.parse(file_id);
73 let root = parse.tree().syntax().clone(); 77 let root = parse.tree().syntax().clone();
74 78
75 fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 { 79 let mut sb = SourceBinder::new(db);
76 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { 80 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
77 use std::{collections::hash_map::DefaultHasher, hash::Hasher}; 81 let mut res = Vec::new();
82 let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None);
78 83
79 let mut hasher = DefaultHasher::new(); 84 let mut in_macro_call = None;
80 x.hash(&mut hasher); 85
81 hasher.finish() 86 for event in root.preorder_with_tokens() {
87 match event {
88 WalkEvent::Enter(node) => match node.kind() {
89 MACRO_CALL => {
90 in_macro_call = Some(node.clone());
91 if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) {
92 res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None });
93 }
94 }
95 _ if in_macro_call.is_some() => {
96 if let Some(token) = node.as_token() {
97 if let Some((tag, binding_hash)) = highlight_token_tree(
98 db,
99 &mut sb,
100 &analyzer,
101 &mut bindings_shadow_count,
102 InFile::new(file_id.into(), token.clone()),
103 ) {
104 res.push(HighlightedRange {
105 range: node.text_range(),
106 tag,
107 binding_hash,
108 });
109 }
110 }
111 }
112 _ => {
113 if let Some((tag, binding_hash)) = highlight_node(
114 db,
115 &mut sb,
116 &mut bindings_shadow_count,
117 InFile::new(file_id.into(), node.clone()),
118 ) {
119 res.push(HighlightedRange { range: node.text_range(), tag, binding_hash });
120 }
121 }
122 },
123 WalkEvent::Leave(node) => {
124 if let Some(m) = in_macro_call.as_ref() {
125 if *m == node {
126 in_macro_call = None;
127 }
128 }
129 }
82 } 130 }
131 }
83 132
84 hash((file_id, name, shadow_count)) 133 res
134}
135
136fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> {
137 let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?;
138 let path = macro_call.path()?;
139 let name_ref = path.segment()?.name_ref()?;
140
141 let range_start = name_ref.syntax().text_range().start();
142 let mut range_end = name_ref.syntax().text_range().end();
143 for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
144 match sibling.kind() {
145 T![!] | IDENT => range_end = sibling.text_range().end(),
146 _ => (),
147 }
85 } 148 }
86 149
87 let mut sb = SourceBinder::new(db); 150 Some(TextRange::from_to(range_start, range_end))
151}
88 152
89 // Visited nodes to handle highlighting priorities 153fn highlight_token_tree(
90 // FIXME: retain only ranges here 154 db: &RootDatabase,
91 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); 155 sb: &mut SourceBinder<RootDatabase>,
92 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 156 analyzer: &SourceAnalyzer,
157 bindings_shadow_count: &mut FxHashMap<Name, u32>,
158 token: InFile<SyntaxToken>,
159) -> Option<(&'static str, Option<u64>)> {
160 if token.value.parent().kind() != TOKEN_TREE {
161 return None;
162 }
163 let token = descend_into_macros_with_analyzer(db, analyzer, token);
164 let expanded = {
165 let parent = token.value.parent();
166 // We only care Name and Name_ref
167 match (token.value.kind(), parent.kind()) {
168 (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()),
169 _ => token.map(|it| it.into()),
170 }
171 };
93 172
94 let mut res = Vec::new(); 173 highlight_node(db, sb, bindings_shadow_count, expanded)
95 for node in root.descendants_with_tokens() { 174}
96 if highlighted.contains(&node) { 175
97 continue; 176fn highlight_node(
177 db: &RootDatabase,
178 sb: &mut SourceBinder<RootDatabase>,
179 bindings_shadow_count: &mut FxHashMap<Name, u32>,
180 node: InFile<SyntaxElement>,
181) -> Option<(&'static str, Option<u64>)> {
182 let mut binding_hash = None;
183 let tag = match node.value.kind() {
184 FN_DEF => {
185 bindings_shadow_count.clear();
186 return None;
98 } 187 }
99 let mut binding_hash = None; 188 COMMENT => tags::LITERAL_COMMENT,
100 let tag = match node.kind() { 189 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING,
101 FN_DEF => { 190 ATTR => tags::LITERAL_ATTRIBUTE,
102 bindings_shadow_count.clear(); 191 // Special-case field init shorthand
103 continue; 192 NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD,
104 } 193 NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None,
105 COMMENT => tags::LITERAL_COMMENT, 194 NAME_REF => {
106 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, 195 let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
107 ATTR => tags::LITERAL_ATTRIBUTE, 196 let name_kind = classify_name_ref(sb, node.with_value(&name_ref)).map(|d| d.kind);
108 // Special-case field init shorthand 197 match name_kind {
109 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, 198 Some(name_kind) => {
110 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, 199 if let Local(local) = &name_kind {
111 NAME_REF => { 200 if let Some(name) = local.name(db) {
112 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 201 let shadow_count =
113 let name_kind = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) 202 bindings_shadow_count.entry(name.clone()).or_default();
114 .map(|d| d.kind); 203 binding_hash =
115 match name_kind { 204 Some(calc_binding_hash(node.file_id, &name, *shadow_count))
116 Some(name_kind) => { 205 }
117 if let Local(local) = &name_kind { 206 };
118 if let Some(name) = local.name(db) { 207
119 let shadow_count = 208 highlight_name(db, name_kind)
120 bindings_shadow_count.entry(name.clone()).or_default();
121 binding_hash =
122 Some(calc_binding_hash(file_id, &name, *shadow_count))
123 }
124 };
125
126 highlight_name(db, name_kind)
127 }
128 _ => continue,
129 }
130 }
131 NAME => {
132 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
133 let name_kind =
134 classify_name(&mut sb, InFile::new(file_id.into(), &name)).map(|d| d.kind);
135
136 if let Some(Local(local)) = &name_kind {
137 if let Some(name) = local.name(db) {
138 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
139 *shadow_count += 1;
140 binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
141 }
142 };
143
144 match name_kind {
145 Some(name_kind) => highlight_name(db, name_kind),
146 None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() {
147 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE,
148 TYPE_PARAM => tags::TYPE_PARAM,
149 RECORD_FIELD_DEF => tags::FIELD,
150 _ => tags::FUNCTION,
151 }),
152 } 209 }
210 _ => return None,
153 } 211 }
154 INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, 212 }
155 BYTE => tags::LITERAL_BYTE, 213 NAME => {
156 CHAR => tags::LITERAL_CHAR, 214 let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap();
157 LIFETIME => tags::TYPE_LIFETIME, 215 let name_kind = classify_name(sb, node.with_value(&name)).map(|d| d.kind);
158 T![unsafe] => tags::KEYWORD_UNSAFE, 216
159 k if is_control_keyword(k) => tags::KEYWORD_CONTROL, 217 if let Some(Local(local)) = &name_kind {
160 k if k.is_keyword() => tags::KEYWORD, 218 if let Some(name) = local.name(db) {
161 _ => { 219 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
162 if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { 220 *shadow_count += 1;
163 if let Some(path) = macro_call.path() { 221 binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count))
164 if let Some(segment) = path.segment() {
165 if let Some(name_ref) = segment.name_ref() {
166 highlighted.insert(name_ref.syntax().clone().into());
167 let range_start = name_ref.syntax().text_range().start();
168 let mut range_end = name_ref.syntax().text_range().end();
169 for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
170 match sibling.kind() {
171 T![!] | IDENT => range_end = sibling.text_range().end(),
172 _ => (),
173 }
174 }
175 res.push(HighlightedRange {
176 range: TextRange::from_to(range_start, range_end),
177 tag: tags::MACRO,
178 binding_hash: None,
179 })
180 }
181 }
182 }
183 } 222 }
184 continue; 223 };
224
225 match name_kind {
226 Some(name_kind) => highlight_name(db, name_kind),
227 None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() {
228 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE,
229 TYPE_PARAM => tags::TYPE_PARAM,
230 RECORD_FIELD_DEF => tags::FIELD,
231 _ => tags::FUNCTION,
232 }),
185 } 233 }
186 }; 234 }
187 res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }) 235 INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC,
236 BYTE => tags::LITERAL_BYTE,
237 CHAR => tags::LITERAL_CHAR,
238 LIFETIME => tags::TYPE_LIFETIME,
239 T![unsafe] => tags::KEYWORD_UNSAFE,
240 k if is_control_keyword(k) => tags::KEYWORD_CONTROL,
241 k if k.is_keyword() => tags::KEYWORD,
242
243 _ => return None,
244 };
245
246 return Some((tag, binding_hash));
247
248 fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 {
249 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
250 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
251
252 let mut hasher = DefaultHasher::new();
253 x.hash(&mut hasher);
254 hasher.finish()
255 }
256
257 hash((file_id, name, shadow_count))
188 } 258 }
189 res
190} 259}
191 260
192pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { 261pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
@@ -251,19 +320,16 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
251fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { 320fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
252 match name_kind { 321 match name_kind {
253 Macro(_) => tags::MACRO, 322 Macro(_) => tags::MACRO,
254 Field(_) => tags::FIELD, 323 StructField(_) => tags::FIELD,
255 AssocItem(hir::AssocItem::Function(_)) => tags::FUNCTION, 324 ModuleDef(hir::ModuleDef::Module(_)) => tags::MODULE,
256 AssocItem(hir::AssocItem::Const(_)) => tags::CONSTANT, 325 ModuleDef(hir::ModuleDef::Function(_)) => tags::FUNCTION,
257 AssocItem(hir::AssocItem::TypeAlias(_)) => tags::TYPE, 326 ModuleDef(hir::ModuleDef::Adt(_)) => tags::TYPE,
258 Def(hir::ModuleDef::Module(_)) => tags::MODULE, 327 ModuleDef(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT,
259 Def(hir::ModuleDef::Function(_)) => tags::FUNCTION, 328 ModuleDef(hir::ModuleDef::Const(_)) => tags::CONSTANT,
260 Def(hir::ModuleDef::Adt(_)) => tags::TYPE, 329 ModuleDef(hir::ModuleDef::Static(_)) => tags::CONSTANT,
261 Def(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, 330 ModuleDef(hir::ModuleDef::Trait(_)) => tags::TYPE,
262 Def(hir::ModuleDef::Const(_)) => tags::CONSTANT, 331 ModuleDef(hir::ModuleDef::TypeAlias(_)) => tags::TYPE,
263 Def(hir::ModuleDef::Static(_)) => tags::CONSTANT, 332 ModuleDef(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN,
264 Def(hir::ModuleDef::Trait(_)) => tags::TYPE,
265 Def(hir::ModuleDef::TypeAlias(_)) => tags::TYPE,
266 Def(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN,
267 SelfType(_) => tags::TYPE_SELF, 333 SelfType(_) => tags::TYPE_SELF,
268 TypeParam(_) => tags::TYPE_PARAM, 334 TypeParam(_) => tags::TYPE_PARAM,
269 Local(local) => { 335 Local(local) => {
@@ -331,6 +397,16 @@ fn foo<T>() -> T {
331 foo::<i32>(); 397 foo::<i32>();
332} 398}
333 399
400macro_rules! def_fn {
401 ($($tt:tt)*) => {$($tt)*}
402}
403
404def_fn!{
405 fn bar() -> u32 {
406 100
407 }
408}
409
334// comment 410// comment
335fn main() { 411fn main() {
336 println!("Hello, {}!", 92); 412 println!("Hello, {}!", 92);
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}