aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/diagnostics.rs141
-rw-r--r--crates/ide/src/diagnostics/fixes.rs1
-rw-r--r--crates/ide/src/display/navigation_target.rs25
-rw-r--r--crates/ide/src/doc_links.rs3
-rw-r--r--crates/ide/src/goto_definition.rs40
-rw-r--r--crates/ide/src/hover.rs2
-rw-r--r--crates/ide/src/lib.rs32
-rw-r--r--crates/ide/src/references.rs72
-rw-r--r--crates/ide/src/references/rename.rs44
-rw-r--r--crates/ide/src/runnables.rs40
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs5
22 files changed, 331 insertions, 108 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 049f808dc..79d126ff2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -199,6 +199,12 @@ fn check_unnecessary_braces_in_use_statement(
199) -> Option<()> { 199) -> Option<()> {
200 let use_tree_list = ast::UseTreeList::cast(node.clone())?; 200 let use_tree_list = ast::UseTreeList::cast(node.clone())?;
201 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { 201 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
202 // If there is a comment inside the bracketed `use`,
203 // assume it is a commented out module path and don't show diagnostic.
204 if use_tree_list.has_inner_comment() {
205 return Some(());
206 }
207
202 let use_range = use_tree_list.syntax().text_range(); 208 let use_range = use_tree_list.syntax().text_range();
203 let edit = 209 let edit =
204 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) 210 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
@@ -248,35 +254,7 @@ mod tests {
248 /// * a diagnostic is produced 254 /// * a diagnostic is produced
249 /// * this diagnostic fix trigger range touches the input cursor position 255 /// * 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 256 /// * 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) { 257 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); 258 let after = trim_indent(ra_fixture_after);
281 259
282 let (analysis, file_position) = fixture::position(ra_fixture_before); 260 let (analysis, file_position) = fixture::position(ra_fixture_before);
@@ -286,10 +264,12 @@ mod tests {
286 .pop() 264 .pop()
287 .unwrap(); 265 .unwrap();
288 let fix = diagnostic.fix.unwrap(); 266 let fix = diagnostic.fix.unwrap();
289 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
290 let actual = { 267 let actual = {
291 let mut actual = target_file_contents.to_string(); 268 let file_id = fix.source_change.source_file_edits.first().unwrap().file_id;
269 let mut actual = analysis.file_text(file_id).unwrap().to_string();
270
292 // Go from the last one to the first one, so that ranges won't be affected by previous edits. 271 // Go from the last one to the first one, so that ranges won't be affected by previous edits.
272 // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309
293 for edit in fix.source_change.source_file_edits.iter().rev() { 273 for edit in fix.source_change.source_file_edits.iter().rev() {
294 edit.edit.apply(&mut actual); 274 edit.edit.apply(&mut actual);
295 } 275 }
@@ -305,29 +285,6 @@ mod tests {
305 ); 285 );
306 } 286 }
307 287
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 288 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
332 /// apply to the file containing the cursor. 289 /// apply to the file containing the cursor.
333 pub(crate) fn check_no_diagnostics(ra_fixture: &str) { 290 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
@@ -619,6 +576,7 @@ fn test_fn() {
619 ), 576 ),
620 path: "foo.rs", 577 path: "foo.rs",
621 }, 578 },
579 initial_contents: "",
622 }, 580 },
623 ], 581 ],
624 is_snippet: false, 582 is_snippet: false,
@@ -686,6 +644,22 @@ mod a {
686} 644}
687"#, 645"#,
688 ); 646 );
647 check_no_diagnostics(
648 r#"
649use a;
650use a::{
651 c,
652 // d::e
653};
654
655mod a {
656 mod c {}
657 mod d {
658 mod e {}
659 }
660}
661"#,
662 );
689 check_fix( 663 check_fix(
690 r" 664 r"
691 mod b {} 665 mod b {}
@@ -763,25 +737,25 @@ struct Foo {
763 737
764 #[test] 738 #[test]
765 fn test_add_field_in_other_file_from_usage() { 739 fn test_add_field_in_other_file_from_usage() {
766 check_apply_diagnostic_fix_in_other_file( 740 check_fix(
767 r" 741 r#"
768 //- /main.rs 742//- /main.rs
769 mod foo; 743mod foo;
770 744
771 fn main() { 745fn main() {
772 <|>foo::Foo { bar: 3, baz: false}; 746 foo::Foo { bar: 3, <|>baz: false};
773 } 747}
774 //- /foo.rs 748//- /foo.rs
775 struct Foo { 749struct Foo {
776 bar: i32 750 bar: i32
777 } 751}
778 ", 752"#,
779 r" 753 r#"
780 struct Foo { 754struct Foo {
781 bar: i32, 755 bar: i32,
782 pub(crate) baz: bool 756 pub(crate) baz: bool
783 } 757}
784 ", 758"#,
785 ) 759 )
786 } 760 }
787 761
@@ -801,7 +775,7 @@ struct Foo {
801 775
802 #[test] 776 #[test]
803 fn test_rename_incorrect_case() { 777 fn test_rename_incorrect_case() {
804 check_fixes( 778 check_fix(
805 r#" 779 r#"
806pub struct test_struct<|> { one: i32 } 780pub struct test_struct<|> { one: i32 }
807 781
@@ -818,7 +792,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct {
818"#, 792"#,
819 ); 793 );
820 794
821 check_fixes( 795 check_fix(
822 r#" 796 r#"
823pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { 797pub fn some_fn(NonSnakeCase<|>: u8) -> u8 {
824 NonSnakeCase 798 NonSnakeCase
@@ -831,7 +805,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 {
831"#, 805"#,
832 ); 806 );
833 807
834 check_fixes( 808 check_fix(
835 r#" 809 r#"
836pub fn SomeFn<|>(val: u8) -> u8 { 810pub fn SomeFn<|>(val: u8) -> u8 {
837 if val != 0 { SomeFn(val - 1) } else { val } 811 if val != 0 { SomeFn(val - 1) } else { val }
@@ -844,7 +818,7 @@ pub fn some_fn(val: u8) -> u8 {
844"#, 818"#,
845 ); 819 );
846 820
847 check_fixes( 821 check_fix(
848 r#" 822 r#"
849fn some_fn() { 823fn some_fn() {
850 let whatAWeird_Formatting<|> = 10; 824 let whatAWeird_Formatting<|> = 10;
@@ -873,7 +847,7 @@ fn foo() {
873 847
874 #[test] 848 #[test]
875 fn test_rename_incorrect_case_struct_method() { 849 fn test_rename_incorrect_case_struct_method() {
876 check_fixes( 850 check_fix(
877 r#" 851 r#"
878pub struct TestStruct; 852pub struct TestStruct;
879 853
@@ -894,4 +868,17 @@ impl TestStruct {
894"#, 868"#,
895 ); 869 );
896 } 870 }
871
872 #[test]
873 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
874 let input = r#"fn FOO<|>() {}"#;
875 let expected = r#"fn foo() {}"#;
876
877 let (analysis, file_position) = fixture::position(input);
878 let diagnostics =
879 analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap();
880 assert_eq!(diagnostics.len(), 1);
881
882 check_fix(input, expected);
883 }
897} 884}
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index e8b896623..d79f5c170 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -40,6 +40,7 @@ impl DiagnosticWithFix for UnresolvedModule {
40 anchor: self.file.original_file(sema.db), 40 anchor: self.file.original_file(sema.db),
41 path: self.candidate.clone(), 41 path: self.candidate.clone(),
42 }, 42 },
43 initial_contents: "".to_string(),
43 } 44 }
44 .into(), 45 .into(),
45 unresolved_module.syntax().text_range(), 46 unresolved_module.syntax().text_range(),
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;
5use either::Either; 5use either::Either;
6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; 6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
7use ide_db::{ 7use ide_db::{
8 base_db::{FileId, SourceDatabase}, 8 base_db::{FileId, FileRange, SourceDatabase},
9 symbol_index::FileSymbolKind, 9 symbol_index::FileSymbolKind,
10}; 10};
11use ide_db::{defs::Definition, RootDatabase}; 11use 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
426impl 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
424impl ToNav for hir::TypeParam { 447impl 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]
758macro_rules! include {}
759
760 include!("foo.rs");
761//^^^^^^^^^^^^^^^^^^^
762
763fn f() {
764 foo<|>();
765}
766
767mod confuse_index {
768 pub fn foo() {}
769}
770
771//- /foo.rs
772fn 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#"
1138fn 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..b3331f03f 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -79,7 +79,7 @@ pub use crate::{
79 HighlightedRange, 79 HighlightedRange,
80 }, 80 },
81}; 81};
82pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; 82pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
83pub use completion::{ 83pub use completion::{
84 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, 84 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability,
85 CompletionScore, ImportEdit, InsertTextFormat, 85 CompletionScore, ImportEdit, InsertTextFormat,
@@ -475,7 +475,7 @@ impl Analysis {
475 config: &CompletionConfig, 475 config: &CompletionConfig,
476 position: FilePosition, 476 position: FilePosition,
477 full_import_path: &str, 477 full_import_path: &str,
478 imported_name: &str, 478 imported_name: String,
479 ) -> Cancelable<Vec<TextEdit>> { 479 ) -> Cancelable<Vec<TextEdit>> {
480 Ok(self 480 Ok(self
481 .with_db(|db| { 481 .with_db(|db| {
@@ -490,23 +490,17 @@ impl Analysis {
490 .unwrap_or_default()) 490 .unwrap_or_default())
491 } 491 }
492 492
493 /// Computes resolved assists with source changes for the given position. 493 /// Computes assists (aka code actions aka intentions) for the given
494 pub fn resolved_assists( 494 /// position. If `resolve == false`, computes enough info to show the
495 &self, 495 /// lightbulb list in the editor, but doesn't compute actual edits, to
496 config: &AssistConfig, 496 /// improve performance.
497 frange: FileRange, 497 pub fn assists(
498 ) -> Cancelable<Vec<ResolvedAssist>> {
499 self.with_db(|db| assists::Assist::resolved(db, config, frange))
500 }
501
502 /// Computes unresolved assists (aka code actions aka intentions) for the given
503 /// position.
504 pub fn unresolved_assists(
505 &self, 498 &self,
506 config: &AssistConfig, 499 config: &AssistConfig,
500 resolve: bool,
507 frange: FileRange, 501 frange: FileRange,
508 ) -> Cancelable<Vec<Assist>> { 502 ) -> Cancelable<Vec<Assist>> {
509 self.with_db(|db| Assist::unresolved(db, config, frange)) 503 self.with_db(|db| Assist::get(db, config, resolve, frange))
510 } 504 }
511 505
512 /// Computes the set of diagnostics for the given file. 506 /// Computes the set of diagnostics for the given file.
@@ -535,6 +529,14 @@ impl Analysis {
535 self.with_db(|db| references::rename::prepare_rename(db, position)) 529 self.with_db(|db| references::rename::prepare_rename(db, position))
536 } 530 }
537 531
532 pub fn will_rename_file(
533 &self,
534 file_id: FileId,
535 new_name_stem: &str,
536 ) -> Cancelable<Option<SourceChange>> {
537 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem))
538 }
539
538 pub fn structural_search_replace( 540 pub fn structural_search_replace(
539 &self, 541 &self,
540 query: &str, 542 query: &str,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 18ea19305..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
@@ -147,20 +147,20 @@ fn find_name(
147) -> Option<RangeInfo<Definition>> { 147) -> Option<RangeInfo<Definition>> {
148 if let Some(name) = opt_name { 148 if let Some(name) = opt_name {
149 let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); 149 let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db);
150 let range = name.syntax().text_range(); 150 let FileRange { range, .. } = sema.original_range(name.syntax());
151 return Some(RangeInfo::new(range, def)); 151 return Some(RangeInfo::new(range, def));
152 } 152 }
153 153
154 let (text_range, def) = if let Some(lifetime) = 154 let (FileRange { range, .. }, def) = if let Some(lifetime) =
155 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) 155 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
156 { 156 {
157 if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) 157 if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime)
158 .map(|class| NameRefClass::referenced(class, sema.db)) 158 .map(|class| NameRefClass::referenced(class, sema.db))
159 { 159 {
160 (lifetime.syntax().text_range(), def) 160 (sema.original_range(lifetime.syntax()), def)
161 } else { 161 } else {
162 ( 162 (
163 lifetime.syntax().text_range(), 163 sema.original_range(lifetime.syntax()),
164 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db), 164 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
165 ) 165 )
166 } 166 }
@@ -168,11 +168,11 @@ fn find_name(
168 let name_ref = 168 let name_ref =
169 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; 169 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
170 ( 170 (
171 name_ref.syntax().text_range(), 171 sema.original_range(name_ref.syntax()),
172 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), 172 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
173 ) 173 )
174 }; 174 };
175 Some(RangeInfo::new(text_range, def)) 175 Some(RangeInfo::new(range, def))
176} 176}
177 177
178fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 178fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -1086,4 +1086,62 @@ impl<'a> Foo<'a> for &'a () {
1086 "#]], 1086 "#]],
1087 ); 1087 );
1088 } 1088 }
1089
1090 #[test]
1091 fn test_map_range_to_original() {
1092 check(
1093 r#"
1094macro_rules! foo {($i:ident) => {$i} }
1095fn main() {
1096 let a<|> = "test";
1097 foo!(a);
1098}
1099"#,
1100 expect![[r#"
1101 a Local FileId(0) 59..60 Other
1102
1103 FileId(0) 80..81 Other Read
1104 "#]],
1105 );
1106 }
1107
1108 #[test]
1109 fn test_map_range_to_original_ref() {
1110 check(
1111 r#"
1112macro_rules! foo {($i:ident) => {$i} }
1113fn main() {
1114 let a = "test";
1115 foo!(a<|>);
1116}
1117"#,
1118 expect![[r#"
1119 a Local FileId(0) 59..60 Other
1120
1121 FileId(0) 80..81 Other Read
1122 "#]],
1123 );
1124 }
1125
1126 #[test]
1127 fn test_find_labels() {
1128 check(
1129 r#"
1130fn 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 }
1089} 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
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 8use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; 9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 10use 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
113pub(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
113fn find_module_at_offset( 130fn 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#"
1549fn foo<'a>() -> &'a () {
1550 'a: {
1551 'b: loop {
1552 break 'a<|>;
1553 }
1554 }
1555}
1556"#,
1557 r#"
1558fn 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
940mod foo;
941//- /foo.rs
942struct Foo;<|>
943impl 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; }
64pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 64pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 205<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</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; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { 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
173enum Option<T> { 178enum Option<T> {