diff options
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r-- | crates/ra_ide/src/assists.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide/src/change.rs | 61 | ||||
-rw-r--r-- | crates/ra_ide/src/expand.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide/src/imports_locator.rs | 76 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/references.rs | 138 | ||||
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 16 | ||||
-rw-r--r-- | crates/ra_ide/src/snapshots/highlighting.html | 10 | ||||
-rw-r--r-- | crates/ra_ide/src/snapshots/rainbow_highlighting.html | 12 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 289 |
10 files changed, 464 insertions, 157 deletions
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 | ||
3 | use ra_db::{FilePosition, FileRange}; | 3 | use ra_db::{FilePosition, FileRange}; |
4 | 4 | ||
5 | use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; | 5 | use crate::{ |
6 | 6 | db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit, | |
7 | }; | ||
7 | use either::Either; | 8 | use either::Either; |
8 | pub use ra_assists::AssistId; | 9 | pub use ra_assists::AssistId; |
9 | use ra_assists::{AssistAction, AssistLabel}; | 10 | use ra_assists::{AssistAction, AssistLabel}; |
@@ -16,7 +17,7 @@ pub struct Assist { | |||
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 | 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/change.rs b/crates/ra_ide/src/change.rs index b0aa2c8e0..45a58690b 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs | |||
@@ -166,13 +166,15 @@ impl LibraryData { | |||
166 | const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); | 166 | const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); |
167 | 167 | ||
168 | impl RootDatabase { | 168 | impl RootDatabase { |
169 | pub(crate) fn request_cancellation(&mut self) { | ||
170 | let _p = profile("RootDatabase::request_cancellation"); | ||
171 | self.salsa_runtime_mut().synthetic_write(Durability::LOW); | ||
172 | } | ||
173 | |||
169 | pub(crate) fn apply_change(&mut self, change: AnalysisChange) { | 174 | pub(crate) fn apply_change(&mut self, change: AnalysisChange) { |
170 | let _p = profile("RootDatabase::apply_change"); | 175 | let _p = profile("RootDatabase::apply_change"); |
176 | self.request_cancellation(); | ||
171 | log::info!("apply_change {:?}", change); | 177 | 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() { | 178 | if !change.new_roots.is_empty() { |
177 | let mut local_roots = Vec::clone(&self.local_roots()); | 179 | let mut local_roots = Vec::clone(&self.local_roots()); |
178 | for (root_id, is_local) in change.new_roots { | 180 | for (root_id, is_local) in change.new_roots { |
@@ -299,45 +301,74 @@ impl RootDatabase { | |||
299 | )*} | 301 | )*} |
300 | } | 302 | } |
301 | sweep_each_query![ | 303 | sweep_each_query![ |
304 | // SourceDatabase | ||
302 | ra_db::ParseQuery | 305 | ra_db::ParseQuery |
303 | ra_db::SourceRootCratesQuery | 306 | ra_db::SourceRootCratesQuery |
307 | |||
308 | // AstDatabase | ||
304 | hir::db::AstIdMapQuery | 309 | hir::db::AstIdMapQuery |
305 | hir::db::ParseMacroQuery | 310 | hir::db::InternMacroQuery |
306 | hir::db::MacroDefQuery | ||
307 | hir::db::MacroArgQuery | 311 | hir::db::MacroArgQuery |
312 | hir::db::MacroDefQuery | ||
313 | hir::db::ParseMacroQuery | ||
308 | hir::db::MacroExpandQuery | 314 | hir::db::MacroExpandQuery |
315 | |||
316 | // DefDatabase | ||
317 | hir::db::RawItemsQuery | ||
318 | hir::db::ComputeCrateDefMapQuery | ||
309 | hir::db::StructDataQuery | 319 | hir::db::StructDataQuery |
320 | hir::db::UnionDataQuery | ||
310 | hir::db::EnumDataQuery | 321 | hir::db::EnumDataQuery |
322 | hir::db::ImplDataQuery | ||
311 | hir::db::TraitDataQuery | 323 | hir::db::TraitDataQuery |
312 | hir::db::RawItemsQuery | ||
313 | hir::db::ComputeCrateDefMapQuery | ||
314 | hir::db::GenericParamsQuery | ||
315 | hir::db::FunctionDataQuery | ||
316 | hir::db::TypeAliasDataQuery | 324 | hir::db::TypeAliasDataQuery |
325 | hir::db::FunctionDataQuery | ||
317 | hir::db::ConstDataQuery | 326 | hir::db::ConstDataQuery |
318 | hir::db::StaticDataQuery | 327 | hir::db::StaticDataQuery |
328 | hir::db::BodyWithSourceMapQuery | ||
329 | hir::db::BodyQuery | ||
330 | hir::db::ExprScopesQuery | ||
331 | hir::db::GenericParamsQuery | ||
332 | hir::db::AttrsQuery | ||
319 | hir::db::ModuleLangItemsQuery | 333 | hir::db::ModuleLangItemsQuery |
320 | hir::db::CrateLangItemsQuery | 334 | hir::db::CrateLangItemsQuery |
321 | hir::db::LangItemQuery | 335 | hir::db::LangItemQuery |
322 | hir::db::DocumentationQuery | 336 | hir::db::DocumentationQuery |
323 | hir::db::ExprScopesQuery | 337 | |
338 | // InternDatabase | ||
339 | hir::db::InternFunctionQuery | ||
340 | hir::db::InternStructQuery | ||
341 | hir::db::InternUnionQuery | ||
342 | hir::db::InternEnumQuery | ||
343 | hir::db::InternConstQuery | ||
344 | hir::db::InternStaticQuery | ||
345 | hir::db::InternTraitQuery | ||
346 | hir::db::InternTypeAliasQuery | ||
347 | hir::db::InternImplQuery | ||
348 | |||
349 | // HirDatabase | ||
324 | hir::db::DoInferQuery | 350 | hir::db::DoInferQuery |
325 | hir::db::TyQuery | 351 | hir::db::TyQuery |
326 | hir::db::ValueTyQuery | 352 | hir::db::ValueTyQuery |
353 | hir::db::ImplSelfTyQuery | ||
354 | hir::db::ImplTraitQuery | ||
327 | hir::db::FieldTypesQuery | 355 | hir::db::FieldTypesQuery |
328 | hir::db::CallableItemSignatureQuery | 356 | hir::db::CallableItemSignatureQuery |
357 | hir::db::GenericPredicatesForParamQuery | ||
329 | hir::db::GenericPredicatesQuery | 358 | hir::db::GenericPredicatesQuery |
330 | hir::db::GenericDefaultsQuery | 359 | hir::db::GenericDefaultsQuery |
331 | hir::db::BodyWithSourceMapQuery | ||
332 | hir::db::BodyQuery | ||
333 | hir::db::ImplsInCrateQuery | 360 | hir::db::ImplsInCrateQuery |
334 | hir::db::ImplsForTraitQuery | 361 | hir::db::ImplsForTraitQuery |
362 | hir::db::TraitSolverQuery | ||
363 | hir::db::InternTypeCtorQuery | ||
364 | hir::db::InternChalkImplQuery | ||
365 | hir::db::InternAssocTyValueQuery | ||
335 | hir::db::AssociatedTyDataQuery | 366 | hir::db::AssociatedTyDataQuery |
367 | hir::db::AssociatedTyValueQuery | ||
368 | hir::db::TraitSolveQuery | ||
336 | hir::db::TraitDatumQuery | 369 | hir::db::TraitDatumQuery |
337 | hir::db::StructDatumQuery | 370 | hir::db::StructDatumQuery |
338 | hir::db::ImplDatumQuery | 371 | hir::db::ImplDatumQuery |
339 | hir::db::ImplDataQuery | ||
340 | hir::db::TraitSolveQuery | ||
341 | ]; | 372 | ]; |
342 | acc.sort_by_key(|it| std::cmp::Reverse(it.1)); | 373 | acc.sort_by_key(|it| std::cmp::Reverse(it.1)); |
343 | acc | 374 | acc |
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 | |||
85 | pub(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 | |||
4 | use crate::{ | ||
5 | db::RootDatabase, | ||
6 | references::{classify_name, NameDefinition, NameKind}, | ||
7 | symbol_index::{self, FileSymbol}, | ||
8 | Query, | ||
9 | }; | ||
10 | use hir::{db::HirDatabase, ModuleDef, SourceBinder}; | ||
11 | use ra_assists::ImportsLocator; | ||
12 | use ra_prof::profile; | ||
13 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; | ||
14 | |||
15 | pub(crate) struct ImportsLocatorIde<'a> { | ||
16 | source_binder: SourceBinder<'a, RootDatabase>, | ||
17 | } | ||
18 | |||
19 | impl<'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 | |||
44 | impl 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 4d8deb21c..03ad6b2c1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -30,6 +30,7 @@ mod syntax_highlighting; | |||
30 | mod parent_module; | 30 | mod parent_module; |
31 | mod references; | 31 | mod references; |
32 | mod impls; | 32 | mod impls; |
33 | mod imports_locator; | ||
33 | mod assists; | 34 | mod assists; |
34 | mod diagnostics; | 35 | mod diagnostics; |
35 | mod syntax_tree; | 36 | mod syntax_tree; |
@@ -202,6 +203,9 @@ impl AnalysisHost { | |||
202 | pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { | 203 | pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { |
203 | self.db.per_query_memory_usage() | 204 | self.db.per_query_memory_usage() |
204 | } | 205 | } |
206 | pub fn request_cancellation(&mut self) { | ||
207 | self.db.request_cancellation(); | ||
208 | } | ||
205 | pub fn raw_database( | 209 | pub fn raw_database( |
206 | &self, | 210 | &self, |
207 | ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { | 211 | ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 5e2fe1905..ebded715d 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult { | |||
112 | 112 | ||
113 | pub(crate) fn find_all_refs( | 113 | pub(crate) fn find_all_refs( |
114 | db: &RootDatabase, | 114 | db: &RootDatabase, |
115 | mut position: FilePosition, | 115 | position: FilePosition, |
116 | search_scope: Option<SearchScope>, | 116 | search_scope: Option<SearchScope>, |
117 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 117 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
118 | let parse = db.parse(position.file_id); | 118 | let parse = db.parse(position.file_id); |
119 | let syntax = parse.tree().syntax().clone(); | 119 | let syntax = parse.tree().syntax().clone(); |
120 | 120 | ||
121 | let token = syntax.token_at_offset(position.offset); | 121 | let (opt_name, search_kind) = |
122 | let mut search_kind = ReferenceKind::Other; | 122 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { |
123 | (Some(name), ReferenceKind::StructLiteral) | ||
124 | } else { | ||
125 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) | ||
126 | }; | ||
123 | 127 | ||
124 | if let TokenAtOffset::Between(ref left, ref right) = token { | 128 | 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 | 129 | ||
135 | let declaration = match def.kind { | 130 | let declaration = match def.kind { |
136 | NameKind::Macro(mac) => mac.to_nav(db), | 131 | NameKind::Macro(mac) => mac.to_nav(db), |
@@ -170,9 +165,10 @@ fn find_name( | |||
170 | db: &RootDatabase, | 165 | db: &RootDatabase, |
171 | syntax: &SyntaxNode, | 166 | syntax: &SyntaxNode, |
172 | position: FilePosition, | 167 | position: FilePosition, |
168 | opt_name: Option<ast::Name>, | ||
173 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 169 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
174 | let mut sb = SourceBinder::new(db); | 170 | let mut sb = SourceBinder::new(db); |
175 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { | 171 | if let Some(name) = opt_name { |
176 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; | 172 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; |
177 | let range = name.syntax().text_range(); | 173 | let range = name.syntax().text_range(); |
178 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 174 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
@@ -218,15 +214,8 @@ fn process_definition( | |||
218 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) | 214 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) |
219 | { | 215 | { |
220 | if d == def { | 216 | if d == def { |
221 | let kind = if name_ref | 217 | let kind = if is_record_lit_name_ref(&name_ref) |
222 | .syntax() | 218 | || 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 | { | 219 | { |
231 | ReferenceKind::StructLiteral | 220 | ReferenceKind::StructLiteral |
232 | } else { | 221 | } else { |
@@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc | |||
301 | mode.or(Some(ReferenceAccess::Read)) | 290 | mode.or(Some(ReferenceAccess::Read)) |
302 | } | 291 | } |
303 | 292 | ||
293 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
294 | name_ref | ||
295 | .syntax() | ||
296 | .ancestors() | ||
297 | .find_map(ast::RecordLit::cast) | ||
298 | .and_then(|l| l.path()) | ||
299 | .and_then(|p| p.segment()) | ||
300 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
301 | .unwrap_or(false) | ||
302 | } | ||
303 | |||
304 | fn get_struct_def_name_for_struc_litetal_search( | ||
305 | syntax: &SyntaxNode, | ||
306 | position: FilePosition, | ||
307 | ) -> Option<ast::Name> { | ||
308 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | ||
309 | if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { | ||
310 | return None; | ||
311 | } | ||
312 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) { | ||
313 | return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); | ||
314 | } | ||
315 | if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() { | ||
316 | return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); | ||
317 | } | ||
318 | } | ||
319 | None | ||
320 | } | ||
321 | |||
322 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
323 | name_ref | ||
324 | .syntax() | ||
325 | .ancestors() | ||
326 | .find_map(ast::CallExpr::cast) | ||
327 | .and_then(|c| match c.expr()? { | ||
328 | ast::Expr::PathExpr(p) => { | ||
329 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
330 | } | ||
331 | _ => None, | ||
332 | }) | ||
333 | .unwrap_or(false) | ||
334 | } | ||
335 | |||
304 | #[cfg(test)] | 336 | #[cfg(test)] |
305 | mod tests { | 337 | mod tests { |
306 | use crate::{ | 338 | use crate::{ |
@@ -309,7 +341,7 @@ mod tests { | |||
309 | }; | 341 | }; |
310 | 342 | ||
311 | #[test] | 343 | #[test] |
312 | fn test_struct_literal() { | 344 | fn test_struct_literal_after_space() { |
313 | let code = r#" | 345 | let code = r#" |
314 | struct Foo <|>{ | 346 | struct Foo <|>{ |
315 | a: i32, | 347 | a: i32, |
@@ -331,6 +363,58 @@ mod tests { | |||
331 | } | 363 | } |
332 | 364 | ||
333 | #[test] | 365 | #[test] |
366 | fn test_struct_literal_befor_space() { | ||
367 | let code = r#" | ||
368 | struct Foo<|> {} | ||
369 | fn main() { | ||
370 | let f: Foo; | ||
371 | f = Foo {}; | ||
372 | }"#; | ||
373 | |||
374 | let refs = get_all_refs(code); | ||
375 | check_result( | ||
376 | refs, | ||
377 | "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other", | ||
378 | &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"], | ||
379 | ); | ||
380 | } | ||
381 | |||
382 | #[test] | ||
383 | fn test_struct_literal_with_generic_type() { | ||
384 | let code = r#" | ||
385 | struct Foo<T> <|>{} | ||
386 | fn main() { | ||
387 | let f: Foo::<i32>; | ||
388 | f = Foo {}; | ||
389 | }"#; | ||
390 | |||
391 | let refs = get_all_refs(code); | ||
392 | check_result( | ||
393 | refs, | ||
394 | "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", | ||
395 | &["FileId(1) [81; 84) StructLiteral"], | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
400 | fn test_struct_literal_for_tuple() { | ||
401 | let code = r#" | ||
402 | struct Foo<|>(i32); | ||
403 | |||
404 | fn main() { | ||
405 | let f: Foo; | ||
406 | f = Foo(1); | ||
407 | }"#; | ||
408 | |||
409 | let refs = get_all_refs(code); | ||
410 | check_result( | ||
411 | refs, | ||
412 | "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", | ||
413 | &["FileId(1) [71; 74) StructLiteral"], | ||
414 | ); | ||
415 | } | ||
416 | |||
417 | #[test] | ||
334 | fn test_find_all_refs_for_local() { | 418 | fn test_find_all_refs_for_local() { |
335 | let code = r#" | 419 | let code = r#" |
336 | fn main() { | 420 | fn main() { |
@@ -564,7 +648,7 @@ mod tests { | |||
564 | check_result( | 648 | check_result( |
565 | refs, | 649 | refs, |
566 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", | 650 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
567 | &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], | 651 | &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"], |
568 | ); | 652 | ); |
569 | 653 | ||
570 | let refs = | 654 | let refs = |
@@ -572,7 +656,7 @@ mod tests { | |||
572 | check_result( | 656 | check_result( |
573 | refs, | 657 | refs, |
574 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", | 658 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
575 | &["FileId(3) [16; 20) Other"], | 659 | &["FileId(3) [16; 20) StructLiteral"], |
576 | ); | 660 | ); |
577 | } | 661 | } |
578 | 662 | ||
@@ -591,7 +675,7 @@ mod tests { | |||
591 | check_result( | 675 | check_result( |
592 | refs, | 676 | refs, |
593 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", | 677 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", |
594 | &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], | 678 | &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"], |
595 | ); | 679 | ); |
596 | } | 680 | } |
597 | 681 | ||
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 7533692f6..8622dd956 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -43,7 +43,7 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | |||
43 | let name = fn_def.name()?.text().clone(); | 43 | let name = fn_def.name()?.text().clone(); |
44 | let kind = if name == "main" { | 44 | let kind = if name == "main" { |
45 | RunnableKind::Bin | 45 | RunnableKind::Bin |
46 | } else if fn_def.has_atom_attr("test") { | 46 | } else if has_test_related_attribute(&fn_def) { |
47 | RunnableKind::Test { name: name.to_string() } | 47 | RunnableKind::Test { name: name.to_string() } |
48 | } else if fn_def.has_atom_attr("bench") { | 48 | } else if fn_def.has_atom_attr("bench") { |
49 | RunnableKind::Bench { name: name.to_string() } | 49 | RunnableKind::Bench { name: name.to_string() } |
@@ -53,6 +53,20 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | |||
53 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 53 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) |
54 | } | 54 | } |
55 | 55 | ||
56 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
57 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
58 | /// Also a regular `#[test]` annotation is supported. | ||
59 | /// | ||
60 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
61 | /// but it's better than not to have the runnables for the tests at all. | ||
62 | fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | ||
63 | fn_def | ||
64 | .attrs() | ||
65 | .filter_map(|attr| attr.path()) | ||
66 | .map(|path| path.syntax().to_string().to_lowercase()) | ||
67 | .any(|attribute_text| attribute_text.contains("test")) | ||
68 | } | ||
69 | |||
56 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { | 70 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { |
57 | let has_test_function = module | 71 | let has_test_function = module |
58 | .item_list()? | 72 | .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/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 | ||
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_prof::profile; | 7 | use ra_prof::profile; |
8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; | 8 | use ra_syntax::{ |
9 | ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, | ||
10 | WalkEvent, T, | ||
11 | }; | ||
9 | 12 | ||
10 | use crate::{ | 13 | use 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 | |||
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 { |
@@ -331,6 +400,16 @@ fn foo<T>() -> T { | |||
331 | foo::<i32>(); | 400 | foo::<i32>(); |
332 | } | 401 | } |
333 | 402 | ||
403 | macro_rules! def_fn { | ||
404 | ($($tt:tt)*) => {$($tt)*} | ||
405 | } | ||
406 | |||
407 | def_fn!{ | ||
408 | fn bar() -> u32 { | ||
409 | 100 | ||
410 | } | ||
411 | } | ||
412 | |||
334 | // comment | 413 | // comment |
335 | fn main() { | 414 | fn main() { |
336 | println!("Hello, {}!", 92); | 415 | println!("Hello, {}!", 92); |