diff options
Diffstat (limited to 'crates/ide/src')
21 files changed, 267 insertions, 98 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 3ad30f0c9..038273750 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -248,35 +248,7 @@ mod tests { | |||
248 | /// * a diagnostic is produced | 248 | /// * a diagnostic is produced |
249 | /// * this diagnostic fix trigger range touches the input cursor position | 249 | /// * this diagnostic fix trigger range touches the input cursor position |
250 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | 250 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied |
251 | pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | 251 | pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { |
252 | let after = trim_indent(ra_fixture_after); | ||
253 | |||
254 | let (analysis, file_position) = fixture::position(ra_fixture_before); | ||
255 | let diagnostic = analysis | ||
256 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) | ||
257 | .unwrap() | ||
258 | .pop() | ||
259 | .unwrap(); | ||
260 | let mut fix = diagnostic.fix.unwrap(); | ||
261 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; | ||
262 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | ||
263 | let actual = { | ||
264 | let mut actual = target_file_contents.to_string(); | ||
265 | edit.apply(&mut actual); | ||
266 | actual | ||
267 | }; | ||
268 | |||
269 | assert_eq_text!(&after, &actual); | ||
270 | assert!( | ||
271 | fix.fix_trigger_range.contains_inclusive(file_position.offset), | ||
272 | "diagnostic fix range {:?} does not touch cursor position {:?}", | ||
273 | fix.fix_trigger_range, | ||
274 | file_position.offset | ||
275 | ); | ||
276 | } | ||
277 | |||
278 | /// Similar to `check_fix`, but applies all the available fixes. | ||
279 | fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) { | ||
280 | let after = trim_indent(ra_fixture_after); | 252 | let after = trim_indent(ra_fixture_after); |
281 | 253 | ||
282 | let (analysis, file_position) = fixture::position(ra_fixture_before); | 254 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
@@ -286,10 +258,12 @@ mod tests { | |||
286 | .pop() | 258 | .pop() |
287 | .unwrap(); | 259 | .unwrap(); |
288 | let fix = diagnostic.fix.unwrap(); | 260 | let fix = diagnostic.fix.unwrap(); |
289 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | ||
290 | let actual = { | 261 | let actual = { |
291 | let mut actual = target_file_contents.to_string(); | 262 | let file_id = fix.source_change.source_file_edits.first().unwrap().file_id; |
263 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | ||
264 | |||
292 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. | 265 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. |
266 | // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309 | ||
293 | for edit in fix.source_change.source_file_edits.iter().rev() { | 267 | for edit in fix.source_change.source_file_edits.iter().rev() { |
294 | edit.edit.apply(&mut actual); | 268 | edit.edit.apply(&mut actual); |
295 | } | 269 | } |
@@ -305,29 +279,6 @@ mod tests { | |||
305 | ); | 279 | ); |
306 | } | 280 | } |
307 | 281 | ||
308 | /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker | ||
309 | /// which has a fix that can apply to other files. | ||
310 | fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { | ||
311 | let ra_fixture_after = &trim_indent(ra_fixture_after); | ||
312 | let (analysis, file_pos) = fixture::position(ra_fixture_before); | ||
313 | let current_file_id = file_pos.file_id; | ||
314 | let diagnostic = analysis | ||
315 | .diagnostics(&DiagnosticsConfig::default(), current_file_id) | ||
316 | .unwrap() | ||
317 | .pop() | ||
318 | .unwrap(); | ||
319 | let mut fix = diagnostic.fix.unwrap(); | ||
320 | let edit = fix.source_change.source_file_edits.pop().unwrap(); | ||
321 | let changed_file_id = edit.file_id; | ||
322 | let before = analysis.file_text(changed_file_id).unwrap(); | ||
323 | let actual = { | ||
324 | let mut actual = before.to_string(); | ||
325 | edit.edit.apply(&mut actual); | ||
326 | actual | ||
327 | }; | ||
328 | assert_eq_text!(ra_fixture_after, &actual); | ||
329 | } | ||
330 | |||
331 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | 282 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
332 | /// apply to the file containing the cursor. | 283 | /// apply to the file containing the cursor. |
333 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { | 284 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { |
@@ -764,25 +715,25 @@ struct Foo { | |||
764 | 715 | ||
765 | #[test] | 716 | #[test] |
766 | fn test_add_field_in_other_file_from_usage() { | 717 | fn test_add_field_in_other_file_from_usage() { |
767 | check_apply_diagnostic_fix_in_other_file( | 718 | check_fix( |
768 | r" | 719 | r#" |
769 | //- /main.rs | 720 | //- /main.rs |
770 | mod foo; | 721 | mod foo; |
771 | 722 | ||
772 | fn main() { | 723 | fn main() { |
773 | <|>foo::Foo { bar: 3, baz: false}; | 724 | foo::Foo { bar: 3, <|>baz: false}; |
774 | } | 725 | } |
775 | //- /foo.rs | 726 | //- /foo.rs |
776 | struct Foo { | 727 | struct Foo { |
777 | bar: i32 | 728 | bar: i32 |
778 | } | 729 | } |
779 | ", | 730 | "#, |
780 | r" | 731 | r#" |
781 | struct Foo { | 732 | struct Foo { |
782 | bar: i32, | 733 | bar: i32, |
783 | pub(crate) baz: bool | 734 | pub(crate) baz: bool |
784 | } | 735 | } |
785 | ", | 736 | "#, |
786 | ) | 737 | ) |
787 | } | 738 | } |
788 | 739 | ||
@@ -802,7 +753,7 @@ struct Foo { | |||
802 | 753 | ||
803 | #[test] | 754 | #[test] |
804 | fn test_rename_incorrect_case() { | 755 | fn test_rename_incorrect_case() { |
805 | check_fixes( | 756 | check_fix( |
806 | r#" | 757 | r#" |
807 | pub struct test_struct<|> { one: i32 } | 758 | pub struct test_struct<|> { one: i32 } |
808 | 759 | ||
@@ -819,7 +770,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct { | |||
819 | "#, | 770 | "#, |
820 | ); | 771 | ); |
821 | 772 | ||
822 | check_fixes( | 773 | check_fix( |
823 | r#" | 774 | r#" |
824 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { | 775 | pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { |
825 | NonSnakeCase | 776 | NonSnakeCase |
@@ -832,7 +783,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 { | |||
832 | "#, | 783 | "#, |
833 | ); | 784 | ); |
834 | 785 | ||
835 | check_fixes( | 786 | check_fix( |
836 | r#" | 787 | r#" |
837 | pub fn SomeFn<|>(val: u8) -> u8 { | 788 | pub fn SomeFn<|>(val: u8) -> u8 { |
838 | if val != 0 { SomeFn(val - 1) } else { val } | 789 | if val != 0 { SomeFn(val - 1) } else { val } |
@@ -845,7 +796,7 @@ pub fn some_fn(val: u8) -> u8 { | |||
845 | "#, | 796 | "#, |
846 | ); | 797 | ); |
847 | 798 | ||
848 | check_fixes( | 799 | check_fix( |
849 | r#" | 800 | r#" |
850 | fn some_fn() { | 801 | fn some_fn() { |
851 | let whatAWeird_Formatting<|> = 10; | 802 | let whatAWeird_Formatting<|> = 10; |
@@ -874,7 +825,7 @@ fn foo() { | |||
874 | 825 | ||
875 | #[test] | 826 | #[test] |
876 | fn test_rename_incorrect_case_struct_method() { | 827 | fn test_rename_incorrect_case_struct_method() { |
877 | check_fixes( | 828 | check_fix( |
878 | r#" | 829 | r#" |
879 | pub struct TestStruct; | 830 | pub struct TestStruct; |
880 | 831 | ||
@@ -895,4 +846,17 @@ impl TestStruct { | |||
895 | "#, | 846 | "#, |
896 | ); | 847 | ); |
897 | } | 848 | } |
849 | |||
850 | #[test] | ||
851 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
852 | let input = r#"fn FOO<|>() {}"#; | ||
853 | let expected = r#"fn foo() {}"#; | ||
854 | |||
855 | let (analysis, file_position) = fixture::position(input); | ||
856 | let diagnostics = | ||
857 | analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap(); | ||
858 | assert_eq!(diagnostics.len(), 1); | ||
859 | |||
860 | check_fix(input, expected); | ||
861 | } | ||
898 | } | 862 | } |
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index cd8ec54fa..6431e7d6d 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -5,7 +5,7 @@ use std::fmt; | |||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; | 6 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; |
7 | use ide_db::{ | 7 | use ide_db::{ |
8 | base_db::{FileId, SourceDatabase}, | 8 | base_db::{FileId, FileRange, SourceDatabase}, |
9 | symbol_index::FileSymbolKind, | 9 | symbol_index::FileSymbolKind, |
10 | }; | 10 | }; |
11 | use ide_db::{defs::Definition, RootDatabase}; | 11 | use ide_db::{defs::Definition, RootDatabase}; |
@@ -28,6 +28,7 @@ pub enum SymbolKind { | |||
28 | ValueParam, | 28 | ValueParam, |
29 | SelfParam, | 29 | SelfParam, |
30 | Local, | 30 | Local, |
31 | Label, | ||
31 | Function, | 32 | Function, |
32 | Const, | 33 | Const, |
33 | Static, | 34 | Static, |
@@ -223,6 +224,7 @@ impl TryToNav for Definition { | |||
223 | Definition::Local(it) => Some(it.to_nav(db)), | 224 | Definition::Local(it) => Some(it.to_nav(db)), |
224 | Definition::TypeParam(it) => Some(it.to_nav(db)), | 225 | Definition::TypeParam(it) => Some(it.to_nav(db)), |
225 | Definition::LifetimeParam(it) => Some(it.to_nav(db)), | 226 | Definition::LifetimeParam(it) => Some(it.to_nav(db)), |
227 | Definition::Label(it) => Some(it.to_nav(db)), | ||
226 | } | 228 | } |
227 | } | 229 | } |
228 | } | 230 | } |
@@ -421,6 +423,27 @@ impl ToNav for hir::Local { | |||
421 | } | 423 | } |
422 | } | 424 | } |
423 | 425 | ||
426 | impl ToNav for hir::Label { | ||
427 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
428 | let src = self.source(db); | ||
429 | let node = src.value.syntax(); | ||
430 | let FileRange { file_id, range } = src.with_value(node).original_file_range(db); | ||
431 | let focus_range = | ||
432 | src.value.lifetime().and_then(|lt| lt.lifetime_ident_token()).map(|lt| lt.text_range()); | ||
433 | let name = self.name(db).to_string().into(); | ||
434 | NavigationTarget { | ||
435 | file_id, | ||
436 | name, | ||
437 | kind: Some(SymbolKind::Label), | ||
438 | full_range: range, | ||
439 | focus_range, | ||
440 | container_name: None, | ||
441 | description: None, | ||
442 | docs: None, | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | |||
424 | impl ToNav for hir::TypeParam { | 447 | impl ToNav for hir::TypeParam { |
425 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 448 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
426 | let src = self.source(db); | 449 | let src = self.source(db); |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index b61ea0b3e..e10516f43 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -193,7 +193,8 @@ fn rewrite_intra_doc_link( | |||
193 | Definition::SelfType(_) | 193 | Definition::SelfType(_) |
194 | | Definition::Local(_) | 194 | | Definition::Local(_) |
195 | | Definition::TypeParam(_) | 195 | | Definition::TypeParam(_) |
196 | | Definition::LifetimeParam(_) => return None, | 196 | | Definition::LifetimeParam(_) |
197 | | Definition::Label(_) => return None, | ||
197 | }?; | 198 | }?; |
198 | let krate = resolved.module(db)?.krate(); | 199 | let krate = resolved.module(db)?.krate(); |
199 | let canonical_path = resolved.canonical_path(db)?; | 200 | let canonical_path = resolved.canonical_path(db)?; |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 431da5d9c..912144f8b 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -750,6 +750,31 @@ fn test() { | |||
750 | } | 750 | } |
751 | 751 | ||
752 | #[test] | 752 | #[test] |
753 | fn goto_through_included_file() { | ||
754 | check( | ||
755 | r#" | ||
756 | //- /main.rs | ||
757 | #[rustc_builtin_macro] | ||
758 | macro_rules! include {} | ||
759 | |||
760 | include!("foo.rs"); | ||
761 | //^^^^^^^^^^^^^^^^^^^ | ||
762 | |||
763 | fn f() { | ||
764 | foo<|>(); | ||
765 | } | ||
766 | |||
767 | mod confuse_index { | ||
768 | pub fn foo() {} | ||
769 | } | ||
770 | |||
771 | //- /foo.rs | ||
772 | fn foo() {} | ||
773 | "#, | ||
774 | ); | ||
775 | } | ||
776 | |||
777 | #[test] | ||
753 | fn goto_for_type_param() { | 778 | fn goto_for_type_param() { |
754 | check( | 779 | check( |
755 | r#" | 780 | r#" |
@@ -1105,4 +1130,19 @@ fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {} | |||
1105 | "#, | 1130 | "#, |
1106 | ); | 1131 | ); |
1107 | } | 1132 | } |
1133 | |||
1134 | #[test] | ||
1135 | fn goto_label() { | ||
1136 | check( | ||
1137 | r#" | ||
1138 | fn foo<'foo>(_: &'foo ()) { | ||
1139 | 'foo: { | ||
1140 | //^^^^ | ||
1141 | 'bar: loop { | ||
1142 | break 'foo<|>; | ||
1143 | } | ||
1144 | } | ||
1145 | }"#, | ||
1146 | ) | ||
1147 | } | ||
1108 | } | 1148 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 52f993cc9..73245fbe7 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -370,7 +370,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
370 | Adt::Enum(it) => from_def_source(db, it, mod_path), | 370 | Adt::Enum(it) => from_def_source(db, it, mod_path), |
371 | }) | 371 | }) |
372 | } | 372 | } |
373 | Definition::TypeParam(_) | Definition::LifetimeParam(_) => { | 373 | Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => { |
374 | // FIXME: Hover for generic param | 374 | // FIXME: Hover for generic param |
375 | None | 375 | None |
376 | } | 376 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index dbad9a84f..a75cc85b6 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -490,8 +490,18 @@ impl Analysis { | |||
490 | .unwrap_or_default()) | 490 | .unwrap_or_default()) |
491 | } | 491 | } |
492 | 492 | ||
493 | /// Computes assists (aka code actions aka intentions) for the given | ||
494 | /// position. Computes enough info to show the lightbulb list in the editor, | ||
495 | /// but doesn't compute actual edits, to improve performance. | ||
496 | /// | ||
497 | /// When the user clicks on the assist, call `resolve_assists` to get the | ||
498 | /// edit. | ||
499 | pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { | ||
500 | self.with_db(|db| Assist::unresolved(db, config, frange)) | ||
501 | } | ||
502 | |||
493 | /// Computes resolved assists with source changes for the given position. | 503 | /// Computes resolved assists with source changes for the given position. |
494 | pub fn resolved_assists( | 504 | pub fn resolve_assists( |
495 | &self, | 505 | &self, |
496 | config: &AssistConfig, | 506 | config: &AssistConfig, |
497 | frange: FileRange, | 507 | frange: FileRange, |
@@ -499,16 +509,6 @@ impl Analysis { | |||
499 | self.with_db(|db| assists::Assist::resolved(db, config, frange)) | 509 | self.with_db(|db| assists::Assist::resolved(db, config, frange)) |
500 | } | 510 | } |
501 | 511 | ||
502 | /// Computes unresolved assists (aka code actions aka intentions) for the given | ||
503 | /// position. | ||
504 | pub fn unresolved_assists( | ||
505 | &self, | ||
506 | config: &AssistConfig, | ||
507 | frange: FileRange, | ||
508 | ) -> Cancelable<Vec<Assist>> { | ||
509 | self.with_db(|db| Assist::unresolved(db, config, frange)) | ||
510 | } | ||
511 | |||
512 | /// Computes the set of diagnostics for the given file. | 512 | /// Computes the set of diagnostics for the given file. |
513 | pub fn diagnostics( | 513 | pub fn diagnostics( |
514 | &self, | 514 | &self, |
@@ -535,6 +535,14 @@ impl Analysis { | |||
535 | self.with_db(|db| references::rename::prepare_rename(db, position)) | 535 | self.with_db(|db| references::rename::prepare_rename(db, position)) |
536 | } | 536 | } |
537 | 537 | ||
538 | pub fn will_rename_file( | ||
539 | &self, | ||
540 | file_id: FileId, | ||
541 | new_name_stem: &str, | ||
542 | ) -> Cancelable<Option<SourceChange>> { | ||
543 | self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) | ||
544 | } | ||
545 | |||
538 | pub fn structural_search_replace( | 546 | pub fn structural_search_replace( |
539 | &self, | 547 | &self, |
540 | query: &str, | 548 | query: &str, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 33b7358f7..21b2d7ca1 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -130,7 +130,7 @@ pub(crate) fn find_all_refs( | |||
130 | kind = ReferenceKind::FieldShorthandForLocal; | 130 | kind = ReferenceKind::FieldShorthandForLocal; |
131 | } | 131 | } |
132 | } | 132 | } |
133 | } else if let Definition::LifetimeParam(_) = def { | 133 | } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { |
134 | kind = ReferenceKind::Lifetime; | 134 | kind = ReferenceKind::Lifetime; |
135 | }; | 135 | }; |
136 | 136 | ||
@@ -1122,4 +1122,26 @@ fn main() { | |||
1122 | "#]], | 1122 | "#]], |
1123 | ); | 1123 | ); |
1124 | } | 1124 | } |
1125 | |||
1126 | #[test] | ||
1127 | fn test_find_labels() { | ||
1128 | check( | ||
1129 | r#" | ||
1130 | fn foo<'a>() -> &'a () { | ||
1131 | 'a: loop { | ||
1132 | 'b: loop { | ||
1133 | continue 'a<|>; | ||
1134 | } | ||
1135 | break 'a; | ||
1136 | } | ||
1137 | } | ||
1138 | "#, | ||
1139 | expect![[r#" | ||
1140 | 'a Label FileId(0) 29..32 29..31 Lifetime | ||
1141 | |||
1142 | FileId(0) 80..82 Lifetime | ||
1143 | FileId(0) 108..110 Lifetime | ||
1144 | "#]], | ||
1145 | ); | ||
1146 | } | ||
1125 | } | 1147 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index cd721b7eb..854bf194e 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; |
9 | use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; | 9 | use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}; |
10 | use ide_db::{ | 10 | use ide_db::{ |
11 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
12 | RootDatabase, | 12 | RootDatabase, |
@@ -110,6 +110,23 @@ pub(crate) fn rename_with_semantics( | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | pub(crate) fn will_rename_file( | ||
114 | db: &RootDatabase, | ||
115 | file_id: FileId, | ||
116 | new_name_stem: &str, | ||
117 | ) -> Option<SourceChange> { | ||
118 | let sema = Semantics::new(db); | ||
119 | let module = sema.to_module_def(file_id)?; | ||
120 | |||
121 | let decl = module.declaration_source(db)?; | ||
122 | let range = decl.value.name()?.syntax().text_range(); | ||
123 | |||
124 | let position = FilePosition { file_id: decl.file_id.original_file(db), offset: range.start() }; | ||
125 | let mut change = rename_mod(&sema, position, module, new_name_stem).ok()?.info; | ||
126 | change.file_system_edits.clear(); | ||
127 | Some(change) | ||
128 | } | ||
129 | |||
113 | fn find_module_at_offset( | 130 | fn find_module_at_offset( |
114 | sema: &Semantics<RootDatabase>, | 131 | sema: &Semantics<RootDatabase>, |
115 | position: FilePosition, | 132 | position: FilePosition, |
@@ -1523,4 +1540,29 @@ fn main() { | |||
1523 | }"#, | 1540 | }"#, |
1524 | ); | 1541 | ); |
1525 | } | 1542 | } |
1543 | |||
1544 | #[test] | ||
1545 | fn test_rename_label() { | ||
1546 | check( | ||
1547 | "'foo", | ||
1548 | r#" | ||
1549 | fn foo<'a>() -> &'a () { | ||
1550 | 'a: { | ||
1551 | 'b: loop { | ||
1552 | break 'a<|>; | ||
1553 | } | ||
1554 | } | ||
1555 | } | ||
1556 | "#, | ||
1557 | r#" | ||
1558 | fn foo<'a>() -> &'a () { | ||
1559 | 'foo: { | ||
1560 | 'b: loop { | ||
1561 | break 'foo; | ||
1562 | } | ||
1563 | } | ||
1564 | } | ||
1565 | "#, | ||
1566 | ) | ||
1567 | } | ||
1526 | } | 1568 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 2f2b99130..891183266 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -193,7 +193,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op | |||
193 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { | 193 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { |
194 | if let Some(adt) = imp.target_ty(sema.db).as_adt() { | 194 | if let Some(adt) = imp.target_ty(sema.db).as_adt() { |
195 | let name = adt.name(sema.db).to_string(); | 195 | let name = adt.name(sema.db).to_string(); |
196 | let idx = path.rfind(':').unwrap_or(0); | 196 | let idx = path.rfind(':').map_or(0, |idx| idx + 1); |
197 | let (prefix, suffix) = path.split_at(idx); | 197 | let (prefix, suffix) = path.split_at(idx); |
198 | return format!("{}{}::{}", prefix, name, suffix); | 198 | return format!("{}{}::{}", prefix, name, suffix); |
199 | } | 199 | } |
@@ -931,4 +931,42 @@ mod test_mod { | |||
931 | "#]], | 931 | "#]], |
932 | ); | 932 | ); |
933 | } | 933 | } |
934 | |||
935 | #[test] | ||
936 | fn test_doc_runnables_impl_mod() { | ||
937 | check( | ||
938 | r#" | ||
939 | //- /lib.rs | ||
940 | mod foo; | ||
941 | //- /foo.rs | ||
942 | struct Foo;<|> | ||
943 | impl Foo { | ||
944 | /// ``` | ||
945 | /// let x = 5; | ||
946 | /// ``` | ||
947 | fn foo() {} | ||
948 | } | ||
949 | "#, | ||
950 | &[&DOCTEST], | ||
951 | expect![[r#" | ||
952 | [ | ||
953 | Runnable { | ||
954 | nav: NavigationTarget { | ||
955 | file_id: FileId( | ||
956 | 1, | ||
957 | ), | ||
958 | full_range: 27..81, | ||
959 | name: "foo", | ||
960 | }, | ||
961 | kind: DocTest { | ||
962 | test_id: Path( | ||
963 | "foo::Foo::foo", | ||
964 | ), | ||
965 | }, | ||
966 | cfg: None, | ||
967 | }, | ||
968 | ] | ||
969 | "#]], | ||
970 | ); | ||
971 | } | ||
934 | } | 972 | } |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 00c717c7c..5ad96581b 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -560,10 +560,20 @@ fn highlight_element( | |||
560 | CHAR => HighlightTag::CharLiteral.into(), | 560 | CHAR => HighlightTag::CharLiteral.into(), |
561 | QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, | 561 | QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, |
562 | LIFETIME => { | 562 | LIFETIME => { |
563 | let h = Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)); | 563 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); |
564 | match element.parent().map(|it| it.kind()) { | 564 | |
565 | Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition, | 565 | match NameClass::classify_lifetime(sema, &lifetime) { |
566 | _ => h, | 566 | Some(NameClass::Definition(def)) => { |
567 | highlight_def(db, def) | HighlightModifier::Definition | ||
568 | } | ||
569 | None => match NameRefClass::classify_lifetime(sema, &lifetime) { | ||
570 | Some(NameRefClass::Definition(def)) => highlight_def(db, def), | ||
571 | _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)), | ||
572 | }, | ||
573 | _ => { | ||
574 | Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)) | ||
575 | | HighlightModifier::Definition | ||
576 | } | ||
567 | } | 577 | } |
568 | } | 578 | } |
569 | p if p.is_punct() => match p { | 579 | p if p.is_punct() => match p { |
@@ -825,6 +835,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
825 | return h; | 835 | return h; |
826 | } | 836 | } |
827 | Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), | 837 | Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), |
838 | Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label), | ||
828 | } | 839 | } |
829 | .into() | 840 | .into() |
830 | } | 841 | } |
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index abcc5cccc..99ba3a59d 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs | |||
@@ -64,6 +64,7 @@ body { margin: 0; } | |||
64 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 64 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
65 | 65 | ||
66 | .lifetime { color: #DFAF8F; font-style: italic; } | 66 | .lifetime { color: #DFAF8F; font-style: italic; } |
67 | .label { color: #DFAF8F; font-style: italic; } | ||
67 | .comment { color: #7F9F7F; } | 68 | .comment { color: #7F9F7F; } |
68 | .documentation { color: #629755; } | 69 | .documentation { color: #629755; } |
69 | .injected { opacity: 0.65 ; } | 70 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 974f54fa0..2a6cc0cab 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -80,6 +80,7 @@ impl HighlightTag { | |||
80 | SymbolKind::LifetimeParam => "lifetime", | 80 | SymbolKind::LifetimeParam => "lifetime", |
81 | SymbolKind::Macro => "macro", | 81 | SymbolKind::Macro => "macro", |
82 | SymbolKind::Local => "variable", | 82 | SymbolKind::Local => "variable", |
83 | SymbolKind::Label => "label", | ||
83 | SymbolKind::ValueParam => "value_param", | 84 | SymbolKind::ValueParam => "value_param", |
84 | SymbolKind::SelfParam => "self_keyword", | 85 | SymbolKind::SelfParam => "self_keyword", |
85 | SymbolKind::Impl => "self_type", | 86 | SymbolKind::Impl => "self_type", |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index db6f32d33..506ebe60e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 4e511baa9..4dd7413ba 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 800d894c7..ed452586a 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 7f18ad297..92e7dc3e4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index c843b5085..31dad5d42 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index d26f48516..e3a0aa317 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 588e86a34..72ff9dd40 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
@@ -194,6 +195,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
194 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> | 195 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> |
195 | 196 | ||
196 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> | 197 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> |
198 | |||
199 | <span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span> | ||
200 | <span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span> | ||
201 | <span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span> | ||
202 | <span class="punctuation">}</span> | ||
197 | <span class="punctuation">}</span> | 203 | <span class="punctuation">}</span> |
198 | 204 | ||
199 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> | 205 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index c7589605f..8b3dfa69f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -4,6 +4,7 @@ body { margin: 0; } | |||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | 4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } |
5 | 5 | ||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | 6 | .lifetime { color: #DFAF8F; font-style: italic; } |
7 | .label { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
8 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
9 | .injected { opacity: 0.65 ; } | 10 | .injected { opacity: 0.65 ; } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index f53d2c3ba..e0df0d2b5 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -168,6 +168,11 @@ fn main() { | |||
168 | let baz = -baz; | 168 | let baz = -baz; |
169 | 169 | ||
170 | let _ = !true; | 170 | let _ = !true; |
171 | |||
172 | 'foo: loop { | ||
173 | break 'foo; | ||
174 | continue 'foo; | ||
175 | } | ||
171 | } | 176 | } |
172 | 177 | ||
173 | enum Option<T> { | 178 | enum Option<T> { |