aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/annotations.rs100
-rw-r--r--crates/ide/src/diagnostics.rs72
-rw-r--r--crates/ide/src/doc_links.rs6
-rw-r--r--crates/ide/src/fixture.rs17
-rwxr-xr-xcrates/ide/src/folding_ranges.rs17
-rw-r--r--crates/ide/src/goto_implementation.rs70
-rw-r--r--crates/ide/src/goto_type_definition.rs32
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/lib.rs128
-rw-r--r--crates/ide/src/runnables.rs2
-rw-r--r--crates/ide/src/syntax_highlighting.rs101
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs107
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html18
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs44
15 files changed, 537 insertions, 185 deletions
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 5ebe7fd0e..8d68dce05 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -5,7 +5,7 @@ use ide_db::{
5 helpers::visit_file_defs, 5 helpers::visit_file_defs,
6 RootDatabase, 6 RootDatabase,
7}; 7};
8use syntax::{ast::NameOwner, AstNode, TextRange, TextSize}; 8use syntax::{ast::NameOwner, AstNode, TextRange};
9 9
10use crate::{ 10use crate::{
11 fn_references::find_all_methods, 11 fn_references::find_all_methods,
@@ -58,7 +58,7 @@ pub(crate) fn annotations(
58 } 58 }
59 59
60 let action = runnable.action(); 60 let action = runnable.action();
61 let range = runnable.nav.full_range; 61 let range = runnable.nav.focus_or_full_range();
62 62
63 if config.run { 63 if config.run {
64 annotations.push(Annotation { 64 annotations.push(Annotation {
@@ -80,26 +80,26 @@ pub(crate) fn annotations(
80 80
81 visit_file_defs(&Semantics::new(db), file_id, &mut |def| match def { 81 visit_file_defs(&Semantics::new(db), file_id, &mut |def| match def {
82 Either::Left(def) => { 82 Either::Left(def) => {
83 let node = match def { 83 let range = match def {
84 hir::ModuleDef::Const(konst) => { 84 hir::ModuleDef::Const(konst) => {
85 konst.source(db).and_then(|node| range_and_position_of(&node, file_id)) 85 konst.source(db).and_then(|node| name_range(&node, file_id))
86 } 86 }
87 hir::ModuleDef::Trait(trait_) => { 87 hir::ModuleDef::Trait(trait_) => {
88 trait_.source(db).and_then(|node| range_and_position_of(&node, file_id)) 88 trait_.source(db).and_then(|node| name_range(&node, file_id))
89 } 89 }
90 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { 90 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
91 strukt.source(db).and_then(|node| range_and_position_of(&node, file_id)) 91 strukt.source(db).and_then(|node| name_range(&node, file_id))
92 } 92 }
93 hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => { 93 hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => {
94 enum_.source(db).and_then(|node| range_and_position_of(&node, file_id)) 94 enum_.source(db).and_then(|node| name_range(&node, file_id))
95 } 95 }
96 hir::ModuleDef::Adt(hir::Adt::Union(union)) => { 96 hir::ModuleDef::Adt(hir::Adt::Union(union)) => {
97 union.source(db).and_then(|node| range_and_position_of(&node, file_id)) 97 union.source(db).and_then(|node| name_range(&node, file_id))
98 } 98 }
99 _ => None, 99 _ => None,
100 }; 100 };
101 let (offset, range) = match node { 101 let (range, offset) = match range {
102 Some(node) => node, 102 Some(range) => (range, range.start()),
103 None => return, 103 None => return,
104 }; 104 };
105 105
@@ -122,18 +122,12 @@ pub(crate) fn annotations(
122 }); 122 });
123 } 123 }
124 124
125 fn range_and_position_of<T: NameOwner>( 125 fn name_range<T: NameOwner>(node: &InFile<T>, file_id: FileId) -> Option<TextRange> {
126 node: &InFile<T>, 126 if node.file_id == file_id.into() {
127 file_id: FileId, 127 node.value.name().map(|it| it.syntax().text_range())
128 ) -> Option<(TextSize, TextRange)> { 128 } else {
129 if node.file_id != file_id.into() {
130 // Node is outside the file we are adding annotations to (e.g. macros). 129 // Node is outside the file we are adding annotations to (e.g. macros).
131 None 130 None
132 } else {
133 Some((
134 node.value.name()?.syntax().text_range().start(),
135 node.value.syntax().text_range(),
136 ))
137 } 131 }
138 } 132 }
139 } 133 }
@@ -141,13 +135,15 @@ pub(crate) fn annotations(
141 }); 135 });
142 136
143 if config.annotate_method_references { 137 if config.annotate_method_references {
144 annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { 138 annotations.extend(find_all_methods(db, file_id).into_iter().map(
145 range: method.range, 139 |FileRange { file_id, range }| Annotation {
146 kind: AnnotationKind::HasReferences { 140 range,
147 position: FilePosition { file_id, offset: method.range.start() }, 141 kind: AnnotationKind::HasReferences {
148 data: None, 142 position: FilePosition { file_id, offset: range.start() },
143 data: None,
144 },
149 }, 145 },
150 })); 146 ));
151 } 147 }
152 148
153 annotations 149 annotations
@@ -228,7 +224,7 @@ fn main() {
228 expect![[r#" 224 expect![[r#"
229 [ 225 [
230 Annotation { 226 Annotation {
231 range: 50..85, 227 range: 53..57,
232 kind: Runnable { 228 kind: Runnable {
233 debug: false, 229 debug: false,
234 runnable: Runnable { 230 runnable: Runnable {
@@ -247,7 +243,7 @@ fn main() {
247 }, 243 },
248 }, 244 },
249 Annotation { 245 Annotation {
250 range: 50..85, 246 range: 53..57,
251 kind: Runnable { 247 kind: Runnable {
252 debug: true, 248 debug: true,
253 runnable: Runnable { 249 runnable: Runnable {
@@ -266,7 +262,7 @@ fn main() {
266 }, 262 },
267 }, 263 },
268 Annotation { 264 Annotation {
269 range: 0..22, 265 range: 6..10,
270 kind: HasReferences { 266 kind: HasReferences {
271 position: FilePosition { 267 position: FilePosition {
272 file_id: FileId( 268 file_id: FileId(
@@ -287,7 +283,7 @@ fn main() {
287 }, 283 },
288 }, 284 },
289 Annotation { 285 Annotation {
290 range: 24..48, 286 range: 30..36,
291 kind: HasReferences { 287 kind: HasReferences {
292 position: FilePosition { 288 position: FilePosition {
293 file_id: FileId( 289 file_id: FileId(
@@ -332,7 +328,7 @@ fn main() {
332 expect![[r#" 328 expect![[r#"
333 [ 329 [
334 Annotation { 330 Annotation {
335 range: 14..48, 331 range: 17..21,
336 kind: Runnable { 332 kind: Runnable {
337 debug: false, 333 debug: false,
338 runnable: Runnable { 334 runnable: Runnable {
@@ -351,7 +347,7 @@ fn main() {
351 }, 347 },
352 }, 348 },
353 Annotation { 349 Annotation {
354 range: 14..48, 350 range: 17..21,
355 kind: Runnable { 351 kind: Runnable {
356 debug: true, 352 debug: true,
357 runnable: Runnable { 353 runnable: Runnable {
@@ -370,7 +366,7 @@ fn main() {
370 }, 366 },
371 }, 367 },
372 Annotation { 368 Annotation {
373 range: 0..12, 369 range: 7..11,
374 kind: HasImpls { 370 kind: HasImpls {
375 position: FilePosition { 371 position: FilePosition {
376 file_id: FileId( 372 file_id: FileId(
@@ -384,7 +380,7 @@ fn main() {
384 }, 380 },
385 }, 381 },
386 Annotation { 382 Annotation {
387 range: 0..12, 383 range: 7..11,
388 kind: HasReferences { 384 kind: HasReferences {
389 position: FilePosition { 385 position: FilePosition {
390 file_id: FileId( 386 file_id: FileId(
@@ -440,7 +436,7 @@ fn main() {
440 expect![[r#" 436 expect![[r#"
441 [ 437 [
442 Annotation { 438 Annotation {
443 range: 66..100, 439 range: 69..73,
444 kind: Runnable { 440 kind: Runnable {
445 debug: false, 441 debug: false,
446 runnable: Runnable { 442 runnable: Runnable {
@@ -459,7 +455,7 @@ fn main() {
459 }, 455 },
460 }, 456 },
461 Annotation { 457 Annotation {
462 range: 66..100, 458 range: 69..73,
463 kind: Runnable { 459 kind: Runnable {
464 debug: true, 460 debug: true,
465 runnable: Runnable { 461 runnable: Runnable {
@@ -478,7 +474,7 @@ fn main() {
478 }, 474 },
479 }, 475 },
480 Annotation { 476 Annotation {
481 range: 0..12, 477 range: 7..11,
482 kind: HasImpls { 478 kind: HasImpls {
483 position: FilePosition { 479 position: FilePosition {
484 file_id: FileId( 480 file_id: FileId(
@@ -502,7 +498,7 @@ fn main() {
502 }, 498 },
503 }, 499 },
504 Annotation { 500 Annotation {
505 range: 0..12, 501 range: 7..11,
506 kind: HasReferences { 502 kind: HasReferences {
507 position: FilePosition { 503 position: FilePosition {
508 file_id: FileId( 504 file_id: FileId(
@@ -529,7 +525,7 @@ fn main() {
529 }, 525 },
530 }, 526 },
531 Annotation { 527 Annotation {
532 range: 14..34, 528 range: 20..31,
533 kind: HasImpls { 529 kind: HasImpls {
534 position: FilePosition { 530 position: FilePosition {
535 file_id: FileId( 531 file_id: FileId(
@@ -553,7 +549,7 @@ fn main() {
553 }, 549 },
554 }, 550 },
555 Annotation { 551 Annotation {
556 range: 14..34, 552 range: 20..31,
557 kind: HasReferences { 553 kind: HasReferences {
558 position: FilePosition { 554 position: FilePosition {
559 file_id: FileId( 555 file_id: FileId(
@@ -601,7 +597,7 @@ fn main() {}
601 expect![[r#" 597 expect![[r#"
602 [ 598 [
603 Annotation { 599 Annotation {
604 range: 0..12, 600 range: 3..7,
605 kind: Runnable { 601 kind: Runnable {
606 debug: false, 602 debug: false,
607 runnable: Runnable { 603 runnable: Runnable {
@@ -620,7 +616,7 @@ fn main() {}
620 }, 616 },
621 }, 617 },
622 Annotation { 618 Annotation {
623 range: 0..12, 619 range: 3..7,
624 kind: Runnable { 620 kind: Runnable {
625 debug: true, 621 debug: true,
626 runnable: Runnable { 622 runnable: Runnable {
@@ -674,7 +670,7 @@ fn main() {
674 expect![[r#" 670 expect![[r#"
675 [ 671 [
676 Annotation { 672 Annotation {
677 range: 58..95, 673 range: 61..65,
678 kind: Runnable { 674 kind: Runnable {
679 debug: false, 675 debug: false,
680 runnable: Runnable { 676 runnable: Runnable {
@@ -693,7 +689,7 @@ fn main() {
693 }, 689 },
694 }, 690 },
695 Annotation { 691 Annotation {
696 range: 58..95, 692 range: 61..65,
697 kind: Runnable { 693 kind: Runnable {
698 debug: true, 694 debug: true,
699 runnable: Runnable { 695 runnable: Runnable {
@@ -712,7 +708,7 @@ fn main() {
712 }, 708 },
713 }, 709 },
714 Annotation { 710 Annotation {
715 range: 0..12, 711 range: 7..11,
716 kind: HasImpls { 712 kind: HasImpls {
717 position: FilePosition { 713 position: FilePosition {
718 file_id: FileId( 714 file_id: FileId(
@@ -736,7 +732,7 @@ fn main() {
736 }, 732 },
737 }, 733 },
738 Annotation { 734 Annotation {
739 range: 0..12, 735 range: 7..11,
740 kind: HasReferences { 736 kind: HasReferences {
741 position: FilePosition { 737 position: FilePosition {
742 file_id: FileId( 738 file_id: FileId(
@@ -816,7 +812,7 @@ mod tests {
816 expect![[r#" 812 expect![[r#"
817 [ 813 [
818 Annotation { 814 Annotation {
819 range: 0..12, 815 range: 3..7,
820 kind: Runnable { 816 kind: Runnable {
821 debug: false, 817 debug: false,
822 runnable: Runnable { 818 runnable: Runnable {
@@ -835,7 +831,7 @@ mod tests {
835 }, 831 },
836 }, 832 },
837 Annotation { 833 Annotation {
838 range: 0..12, 834 range: 3..7,
839 kind: Runnable { 835 kind: Runnable {
840 debug: true, 836 debug: true,
841 runnable: Runnable { 837 runnable: Runnable {
@@ -854,7 +850,7 @@ mod tests {
854 }, 850 },
855 }, 851 },
856 Annotation { 852 Annotation {
857 range: 14..64, 853 range: 18..23,
858 kind: Runnable { 854 kind: Runnable {
859 debug: false, 855 debug: false,
860 runnable: Runnable { 856 runnable: Runnable {
@@ -875,7 +871,7 @@ mod tests {
875 }, 871 },
876 }, 872 },
877 Annotation { 873 Annotation {
878 range: 14..64, 874 range: 18..23,
879 kind: Runnable { 875 kind: Runnable {
880 debug: true, 876 debug: true,
881 runnable: Runnable { 877 runnable: Runnable {
@@ -896,7 +892,7 @@ mod tests {
896 }, 892 },
897 }, 893 },
898 Annotation { 894 Annotation {
899 range: 30..62, 895 range: 45..57,
900 kind: Runnable { 896 kind: Runnable {
901 debug: false, 897 debug: false,
902 runnable: Runnable { 898 runnable: Runnable {
@@ -922,7 +918,7 @@ mod tests {
922 }, 918 },
923 }, 919 },
924 Annotation { 920 Annotation {
925 range: 30..62, 921 range: 45..57,
926 kind: Runnable { 922 kind: Runnable {
927 debug: true, 923 debug: true,
928 runnable: Runnable { 924 runnable: Runnable {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 27d347dbd..d5c954b8b 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -182,6 +182,11 @@ pub(crate) fn diagnostics(
182 res.borrow_mut() 182 res.borrow_mut()
183 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code()))); 183 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code())));
184 }) 184 })
185 .on::<hir::diagnostics::UnimplementedBuiltinMacro, _>(|d| {
186 let display_range = sema.diagnostics_display_range(d.display_source()).range;
187 res.borrow_mut()
188 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
189 })
185 // Only collect experimental diagnostics when they're enabled. 190 // Only collect experimental diagnostics when they're enabled.
186 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 191 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
187 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 192 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
@@ -299,10 +304,10 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
299 304
300#[cfg(test)] 305#[cfg(test)]
301mod tests { 306mod tests {
302 use expect_test::{expect, Expect}; 307 use expect_test::Expect;
303 use ide_assists::AssistResolveStrategy; 308 use ide_assists::AssistResolveStrategy;
304 use stdx::trim_indent; 309 use stdx::trim_indent;
305 use test_utils::assert_eq_text; 310 use test_utils::{assert_eq_text, extract_annotations};
306 311
307 use crate::{fixture, DiagnosticsConfig}; 312 use crate::{fixture, DiagnosticsConfig};
308 313
@@ -311,6 +316,7 @@ mod tests {
311 /// * a diagnostic is produced 316 /// * a diagnostic is produced
312 /// * the first diagnostic fix trigger range touches the input cursor position 317 /// * the first diagnostic fix trigger range touches the input cursor position
313 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 318 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
319 #[track_caller]
314 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 320 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
315 check_nth_fix(0, ra_fixture_before, ra_fixture_after); 321 check_nth_fix(0, ra_fixture_before, ra_fixture_after);
316 } 322 }
@@ -325,6 +331,7 @@ mod tests {
325 } 331 }
326 } 332 }
327 333
334 #[track_caller]
328 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { 335 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
329 let after = trim_indent(ra_fixture_after); 336 let after = trim_indent(ra_fixture_after);
330 337
@@ -396,26 +403,51 @@ mod tests {
396 expect.assert_debug_eq(&diagnostics) 403 expect.assert_debug_eq(&diagnostics)
397 } 404 }
398 405
406 pub(crate) fn check_diagnostics(ra_fixture: &str) {
407 let (analysis, file_id) = fixture::file(ra_fixture);
408 let diagnostics = analysis
409 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
410 .unwrap();
411
412 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
413 let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
414 assert_eq!(expected, actual);
415 }
416
399 #[test] 417 #[test]
400 fn test_unresolved_macro_range() { 418 fn test_unresolved_macro_range() {
401 check_expect( 419 check_diagnostics(
402 r#"foo::bar!(92);"#, 420 r#"
403 expect![[r#" 421foo::bar!(92);
404 [ 422 //^^^ unresolved macro `foo::bar!`
405 Diagnostic { 423"#,
406 message: "unresolved macro `foo::bar!`", 424 );
407 range: 5..8, 425 }
408 severity: Error, 426
409 fixes: None, 427 #[test]
410 unused: false, 428 fn unresolved_import_in_use_tree() {
411 code: Some( 429 // Only the relevant part of a nested `use` item should be highlighted.
412 DiagnosticCode( 430 check_diagnostics(
413 "unresolved-macro-call", 431 r#"
414 ), 432use does_exist::{Exists, DoesntExist};
415 ), 433 //^^^^^^^^^^^ unresolved import
416 }, 434
417 ] 435use {does_not_exist::*, does_exist};
418 "#]], 436 //^^^^^^^^^^^^^^^^^ unresolved import
437
438use does_not_exist::{
439 a,
440 //^ unresolved import
441 b,
442 //^ unresolved import
443 c,
444 //^ unresolved import
445};
446
447mod does_exist {
448 pub struct Exists;
449}
450"#,
419 ); 451 );
420 } 452 }
421 453
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 320694a17..ec3828ab2 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -286,7 +286,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
286 .and_then( 286 .and_then(
287 |url| if let Some(fragment) = fragment { url.join(&fragment).ok() } else { Some(url) }, 287 |url| if let Some(fragment) = fragment { url.join(&fragment).ok() } else { Some(url) },
288 ) 288 )
289 .map(|url| url.into_string()) 289 .map(|url| url.into())
290} 290}
291 291
292fn rewrite_intra_doc_link( 292fn rewrite_intra_doc_link(
@@ -325,7 +325,7 @@ fn rewrite_intra_doc_link(
325 }; 325 };
326 } 326 }
327 327
328 Some((new_url.into_string(), strip_prefixes_suffixes(title).to_string())) 328 Some((new_url.into(), strip_prefixes_suffixes(title).to_string()))
329} 329}
330 330
331/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). 331/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
@@ -345,7 +345,7 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
345 get_symbol_filename(db, &def).as_deref().map(|f| url.join(f).ok()).flatten() 345 get_symbol_filename(db, &def).as_deref().map(|f| url.join(f).ok()).flatten()
346 }) 346 })
347 .and_then(|url| url.join(target).ok()) 347 .and_then(|url| url.join(target).ok())
348 .map(|url| url.into_string()) 348 .map(|url| url.into())
349} 349}
350 350
351/// Rewrites a markdown document, applying 'callback' to each link. 351/// Rewrites a markdown document, applying 'callback' to each link.
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index cc6641ba1..6780af617 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -1,7 +1,7 @@
1//! Utilities for creating `Analysis` instances for tests. 1//! Utilities for creating `Analysis` instances for tests.
2use ide_db::base_db::fixture::ChangeFixture; 2use ide_db::base_db::fixture::ChangeFixture;
3use syntax::{TextRange, TextSize}; 3use syntax::{TextRange, TextSize};
4use test_utils::{extract_annotations, RangeOrOffset}; 4use test_utils::extract_annotations;
5 5
6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; 6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
7 7
@@ -27,10 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
27 let change_fixture = ChangeFixture::parse(ra_fixture); 27 let change_fixture = ChangeFixture::parse(ra_fixture);
28 host.db.apply_change(change_fixture.change); 28 host.db.apply_change(change_fixture.change);
29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
30 let offset = match range_or_offset { 30 let offset = range_or_offset.expect_offset();
31 RangeOrOffset::Range(_) => panic!(),
32 RangeOrOffset::Offset(it) => it,
33 };
34 (host.analysis(), FilePosition { file_id, offset }) 31 (host.analysis(), FilePosition { file_id, offset })
35} 32}
36 33
@@ -40,10 +37,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
40 let change_fixture = ChangeFixture::parse(ra_fixture); 37 let change_fixture = ChangeFixture::parse(ra_fixture);
41 host.db.apply_change(change_fixture.change); 38 host.db.apply_change(change_fixture.change);
42 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 39 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
43 let range = match range_or_offset { 40 let range = range_or_offset.expect_range();
44 RangeOrOffset::Range(it) => it,
45 RangeOrOffset::Offset(_) => panic!(),
46 };
47 (host.analysis(), FileRange { file_id, range }) 41 (host.analysis(), FileRange { file_id, range })
48} 42}
49 43
@@ -53,10 +47,7 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
53 let change_fixture = ChangeFixture::parse(ra_fixture); 47 let change_fixture = ChangeFixture::parse(ra_fixture);
54 host.db.apply_change(change_fixture.change); 48 host.db.apply_change(change_fixture.change);
55 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 49 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
56 let offset = match range_or_offset { 50 let offset = range_or_offset.expect_offset();
57 RangeOrOffset::Range(_) => panic!(),
58 RangeOrOffset::Offset(it) => it,
59 };
60 51
61 let annotations = change_fixture 52 let annotations = change_fixture
62 .files 53 .files
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index b893c1c54..c5015a345 100755
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -19,6 +19,7 @@ pub enum FoldKind {
19 Statics, 19 Statics,
20 Array, 20 Array,
21 WhereClause, 21 WhereClause,
22 ReturnType,
22} 23}
23 24
24#[derive(Debug)] 25#[derive(Debug)]
@@ -131,6 +132,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
131 COMMENT => Some(FoldKind::Comment), 132 COMMENT => Some(FoldKind::Comment),
132 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), 133 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
133 ARRAY_EXPR => Some(FoldKind::Array), 134 ARRAY_EXPR => Some(FoldKind::Array),
135 RET_TYPE => Some(FoldKind::ReturnType),
134 ASSOC_ITEM_LIST 136 ASSOC_ITEM_LIST
135 | RECORD_FIELD_LIST 137 | RECORD_FIELD_LIST
136 | RECORD_PAT_FIELD_LIST 138 | RECORD_PAT_FIELD_LIST
@@ -300,6 +302,7 @@ mod tests {
300 FoldKind::Statics => "statics", 302 FoldKind::Statics => "statics",
301 FoldKind::Array => "array", 303 FoldKind::Array => "array",
302 FoldKind::WhereClause => "whereclause", 304 FoldKind::WhereClause => "whereclause",
305 FoldKind::ReturnType => "returntype",
303 }; 306 };
304 assert_eq!(kind, &attr.unwrap()); 307 assert_eq!(kind, &attr.unwrap());
305 } 308 }
@@ -560,4 +563,18 @@ where
560"#, 563"#,
561 ) 564 )
562 } 565 }
566
567 #[test]
568 fn fold_return_type() {
569 check(
570 r#"
571fn foo()<fold returntype>-> (
572 bool,
573 bool,
574)</fold> { (true, true) }
575
576fn bar() -> (bool, bool) { (true, true) }
577 "#,
578 )
579 }
563} 580}
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 05130a237..43356a94e 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -1,4 +1,4 @@
1use hir::{Impl, Semantics}; 1use hir::{AsAssocItem, Impl, Semantics};
2use ide_db::{ 2use ide_db::{
3 defs::{Definition, NameClass, NameRefClass}, 3 defs::{Definition, NameClass, NameRefClass},
4 RootDatabase, 4 RootDatabase,
@@ -36,6 +36,7 @@ pub(crate) fn goto_implementation(
36 } 36 }
37 ast::NameLike::Lifetime(_) => None, 37 ast::NameLike::Lifetime(_) => None,
38 }?; 38 }?;
39
39 let def = match def { 40 let def = match def {
40 Definition::ModuleDef(def) => def, 41 Definition::ModuleDef(def) => def,
41 _ => return None, 42 _ => return None,
@@ -48,6 +49,18 @@ pub(crate) fn goto_implementation(
48 let module = sema.to_module_def(position.file_id)?; 49 let module = sema.to_module_def(position.file_id)?;
49 impls_for_ty(&sema, builtin.ty(sema.db, module)) 50 impls_for_ty(&sema, builtin.ty(sema.db, module))
50 } 51 }
52 hir::ModuleDef::Function(f) => {
53 let assoc = f.as_assoc_item(sema.db)?;
54 let name = assoc.name(sema.db)?;
55 let trait_ = assoc.containing_trait(sema.db)?;
56 impls_for_trait_item(&sema, trait_, name)
57 }
58 hir::ModuleDef::Const(c) => {
59 let assoc = c.as_assoc_item(sema.db)?;
60 let name = assoc.name(sema.db)?;
61 let trait_ = assoc.containing_trait(sema.db)?;
62 impls_for_trait_item(&sema, trait_, name)
63 }
51 _ => return None, 64 _ => return None,
52 }; 65 };
53 Some(RangeInfo { range: node.syntax().text_range(), info: navs }) 66 Some(RangeInfo { range: node.syntax().text_range(), info: navs })
@@ -64,6 +77,23 @@ fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<Na
64 .collect() 77 .collect()
65} 78}
66 79
80fn impls_for_trait_item(
81 sema: &Semantics<RootDatabase>,
82 trait_: hir::Trait,
83 fun_name: hir::Name,
84) -> Vec<NavigationTarget> {
85 Impl::all_for_trait(sema.db, trait_)
86 .into_iter()
87 .filter_map(|imp| {
88 let item = imp.items(sema.db).iter().find_map(|itm| {
89 let itm_name = itm.name(sema.db)?;
90 (itm_name == fun_name).then(|| itm.clone())
91 })?;
92 item.try_to_nav(sema.db)
93 })
94 .collect()
95}
96
67#[cfg(test)] 97#[cfg(test)]
68mod tests { 98mod tests {
69 use ide_db::base_db::FileRange; 99 use ide_db::base_db::FileRange;
@@ -262,4 +292,42 @@ impl bool {}
262"#, 292"#,
263 ); 293 );
264 } 294 }
295
296 #[test]
297 fn goto_implementation_trait_functions() {
298 check(
299 r#"
300trait Tr {
301 fn f$0();
302}
303
304struct S;
305
306impl Tr for S {
307 fn f() {
308 //^
309 println!("Hello, world!");
310 }
311}
312"#,
313 );
314 }
315
316 #[test]
317 fn goto_implementation_trait_assoc_const() {
318 check(
319 r#"
320trait Tr {
321 const C$0: usize;
322}
323
324struct S;
325
326impl Tr for S {
327 const C: usize = 4;
328 //^
329}
330"#,
331 );
332 }
265} 333}
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index f3284bb96..004d9cb68 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -1,3 +1,4 @@
1use ide_db::base_db::Upcast;
1use ide_db::RootDatabase; 2use ide_db::RootDatabase;
2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 3use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 4
@@ -31,6 +32,7 @@ pub(crate) fn goto_type_definition(
31 ast::Pat(it) => sema.type_of_pat(&it)?, 32 ast::Pat(it) => sema.type_of_pat(&it)?,
32 ast::SelfParam(it) => sema.type_of_self(&it)?, 33 ast::SelfParam(it) => sema.type_of_self(&it)?,
33 ast::Type(it) => sema.resolve_type(&it)?, 34 ast::Type(it) => sema.resolve_type(&it)?,
35 ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
34 _ => return None, 36 _ => return None,
35 } 37 }
36 }; 38 };
@@ -161,4 +163,34 @@ impl Foo$0 {}
161"#, 163"#,
162 ) 164 )
163 } 165 }
166
167 #[test]
168 fn goto_def_for_struct_field() {
169 check(
170 r#"
171struct Bar;
172 //^^^
173
174struct Foo {
175 bar$0: Bar,
176}
177"#,
178 );
179 }
180
181 #[test]
182 fn goto_def_for_enum_struct_field() {
183 check(
184 r#"
185struct Bar;
186 //^^^
187
188enum Foo {
189 Bar {
190 bar$0: Bar
191 },
192}
193"#,
194 );
195 }
164} 196}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 8f490e922..85f887737 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -219,7 +219,7 @@ fn hint_iterator(
219) -> Option<SmolStr> { 219) -> Option<SmolStr> {
220 let db = sema.db; 220 let db = sema.db;
221 let strukt = ty.strip_references().as_adt()?; 221 let strukt = ty.strip_references().as_adt()?;
222 let krate = strukt.krate(db); 222 let krate = strukt.module(db).krate();
223 if krate != famous_defs.core()? { 223 if krate != famous_defs.core()? {
224 return None; 224 return None;
225 } 225 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index ff2a54117..97c9e5d2b 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -58,7 +58,7 @@ use cfg::CfgOptions;
58 58
59use ide_db::base_db::{ 59use ide_db::base_db::{
60 salsa::{self, ParallelDatabase}, 60 salsa::{self, ParallelDatabase},
61 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, 61 Env, FileLoader, FileSet, SourceDatabase, VfsPath,
62}; 62};
63use ide_db::{ 63use ide_db::{
64 symbol_index::{self, FileSymbol}, 64 symbol_index::{self, FileSymbol},
@@ -98,7 +98,7 @@ pub use ide_completion::{
98}; 98};
99pub use ide_db::{ 99pub use ide_db::{
100 base_db::{ 100 base_db::{
101 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, 101 Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
102 SourceRoot, SourceRootId, 102 SourceRoot, SourceRootId,
103 }, 103 },
104 call_info::CallInfo, 104 call_info::CallInfo,
@@ -113,7 +113,7 @@ pub use ide_ssr::SsrError;
113pub use syntax::{TextRange, TextSize}; 113pub use syntax::{TextRange, TextSize};
114pub use text_edit::{Indel, TextEdit}; 114pub use text_edit::{Indel, TextEdit};
115 115
116pub type Cancelable<T> = Result<T, Canceled>; 116pub type Cancellable<T> = Result<T, Cancelled>;
117 117
118/// Info associated with a text range. 118/// Info associated with a text range.
119#[derive(Debug)] 119#[derive(Debug)]
@@ -227,11 +227,11 @@ impl Analysis {
227 } 227 }
228 228
229 /// Debug info about the current state of the analysis. 229 /// Debug info about the current state of the analysis.
230 pub fn status(&self, file_id: Option<FileId>) -> Cancelable<String> { 230 pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
231 self.with_db(|db| status::status(&*db, file_id)) 231 self.with_db(|db| status::status(&*db, file_id))
232 } 232 }
233 233
234 pub fn prime_caches<F>(&self, cb: F) -> Cancelable<()> 234 pub fn prime_caches<F>(&self, cb: F) -> Cancellable<()>
235 where 235 where
236 F: Fn(PrimeCachesProgress) + Sync + std::panic::UnwindSafe, 236 F: Fn(PrimeCachesProgress) + Sync + std::panic::UnwindSafe,
237 { 237 {
@@ -239,35 +239,35 @@ impl Analysis {
239 } 239 }
240 240
241 /// Gets the text of the source file. 241 /// Gets the text of the source file.
242 pub fn file_text(&self, file_id: FileId) -> Cancelable<Arc<String>> { 242 pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
243 self.with_db(|db| db.file_text(file_id)) 243 self.with_db(|db| db.file_text(file_id))
244 } 244 }
245 245
246 /// Gets the syntax tree of the file. 246 /// Gets the syntax tree of the file.
247 pub fn parse(&self, file_id: FileId) -> Cancelable<SourceFile> { 247 pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
248 self.with_db(|db| db.parse(file_id).tree()) 248 self.with_db(|db| db.parse(file_id).tree())
249 } 249 }
250 250
251 /// Returns true if this file belongs to an immutable library. 251 /// Returns true if this file belongs to an immutable library.
252 pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> { 252 pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
253 use ide_db::base_db::SourceDatabaseExt; 253 use ide_db::base_db::SourceDatabaseExt;
254 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) 254 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
255 } 255 }
256 256
257 /// Gets the file's `LineIndex`: data structure to convert between absolute 257 /// Gets the file's `LineIndex`: data structure to convert between absolute
258 /// offsets and line/column representation. 258 /// offsets and line/column representation.
259 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { 259 pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
260 self.with_db(|db| db.line_index(file_id)) 260 self.with_db(|db| db.line_index(file_id))
261 } 261 }
262 262
263 /// Selects the next syntactic nodes encompassing the range. 263 /// Selects the next syntactic nodes encompassing the range.
264 pub fn extend_selection(&self, frange: FileRange) -> Cancelable<TextRange> { 264 pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> {
265 self.with_db(|db| extend_selection::extend_selection(db, frange)) 265 self.with_db(|db| extend_selection::extend_selection(db, frange))
266 } 266 }
267 267
268 /// Returns position of the matching brace (all types of braces are 268 /// Returns position of the matching brace (all types of braces are
269 /// supported). 269 /// supported).
270 pub fn matching_brace(&self, position: FilePosition) -> Cancelable<Option<TextSize>> { 270 pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
271 self.with_db(|db| { 271 self.with_db(|db| {
272 let parse = db.parse(position.file_id); 272 let parse = db.parse(position.file_id);
273 let file = parse.tree(); 273 let file = parse.tree();
@@ -281,30 +281,30 @@ impl Analysis {
281 &self, 281 &self,
282 file_id: FileId, 282 file_id: FileId,
283 text_range: Option<TextRange>, 283 text_range: Option<TextRange>,
284 ) -> Cancelable<String> { 284 ) -> Cancellable<String> {
285 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) 285 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range))
286 } 286 }
287 287
288 pub fn view_hir(&self, position: FilePosition) -> Cancelable<String> { 288 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
289 self.with_db(|db| view_hir::view_hir(&db, position)) 289 self.with_db(|db| view_hir::view_hir(&db, position))
290 } 290 }
291 291
292 pub fn view_item_tree(&self, file_id: FileId) -> Cancelable<String> { 292 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
293 self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) 293 self.with_db(|db| view_item_tree::view_item_tree(&db, file_id))
294 } 294 }
295 295
296 /// Renders the crate graph to GraphViz "dot" syntax. 296 /// Renders the crate graph to GraphViz "dot" syntax.
297 pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { 297 pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> {
298 self.with_db(|db| view_crate_graph::view_crate_graph(&db)) 298 self.with_db(|db| view_crate_graph::view_crate_graph(&db))
299 } 299 }
300 300
301 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 301 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
302 self.with_db(|db| expand_macro::expand_macro(db, position)) 302 self.with_db(|db| expand_macro::expand_macro(db, position))
303 } 303 }
304 304
305 /// Returns an edit to remove all newlines in the range, cleaning up minor 305 /// Returns an edit to remove all newlines in the range, cleaning up minor
306 /// stuff like trailing commas. 306 /// stuff like trailing commas.
307 pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> { 307 pub fn join_lines(&self, frange: FileRange) -> Cancellable<TextEdit> {
308 self.with_db(|db| { 308 self.with_db(|db| {
309 let parse = db.parse(frange.file_id); 309 let parse = db.parse(frange.file_id);
310 join_lines::join_lines(&parse.tree(), frange.range) 310 join_lines::join_lines(&parse.tree(), frange.range)
@@ -314,7 +314,7 @@ impl Analysis {
314 /// Returns an edit which should be applied when opening a new line, fixing 314 /// Returns an edit which should be applied when opening a new line, fixing
315 /// up minor stuff like continuing the comment. 315 /// up minor stuff like continuing the comment.
316 /// The edit will be a snippet (with `$0`). 316 /// The edit will be a snippet (with `$0`).
317 pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> { 317 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
318 self.with_db(|db| typing::on_enter(&db, position)) 318 self.with_db(|db| typing::on_enter(&db, position))
319 } 319 }
320 320
@@ -326,7 +326,7 @@ impl Analysis {
326 &self, 326 &self,
327 position: FilePosition, 327 position: FilePosition,
328 char_typed: char, 328 char_typed: char,
329 ) -> Cancelable<Option<SourceChange>> { 329 ) -> Cancellable<Option<SourceChange>> {
330 // Fast path to not even parse the file. 330 // Fast path to not even parse the file.
331 if !typing::TRIGGER_CHARS.contains(char_typed) { 331 if !typing::TRIGGER_CHARS.contains(char_typed) {
332 return Ok(None); 332 return Ok(None);
@@ -336,7 +336,7 @@ impl Analysis {
336 336
337 /// Returns a tree representation of symbols in the file. Useful to draw a 337 /// Returns a tree representation of symbols in the file. Useful to draw a
338 /// file outline. 338 /// file outline.
339 pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> { 339 pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> {
340 self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree())) 340 self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
341 } 341 }
342 342
@@ -345,17 +345,17 @@ impl Analysis {
345 &self, 345 &self,
346 file_id: FileId, 346 file_id: FileId,
347 config: &InlayHintsConfig, 347 config: &InlayHintsConfig,
348 ) -> Cancelable<Vec<InlayHint>> { 348 ) -> Cancellable<Vec<InlayHint>> {
349 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config)) 349 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
350 } 350 }
351 351
352 /// Returns the set of folding ranges. 352 /// Returns the set of folding ranges.
353 pub fn folding_ranges(&self, file_id: FileId) -> Cancelable<Vec<Fold>> { 353 pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> {
354 self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) 354 self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree()))
355 } 355 }
356 356
357 /// Fuzzy searches for a symbol. 357 /// Fuzzy searches for a symbol.
358 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { 358 pub fn symbol_search(&self, query: Query) -> Cancellable<Vec<NavigationTarget>> {
359 self.with_db(|db| { 359 self.with_db(|db| {
360 symbol_index::world_symbols(db, query) 360 symbol_index::world_symbols(db, query)
361 .into_iter() 361 .into_iter()
@@ -368,7 +368,7 @@ impl Analysis {
368 pub fn goto_definition( 368 pub fn goto_definition(
369 &self, 369 &self,
370 position: FilePosition, 370 position: FilePosition,
371 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 371 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
372 self.with_db(|db| goto_definition::goto_definition(db, position)) 372 self.with_db(|db| goto_definition::goto_definition(db, position))
373 } 373 }
374 374
@@ -376,7 +376,7 @@ impl Analysis {
376 pub fn goto_implementation( 376 pub fn goto_implementation(
377 &self, 377 &self,
378 position: FilePosition, 378 position: FilePosition,
379 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 379 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
380 self.with_db(|db| goto_implementation::goto_implementation(db, position)) 380 self.with_db(|db| goto_implementation::goto_implementation(db, position))
381 } 381 }
382 382
@@ -384,7 +384,7 @@ impl Analysis {
384 pub fn goto_type_definition( 384 pub fn goto_type_definition(
385 &self, 385 &self,
386 position: FilePosition, 386 position: FilePosition,
387 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 387 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
388 self.with_db(|db| goto_type_definition::goto_type_definition(db, position)) 388 self.with_db(|db| goto_type_definition::goto_type_definition(db, position))
389 } 389 }
390 390
@@ -393,12 +393,12 @@ impl Analysis {
393 &self, 393 &self,
394 position: FilePosition, 394 position: FilePosition,
395 search_scope: Option<SearchScope>, 395 search_scope: Option<SearchScope>,
396 ) -> Cancelable<Option<ReferenceSearchResult>> { 396 ) -> Cancellable<Option<ReferenceSearchResult>> {
397 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) 397 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
398 } 398 }
399 399
400 /// Finds all methods and free functions for the file. Does not return tests! 400 /// Finds all methods and free functions for the file. Does not return tests!
401 pub fn find_all_methods(&self, file_id: FileId) -> Cancelable<Vec<FileRange>> { 401 pub fn find_all_methods(&self, file_id: FileId) -> Cancellable<Vec<FileRange>> {
402 self.with_db(|db| fn_references::find_all_methods(db, file_id)) 402 self.with_db(|db| fn_references::find_all_methods(db, file_id))
403 } 403 }
404 404
@@ -408,7 +408,7 @@ impl Analysis {
408 position: FilePosition, 408 position: FilePosition,
409 links_in_hover: bool, 409 links_in_hover: bool,
410 markdown: bool, 410 markdown: bool,
411 ) -> Cancelable<Option<RangeInfo<HoverResult>>> { 411 ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
412 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) 412 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown))
413 } 413 }
414 414
@@ -416,12 +416,12 @@ impl Analysis {
416 pub fn external_docs( 416 pub fn external_docs(
417 &self, 417 &self,
418 position: FilePosition, 418 position: FilePosition,
419 ) -> Cancelable<Option<doc_links::DocumentationLink>> { 419 ) -> Cancellable<Option<doc_links::DocumentationLink>> {
420 self.with_db(|db| doc_links::external_docs(db, &position)) 420 self.with_db(|db| doc_links::external_docs(db, &position))
421 } 421 }
422 422
423 /// Computes parameter information for the given call expression. 423 /// Computes parameter information for the given call expression.
424 pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> { 424 pub fn call_info(&self, position: FilePosition) -> Cancellable<Option<CallInfo>> {
425 self.with_db(|db| ide_db::call_info::call_info(db, position)) 425 self.with_db(|db| ide_db::call_info::call_info(db, position))
426 } 426 }
427 427
@@ -429,42 +429,42 @@ impl Analysis {
429 pub fn call_hierarchy( 429 pub fn call_hierarchy(
430 &self, 430 &self,
431 position: FilePosition, 431 position: FilePosition,
432 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 432 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
433 self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) 433 self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
434 } 434 }
435 435
436 /// Computes incoming calls for the given file position. 436 /// Computes incoming calls for the given file position.
437 pub fn incoming_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> { 437 pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
438 self.with_db(|db| call_hierarchy::incoming_calls(db, position)) 438 self.with_db(|db| call_hierarchy::incoming_calls(db, position))
439 } 439 }
440 440
441 /// Computes incoming calls for the given file position. 441 /// Computes incoming calls for the given file position.
442 pub fn outgoing_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> { 442 pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
443 self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) 443 self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
444 } 444 }
445 445
446 /// Returns a `mod name;` declaration which created the current module. 446 /// Returns a `mod name;` declaration which created the current module.
447 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 447 pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
448 self.with_db(|db| parent_module::parent_module(db, position)) 448 self.with_db(|db| parent_module::parent_module(db, position))
449 } 449 }
450 450
451 /// Returns crates this file belongs too. 451 /// Returns crates this file belongs too.
452 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 452 pub fn crate_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
453 self.with_db(|db| parent_module::crate_for(db, file_id)) 453 self.with_db(|db| parent_module::crate_for(db, file_id))
454 } 454 }
455 455
456 /// Returns the edition of the given crate. 456 /// Returns the edition of the given crate.
457 pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { 457 pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> {
458 self.with_db(|db| db.crate_graph()[crate_id].edition) 458 self.with_db(|db| db.crate_graph()[crate_id].edition)
459 } 459 }
460 460
461 /// Returns the root file of the given crate. 461 /// Returns the root file of the given crate.
462 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 462 pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
463 self.with_db(|db| db.crate_graph()[crate_id].root_file_id) 463 self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
464 } 464 }
465 465
466 /// Returns the set of possible targets to run for the current file. 466 /// Returns the set of possible targets to run for the current file.
467 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 467 pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> {
468 self.with_db(|db| runnables::runnables(db, file_id)) 468 self.with_db(|db| runnables::runnables(db, file_id))
469 } 469 }
470 470
@@ -473,24 +473,24 @@ impl Analysis {
473 &self, 473 &self,
474 position: FilePosition, 474 position: FilePosition,
475 search_scope: Option<SearchScope>, 475 search_scope: Option<SearchScope>,
476 ) -> Cancelable<Vec<Runnable>> { 476 ) -> Cancellable<Vec<Runnable>> {
477 self.with_db(|db| runnables::related_tests(db, position, search_scope)) 477 self.with_db(|db| runnables::related_tests(db, position, search_scope))
478 } 478 }
479 479
480 /// Computes syntax highlighting for the given file 480 /// Computes syntax highlighting for the given file
481 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> { 481 pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> {
482 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) 482 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
483 } 483 }
484 484
485 /// Computes syntax highlighting for the given file range. 485 /// Computes syntax highlighting for the given file range.
486 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> { 486 pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
487 self.with_db(|db| { 487 self.with_db(|db| {
488 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) 488 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
489 }) 489 })
490 } 490 }
491 491
492 /// Computes syntax highlighting for the given file. 492 /// Computes syntax highlighting for the given file.
493 pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancelable<String> { 493 pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
494 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) 494 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
495 } 495 }
496 496
@@ -499,7 +499,7 @@ impl Analysis {
499 &self, 499 &self,
500 config: &CompletionConfig, 500 config: &CompletionConfig,
501 position: FilePosition, 501 position: FilePosition,
502 ) -> Cancelable<Option<Vec<CompletionItem>>> { 502 ) -> Cancellable<Option<Vec<CompletionItem>>> {
503 self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into)) 503 self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into))
504 } 504 }
505 505
@@ -510,7 +510,7 @@ impl Analysis {
510 position: FilePosition, 510 position: FilePosition,
511 full_import_path: &str, 511 full_import_path: &str,
512 imported_name: String, 512 imported_name: String,
513 ) -> Cancelable<Vec<TextEdit>> { 513 ) -> Cancellable<Vec<TextEdit>> {
514 Ok(self 514 Ok(self
515 .with_db(|db| { 515 .with_db(|db| {
516 ide_completion::resolve_completion_edits( 516 ide_completion::resolve_completion_edits(
@@ -533,7 +533,7 @@ impl Analysis {
533 config: &AssistConfig, 533 config: &AssistConfig,
534 resolve: AssistResolveStrategy, 534 resolve: AssistResolveStrategy,
535 frange: FileRange, 535 frange: FileRange,
536 ) -> Cancelable<Vec<Assist>> { 536 ) -> Cancellable<Vec<Assist>> {
537 self.with_db(|db| { 537 self.with_db(|db| {
538 let ssr_assists = ssr::ssr_assists(db, &resolve, frange); 538 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
539 let mut acc = Assist::get(db, config, resolve, frange); 539 let mut acc = Assist::get(db, config, resolve, frange);
@@ -548,7 +548,7 @@ impl Analysis {
548 config: &DiagnosticsConfig, 548 config: &DiagnosticsConfig,
549 resolve: AssistResolveStrategy, 549 resolve: AssistResolveStrategy,
550 file_id: FileId, 550 file_id: FileId,
551 ) -> Cancelable<Vec<Diagnostic>> { 551 ) -> Cancellable<Vec<Diagnostic>> {
552 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) 552 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id))
553 } 553 }
554 554
@@ -559,7 +559,7 @@ impl Analysis {
559 diagnostics_config: &DiagnosticsConfig, 559 diagnostics_config: &DiagnosticsConfig,
560 resolve: AssistResolveStrategy, 560 resolve: AssistResolveStrategy,
561 frange: FileRange, 561 frange: FileRange,
562 ) -> Cancelable<Vec<Assist>> { 562 ) -> Cancellable<Vec<Assist>> {
563 let include_fixes = match &assist_config.allowed { 563 let include_fixes = match &assist_config.allowed {
564 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix), 564 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix),
565 None => true, 565 None => true,
@@ -591,14 +591,14 @@ impl Analysis {
591 &self, 591 &self,
592 position: FilePosition, 592 position: FilePosition,
593 new_name: &str, 593 new_name: &str,
594 ) -> Cancelable<Result<SourceChange, RenameError>> { 594 ) -> Cancellable<Result<SourceChange, RenameError>> {
595 self.with_db(|db| references::rename::rename(db, position, new_name)) 595 self.with_db(|db| references::rename::rename(db, position, new_name))
596 } 596 }
597 597
598 pub fn prepare_rename( 598 pub fn prepare_rename(
599 &self, 599 &self,
600 position: FilePosition, 600 position: FilePosition,
601 ) -> Cancelable<Result<RangeInfo<()>, RenameError>> { 601 ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
602 self.with_db(|db| references::rename::prepare_rename(db, position)) 602 self.with_db(|db| references::rename::prepare_rename(db, position))
603 } 603 }
604 604
@@ -606,7 +606,7 @@ impl Analysis {
606 &self, 606 &self,
607 file_id: FileId, 607 file_id: FileId,
608 new_name_stem: &str, 608 new_name_stem: &str,
609 ) -> Cancelable<Option<SourceChange>> { 609 ) -> Cancellable<Option<SourceChange>> {
610 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) 610 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem))
611 } 611 }
612 612
@@ -616,7 +616,7 @@ impl Analysis {
616 parse_only: bool, 616 parse_only: bool,
617 resolve_context: FilePosition, 617 resolve_context: FilePosition,
618 selections: Vec<FileRange>, 618 selections: Vec<FileRange>,
619 ) -> Cancelable<Result<SourceChange, SsrError>> { 619 ) -> Cancellable<Result<SourceChange, SsrError>> {
620 self.with_db(|db| { 620 self.with_db(|db| {
621 let rule: ide_ssr::SsrRule = query.parse()?; 621 let rule: ide_ssr::SsrRule = query.parse()?;
622 let mut match_finder = 622 let mut match_finder =
@@ -631,11 +631,11 @@ impl Analysis {
631 &self, 631 &self,
632 file_id: FileId, 632 file_id: FileId,
633 config: AnnotationConfig, 633 config: AnnotationConfig,
634 ) -> Cancelable<Vec<Annotation>> { 634 ) -> Cancellable<Vec<Annotation>> {
635 self.with_db(|db| annotations::annotations(db, file_id, config)) 635 self.with_db(|db| annotations::annotations(db, file_id, config))
636 } 636 }
637 637
638 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> { 638 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> {
639 self.with_db(|db| annotations::resolve_annotation(db, annotation)) 639 self.with_db(|db| annotations::resolve_annotation(db, annotation))
640 } 640 }
641 641
@@ -643,16 +643,28 @@ impl Analysis {
643 &self, 643 &self,
644 range: FileRange, 644 range: FileRange,
645 direction: Direction, 645 direction: Direction,
646 ) -> Cancelable<Option<TextEdit>> { 646 ) -> Cancellable<Option<TextEdit>> {
647 self.with_db(|db| move_item::move_item(db, range, direction)) 647 self.with_db(|db| move_item::move_item(db, range, direction))
648 } 648 }
649 649
650 /// Performs an operation on that may be Canceled. 650 /// Performs an operation on the database that may be canceled.
651 fn with_db<F, T>(&self, f: F) -> Cancelable<T> 651 ///
652 /// rust-analyzer needs to be able to answer semantic questions about the
653 /// code while the code is being modified. A common problem is that a
654 /// long-running query is being calculated when a new change arrives.
655 ///
656 /// We can't just apply the change immediately: this will cause the pending
657 /// query to see inconsistent state (it will observe an absence of
658 /// repeatable read). So what we do is we **cancel** all pending queries
659 /// before applying the change.
660 ///
661 /// Salsa implements cancelation by unwinding with a special value and
662 /// catching it on the API boundary.
663 fn with_db<F, T>(&self, f: F) -> Cancellable<T>
652 where 664 where
653 F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, 665 F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
654 { 666 {
655 self.db.catch_canceled(f) 667 Cancelled::catch(|| f(&self.db))
656 } 668 }
657} 669}
658 670
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 0ba7f7225..552054951 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -228,7 +228,7 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) ->
228 let func = def.source(sema.db)?; 228 let func = def.source(sema.db)?;
229 let name_string = def.name(sema.db).to_string(); 229 let name_string = def.name(sema.db).to_string();
230 230
231 let root = def.krate(sema.db)?.root_module(sema.db); 231 let root = def.module(sema.db).krate().root_module(sema.db);
232 232
233 let kind = if name_string == "main" && def.module(sema.db) == root { 233 let kind = if name_string == "main" && def.module(sema.db) == root {
234 RunnableKind::Bin 234 RunnableKind::Bin
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 9df8d21af..79c2f4a1e 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -42,13 +42,107 @@ pub struct HlRange {
42// Feature: Semantic Syntax Highlighting 42// Feature: Semantic Syntax Highlighting
43// 43//
44// rust-analyzer highlights the code semantically. 44// rust-analyzer highlights the code semantically.
45// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. 45// For example, `Bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
46// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. 46// rust-analyzer does not specify colors directly, instead it assigns a tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
47// It's up to the client to map those to specific colors. 47// It's up to the client to map those to specific colors.
48// 48//
49// The general rule is that a reference to an entity gets colored the same way as the entity itself. 49// The general rule is that a reference to an entity gets colored the same way as the entity itself.
50// We also give special modifier for `mut` and `&mut` local variables. 50// We also give special modifier for `mut` and `&mut` local variables.
51// 51//
52//
53// .Token Tags
54//
55// Rust-analyzer currently emits the following token tags:
56//
57// - For items:
58// +
59// [horizontal]
60// enum:: Emitted for enums.
61// function:: Emitted for free-standing functions.
62// macro:: Emitted for macros.
63// method:: Emitted for associated functions, also knowns as methods.
64// namespace:: Emitted for modules.
65// struct:: Emitted for structs.
66// trait:: Emitted for traits.
67// typeAlias:: Emitted for type aliases and `Self` in `impl`s.
68// union:: Emitted for unions.
69//
70// - For literals:
71// +
72// [horizontal]
73// boolean:: Emitted for the boolean literals `true` and `false`.
74// character:: Emitted for character literals.
75// number:: Emitted for numeric literals.
76// string:: Emitted for string literals.
77// escapeSequence:: Emitted for escaped sequences inside strings like `\n`.
78// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros.
79//
80// - For operators:
81// +
82// [horizontal]
83// operator:: Emitted for general operators.
84// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.
85// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.
86// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`.
87// logical:: Emitted for the logical operators `||`, `&&`, `!`.
88//
89// - For punctuation:
90// +
91// [horizontal]
92// punctuation:: Emitted for general punctuation.
93// angle:: Emitted for `<>` angle brackets.
94// brace:: Emitted for `{}` braces.
95// bracket:: Emitted for `[]` brackets.
96// parenthesis:: Emitted for `()` parentheses.
97// colon:: Emitted for the `:` token.
98// comma:: Emitted for the `,` token.
99// dot:: Emitted for the `.` token.
100// Semi:: Emitted for the `;` token.
101//
102// //-
103//
104// [horizontal]
105// attribute:: Emitted for attributes.
106// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`.
107// comment:: Emitted for comments.
108// constParameter:: Emitted for const parameters.
109// enumMember:: Emitted for enum variants.
110// generic:: Emitted for generic tokens that have no mapping.
111// keyword:: Emitted for keywords.
112// label:: Emitted for labels.
113// lifetime:: Emitted for lifetimes.
114// parameter:: Emitted for non-self function parameters.
115// property:: Emitted for struct and union fields.
116// selfKeyword:: Emitted for the self function parameter and self path-specifier.
117// typeParameter:: Emitted for type parameters.
118// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of.
119// variable:: Emitted for locals, constants and statics.
120//
121//
122// .Token Modifiers
123//
124// Token modifiers allow to style some elements in the source code more precisely.
125//
126// Rust-analyzer currently emits the following token modifiers:
127//
128// [horizontal]
129// async:: Emitted for async functions and the `async` and `await` keywords.
130// attribute:: Emitted for tokens inside attributes.
131// callable:: Emitted for locals whose types implements one of the `Fn*` traits.
132// constant:: Emitted for consts.
133// consuming:: Emitted for locals that are being consumed when use in a function call.
134// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator.
135// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`.
136// documentation:: Emitted for documentation comments.
137// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
138// intraDocLink:: Emitted for intra doc links in doc-strings.
139// library:: Emitted for items that are defined outside of the current crate.
140// mutable:: Emitted for mutable locals and statics.
141// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts.
142// trait:: Emitted for associated trait items.
143// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token.
144//
145//
52// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[] 146// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[]
53// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[] 147// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
54pub(crate) fn highlight( 148pub(crate) fn highlight(
@@ -80,6 +174,7 @@ pub(crate) fn highlight(
80 &mut hl, 174 &mut hl,
81 &sema, 175 &sema,
82 InFile::new(file_id.into(), &root), 176 InFile::new(file_id.into(), &root),
177 sema.scope(&root).krate(),
83 range_to_highlight, 178 range_to_highlight,
84 syntactic_name_ref_highlighting, 179 syntactic_name_ref_highlighting,
85 ); 180 );
@@ -90,6 +185,7 @@ fn traverse(
90 hl: &mut Highlights, 185 hl: &mut Highlights,
91 sema: &Semantics<RootDatabase>, 186 sema: &Semantics<RootDatabase>,
92 root: InFile<&SyntaxNode>, 187 root: InFile<&SyntaxNode>,
188 krate: Option<hir::Crate>,
93 range_to_highlight: TextRange, 189 range_to_highlight: TextRange,
94 syntactic_name_ref_highlighting: bool, 190 syntactic_name_ref_highlighting: bool,
95) { 191) {
@@ -209,6 +305,7 @@ fn traverse(
209 305
210 if let Some((mut highlight, binding_hash)) = highlight::element( 306 if let Some((mut highlight, binding_hash)) = highlight::element(
211 &sema, 307 &sema,
308 krate,
212 &mut bindings_shadow_count, 309 &mut bindings_shadow_count,
213 syntactic_name_ref_highlighting, 310 syntactic_name_ref_highlighting,
214 element_to_highlight.clone(), 311 element_to_highlight.clone(),
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 058e37ff0..9503c936d 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -19,6 +19,7 @@ use crate::{
19 19
20pub(super) fn element( 20pub(super) fn element(
21 sema: &Semantics<RootDatabase>, 21 sema: &Semantics<RootDatabase>,
22 krate: Option<hir::Crate>,
22 bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, 23 bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
23 syntactic_name_ref_highlighting: bool, 24 syntactic_name_ref_highlighting: bool,
24 element: SyntaxElement, 25 element: SyntaxElement,
@@ -46,8 +47,10 @@ pub(super) fn element(
46 47
47 match name_kind { 48 match name_kind {
48 Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(), 49 Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(),
49 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, 50 Some(NameClass::Definition(def)) => {
50 Some(NameClass::ConstReference(def)) => highlight_def(db, def), 51 highlight_def(db, krate, def) | HlMod::Definition
52 }
53 Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def),
51 Some(NameClass::PatFieldShorthand { field_ref, .. }) => { 54 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
52 let mut h = HlTag::Symbol(SymbolKind::Field).into(); 55 let mut h = HlTag::Symbol(SymbolKind::Field).into();
53 if let Definition::Field(field) = field_ref { 56 if let Definition::Field(field) = field_ref {
@@ -68,7 +71,7 @@ pub(super) fn element(
68 } 71 }
69 NAME_REF => { 72 NAME_REF => {
70 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 73 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
71 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { 74 highlight_func_by_name_ref(sema, krate, &name_ref).unwrap_or_else(|| {
72 let is_self = name_ref.self_token().is_some(); 75 let is_self = name_ref.self_token().is_some();
73 let h = match NameRefClass::classify(sema, &name_ref) { 76 let h = match NameRefClass::classify(sema, &name_ref) {
74 Some(name_kind) => match name_kind { 77 Some(name_kind) => match name_kind {
@@ -82,7 +85,7 @@ pub(super) fn element(
82 } 85 }
83 }; 86 };
84 87
85 let mut h = highlight_def(db, def); 88 let mut h = highlight_def(db, krate, def);
86 89
87 if let Definition::Local(local) = &def { 90 if let Definition::Local(local) = &def {
88 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { 91 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) {
@@ -105,7 +108,7 @@ pub(super) fn element(
105 NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(), 108 NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(),
106 }, 109 },
107 None if syntactic_name_ref_highlighting => { 110 None if syntactic_name_ref_highlighting => {
108 highlight_name_ref_by_syntax(name_ref, sema) 111 highlight_name_ref_by_syntax(name_ref, sema, krate)
109 } 112 }
110 None => HlTag::UnresolvedReference.into(), 113 None => HlTag::UnresolvedReference.into(),
111 }; 114 };
@@ -136,9 +139,11 @@ pub(super) fn element(
136 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); 139 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
137 140
138 match NameClass::classify_lifetime(sema, &lifetime) { 141 match NameClass::classify_lifetime(sema, &lifetime) {
139 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, 142 Some(NameClass::Definition(def)) => {
143 highlight_def(db, krate, def) | HlMod::Definition
144 }
140 None => match NameRefClass::classify_lifetime(sema, &lifetime) { 145 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
141 Some(NameRefClass::Definition(def)) => highlight_def(db, def), 146 Some(NameRefClass::Definition(def)) => highlight_def(db, krate, def),
142 _ => SymbolKind::LifetimeParam.into(), 147 _ => SymbolKind::LifetimeParam.into(),
143 }, 148 },
144 _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition, 149 _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
@@ -277,12 +282,12 @@ pub(super) fn element(
277 hash((name, shadow_count)) 282 hash((name, shadow_count))
278 } 283 }
279} 284}
280fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { 285fn highlight_def(db: &RootDatabase, krate: Option<hir::Crate>, def: Definition) -> Highlight {
281 match def { 286 let mut h = match def {
282 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro), 287 Definition::Macro(_) => Highlight::new(HlTag::Symbol(SymbolKind::Macro)),
283 Definition::Field(_) => HlTag::Symbol(SymbolKind::Field), 288 Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)),
284 Definition::ModuleDef(def) => match def { 289 Definition::ModuleDef(def) => match def {
285 hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module), 290 hir::ModuleDef::Module(_) => Highlight::new(HlTag::Symbol(SymbolKind::Module)),
286 hir::ModuleDef::Function(func) => { 291 hir::ModuleDef::Function(func) => {
287 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function)); 292 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
288 if let Some(item) = func.as_assoc_item(db) { 293 if let Some(item) = func.as_assoc_item(db) {
@@ -314,14 +319,22 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
314 if func.is_async(db) { 319 if func.is_async(db) {
315 h |= HlMod::Async; 320 h |= HlMod::Async;
316 } 321 }
317 return h; 322
323 h
324 }
325 hir::ModuleDef::Adt(adt) => {
326 let h = match adt {
327 hir::Adt::Struct(_) => HlTag::Symbol(SymbolKind::Struct),
328 hir::Adt::Enum(_) => HlTag::Symbol(SymbolKind::Enum),
329 hir::Adt::Union(_) => HlTag::Symbol(SymbolKind::Union),
330 };
331
332 Highlight::new(h)
318 } 333 }
319 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct), 334 hir::ModuleDef::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)),
320 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HlTag::Symbol(SymbolKind::Enum),
321 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HlTag::Symbol(SymbolKind::Union),
322 hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant),
323 hir::ModuleDef::Const(konst) => { 335 hir::ModuleDef::Const(konst) => {
324 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); 336 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const));
337
325 if let Some(item) = konst.as_assoc_item(db) { 338 if let Some(item) = konst.as_assoc_item(db) {
326 h |= HlMod::Associated; 339 h |= HlMod::Associated;
327 match item.container(db) { 340 match item.container(db) {
@@ -336,7 +349,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
336 } 349 }
337 } 350 }
338 351
339 return h; 352 h
340 } 353 }
341 hir::ModuleDef::Trait(trait_) => { 354 hir::ModuleDef::Trait(trait_) => {
342 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Trait)); 355 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Trait));
@@ -344,10 +357,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
344 if trait_.is_unsafe(db) { 357 if trait_.is_unsafe(db) {
345 h |= HlMod::Unsafe; 358 h |= HlMod::Unsafe;
346 } 359 }
347 return h; 360
361 h
348 } 362 }
349 hir::ModuleDef::TypeAlias(type_) => { 363 hir::ModuleDef::TypeAlias(type_) => {
350 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); 364 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
365
351 if let Some(item) = type_.as_assoc_item(db) { 366 if let Some(item) = type_.as_assoc_item(db) {
352 h |= HlMod::Associated; 367 h |= HlMod::Associated;
353 match item.container(db) { 368 match item.container(db) {
@@ -361,23 +376,30 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
361 } 376 }
362 } 377 }
363 } 378 }
364 return h; 379
380 h
365 } 381 }
366 hir::ModuleDef::BuiltinType(_) => HlTag::BuiltinType, 382 hir::ModuleDef::BuiltinType(_) => Highlight::new(HlTag::BuiltinType),
367 hir::ModuleDef::Static(s) => { 383 hir::ModuleDef::Static(s) => {
368 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static)); 384 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static));
385
369 if s.is_mut(db) { 386 if s.is_mut(db) {
370 h |= HlMod::Mutable; 387 h |= HlMod::Mutable;
371 h |= HlMod::Unsafe; 388 h |= HlMod::Unsafe;
372 } 389 }
373 return h; 390
391 h
374 } 392 }
375 }, 393 },
376 Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl), 394 Definition::SelfType(_) => Highlight::new(HlTag::Symbol(SymbolKind::Impl)),
377 Definition::GenericParam(it) => match it { 395 Definition::GenericParam(it) => match it {
378 hir::GenericParam::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam), 396 hir::GenericParam::TypeParam(_) => Highlight::new(HlTag::Symbol(SymbolKind::TypeParam)),
379 hir::GenericParam::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam), 397 hir::GenericParam::ConstParam(_) => {
380 hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), 398 Highlight::new(HlTag::Symbol(SymbolKind::ConstParam))
399 }
400 hir::GenericParam::LifetimeParam(_) => {
401 Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam))
402 }
381 }, 403 },
382 Definition::Local(local) => { 404 Definition::Local(local) => {
383 let tag = if local.is_self(db) { 405 let tag = if local.is_self(db) {
@@ -395,28 +417,40 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
395 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) { 417 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
396 h |= HlMod::Callable; 418 h |= HlMod::Callable;
397 } 419 }
398 return h; 420 h
399 } 421 }
400 Definition::Label(_) => HlTag::Symbol(SymbolKind::Label), 422 Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
423 };
424
425 let is_from_other_crate = def.module(db).map(hir::Module::krate) != krate;
426 let is_builtin_type = matches!(def, Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)));
427
428 if is_from_other_crate && !is_builtin_type {
429 h |= HlMod::Library;
401 } 430 }
402 .into() 431
432 h
403} 433}
404 434
405fn highlight_func_by_name_ref( 435fn highlight_func_by_name_ref(
406 sema: &Semantics<RootDatabase>, 436 sema: &Semantics<RootDatabase>,
437 krate: Option<hir::Crate>,
407 name_ref: &ast::NameRef, 438 name_ref: &ast::NameRef,
408) -> Option<Highlight> { 439) -> Option<Highlight> {
409 let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; 440 let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
410 highlight_method_call(sema, &mc) 441 highlight_method_call(sema, krate, &mc)
411} 442}
412 443
413fn highlight_method_call( 444fn highlight_method_call(
414 sema: &Semantics<RootDatabase>, 445 sema: &Semantics<RootDatabase>,
446 krate: Option<hir::Crate>,
415 method_call: &ast::MethodCallExpr, 447 method_call: &ast::MethodCallExpr,
416) -> Option<Highlight> { 448) -> Option<Highlight> {
417 let func = sema.resolve_method_call(&method_call)?; 449 let func = sema.resolve_method_call(&method_call)?;
450
418 let mut h = SymbolKind::Function.into(); 451 let mut h = SymbolKind::Function.into();
419 h |= HlMod::Associated; 452 h |= HlMod::Associated;
453
420 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 454 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
421 h |= HlMod::Unsafe; 455 h |= HlMod::Unsafe;
422 } 456 }
@@ -424,7 +458,10 @@ fn highlight_method_call(
424 h |= HlMod::Async; 458 h |= HlMod::Async;
425 } 459 }
426 if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() { 460 if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
427 h |= HlMod::Trait 461 h |= HlMod::Trait;
462 }
463 if Some(func.module(sema.db).krate()) != krate {
464 h |= HlMod::Library;
428 } 465 }
429 466
430 if let Some(self_param) = func.self_param(sema.db) { 467 if let Some(self_param) = func.self_param(sema.db) {
@@ -473,7 +510,11 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
473 tag.into() 510 tag.into()
474} 511}
475 512
476fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight { 513fn highlight_name_ref_by_syntax(
514 name: ast::NameRef,
515 sema: &Semantics<RootDatabase>,
516 krate: Option<hir::Crate>,
517) -> Highlight {
477 let default = HlTag::UnresolvedReference; 518 let default = HlTag::UnresolvedReference;
478 519
479 let parent = match name.syntax().parent() { 520 let parent = match name.syntax().parent() {
@@ -484,7 +525,7 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
484 match parent.kind() { 525 match parent.kind() {
485 METHOD_CALL_EXPR => { 526 METHOD_CALL_EXPR => {
486 return ast::MethodCallExpr::cast(parent) 527 return ast::MethodCallExpr::cast(parent)
487 .and_then(|it| highlight_method_call(sema, &it)) 528 .and_then(|it| highlight_method_call(sema, krate, &it))
488 .unwrap_or_else(|| SymbolKind::Function.into()); 529 .unwrap_or_else(|| SymbolKind::Function.into());
489 } 530 }
490 FIELD_EXPR => { 531 FIELD_EXPR => {
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 27473a2f9..9d481deae 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -37,6 +37,8 @@ pub enum HlTag {
37 None, 37 None,
38} 38}
39 39
40// Don't forget to adjust the feature description in crates/ide/src/syntax_highlighting.rs.
41// And make sure to use the lsp strings used when converting to the protocol in crates\rust-analyzer\src\semantic_tokens.rs, not the names of the variants here.
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41#[repr(u8)] 43#[repr(u8)]
42pub enum HlMod { 44pub enum HlMod {
@@ -67,6 +69,8 @@ pub enum HlMod {
67 Trait, 69 Trait,
68 /// Used with keywords like `async` and `await`. 70 /// Used with keywords like `async` and `await`.
69 Async, 71 Async,
72 /// Used for items from other crates.
73 Library,
70 // Keep this last! 74 // Keep this last!
71 /// Used for unsafe functions, unsafe traits, mutable statics, union accesses and unsafe operations. 75 /// Used for unsafe functions, unsafe traits, mutable statics, union accesses and unsafe operations.
72 Unsafe, 76 Unsafe,
@@ -189,6 +193,7 @@ impl HlMod {
189 HlMod::Static, 193 HlMod::Static,
190 HlMod::Trait, 194 HlMod::Trait,
191 HlMod::Async, 195 HlMod::Async,
196 HlMod::Library,
192 HlMod::Unsafe, 197 HlMod::Unsafe,
193 ]; 198 ];
194 199
@@ -207,6 +212,7 @@ impl HlMod {
207 HlMod::Static => "static", 212 HlMod::Static => "static",
208 HlMod::Trait => "trait", 213 HlMod::Trait => "trait",
209 HlMod::Async => "async", 214 HlMod::Async => "async",
215 HlMod::Library => "library",
210 HlMod::Unsafe => "unsafe", 216 HlMod::Unsafe => "unsafe",
211 } 217 }
212 } 218 }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 878431b56..0264e39a3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -248,4 +248,20 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
248<span class="brace">}</span> 248<span class="brace">}</span>
249 249
250<span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">Dangerous</span> <span class="brace">{</span><span class="brace">}</span> 250<span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">Dangerous</span> <span class="brace">{</span><span class="brace">}</span>
251<span class="keyword">impl</span> <span class="trait unsafe">Dangerous</span> <span class="keyword">for</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file 251<span class="keyword">impl</span> <span class="trait unsafe">Dangerous</span> <span class="keyword">for</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
252
253<span class="keyword">fn</span> <span class="function declaration">use_foo_items</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
254 <span class="keyword">let</span> <span class="variable declaration">bob</span> <span class="operator">=</span> <span class="module library">foo</span><span class="operator">::</span><span class="struct library">Person</span> <span class="brace">{</span>
255 <span class="field library">name</span><span class="colon">:</span> <span class="string_literal">"Bob"</span><span class="comma">,</span>
256 <span class="field library">age</span><span class="colon">:</span> <span class="module library">foo</span><span class="operator">::</span><span class="module library">consts</span><span class="operator">::</span><span class="constant library">NUMBER</span><span class="comma">,</span>
257 <span class="brace">}</span><span class="semicolon">;</span>
258
259 <span class="keyword">let</span> <span class="variable declaration">control_flow</span> <span class="operator">=</span> <span class="module library">foo</span><span class="operator">::</span><span class="function library">identity</span><span class="parenthesis">(</span><span class="module library">foo</span><span class="operator">::</span><span class="enum library">ControlFlow</span><span class="operator">::</span><span class="enum_variant library">Continue</span><span class="parenthesis">)</span><span class="semicolon">;</span>
260
261 <span class="keyword control">if</span> <span class="variable">control_flow</span><span class="operator">.</span><span class="function associated consuming library">should_die</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
262 foo::<span class="macro">die!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
263 <span class="brace">}</span>
264<span class="brace">}</span>
265
266
267</code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 9ce26e930..662b53481 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -10,6 +10,7 @@ use crate::{fixture, FileRange, HlTag, TextRange};
10fn test_highlighting() { 10fn test_highlighting() {
11 check_highlighting( 11 check_highlighting(
12 r#" 12 r#"
13//- /main.rs crate:main deps:foo
13use inner::{self as inner_mod}; 14use inner::{self as inner_mod};
14mod inner {} 15mod inner {}
15 16
@@ -222,6 +223,49 @@ async fn async_main() {
222 223
223unsafe trait Dangerous {} 224unsafe trait Dangerous {}
224impl Dangerous for () {} 225impl Dangerous for () {}
226
227fn use_foo_items() {
228 let bob = foo::Person {
229 name: "Bob",
230 age: foo::consts::NUMBER,
231 };
232
233 let control_flow = foo::identity(foo::ControlFlow::Continue);
234
235 if control_flow.should_die() {
236 foo::die!();
237 }
238}
239
240
241//- /foo.rs crate:foo
242pub struct Person {
243 pub name: &'static str,
244 pub age: u8,
245}
246
247pub enum ControlFlow {
248 Continue,
249 Die,
250}
251
252impl ControlFlow {
253 pub fn should_die(self) -> bool {
254 matches!(self, ControlFlow::Die)
255 }
256}
257
258pub fn identity<T>(x: T) -> T { x }
259
260pub mod consts {
261 pub const NUMBER: i64 = 92;
262}
263
264macro_rules! die {
265 () => {
266 panic!();
267 };
268}
225"# 269"#
226 .trim(), 270 .trim(),
227 expect_file!["./test_data/highlighting.html"], 271 expect_file!["./test_data/highlighting.html"],