diff options
Diffstat (limited to 'crates/ra_ide')
42 files changed, 659 insertions, 2294 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 2c9f9dce0..9ace35229 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -28,6 +28,7 @@ once_cell = "1.2.0" | |||
28 | ra_syntax = { path = "../ra_syntax" } | 28 | ra_syntax = { path = "../ra_syntax" } |
29 | ra_text_edit = { path = "../ra_text_edit" } | 29 | ra_text_edit = { path = "../ra_text_edit" } |
30 | ra_db = { path = "../ra_db" } | 30 | ra_db = { path = "../ra_db" } |
31 | ra_ide_db = { path = "../ra_ide_db" } | ||
31 | ra_cfg = { path = "../ra_cfg" } | 32 | ra_cfg = { path = "../ra_cfg" } |
32 | ra_fmt = { path = "../ra_fmt" } | 33 | ra_fmt = { path = "../ra_fmt" } |
33 | ra_prof = { path = "../ra_prof" } | 34 | ra_prof = { path = "../ra_prof" } |
@@ -39,7 +40,7 @@ ra_assists = { path = "../ra_assists" } | |||
39 | hir = { path = "../ra_hir", package = "ra_hir" } | 40 | hir = { path = "../ra_hir", package = "ra_hir" } |
40 | 41 | ||
41 | [dev-dependencies] | 42 | [dev-dependencies] |
42 | insta = "0.12.0" | 43 | insta = "0.13.0" |
43 | 44 | ||
44 | [dev-dependencies.proptest] | 45 | [dev-dependencies.proptest] |
45 | version = "0.9.0" | 46 | version = "0.9.0" |
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 | ||
3 | use ra_assists::{resolved_assists, AssistAction, AssistLabel}; | ||
3 | use ra_db::{FilePosition, FileRange}; | 4 | use ra_db::{FilePosition, FileRange}; |
5 | use ra_ide_db::RootDatabase; | ||
4 | 6 | ||
5 | use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; | 7 | use crate::{FileId, SourceChange, SourceFileEdit}; |
6 | 8 | ||
7 | use either::Either; | ||
8 | pub use ra_assists::AssistId; | 9 | pub use ra_assists::AssistId; |
9 | use ra_assists::{AssistAction, AssistLabel}; | ||
10 | 10 | ||
11 | #[derive(Debug)] | 11 | #[derive(Debug)] |
12 | pub struct Assist { | 12 | pub 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 | ||
18 | pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { | 19 | pub(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 @@ | |||
3 | use indexmap::IndexMap; | 3 | use indexmap::IndexMap; |
4 | 4 | ||
5 | use hir::db::AstDatabase; | 5 | use hir::db::AstDatabase; |
6 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | 7 | use 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 | ||
11 | use crate::{ | 12 | use 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 |
2 | use hir::db::AstDatabase; | 2 | use hir::db::AstDatabase; |
3 | use ra_ide_db::RootDatabase; | ||
3 | use ra_syntax::{ | 4 | use ra_syntax::{ |
4 | ast::{self, ArgListOwner}, | 5 | ast::{self, ArgListOwner}, |
5 | match_ast, AstNode, SyntaxNode, | 6 | match_ast, AstNode, SyntaxNode, |
6 | }; | 7 | }; |
7 | |||
8 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
9 | 9 | ||
10 | use crate::{ | 10 | use 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. |
15 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { | 13 | pub(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 | |||
3 | use std::{fmt, sync::Arc, time}; | ||
4 | |||
5 | use ra_db::{ | ||
6 | salsa::{Database, Durability, SweepStrategy}, | ||
7 | CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, | ||
8 | SourceRootId, | ||
9 | }; | ||
10 | use ra_prof::{memory_usage, profile, Bytes}; | ||
11 | use ra_syntax::SourceFile; | ||
12 | #[cfg(not(feature = "wasm"))] | ||
13 | use rayon::prelude::*; | ||
14 | use rustc_hash::FxHashMap; | ||
15 | |||
16 | use crate::{ | ||
17 | db::{DebugData, RootDatabase}, | ||
18 | symbol_index::{SymbolIndex, SymbolsDatabase}, | ||
19 | }; | ||
20 | |||
21 | #[derive(Default)] | ||
22 | pub 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 | |||
31 | impl 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 | |||
53 | impl 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)] | ||
100 | struct AddFile { | ||
101 | file_id: FileId, | ||
102 | path: RelativePathBuf, | ||
103 | text: Arc<String>, | ||
104 | } | ||
105 | |||
106 | #[derive(Debug)] | ||
107 | struct RemoveFile { | ||
108 | file_id: FileId, | ||
109 | path: RelativePathBuf, | ||
110 | } | ||
111 | |||
112 | #[derive(Default)] | ||
113 | struct RootChange { | ||
114 | added: Vec<AddFile>, | ||
115 | removed: Vec<RemoveFile>, | ||
116 | } | ||
117 | |||
118 | impl 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 | |||
127 | pub struct LibraryData { | ||
128 | root_id: SourceRootId, | ||
129 | root_change: RootChange, | ||
130 | symbol_index: SymbolIndex, | ||
131 | } | ||
132 | |||
133 | impl 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 | |||
143 | impl 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 | |||
166 | const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); | ||
167 | |||
168 | impl 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 | |||
347 | fn 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; | |||
18 | mod complete_trait_impl; | 18 | mod complete_trait_impl; |
19 | 19 | ||
20 | use ra_db::SourceDatabase; | 20 | use ra_db::SourceDatabase; |
21 | use ra_ide_db::RootDatabase; | ||
21 | 22 | ||
22 | #[cfg(test)] | 23 | #[cfg(test)] |
23 | use crate::completion::completion_item::do_completion; | 24 | use 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 | ||
32 | pub use crate::completion::completion_item::{ | 33 | pub 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). |
58 | pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Option<Completions> { | 59 | pub(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 | ||
3 | use ra_assists::auto_import_text_edit; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | use ra_syntax::{ast, AstNode, SmolStr}; | ||
5 | use ra_text_edit::TextEditBuilder; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | ||
9 | 4 | ||
10 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(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 | |||
57 | fn 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 | |||
66 | fn 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)] | ||
78 | pub(crate) struct ImportResolver { | ||
79 | // todo: use fst crate or something like that | ||
80 | dummy_names: Vec<(SmolStr, Vec<SmolStr>)>, | ||
81 | } | ||
82 | |||
83 | impl 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)] |
128 | mod tests { | 16 | mod 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 | ||
3 | use ra_syntax::ast::{self, NameOwner, AstNode}; | 3 | use ra_syntax::ast::{self, NameOwner, AstNode}; |
4 | 4 | ||
5 | use hir::{self, db::HirDatabase}; | 5 | use hir::{self, db::HirDatabase, Docs}; |
6 | 6 | ||
7 | 7 | ||
8 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 8 | pub(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 | ||
3 | use ra_ide_db::RootDatabase; | ||
3 | use ra_syntax::{ | 4 | use 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 | }; |
9 | use ra_text_edit::AtomTextEdit; | 10 | use ra_text_edit::AtomTextEdit; |
10 | 11 | ||
11 | use crate::{db, FilePosition}; | 12 | use 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)] |
16 | pub(crate) struct CompletionContext<'a> { | 17 | pub(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 | ||
50 | impl<'a> CompletionContext<'a> { | 51 | impl<'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 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use ra_db::{ | ||
6 | salsa::{self, Database, Durability}, | ||
7 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, | ||
8 | SourceDatabase, SourceRootId, | ||
9 | }; | ||
10 | use rustc_hash::FxHashMap; | ||
11 | |||
12 | use 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)] | ||
28 | pub(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 | |||
36 | impl 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 | |||
52 | impl 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 | |||
73 | impl Default for RootDatabase { | ||
74 | fn default() -> RootDatabase { | ||
75 | RootDatabase::new(None, FeatureFlags::default()) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | impl 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 | |||
99 | impl 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)] | ||
112 | pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { | ||
113 | fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; | ||
114 | } | ||
115 | |||
116 | fn 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)] | ||
122 | pub(crate) struct DebugData { | ||
123 | pub(crate) root_paths: FxHashMap<SourceRootId, String>, | ||
124 | pub(crate) crate_names: FxHashMap<CrateId, String>, | ||
125 | } | ||
126 | |||
127 | impl 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; | |||
5 | use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; | 5 | use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; | 7 | use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; |
8 | use ra_ide_db::RootDatabase; | ||
8 | use ra_prof::profile; | 9 | use ra_prof::profile; |
9 | use ra_syntax::{ | 10 | use ra_syntax::{ |
10 | algo, | 11 | algo, |
@@ -13,7 +14,7 @@ use ra_syntax::{ | |||
13 | }; | 14 | }; |
14 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 15 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
15 | 16 | ||
16 | use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; | 17 | use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; |
17 | 18 | ||
18 | #[derive(Debug, Copy, Clone)] | 19 | #[derive(Debug, Copy, Clone)] |
19 | pub enum Severity { | 20 | pub 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 | ||
5 | use hir::{Docs, Documentation, HasSource, HirDisplay}; | 5 | use hir::{Docs, Documentation, HasSource, HirDisplay}; |
6 | use join_to_string::join; | 6 | use join_to_string::join; |
7 | use ra_ide_db::RootDatabase; | ||
7 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | 8 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
8 | use std::convert::From; | 9 | use std::convert::From; |
9 | 10 | ||
10 | use crate::{ | 11 | use crate::display::{generic_parameters, where_predicates}; |
11 | db, | ||
12 | display::{generic_parameters, where_predicates}, | ||
13 | }; | ||
14 | 12 | ||
15 | #[derive(Debug)] | 13 | #[derive(Debug)] |
16 | pub enum CallableKind { | 14 | pub 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 @@ | |||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; | 4 | use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; |
5 | use ra_db::{FileId, SourceDatabase}; | 5 | use ra_db::{FileId, SourceDatabase}; |
6 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | 7 | use 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 | ||
13 | use crate::{db::RootDatabase, expand::original_range, FileSymbol}; | 14 | use crate::{expand::original_range, FileSymbol}; |
14 | 15 | ||
15 | use super::short_label::ShortLabel; | 16 | use 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 | ||
4 | use hir::{InFile, Origin}; | 4 | use hir::{InFile, Origin}; |
5 | use ra_db::FileId; | 5 | use ra_db::FileId; |
6 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; | 7 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; |
7 | 8 | ||
8 | use crate::{db::RootDatabase, FileRange}; | 9 | use crate::FileRange; |
9 | 10 | ||
10 | pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { | 11 | pub(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 | |||
86 | pub(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 | ||
3 | use crate::{db::RootDatabase, FilePosition}; | ||
4 | use hir::db::AstDatabase; | 3 | use hir::db::AstDatabase; |
5 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
6 | use rustc_hash::FxHashMap; | 5 | use ra_ide_db::RootDatabase; |
7 | |||
8 | use ra_syntax::{ | 6 | use 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 | }; |
10 | use rustc_hash::FxHashMap; | ||
11 | |||
12 | use crate::FilePosition; | ||
12 | 13 | ||
13 | pub struct ExpandedMacro { | 14 | pub 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 | ||
3 | use std::iter::successors; | ||
4 | |||
5 | use hir::db::AstDatabase; | ||
3 | use ra_db::SourceDatabase; | 6 | use ra_db::SourceDatabase; |
7 | use ra_ide_db::RootDatabase; | ||
4 | use ra_syntax::{ | 8 | use 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 | ||
12 | use crate::{db::RootDatabase, expand::descend_into_macros, FileId, FileRange}; | 16 | use crate::{expand::descend_into_macros, FileId, FileRange}; |
13 | use hir::db::AstDatabase; | ||
14 | use std::iter::successors; | ||
15 | 17 | ||
16 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { | 18 | pub(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#" |
515 | fn foo<R>() | 517 | fn 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 | |||
3 | use 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)] | ||
18 | pub struct FeatureFlags { | ||
19 | flags: FxHashMap<String, bool>, | ||
20 | } | ||
21 | |||
22 | impl 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 | |||
52 | impl 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 | |||
64 | fn 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 | ||
3 | use hir::{db::AstDatabase, InFile, SourceBinder}; | 3 | use hir::{db::AstDatabase, InFile, SourceBinder}; |
4 | use ra_ide_db::{symbol_index, RootDatabase}; | ||
4 | use ra_syntax::{ | 5 | use 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 | ||
11 | use crate::{ | 12 | use 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 | ||
3 | use hir::db::AstDatabase; | 3 | use hir::db::AstDatabase; |
4 | use ra_ide_db::RootDatabase; | ||
4 | use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 5 | use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
5 | 6 | ||
6 | use crate::{ | 7 | use 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 | ||
11 | pub(crate) fn goto_type_definition( | 11 | pub(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 | ||
3 | use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; | 3 | use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::{ | 6 | use 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 | ||
13 | use crate::{ | 14 | use 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 | ||
3 | use hir::{Crate, ImplBlock, SourceBinder}; | 3 | use hir::{Crate, ImplBlock, SourceBinder}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | 6 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; |
6 | 7 | ||
7 | use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo}; | 8 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; |
8 | 9 | ||
9 | pub(crate) fn goto_implementation( | 10 | pub(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 | ||
3 | use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; | 3 | use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; |
4 | use once_cell::unsync::Lazy; | 4 | use once_cell::unsync::Lazy; |
5 | use ra_ide_db::RootDatabase; | ||
5 | use ra_prof::profile; | 6 | use ra_prof::profile; |
6 | use ra_syntax::{ | 7 | use 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 | ||
11 | use crate::{db::RootDatabase, FileId, FunctionSignature}; | 12 | use crate::{FileId, FunctionSignature}; |
12 | 13 | ||
13 | #[derive(Debug, PartialEq, Eq)] | 14 | #[derive(Debug, PartialEq, Eq)] |
14 | pub enum InlayKind { | 15 | pub 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 | ||
13 | mod db; | ||
14 | pub mod mock_analysis; | 13 | pub mod mock_analysis; |
15 | mod symbol_index; | ||
16 | mod change; | ||
17 | mod source_change; | 14 | mod source_change; |
18 | mod feature_flags; | ||
19 | 15 | ||
20 | mod status; | 16 | mod status; |
21 | mod completion; | 17 | mod completion; |
@@ -34,14 +30,11 @@ mod assists; | |||
34 | mod diagnostics; | 30 | mod diagnostics; |
35 | mod syntax_tree; | 31 | mod syntax_tree; |
36 | mod folding_ranges; | 32 | mod folding_ranges; |
37 | mod line_index; | ||
38 | mod line_index_utils; | ||
39 | mod join_lines; | 33 | mod join_lines; |
40 | mod typing; | 34 | mod typing; |
41 | mod matching_brace; | 35 | mod matching_brace; |
42 | mod display; | 36 | mod display; |
43 | mod inlay_hints; | 37 | mod inlay_hints; |
44 | mod wasm_shims; | ||
45 | mod expand; | 38 | mod expand; |
46 | mod expand_macro; | 39 | mod 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 | }; |
53 | use ra_ide_db::{ | ||
54 | symbol_index::{self, FileSymbol}, | ||
55 | LineIndexDatabase, | ||
56 | }; | ||
60 | use ra_syntax::{SourceFile, TextRange, TextUnit}; | 57 | use ra_syntax::{SourceFile, TextRange, TextUnit}; |
61 | 58 | ||
62 | use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol}; | 59 | use crate::display::ToNav; |
63 | 60 | ||
64 | pub use crate::{ | 61 | pub 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; | |||
87 | pub use ra_db::{ | 80 | pub use ra_db::{ |
88 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, | 81 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, |
89 | }; | 82 | }; |
83 | pub 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 | ||
91 | pub type Cancelable<T> = Result<T, Canceled>; | 92 | pub 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)] | ||
102 | pub struct Query { | ||
103 | query: String, | ||
104 | lowercased: String, | ||
105 | only_types: bool, | ||
106 | libs: bool, | ||
107 | exact: bool, | ||
108 | limit: usize, | ||
109 | } | ||
110 | |||
111 | impl 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)] |
143 | pub struct RangeInfo<T> { | 104 | pub 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)] |
164 | pub struct AnalysisHost { | 125 | pub struct AnalysisHost { |
165 | db: db::RootDatabase, | 126 | db: RootDatabase, |
166 | } | 127 | } |
167 | 128 | ||
168 | impl Default for AnalysisHost { | 129 | impl Default for AnalysisHost { |
@@ -173,7 +134,7 @@ impl Default for AnalysisHost { | |||
173 | 134 | ||
174 | impl AnalysisHost { | 135 | impl 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)] |
222 | pub struct Analysis { | 186 | pub 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)] | ||
483 | mod 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#" | ||
507 | fn 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#" | ||
520 | mod 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#" | ||
536 | fn foo() {} | ||
537 | |||
538 | struct 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 | |||
3 | use crate::TextUnit; | ||
4 | use rustc_hash::FxHashMap; | ||
5 | use superslice::Ext; | ||
6 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
8 | pub 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)] | ||
14 | pub 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)] | ||
22 | pub(crate) struct Utf16Char { | ||
23 | pub(crate) start: TextUnit, | ||
24 | pub(crate) end: TextUnit, | ||
25 | } | ||
26 | |||
27 | impl Utf16Char { | ||
28 | fn len(&self) -> TextUnit { | ||
29 | self.end - self.start | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl 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 | ||
128 | pub 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)] | ||
147 | mod 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)] | ||
210 | mod 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 | " | ||
223 | const 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 | " | ||
233 | const 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 | " | ||
258 | const 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 | |||
3 | use crate::{line_index::Utf16Char, LineCol, LineIndex}; | ||
4 | use ra_syntax::{TextRange, TextUnit}; | ||
5 | use ra_text_edit::{AtomTextEdit, TextEdit}; | ||
6 | |||
7 | #[derive(Debug, Clone)] | ||
8 | enum Step { | ||
9 | Newline(TextUnit), | ||
10 | Utf16Char(TextRange), | ||
11 | } | ||
12 | |||
13 | #[derive(Debug)] | ||
14 | struct LineIndexStepIter<'a> { | ||
15 | line_index: &'a LineIndex, | ||
16 | next_newline_idx: usize, | ||
17 | utf16_chars: Option<(TextUnit, std::slice::Iter<'a, Utf16Char>)>, | ||
18 | } | ||
19 | |||
20 | impl<'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 | |||
29 | impl<'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)] | ||
52 | struct OffsetStepIter<'a> { | ||
53 | text: &'a str, | ||
54 | offset: TextUnit, | ||
55 | } | ||
56 | |||
57 | impl<'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)] | ||
90 | enum NextSteps<'a> { | ||
91 | Use, | ||
92 | ReplaceMany(OffsetStepIter<'a>), | ||
93 | AddMany(OffsetStepIter<'a>), | ||
94 | } | ||
95 | |||
96 | #[derive(Debug)] | ||
97 | struct TranslatedEdit<'a> { | ||
98 | delete: TextRange, | ||
99 | insert: &'a str, | ||
100 | diff: i64, | ||
101 | } | ||
102 | |||
103 | struct Edits<'a> { | ||
104 | edits: &'a [AtomTextEdit], | ||
105 | current: Option<TranslatedEdit<'a>>, | ||
106 | acc_diff: i64, | ||
107 | } | ||
108 | |||
109 | impl<'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)] | ||
194 | struct RunningLineCol { | ||
195 | line: u32, | ||
196 | last_newline: TextUnit, | ||
197 | col_adjust: TextUnit, | ||
198 | } | ||
199 | |||
200 | impl 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 | |||
223 | pub 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)] | ||
294 | mod 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 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use ra_cfg::CfgOptions; | 5 | use ra_cfg::CfgOptions; |
6 | use ra_db::{Env, RelativePathBuf}; | 6 | use ra_db::{CrateName, Env, RelativePathBuf}; |
7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; |
8 | 8 | ||
9 | use crate::{ | 9 | use 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 | ||
3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; | 3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; |
4 | use ra_ide_db::RootDatabase; | ||
4 | use ra_syntax::{ | 5 | use ra_syntax::{ |
5 | algo::find_node_at_offset, | 6 | algo::find_node_at_offset, |
6 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
7 | }; | 8 | }; |
9 | use test_utils::tested_by; | ||
8 | 10 | ||
9 | use crate::{db::RootDatabase, NavigationTarget}; | 11 | use 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. |
13 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { | 15 | pub(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> { | |||
40 | mod tests { | 56 | mod 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 | ||
12 | mod classify; | 12 | mod classify; |
13 | mod name_definition; | ||
14 | mod rename; | 13 | mod rename; |
15 | mod search_scope; | 14 | mod search_scope; |
16 | 15 | ||
17 | use hir::{InFile, SourceBinder}; | 16 | use hir::{InFile, SourceBinder}; |
18 | use once_cell::unsync::Lazy; | 17 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 18 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
19 | use ra_ide_db::RootDatabase; | ||
20 | use ra_prof::profile; | 20 | use ra_prof::profile; |
21 | use ra_syntax::{ | 21 | use 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 | ||
27 | use crate::{ | 27 | use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
28 | db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, | ||
29 | }; | ||
30 | 28 | ||
31 | pub(crate) use self::{ | 29 | pub(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 | }; |
33 | pub(crate) use ra_ide_db::defs::{NameDefinition, NameKind}; | ||
36 | 34 | ||
37 | pub use self::search_scope::SearchScope; | 35 | pub use self::search_scope::SearchScope; |
38 | 36 | ||
@@ -112,38 +110,32 @@ impl IntoIterator for ReferenceSearchResult { | |||
112 | 110 | ||
113 | pub(crate) fn find_all_refs( | 111 | pub(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( | |||
273 | fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 259 | fn 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 | ||
290 | fn 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 | |||
301 | fn 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 | |||
319 | fn 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)] |
305 | mod tests { | 334 | mod 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 | ||
3 | use hir::{InFile, PathResolution, SourceBinder}; | 3 | use hir::{InFile, PathResolution, SourceBinder}; |
4 | use ra_prof::profile; | 4 | use ra_prof::profile; |
5 | use ra_syntax::{ast, match_ast, AstNode}; | 5 | use ra_syntax::{ast, AstNode}; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use super::{ | 8 | use super::{NameDefinition, NameKind}; |
9 | name_definition::{from_assoc_item, from_module_def, from_struct_field}, | 9 | use ra_ide_db::RootDatabase; |
10 | NameDefinition, NameKind, | ||
11 | }; | ||
12 | use crate::db::RootDatabase; | ||
13 | 10 | ||
14 | pub(crate) fn classify_name( | 11 | pub 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 | ||
119 | pub(crate) fn classify_name_ref( | 13 | pub(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 | |||
6 | use hir::{ | ||
7 | Adt, AssocItem, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, StructField, | ||
8 | TypeParam, VariantDef, | ||
9 | }; | ||
10 | use ra_syntax::{ast, ast::VisibilityOwner}; | ||
11 | |||
12 | use crate::db::RootDatabase; | ||
13 | |||
14 | #[derive(Debug, PartialEq, Eq)] | ||
15 | pub 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)] | ||
26 | pub(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 | |||
34 | pub(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 | |||
45 | pub(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 | |||
57 | pub(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 | ||
3 | use hir::ModuleSource; | 3 | use hir::ModuleSource; |
4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; | 4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; |
5 | use ra_syntax::{algo::find_node_at_offset, ast, tokenize, AstNode, SyntaxKind, SyntaxNode}; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | ||
7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, | ||
8 | }; | ||
6 | use ra_text_edit::TextEdit; | 9 | use ra_text_edit::TextEdit; |
7 | 10 | ||
8 | use crate::{ | 11 | use crate::{ |
9 | db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, | 12 | FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, SourceFileEdit, TextRange, |
10 | SourceFileEdit, TextRange, | ||
11 | }; | 13 | }; |
12 | 14 | ||
13 | use super::find_all_refs; | 15 | use 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; | |||
10 | use ra_syntax::{AstNode, TextRange}; | 10 | use ra_syntax::{AstNode, TextRange}; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
12 | 12 | ||
13 | use crate::db::RootDatabase; | 13 | use ra_ide_db::RootDatabase; |
14 | 14 | ||
15 | use super::{NameDefinition, NameKind}; | 15 | use super::{NameDefinition, NameKind}; |
16 | 16 | ||
@@ -19,59 +19,13 @@ pub struct SearchScope { | |||
19 | } | 19 | } |
20 | 20 | ||
21 | impl SearchScope { | 21 | impl 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 | |||
59 | impl 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 | |||
67 | impl 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 | |||
132 | impl 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 @@ | |||
3 | use hir::InFile; | 3 | use hir::InFile; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
6 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | 7 | use 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 | ||
11 | use crate::{db::RootDatabase, FileId}; | 12 | use crate::FileId; |
12 | 13 | ||
13 | #[derive(Debug)] | 14 | #[derive(Debug)] |
14 | pub struct Runnable { | 15 | pub 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. | ||
63 | fn 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 | |||
56 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { | 71 | fn 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>::<<span class="type.builtin">i32</span>>(); | 34 | <span class="function">foo</span>::<<span class="type.builtin">i32</span>>(); |
35 | } | 35 | } |
36 | 36 | ||
37 | <span class="macro">macro_rules</span><span class="macro">!</span> def_fn { | ||
38 | ($($tt:tt)*) => {$($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>() -> <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 | }; |
13 | use ra_ide_db::{ | ||
14 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, | ||
15 | RootDatabase, | ||
16 | }; | ||
13 | use ra_prof::{memory_usage, Bytes}; | 17 | use ra_prof::{memory_usage, Bytes}; |
14 | use ra_syntax::{ast, Parse, SyntaxNode}; | 18 | use ra_syntax::{ast, Parse, SyntaxNode}; |
15 | 19 | ||
16 | use crate::{ | 20 | use crate::FileId; |
17 | db::RootDatabase, | ||
18 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, | ||
19 | FileId, | ||
20 | }; | ||
21 | 21 | ||
22 | fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | 22 | fn 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. | ||
22 | use std::{ | ||
23 | fmt, | ||
24 | hash::{Hash, Hasher}, | ||
25 | mem, | ||
26 | sync::Arc, | ||
27 | }; | ||
28 | |||
29 | use fst::{self, Streamer}; | ||
30 | use ra_db::{ | ||
31 | salsa::{self, ParallelDatabase}, | ||
32 | SourceDatabaseExt, SourceRootId, | ||
33 | }; | ||
34 | use 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"))] | ||
41 | use rayon::prelude::*; | ||
42 | |||
43 | use crate::{db::RootDatabase, FileId, Query}; | ||
44 | |||
45 | #[salsa::query_group(SymbolsDatabaseStorage)] | ||
46 | pub(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 | |||
60 | fn 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 | |||
71 | pub(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 | |||
113 | pub(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)] | ||
122 | pub(crate) struct SymbolIndex { | ||
123 | symbols: Vec<FileSymbol>, | ||
124 | map: fst::Map, | ||
125 | } | ||
126 | |||
127 | impl 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 | |||
133 | impl PartialEq for SymbolIndex { | ||
134 | fn eq(&self, other: &SymbolIndex) -> bool { | ||
135 | self.symbols == other.symbols | ||
136 | } | ||
137 | } | ||
138 | |||
139 | impl Eq for SymbolIndex {} | ||
140 | |||
141 | impl Hash for SymbolIndex { | ||
142 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
143 | self.symbols.hash(hasher) | ||
144 | } | ||
145 | } | ||
146 | |||
147 | impl 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 | |||
223 | impl 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 | |||
255 | fn 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)] | ||
265 | pub(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 | |||
273 | fn 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 | |||
299 | fn 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 | |||
323 | fn 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)] | ||
334 | mod 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#" | ||
358 | fn 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#" | ||
371 | mod 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#" | ||
387 | fn foo() {} | ||
388 | |||
389 | struct 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 | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::FxHashMap; |
4 | 4 | ||
5 | use hir::{InFile, Name, SourceBinder}; | 5 | use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; |
6 | use ra_db::SourceDatabase; | 6 | use ra_db::SourceDatabase; |
7 | use ra_ide_db::RootDatabase; | ||
7 | use ra_prof::profile; | 8 | use ra_prof::profile; |
8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; | 9 | use ra_syntax::{ |
10 | ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, | ||
11 | WalkEvent, T, | ||
12 | }; | ||
9 | 13 | ||
10 | use crate::{ | 14 | use 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 | |||
136 | fn 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 | 153 | fn 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; | 176 | fn 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 | ||
192 | pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { | 261 | pub(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 | |||
251 | fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { | 320 | fn 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 | ||
400 | macro_rules! def_fn { | ||
401 | ($($tt:tt)*) => {$($tt)*} | ||
402 | } | ||
403 | |||
404 | def_fn!{ | ||
405 | fn bar() -> u32 { | ||
406 | 100 | ||
407 | } | ||
408 | } | ||
409 | |||
334 | // comment | 410 | // comment |
335 | fn main() { | 411 | fn 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 | ||
3 | use crate::db::RootDatabase; | ||
4 | use ra_db::SourceDatabase; | 3 | use ra_db::SourceDatabase; |
4 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::{ | 5 | use 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 | ||
16 | use ra_db::{FilePosition, SourceDatabase}; | 16 | use ra_db::{FilePosition, SourceDatabase}; |
17 | use ra_fmt::leading_indent; | 17 | use ra_fmt::leading_indent; |
18 | use ra_ide_db::RootDatabase; | ||
18 | use ra_syntax::{ | 19 | use 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 | }; |
25 | use ra_text_edit::TextEdit; | 26 | use ra_text_edit::TextEdit; |
26 | 27 | ||
27 | use crate::{db::RootDatabase, source_change::SingleFileChange, SourceChange, SourceFileEdit}; | 28 | use crate::{source_change::SingleFileChange, SourceChange, SourceFileEdit}; |
28 | 29 | ||
29 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> { | 30 | pub(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"))] | ||
4 | pub use std::time::Instant; | ||
5 | |||
6 | #[cfg(feature = "wasm")] | ||
7 | #[derive(Clone, Copy, Debug)] | ||
8 | pub struct Instant; | ||
9 | |||
10 | #[cfg(feature = "wasm")] | ||
11 | impl 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 | } | ||