aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs2
-rw-r--r--crates/ra_assists/src/assists/auto_import.rs222
-rw-r--r--crates/ra_assists/src/doc_tests.rs4
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs19
-rw-r--r--crates/ra_assists/src/lib.rs177
-rw-r--r--crates/ra_cargo_watch/Cargo.toml2
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap)0
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap)0
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap)0
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap)0
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap)0
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap)0
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap)0
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir_def/Cargo.toml2
-rw-r--r--crates/ra_hir_ty/Cargo.toml2
-rw-r--r--crates/ra_ide/Cargo.toml2
-rw-r--r--crates/ra_ide/src/assists.rs7
-rw-r--r--crates/ra_ide/src/expand.rs8
-rw-r--r--crates/ra_ide/src/imports_locator.rs76
-rw-r--r--crates/ra_ide/src/lib.rs1
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html10
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html12
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs289
24 files changed, 703 insertions, 133 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 43f0d664b..2ab65ab99 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -101,7 +101,6 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
101 Some(assist) 101 Some(assist)
102 } 102 }
103 103
104 #[allow(dead_code)] // will be used for auto import assist with multiple actions
105 pub(crate) fn add_assist_group( 104 pub(crate) fn add_assist_group(
106 self, 105 self,
107 id: AssistId, 106 id: AssistId,
@@ -168,7 +167,6 @@ pub(crate) struct ActionBuilder {
168} 167}
169 168
170impl ActionBuilder { 169impl ActionBuilder {
171 #[allow(dead_code)]
172 /// Adds a custom label to the action, if it needs to be different from the assist label 170 /// Adds a custom label to the action, if it needs to be different from the assist label
173 pub(crate) fn label(&mut self, label: impl Into<String>) { 171 pub(crate) fn label(&mut self, label: impl Into<String>) {
174 self.label = Some(label.into()) 172 self.label = Some(label.into())
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs
new file mode 100644
index 000000000..9163cc662
--- /dev/null
+++ b/crates/ra_assists/src/assists/auto_import.rs
@@ -0,0 +1,222 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 ast::{self, AstNode},
4 SmolStr, SyntaxElement,
5 SyntaxKind::{NAME_REF, USE_ITEM},
6 SyntaxNode,
7};
8
9use crate::{
10 assist_ctx::{ActionBuilder, Assist, AssistCtx},
11 auto_import_text_edit, AssistId, ImportsLocator,
12};
13
14// Assist: auto_import
15//
16// If the name is unresolved, provides all possible imports for it.
17//
18// ```
19// fn main() {
20// let map = HashMap<|>::new();
21// }
22// ```
23// ->
24// ```
25// use std::collections::HashMap;
26//
27// fn main() {
28// let map = HashMap<|>::new();
29// }
30// ```
31pub(crate) fn auto_import<F: ImportsLocator>(
32 ctx: AssistCtx<impl HirDatabase>,
33 imports_locator: &mut F,
34) -> Option<Assist> {
35 let path: ast::Path = ctx.find_node_at_offset()?;
36 let module = path.syntax().ancestors().find_map(ast::Module::cast);
37 let position = match module.and_then(|it| it.item_list()) {
38 Some(item_list) => item_list.syntax().clone(),
39 None => {
40 let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
41 current_file.syntax().clone()
42 }
43 };
44 let source_analyzer = ctx.source_analyzer(&position, None);
45 let module_with_name_to_import = source_analyzer.module()?;
46 let path_to_import = ctx.covering_element().ancestors().find_map(ast::Path::cast)?;
47 if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() {
48 return None;
49 }
50
51 let name_to_import = &find_applicable_name_ref(ctx.covering_element())?.syntax().to_string();
52 let proposed_imports = imports_locator
53 .find_imports(&name_to_import.to_string())
54 .into_iter()
55 .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
56 .filter(|use_path| !use_path.segments.is_empty())
57 .take(20)
58 .map(|import| import.to_string())
59 .collect::<std::collections::BTreeSet<_>>();
60 if proposed_imports.is_empty() {
61 return None;
62 }
63
64 ctx.add_assist_group(AssistId("auto_import"), "auto import", || {
65 proposed_imports
66 .into_iter()
67 .map(|import| import_to_action(import, &position, &path_to_import.syntax()))
68 .collect()
69 })
70}
71
72fn find_applicable_name_ref(element: SyntaxElement) -> Option<ast::NameRef> {
73 if element.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() {
74 None
75 } else if element.kind() == NAME_REF {
76 Some(element.as_node().cloned().and_then(ast::NameRef::cast)?)
77 } else {
78 let parent = element.parent()?;
79 if parent.kind() == NAME_REF {
80 Some(ast::NameRef::cast(parent)?)
81 } else {
82 None
83 }
84 }
85}
86
87fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
88 let mut action_builder = ActionBuilder::default();
89 action_builder.label(format!("Import `{}`", &import));
90 auto_import_text_edit(
91 position,
92 anchor,
93 &[SmolStr::new(import)],
94 action_builder.text_edit_builder(),
95 );
96 action_builder
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::helpers::{
103 check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable,
104 TestImportsLocator,
105 };
106
107 #[test]
108 fn applicable_when_found_an_import() {
109 check_assist_with_imports_locator(
110 auto_import,
111 TestImportsLocator::new,
112 r"
113 PubStruct<|>
114
115 pub mod PubMod {
116 pub struct PubStruct;
117 }
118 ",
119 r"
120 use PubMod::PubStruct;
121
122 PubStruct<|>
123
124 pub mod PubMod {
125 pub struct PubStruct;
126 }
127 ",
128 );
129 }
130
131 #[test]
132 fn applicable_when_found_multiple_imports() {
133 check_assist_with_imports_locator(
134 auto_import,
135 TestImportsLocator::new,
136 r"
137 PubStruct<|>
138
139 pub mod PubMod1 {
140 pub struct PubStruct;
141 }
142 pub mod PubMod2 {
143 pub struct PubStruct;
144 }
145 pub mod PubMod3 {
146 pub struct PubStruct;
147 }
148 ",
149 r"
150 use PubMod1::PubStruct;
151
152 PubStruct<|>
153
154 pub mod PubMod1 {
155 pub struct PubStruct;
156 }
157 pub mod PubMod2 {
158 pub struct PubStruct;
159 }
160 pub mod PubMod3 {
161 pub struct PubStruct;
162 }
163 ",
164 );
165 }
166
167 #[test]
168 fn not_applicable_for_already_imported_types() {
169 check_assist_with_imports_locator_not_applicable(
170 auto_import,
171 TestImportsLocator::new,
172 r"
173 use PubMod::PubStruct;
174
175 PubStruct<|>
176
177 pub mod PubMod {
178 pub struct PubStruct;
179 }
180 ",
181 );
182 }
183
184 #[test]
185 fn not_applicable_for_types_with_private_paths() {
186 check_assist_with_imports_locator_not_applicable(
187 auto_import,
188 TestImportsLocator::new,
189 r"
190 PrivateStruct<|>
191
192 pub mod PubMod {
193 struct PrivateStruct;
194 }
195 ",
196 );
197 }
198
199 #[test]
200 fn not_applicable_when_no_imports_found() {
201 check_assist_with_imports_locator_not_applicable(
202 auto_import,
203 TestImportsLocator::new,
204 "
205 PubStruct<|>",
206 );
207 }
208
209 #[test]
210 fn not_applicable_in_import_statements() {
211 check_assist_with_imports_locator_not_applicable(
212 auto_import,
213 TestImportsLocator::new,
214 r"
215 use PubStruct<|>;
216
217 pub mod PubMod {
218 pub struct PubStruct;
219 }",
220 );
221 }
222}
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
index 5dc1ee233..65d51428b 100644
--- a/crates/ra_assists/src/doc_tests.rs
+++ b/crates/ra_assists/src/doc_tests.rs
@@ -11,6 +11,10 @@ use test_utils::{assert_eq_text, extract_range_or_offset};
11use crate::test_db::TestDB; 11use crate::test_db::TestDB;
12 12
13fn check(assist_id: &str, before: &str, after: &str) { 13fn check(assist_id: &str, before: &str, after: &str) {
14 // FIXME we cannot get the imports search functionality here yet, but still need to generate a test and a doc for an assist
15 if assist_id == "auto_import" {
16 return;
17 }
14 let (selection, before) = extract_range_or_offset(before); 18 let (selection, before) = extract_range_or_offset(before);
15 let (db, file_id) = TestDB::with_single_file(&before); 19 let (db, file_id) = TestDB::with_single_file(&before);
16 let frange = FileRange { file_id, range: selection.into() }; 20 let frange = FileRange { file_id, range: selection.into() };
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 7d84dc8fb..ec4587ce7 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -215,6 +215,25 @@ fn main() {
215} 215}
216 216
217#[test] 217#[test]
218fn doctest_auto_import() {
219 check(
220 "auto_import",
221 r#####"
222fn main() {
223 let map = HashMap<|>::new();
224}
225"#####,
226 r#####"
227use std::collections::HashMap;
228
229fn main() {
230 let map = HashMap<|>::new();
231}
232"#####,
233 )
234}
235
236#[test]
218fn doctest_change_visibility() { 237fn doctest_change_visibility() {
219 check( 238 check(
220 "change_visibility", 239 "change_visibility",
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3337805a5..625ebc4a2 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -14,7 +14,7 @@ mod test_db;
14pub mod ast_transform; 14pub mod ast_transform;
15 15
16use either::Either; 16use either::Either;
17use hir::db::HirDatabase; 17use hir::{db::HirDatabase, ModuleDef};
18use ra_db::FileRange; 18use ra_db::FileRange;
19use ra_syntax::{TextRange, TextUnit}; 19use ra_syntax::{TextRange, TextUnit};
20use ra_text_edit::TextEdit; 20use ra_text_edit::TextEdit;
@@ -77,6 +77,51 @@ where
77 }) 77 })
78} 78}
79 79
80/// A functionality for locating imports for the given name.
81///
82/// Currently has to be a trait with the real implementation provided by the ra_ide_api crate,
83/// due to the search functionality located there.
84/// Later, this trait should be removed completely and the search functionality moved to a separate crate,
85/// accessible from the ra_assists crate.
86pub trait ImportsLocator {
87 /// Finds all imports for the given name and the module that contains this name.
88 fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef>;
89}
90
91/// Return all the assists applicable at the given position
92/// and additional assists that need the imports locator functionality to work.
93///
94/// Assists are returned in the "resolved" state, that is with edit fully
95/// computed.
96pub fn assists_with_imports_locator<H, F>(
97 db: &H,
98 range: FileRange,
99 mut imports_locator: F,
100) -> Vec<ResolvedAssist>
101where
102 H: HirDatabase + 'static,
103 F: ImportsLocator,
104{
105 AssistCtx::with_ctx(db, range, true, |ctx| {
106 let mut assists = assists::all()
107 .iter()
108 .map(|f| f(ctx.clone()))
109 .chain(
110 assists::all_with_imports_locator()
111 .iter()
112 .map(|f| f(ctx.clone(), &mut imports_locator)),
113 )
114 .filter_map(std::convert::identity)
115 .map(|a| match a {
116 Assist::Resolved { assist } => assist,
117 Assist::Unresolved { .. } => unreachable!(),
118 })
119 .collect();
120 sort_assists(&mut assists);
121 assists
122 })
123}
124
80/// Return all the assists applicable at the given position. 125/// Return all the assists applicable at the given position.
81/// 126///
82/// Assists are returned in the "resolved" state, that is with edit fully 127/// Assists are returned in the "resolved" state, that is with edit fully
@@ -85,8 +130,6 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist>
85where 130where
86 H: HirDatabase + 'static, 131 H: HirDatabase + 'static,
87{ 132{
88 use std::cmp::Ordering;
89
90 AssistCtx::with_ctx(db, range, true, |ctx| { 133 AssistCtx::with_ctx(db, range, true, |ctx| {
91 let mut a = assists::all() 134 let mut a = assists::all()
92 .iter() 135 .iter()
@@ -95,19 +138,24 @@ where
95 Assist::Resolved { assist } => assist, 138 Assist::Resolved { assist } => assist,
96 Assist::Unresolved { .. } => unreachable!(), 139 Assist::Unresolved { .. } => unreachable!(),
97 }) 140 })
98 .collect::<Vec<_>>(); 141 .collect();
99 a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { 142 sort_assists(&mut a);
100 (Some(a), Some(b)) => a.len().cmp(&b.len()),
101 (Some(_), None) => Ordering::Less,
102 (None, Some(_)) => Ordering::Greater,
103 (None, None) => Ordering::Equal,
104 });
105 a 143 a
106 }) 144 })
107} 145}
108 146
147fn sort_assists(assists: &mut Vec<ResolvedAssist>) {
148 use std::cmp::Ordering;
149 assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) {
150 (Some(a), Some(b)) => a.len().cmp(&b.len()),
151 (Some(_), None) => Ordering::Less,
152 (None, Some(_)) => Ordering::Greater,
153 (None, None) => Ordering::Equal,
154 });
155}
156
109mod assists { 157mod assists {
110 use crate::{Assist, AssistCtx}; 158 use crate::{Assist, AssistCtx, ImportsLocator};
111 use hir::db::HirDatabase; 159 use hir::db::HirDatabase;
112 160
113 mod add_derive; 161 mod add_derive;
@@ -116,6 +164,7 @@ mod assists {
116 mod add_custom_impl; 164 mod add_custom_impl;
117 mod add_new; 165 mod add_new;
118 mod apply_demorgan; 166 mod apply_demorgan;
167 mod auto_import;
119 mod invert_if; 168 mod invert_if;
120 mod flip_comma; 169 mod flip_comma;
121 mod flip_binexpr; 170 mod flip_binexpr;
@@ -168,15 +217,69 @@ mod assists {
168 early_return::convert_to_guarded_return, 217 early_return::convert_to_guarded_return,
169 ] 218 ]
170 } 219 }
220
221 pub(crate) fn all_with_imports_locator<'a, DB: HirDatabase, F: ImportsLocator>(
222 ) -> &'a [fn(AssistCtx<DB>, &mut F) -> Option<Assist>] {
223 &[auto_import::auto_import]
224 }
171} 225}
172 226
173#[cfg(test)] 227#[cfg(test)]
174mod helpers { 228mod helpers {
175 use ra_db::{fixture::WithFixture, FileRange}; 229 use hir::db::DefDatabase;
230 use ra_db::{fixture::WithFixture, FileId, FileRange};
176 use ra_syntax::TextRange; 231 use ra_syntax::TextRange;
177 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 232 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
178 233
179 use crate::{test_db::TestDB, Assist, AssistCtx}; 234 use crate::{test_db::TestDB, Assist, AssistCtx, ImportsLocator};
235 use std::sync::Arc;
236
237 // FIXME remove the `ModuleDefId` reexport from `ra_hir` when this gets removed.
238 pub(crate) struct TestImportsLocator {
239 db: Arc<TestDB>,
240 test_file_id: FileId,
241 }
242
243 impl TestImportsLocator {
244 pub(crate) fn new(db: Arc<TestDB>, test_file_id: FileId) -> Self {
245 TestImportsLocator { db, test_file_id }
246 }
247 }
248
249 impl ImportsLocator for TestImportsLocator {
250 fn find_imports(&mut self, name_to_import: &str) -> Vec<hir::ModuleDef> {
251 let crate_def_map = self.db.crate_def_map(self.db.test_crate());
252 let mut findings = Vec::new();
253
254 let mut module_ids_to_process =
255 crate_def_map.modules_for_file(self.test_file_id).collect::<Vec<_>>();
256
257 while !module_ids_to_process.is_empty() {
258 let mut more_ids_to_process = Vec::new();
259 for local_module_id in module_ids_to_process.drain(..) {
260 for (name, namespace_data) in
261 crate_def_map[local_module_id].scope.entries_without_primitives()
262 {
263 let found_a_match = &name.to_string() == name_to_import;
264 vec![namespace_data.types, namespace_data.values]
265 .into_iter()
266 .filter_map(std::convert::identity)
267 .for_each(|(module_def_id, _)| {
268 if found_a_match {
269 findings.push(module_def_id.into());
270 }
271 if let hir::ModuleDefId::ModuleId(module_id) = module_def_id {
272 more_ids_to_process.push(module_id.local_id);
273 }
274 });
275 }
276 }
277 module_ids_to_process = more_ids_to_process;
278 }
279
280 findings
281 }
282 }
180 283
181 pub(crate) fn check_assist( 284 pub(crate) fn check_assist(
182 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 285 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
@@ -206,6 +309,38 @@ mod helpers {
206 assert_eq_text!(after, &actual); 309 assert_eq_text!(after, &actual);
207 } 310 }
208 311
312 pub(crate) fn check_assist_with_imports_locator<F: ImportsLocator>(
313 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
314 imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F,
315 before: &str,
316 after: &str,
317 ) {
318 let (before_cursor_pos, before) = extract_offset(before);
319 let (db, file_id) = TestDB::with_single_file(&before);
320 let db = Arc::new(db);
321 let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id);
322 let frange =
323 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
324 let assist =
325 AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator))
326 .expect("code action is not applicable");
327 let action = match assist {
328 Assist::Unresolved { .. } => unreachable!(),
329 Assist::Resolved { assist } => assist.get_first_action(),
330 };
331
332 let actual = action.edit.apply(&before);
333 let actual_cursor_pos = match action.cursor_position {
334 None => action
335 .edit
336 .apply_to_offset(before_cursor_pos)
337 .expect("cursor position is affected by the edit"),
338 Some(off) => off,
339 };
340 let actual = add_cursor(&actual, actual_cursor_pos);
341 assert_eq_text!(after, &actual);
342 }
343
209 pub(crate) fn check_assist_range( 344 pub(crate) fn check_assist_range(
210 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 345 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
211 before: &str, 346 before: &str,
@@ -279,6 +414,22 @@ mod helpers {
279 assert!(assist.is_none()); 414 assert!(assist.is_none());
280 } 415 }
281 416
417 pub(crate) fn check_assist_with_imports_locator_not_applicable<F: ImportsLocator>(
418 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
419 imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F,
420 before: &str,
421 ) {
422 let (before_cursor_pos, before) = extract_offset(before);
423 let (db, file_id) = TestDB::with_single_file(&before);
424 let db = Arc::new(db);
425 let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id);
426 let frange =
427 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
428 let assist =
429 AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator));
430 assert!(assist.is_none());
431 }
432
282 pub(crate) fn check_assist_range_not_applicable( 433 pub(crate) fn check_assist_range_not_applicable(
283 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 434 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
284 before: &str, 435 before: &str,
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml
index e88295539..49e06e0d3 100644
--- a/crates/ra_cargo_watch/Cargo.toml
+++ b/crates/ra_cargo_watch/Cargo.toml
@@ -13,5 +13,5 @@ jod-thread = "0.1.0"
13parking_lot = "0.10.0" 13parking_lot = "0.10.0"
14 14
15[dev-dependencies] 15[dev-dependencies]
16insta = "0.12.0" 16insta = "0.13.0"
17serde_json = "1.0" \ No newline at end of file 17serde_json = "1.0" \ No newline at end of file
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
index cb0920914..cb0920914 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
index 19510ecc1..19510ecc1 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
index 92f7eec05..92f7eec05 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
index cf683e4b6..cf683e4b6 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
index 8c1483c74..8c1483c74 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
index eb5a2247b..eb5a2247b 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
index 2f4518931..2f4518931 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index e1c7b7a20..9e2673d13 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -56,6 +56,7 @@ pub use hir_def::{
56 nameres::ModuleSource, 56 nameres::ModuleSource,
57 path::{ModPath, Path, PathKind}, 57 path::{ModPath, Path, PathKind},
58 type_ref::Mutability, 58 type_ref::Mutability,
59 ModuleDefId, // FIXME this is exposed and should be used for implementing the `TestImportsLocator` in `ra_assists` only, should be removed later along with the trait and the implementation.
59}; 60};
60pub use hir_expand::{ 61pub use hir_expand::{
61 name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, 62 name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin,
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 2c368f690..1efa00fe0 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -26,4 +26,4 @@ ra_cfg = { path = "../ra_cfg" }
26tt = { path = "../ra_tt", package = "ra_tt" } 26tt = { path = "../ra_tt", package = "ra_tt" }
27 27
28[dev-dependencies] 28[dev-dependencies]
29insta = "0.12.0" 29insta = "0.13.0"
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 60793db44..d229639d9 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -28,4 +28,4 @@ chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "ff65b5a
28lalrpop-intern = "0.15.1" 28lalrpop-intern = "0.15.1"
29 29
30[dev-dependencies] 30[dev-dependencies]
31insta = "0.12.0" 31insta = "0.13.0"
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 2c9f9dce0..53817d1f7 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -39,7 +39,7 @@ ra_assists = { path = "../ra_assists" }
39hir = { path = "../ra_hir", package = "ra_hir" } 39hir = { path = "../ra_hir", package = "ra_hir" }
40 40
41[dev-dependencies] 41[dev-dependencies]
42insta = "0.12.0" 42insta = "0.13.0"
43 43
44[dev-dependencies.proptest] 44[dev-dependencies.proptest]
45version = "0.9.0" 45version = "0.9.0"
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index a936900da..c43c45c65 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -2,8 +2,9 @@
2 2
3use ra_db::{FilePosition, FileRange}; 3use ra_db::{FilePosition, FileRange};
4 4
5use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; 5use crate::{
6 6 db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit,
7};
7use either::Either; 8use either::Either;
8pub use ra_assists::AssistId; 9pub use ra_assists::AssistId;
9use ra_assists::{AssistAction, AssistLabel}; 10use ra_assists::{AssistAction, AssistLabel};
@@ -16,7 +17,7 @@ pub struct Assist {
16} 17}
17 18
18pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { 19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
19 ra_assists::assists(db, frange) 20 ra_assists::assists_with_imports_locator(db, frange, ImportsLocatorIde::new(db))
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;
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs
index b82259a3d..831438c09 100644
--- a/crates/ra_ide/src/expand.rs
+++ b/crates/ra_ide/src/expand.rs
@@ -79,6 +79,14 @@ pub(crate) fn descend_into_macros(
79 let source_analyzer = 79 let source_analyzer =
80 hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None); 80 hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None);
81 81
82 descend_into_macros_with_analyzer(db, &source_analyzer, src)
83}
84
85pub(crate) fn descend_into_macros_with_analyzer(
86 db: &RootDatabase,
87 source_analyzer: &hir::SourceAnalyzer,
88 src: InFile<SyntaxToken>,
89) -> InFile<SyntaxToken> {
82 successors(Some(src), |token| { 90 successors(Some(src), |token| {
83 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 91 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
84 let tt = macro_call.token_tree()?; 92 let tt = macro_call.token_tree()?;
diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs
new file mode 100644
index 000000000..48b014c7d
--- /dev/null
+++ b/crates/ra_ide/src/imports_locator.rs
@@ -0,0 +1,76 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3
4use crate::{
5 db::RootDatabase,
6 references::{classify_name, NameDefinition, NameKind},
7 symbol_index::{self, FileSymbol},
8 Query,
9};
10use hir::{db::HirDatabase, ModuleDef, SourceBinder};
11use ra_assists::ImportsLocator;
12use ra_prof::profile;
13use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
14
15pub(crate) struct ImportsLocatorIde<'a> {
16 source_binder: SourceBinder<'a, RootDatabase>,
17}
18
19impl<'a> ImportsLocatorIde<'a> {
20 pub(crate) fn new(db: &'a RootDatabase) -> Self {
21 Self { source_binder: SourceBinder::new(db) }
22 }
23
24 fn get_name_definition(
25 &mut self,
26 db: &impl HirDatabase,
27 import_candidate: &FileSymbol,
28 ) -> Option<NameDefinition> {
29 let _p = profile("get_name_definition");
30 let file_id = import_candidate.file_id.into();
31 let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?);
32 let candidate_name_node = if candidate_node.kind() != NAME {
33 candidate_node.children().find(|it| it.kind() == NAME)?
34 } else {
35 candidate_node
36 };
37 classify_name(
38 &mut self.source_binder,
39 hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? },
40 )
41 }
42}
43
44impl ImportsLocator for ImportsLocatorIde<'_> {
45 fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> {
46 let _p = profile("search_for_imports");
47 let db = self.source_binder.db;
48
49 let project_results = {
50 let mut query = Query::new(name_to_import.to_string());
51 query.exact();
52 query.limit(40);
53 symbol_index::world_symbols(db, query)
54 };
55 let lib_results = {
56 let mut query = Query::new(name_to_import.to_string());
57 query.libs();
58 query.exact();
59 query.limit(40);
60 symbol_index::world_symbols(db, query)
61 };
62
63 project_results
64 .into_iter()
65 .chain(lib_results.into_iter())
66 .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate))
67 .filter_map(|name_definition_to_import| {
68 if let NameKind::Def(module_def) = name_definition_to_import.kind {
69 Some(module_def)
70 } else {
71 None
72 }
73 })
74 .collect()
75 }
76}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 62fe6d2a9..03ad6b2c1 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -30,6 +30,7 @@ mod syntax_highlighting;
30mod parent_module; 30mod parent_module;
31mod references; 31mod references;
32mod impls; 32mod impls;
33mod imports_locator;
33mod assists; 34mod assists;
34mod diagnostics; 35mod diagnostics;
35mod syntax_tree; 36mod syntax_tree;
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 1d130544f..1cc55e78b 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -34,6 +34,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
34 <span class="function">foo</span>::&lt;<span class="type.builtin">i32</span>&gt;(); 34 <span class="function">foo</span>::&lt;<span class="type.builtin">i32</span>&gt;();
35} 35}
36 36
37<span class="macro">macro_rules</span><span class="macro">!</span> def_fn {
38 ($($tt:tt)*) =&gt; {$($tt)*}
39}
40
41<span class="macro">def_fn</span><span class="macro">!</span>{
42 <span class="keyword">fn</span> <span class="function">bar</span>() -&gt; <span class="type.builtin">u32</span> {
43 <span class="literal.numeric">100</span>
44 }
45}
46
37<span class="comment">// comment</span> 47<span class="comment">// comment</span>
38<span class="keyword">fn</span> <span class="function">main</span>() { 48<span class="keyword">fn</span> <span class="function">main</span>() {
39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); 49 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>);
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index d90ee8540..918fd4b97 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -24,14 +24,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
24.keyword\.control { color: #F0DFAF; font-weight: bold; } 24.keyword\.control { color: #F0DFAF; font-weight: bold; }
25</style> 25</style>
26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() { 26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
27 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>; 27 <span class="keyword">let</span> <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string(); 28 <span class="keyword">let</span> <span class="variable" data-binding-hash="4303609361109701698" style="color: hsl(242,75%,88%);">x</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
29 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string(); 29 <span class="keyword">let</span> <span class="variable" data-binding-hash="13865792086344377029" style="color: hsl(340,64%,86%);">y</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
30 30
31 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>; 31 <span class="keyword">let</span> <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span> = <span class="string">"other color please!"</span>;
32 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.to_string(); 32 <span class="keyword">let</span> <span class="variable" data-binding-hash="12461245066629867975" style="color: hsl(132,91%,68%);">y</span> = <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span>.to_string();
33} 33}
34 34
35<span class="keyword">fn</span> <span class="function">bar</span>() { 35<span class="keyword">fn</span> <span class="function">bar</span>() {
36 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>; 36 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
37}</code></pre> \ No newline at end of file 37}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0411977b9..530b984fc 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,14 +1,18 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::FxHashMap;
4 4
5use hir::{InFile, Name, SourceBinder}; 5use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder};
6use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
7use ra_prof::profile; 7use ra_prof::profile;
8use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; 8use ra_syntax::{
9 ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange,
10 WalkEvent, T,
11};
9 12
10use crate::{ 13use crate::{
11 db::RootDatabase, 14 db::RootDatabase,
15 expand::descend_into_macros_with_analyzer,
12 references::{ 16 references::{
13 classify_name, classify_name_ref, 17 classify_name, classify_name_ref,
14 NameKind::{self, *}, 18 NameKind::{self, *},
@@ -72,121 +76,186 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
72 let parse = db.parse(file_id); 76 let parse = db.parse(file_id);
73 let root = parse.tree().syntax().clone(); 77 let root = parse.tree().syntax().clone();
74 78
75 fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 { 79 let mut sb = SourceBinder::new(db);
76 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { 80 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
77 use std::{collections::hash_map::DefaultHasher, hash::Hasher}; 81 let mut res = Vec::new();
82 let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None);
78 83
79 let mut hasher = DefaultHasher::new(); 84 let mut in_macro_call = None;
80 x.hash(&mut hasher); 85
81 hasher.finish() 86 for event in root.preorder_with_tokens() {
87 match event {
88 WalkEvent::Enter(node) => match node.kind() {
89 MACRO_CALL => {
90 in_macro_call = Some(node.clone());
91 if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) {
92 res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None });
93 }
94 }
95 _ if in_macro_call.is_some() => {
96 if let Some(token) = node.as_token() {
97 if let Some((tag, binding_hash)) = highlight_token_tree(
98 db,
99 &mut sb,
100 &analyzer,
101 &mut bindings_shadow_count,
102 InFile::new(file_id.into(), token.clone()),
103 ) {
104 res.push(HighlightedRange {
105 range: node.text_range(),
106 tag,
107 binding_hash,
108 });
109 }
110 }
111 }
112 _ => {
113 if let Some((tag, binding_hash)) = highlight_node(
114 db,
115 &mut sb,
116 &mut bindings_shadow_count,
117 InFile::new(file_id.into(), node.clone()),
118 ) {
119 res.push(HighlightedRange { range: node.text_range(), tag, binding_hash });
120 }
121 }
122 },
123 WalkEvent::Leave(node) => {
124 if let Some(m) = in_macro_call.as_ref() {
125 if *m == node {
126 in_macro_call = None;
127 }
128 }
129 }
82 } 130 }
131 }
83 132
84 hash((file_id, name, shadow_count)) 133 res
134}
135
136fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> {
137 let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?;
138 let path = macro_call.path()?;
139 let name_ref = path.segment()?.name_ref()?;
140
141 let range_start = name_ref.syntax().text_range().start();
142 let mut range_end = name_ref.syntax().text_range().end();
143 for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
144 match sibling.kind() {
145 T![!] | IDENT => range_end = sibling.text_range().end(),
146 _ => (),
147 }
85 } 148 }
86 149
87 let mut sb = SourceBinder::new(db); 150 Some(TextRange::from_to(range_start, range_end))
151}
88 152
89 // Visited nodes to handle highlighting priorities 153fn highlight_token_tree(
90 // FIXME: retain only ranges here 154 db: &RootDatabase,
91 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); 155 sb: &mut SourceBinder<RootDatabase>,
92 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 156 analyzer: &SourceAnalyzer,
157 bindings_shadow_count: &mut FxHashMap<Name, u32>,
158 token: InFile<SyntaxToken>,
159) -> Option<(&'static str, Option<u64>)> {
160 if token.value.parent().kind() != TOKEN_TREE {
161 return None;
162 }
163 let token = descend_into_macros_with_analyzer(db, analyzer, token);
164 let expanded = {
165 let parent = token.value.parent();
166 // We only care Name and Name_ref
167 match (token.value.kind(), parent.kind()) {
168 (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()),
169 _ => token.map(|it| it.into()),
170 }
171 };
93 172
94 let mut res = Vec::new(); 173 highlight_node(db, sb, bindings_shadow_count, expanded)
95 for node in root.descendants_with_tokens() { 174}
96 if highlighted.contains(&node) { 175
97 continue; 176fn highlight_node(
177 db: &RootDatabase,
178 sb: &mut SourceBinder<RootDatabase>,
179 bindings_shadow_count: &mut FxHashMap<Name, u32>,
180 node: InFile<SyntaxElement>,
181) -> Option<(&'static str, Option<u64>)> {
182 let mut binding_hash = None;
183 let tag = match node.value.kind() {
184 FN_DEF => {
185 bindings_shadow_count.clear();
186 return None;
98 } 187 }
99 let mut binding_hash = None; 188 COMMENT => tags::LITERAL_COMMENT,
100 let tag = match node.kind() { 189 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING,
101 FN_DEF => { 190 ATTR => tags::LITERAL_ATTRIBUTE,
102 bindings_shadow_count.clear(); 191 // Special-case field init shorthand
103 continue; 192 NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD,
104 } 193 NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None,
105 COMMENT => tags::LITERAL_COMMENT, 194 NAME_REF => {
106 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, 195 let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
107 ATTR => tags::LITERAL_ATTRIBUTE, 196 let name_kind = classify_name_ref(sb, node.with_value(&name_ref)).map(|d| d.kind);
108 // Special-case field init shorthand 197 match name_kind {
109 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, 198 Some(name_kind) => {
110 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, 199 if let Local(local) = &name_kind {
111 NAME_REF => { 200 if let Some(name) = local.name(db) {
112 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 201 let shadow_count =
113 let name_kind = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) 202 bindings_shadow_count.entry(name.clone()).or_default();
114 .map(|d| d.kind); 203 binding_hash =
115 match name_kind { 204 Some(calc_binding_hash(node.file_id, &name, *shadow_count))
116 Some(name_kind) => { 205 }
117 if let Local(local) = &name_kind { 206 };
118 if let Some(name) = local.name(db) { 207
119 let shadow_count = 208 highlight_name(db, name_kind)
120 bindings_shadow_count.entry(name.clone()).or_default();
121 binding_hash =
122 Some(calc_binding_hash(file_id, &name, *shadow_count))
123 }
124 };
125
126 highlight_name(db, name_kind)
127 }
128 _ => continue,
129 }
130 }
131 NAME => {
132 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
133 let name_kind =
134 classify_name(&mut sb, InFile::new(file_id.into(), &name)).map(|d| d.kind);
135
136 if let Some(Local(local)) = &name_kind {
137 if let Some(name) = local.name(db) {
138 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
139 *shadow_count += 1;
140 binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
141 }
142 };
143
144 match name_kind {
145 Some(name_kind) => highlight_name(db, name_kind),
146 None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() {
147 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE,
148 TYPE_PARAM => tags::TYPE_PARAM,
149 RECORD_FIELD_DEF => tags::FIELD,
150 _ => tags::FUNCTION,
151 }),
152 } 209 }
210 _ => return None,
153 } 211 }
154 INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, 212 }
155 BYTE => tags::LITERAL_BYTE, 213 NAME => {
156 CHAR => tags::LITERAL_CHAR, 214 let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap();
157 LIFETIME => tags::TYPE_LIFETIME, 215 let name_kind = classify_name(sb, node.with_value(&name)).map(|d| d.kind);
158 T![unsafe] => tags::KEYWORD_UNSAFE, 216
159 k if is_control_keyword(k) => tags::KEYWORD_CONTROL, 217 if let Some(Local(local)) = &name_kind {
160 k if k.is_keyword() => tags::KEYWORD, 218 if let Some(name) = local.name(db) {
161 _ => { 219 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
162 if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { 220 *shadow_count += 1;
163 if let Some(path) = macro_call.path() { 221 binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count))
164 if let Some(segment) = path.segment() {
165 if let Some(name_ref) = segment.name_ref() {
166 highlighted.insert(name_ref.syntax().clone().into());
167 let range_start = name_ref.syntax().text_range().start();
168 let mut range_end = name_ref.syntax().text_range().end();
169 for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
170 match sibling.kind() {
171 T![!] | IDENT => range_end = sibling.text_range().end(),
172 _ => (),
173 }
174 }
175 res.push(HighlightedRange {
176 range: TextRange::from_to(range_start, range_end),
177 tag: tags::MACRO,
178 binding_hash: None,
179 })
180 }
181 }
182 }
183 } 222 }
184 continue; 223 };
224
225 match name_kind {
226 Some(name_kind) => highlight_name(db, name_kind),
227 None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() {
228 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE,
229 TYPE_PARAM => tags::TYPE_PARAM,
230 RECORD_FIELD_DEF => tags::FIELD,
231 _ => tags::FUNCTION,
232 }),
185 } 233 }
186 }; 234 }
187 res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }) 235 INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC,
236 BYTE => tags::LITERAL_BYTE,
237 CHAR => tags::LITERAL_CHAR,
238 LIFETIME => tags::TYPE_LIFETIME,
239 T![unsafe] => tags::KEYWORD_UNSAFE,
240 k if is_control_keyword(k) => tags::KEYWORD_CONTROL,
241 k if k.is_keyword() => tags::KEYWORD,
242
243 _ => return None,
244 };
245
246 return Some((tag, binding_hash));
247
248 fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 {
249 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
250 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
251
252 let mut hasher = DefaultHasher::new();
253 x.hash(&mut hasher);
254 hasher.finish()
255 }
256
257 hash((file_id, name, shadow_count))
188 } 258 }
189 res
190} 259}
191 260
192pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { 261pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
@@ -331,6 +400,16 @@ fn foo<T>() -> T {
331 foo::<i32>(); 400 foo::<i32>();
332} 401}
333 402
403macro_rules! def_fn {
404 ($($tt:tt)*) => {$($tt)*}
405}
406
407def_fn!{
408 fn bar() -> u32 {
409 100
410 }
411}
412
334// comment 413// comment
335fn main() { 414fn main() {
336 println!("Hello, {}!", 92); 415 println!("Hello, {}!", 92);