aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/Cargo.toml3
-rw-r--r--crates/ide/src/display/short_label.rs6
-rw-r--r--crates/ide/src/hover.rs71
-rw-r--r--crates/ide/src/join_lines.rs8
-rw-r--r--crates/ide/src/lib.rs12
-rw-r--r--crates/ide/src/matching_brace.rs5
-rw-r--r--crates/ide/src/parent_module.rs6
-rw-r--r--crates/ide/src/references/rename.rs113
-rw-r--r--crates/ide/src/runnables.rs8
-rw-r--r--crates/ide/src/ssr.rs259
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs5
-rw-r--r--crates/ide/src/typing/on_enter.rs8
12 files changed, 414 insertions, 90 deletions
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f6aaaeda4..107bd8432 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13either = "1.5.3" 14either = "1.5.3"
14indexmap = "1.4.0" 15indexmap = "1.4.0"
15itertools = "0.10.0" 16itertools = "0.10.0"
@@ -26,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
26ide_db = { path = "../ide_db", version = "0.0.0" } 27ide_db = { path = "../ide_db", version = "0.0.0" }
27cfg = { path = "../cfg", version = "0.0.0" } 28cfg = { path = "../cfg", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
29test_utils = { path = "../test_utils", version = "0.0.0" }
30ide_assists = { path = "../ide_assists", version = "0.0.0" } 30ide_assists = { path = "../ide_assists", version = "0.0.0" }
31ide_ssr = { path = "../ide_ssr", version = "0.0.0" } 31ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
32ide_completion = { path = "../ide_completion", version = "0.0.0" } 32ide_completion = { path = "../ide_completion", version = "0.0.0" }
@@ -36,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" }
36hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
37 37
38[dev-dependencies] 38[dev-dependencies]
39test_utils = { path = "../test_utils" }
39expect-test = "1.1" 40expect-test = "1.1"
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index 84b8883de..2df9266b4 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -71,11 +71,7 @@ impl ShortLabel for ast::TypeAlias {
71 71
72impl ShortLabel for ast::Const { 72impl ShortLabel for ast::Const {
73 fn short_label(&self) -> Option<String> { 73 fn short_label(&self) -> Option<String> {
74 let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; 74 short_label_from_ty(self, self.ty(), "const ")
75 if let Some(expr) = self.body() {
76 format_to!(new_buf, " = {}", expr.syntax());
77 }
78 Some(new_buf)
79 } 75 }
80} 76}
81 77
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index a9454cfa3..ea45086ce 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use hir::{ 2use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, 3 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics, 4 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
@@ -11,7 +12,6 @@ use ide_db::{
11use itertools::Itertools; 12use itertools::Itertools;
12use stdx::format_to; 13use stdx::format_to;
13use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 14use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
14use test_utils::mark;
15 15
16use crate::{ 16use crate::{
17 display::{macro_label, ShortLabel, TryToNav}, 17 display::{macro_label, ShortLabel, TryToNav},
@@ -193,8 +193,8 @@ fn runnable_action(
193 ModuleDef::Function(func) => { 193 ModuleDef::Function(func) => {
194 let src = func.source(sema.db)?; 194 let src = func.source(sema.db)?;
195 if src.file_id != file_id.into() { 195 if src.file_id != file_id.into() {
196 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 196 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
197 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 197 cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
198 return None; 198 return None;
199 } 199 }
200 200
@@ -367,7 +367,7 @@ fn hover_for_definition(
367 .and_then(|fd| hover_for_builtin(fd, it)) 367 .and_then(|fd| hover_for_builtin(fd, it))
368 .or_else(|| Some(Markup::fenced_block(&it.name()))), 368 .or_else(|| Some(Markup::fenced_block(&it.name()))),
369 }, 369 },
370 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => hover_for_local(it, db),
371 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
373 Adt::Struct(it) => from_def_source(db, it, mod_path), 373 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -406,6 +406,29 @@ fn hover_for_definition(
406 } 406 }
407} 407}
408 408
409fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
410 let ty = it.ty(db);
411 let ty = ty.display(db);
412 let is_mut = if it.is_mut(db) { "mut " } else { "" };
413 let desc = match it.source(db).value {
414 Either::Left(ident) => {
415 let name = it.name(db).unwrap();
416 let let_kw = if ident
417 .syntax()
418 .parent()
419 .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
420 {
421 "let "
422 } else {
423 ""
424 };
425 format!("{}{}{}: {}", let_kw, is_mut, name, ty)
426 }
427 Either::Right(_) => format!("{}self: {}", is_mut, ty),
428 };
429 hover_markup(None, Some(desc), None)
430}
431
409fn hover_for_keyword( 432fn hover_for_keyword(
410 sema: &Semantics<RootDatabase>, 433 sema: &Semantics<RootDatabase>,
411 links_in_hover: bool, 434 links_in_hover: bool,
@@ -575,7 +598,7 @@ fn main() {
575 *iter* 598 *iter*
576 599
577 ```rust 600 ```rust
578 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> 601 let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
579 ``` 602 ```
580 "#]], 603 "#]],
581 ); 604 );
@@ -799,7 +822,7 @@ fn main() {
799 ``` 822 ```
800 823
801 ```rust 824 ```rust
802 const foo: u32 = 123 825 const foo: u32
803 ``` 826 ```
804 "#]], 827 "#]],
805 ); 828 );
@@ -832,7 +855,7 @@ fn main() {
832 *zz* 855 *zz*
833 856
834 ```rust 857 ```rust
835 Test<i32, u8> 858 let zz: Test<i32, u8>
836 ``` 859 ```
837 "#]], 860 "#]],
838 ); 861 );
@@ -871,7 +894,7 @@ fn main() { let b$0ar = Some(12); }
871 *bar* 894 *bar*
872 895
873 ```rust 896 ```rust
874 Option<i32> 897 let bar: Option<i32>
875 ``` 898 ```
876 "#]], 899 "#]],
877 ); 900 );
@@ -939,7 +962,7 @@ fn main() {
939 *foo* 962 *foo*
940 963
941 ```rust 964 ```rust
942 i32 965 foo: i32
943 ``` 966 ```
944 "#]], 967 "#]],
945 ) 968 )
@@ -953,7 +976,7 @@ fn main() {
953 *foo* 976 *foo*
954 977
955 ```rust 978 ```rust
956 i32 979 foo: i32
957 ``` 980 ```
958 "#]], 981 "#]],
959 ) 982 )
@@ -967,7 +990,7 @@ fn main() {
967 *foo* 990 *foo*
968 991
969 ```rust 992 ```rust
970 i32 993 foo: i32
971 ``` 994 ```
972 "#]], 995 "#]],
973 ) 996 )
@@ -981,7 +1004,7 @@ fn main() {
981 *foo* 1004 *foo*
982 1005
983 ```rust 1006 ```rust
984 i32 1007 foo: i32
985 ``` 1008 ```
986 "#]], 1009 "#]],
987 ) 1010 )
@@ -1001,7 +1024,7 @@ fn main() {
1001 *_x* 1024 *_x*
1002 1025
1003 ```rust 1026 ```rust
1004 impl Deref<Target = u8> + DerefMut<Target = u8> 1027 _x: impl Deref<Target = u8> + DerefMut<Target = u8>
1005 ``` 1028 ```
1006 "#]], 1029 "#]],
1007 ) 1030 )
@@ -1023,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); }
1023 *foo_test* 1046 *foo_test*
1024 1047
1025 ```rust 1048 ```rust
1026 Thing 1049 let foo_test: Thing
1027 ``` 1050 ```
1028 "#]], 1051 "#]],
1029 ) 1052 )
@@ -1082,7 +1105,7 @@ fn main() {
1082 ``` 1105 ```
1083 1106
1084 ```rust 1107 ```rust
1085 const C: u32 = 1 1108 const C: u32
1086 ``` 1109 ```
1087 "#]], 1110 "#]],
1088 ) 1111 )
@@ -1183,7 +1206,7 @@ fn y() {
1183 *x* 1206 *x*
1184 1207
1185 ```rust 1208 ```rust
1186 i32 1209 let x: i32
1187 ``` 1210 ```
1188 "#]], 1211 "#]],
1189 ) 1212 )
@@ -1260,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1260 *bar* 1283 *bar*
1261 1284
1262 ```rust 1285 ```rust
1263 u32 1286 bar: u32
1264 ``` 1287 ```
1265 "#]], 1288 "#]],
1266 ); 1289 );
@@ -1278,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1278 *bar* 1301 *bar*
1279 1302
1280 ```rust 1303 ```rust
1281 u32 1304 bar: u32
1282 ``` 1305 ```
1283 "#]], 1306 "#]],
1284 ); 1307 );
@@ -2101,7 +2124,7 @@ pub fn fo$0o() {}
2101 2124
2102 #[test] 2125 #[test]
2103 fn test_hover_macro_generated_struct_fn_doc_comment() { 2126 fn test_hover_macro_generated_struct_fn_doc_comment() {
2104 mark::check!(hover_macro_generated_struct_fn_doc_comment); 2127 cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
2105 2128
2106 check( 2129 check(
2107 r#" 2130 r#"
@@ -2139,7 +2162,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); }
2139 2162
2140 #[test] 2163 #[test]
2141 fn test_hover_macro_generated_struct_fn_doc_attr() { 2164 fn test_hover_macro_generated_struct_fn_doc_attr() {
2142 mark::check!(hover_macro_generated_struct_fn_doc_attr); 2165 cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
2143 2166
2144 check( 2167 check(
2145 r#" 2168 r#"
@@ -3303,7 +3326,7 @@ fn main() {
3303 *f* 3326 *f*
3304 3327
3305 ```rust 3328 ```rust
3306 &i32 3329 f: &i32
3307 ``` 3330 ```
3308 "#]], 3331 "#]],
3309 ); 3332 );
@@ -3322,7 +3345,7 @@ impl Foo {
3322 *self* 3345 *self*
3323 3346
3324 ```rust 3347 ```rust
3325 &Foo 3348 self: &Foo
3326 ``` 3349 ```
3327 "#]], 3350 "#]],
3328 ); 3351 );
@@ -3342,7 +3365,7 @@ impl Foo {
3342 *self* 3365 *self*
3343 3366
3344 ```rust 3367 ```rust
3345 Arc<Foo> 3368 self: Arc<Foo>
3346 ``` 3369 ```
3347 "#]], 3370 "#]],
3348 ); 3371 );
@@ -3538,7 +3561,7 @@ fn foo() {
3538 ``` 3561 ```
3539 3562
3540 ```rust 3563 ```rust
3541 const FOO: usize = 3 3564 const FOO: usize
3542 ``` 3565 ```
3543 3566
3544 --- 3567 ---
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 7fcae13e0..20a920ddb 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -7,7 +7,7 @@ use syntax::{
7 SyntaxKind::{self, USE_TREE, WHITESPACE}, 7 SyntaxKind::{self, USE_TREE, WHITESPACE},
8 SyntaxNode, SyntaxToken, TextRange, TextSize, T, 8 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
9}; 9};
10use test_utils::mark; 10
11use text_edit::{TextEdit, TextEditBuilder}; 11use text_edit::{TextEdit, TextEditBuilder};
12 12
13// Feature: Join Lines 13// Feature: Join Lines
@@ -60,7 +60,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
60 let mut string_open_quote = false; 60 let mut string_open_quote = false;
61 if let Some(string) = ast::String::cast(token.clone()) { 61 if let Some(string) = ast::String::cast(token.clone()) {
62 if let Some(range) = string.open_quote_text_range() { 62 if let Some(range) = string.open_quote_text_range() {
63 mark::hit!(join_string_literal); 63 cov_mark::hit!(join_string_literal);
64 string_open_quote = range.end() == offset; 64 string_open_quote = range.end() == offset;
65 } 65 }
66 } 66 }
@@ -206,7 +206,7 @@ fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str {
206#[cfg(test)] 206#[cfg(test)]
207mod tests { 207mod tests {
208 use syntax::SourceFile; 208 use syntax::SourceFile;
209 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range, mark}; 209 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
210 210
211 use super::*; 211 use super::*;
212 212
@@ -786,7 +786,7 @@ fn foo() {
786 786
787 #[test] 787 #[test]
788 fn join_string_literal() { 788 fn join_string_literal() {
789 mark::check!(join_string_literal); 789 cov_mark::check!(join_string_literal);
790 check_join_lines( 790 check_join_lines(
791 r#" 791 r#"
792fn main() { 792fn main() {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b600178ee..0a493d2f3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -41,6 +41,7 @@ mod parent_module;
41mod references; 41mod references;
42mod fn_references; 42mod fn_references;
43mod runnables; 43mod runnables;
44mod ssr;
44mod status; 45mod status;
45mod syntax_highlighting; 46mod syntax_highlighting;
46mod syntax_tree; 47mod syntax_tree;
@@ -51,6 +52,7 @@ mod doc_links;
51use std::sync::Arc; 52use std::sync::Arc;
52 53
53use cfg::CfgOptions; 54use cfg::CfgOptions;
55
54use ide_db::base_db::{ 56use ide_db::base_db::{
55 salsa::{self, ParallelDatabase}, 57 salsa::{self, ParallelDatabase},
56 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, 58 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
@@ -85,7 +87,7 @@ pub use crate::{
85pub use hir::{Documentation, Semantics}; 87pub use hir::{Documentation, Semantics};
86pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; 88pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind};
87pub use ide_completion::{ 89pub use ide_completion::{
88 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, 90 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit,
89 InsertTextFormat, 91 InsertTextFormat,
90}; 92};
91pub use ide_db::{ 93pub use ide_db::{
@@ -478,7 +480,6 @@ impl Analysis {
478 position: FilePosition, 480 position: FilePosition,
479 full_import_path: &str, 481 full_import_path: &str,
480 imported_name: String, 482 imported_name: String,
481 import_for_trait_assoc_item: bool,
482 ) -> Cancelable<Vec<TextEdit>> { 483 ) -> Cancelable<Vec<TextEdit>> {
483 Ok(self 484 Ok(self
484 .with_db(|db| { 485 .with_db(|db| {
@@ -488,7 +489,6 @@ impl Analysis {
488 position, 489 position,
489 full_import_path, 490 full_import_path,
490 imported_name, 491 imported_name,
491 import_for_trait_assoc_item,
492 ) 492 )
493 })? 493 })?
494 .unwrap_or_default()) 494 .unwrap_or_default())
@@ -504,7 +504,11 @@ impl Analysis {
504 resolve: bool, 504 resolve: bool,
505 frange: FileRange, 505 frange: FileRange,
506 ) -> Cancelable<Vec<Assist>> { 506 ) -> Cancelable<Vec<Assist>> {
507 self.with_db(|db| Assist::get(db, config, resolve, frange)) 507 self.with_db(|db| {
508 let mut acc = Assist::get(db, config, resolve, frange);
509 ssr::add_ssr_assist(db, &mut acc, resolve, frange);
510 acc
511 })
508 } 512 }
509 513
510 /// Computes the set of diagnostics for the given file. 514 /// Computes the set of diagnostics for the given file.
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 1bfa1439d..000c412d9 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -2,7 +2,6 @@ use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SourceFile, SyntaxKind, TextSize, T, 3 SourceFile, SyntaxKind, TextSize, T,
4}; 4};
5use test_utils::mark;
6 5
7// Feature: Matching Brace 6// Feature: Matching Brace
8// 7//
@@ -28,7 +27,7 @@ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<Text
28 .next()?; 27 .next()?;
29 let parent = brace_token.parent(); 28 let parent = brace_token.parent();
30 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { 29 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
31 mark::hit!(pipes_not_braces); 30 cov_mark::hit!(pipes_not_braces);
32 return None; 31 return None;
33 } 32 }
34 let matching_kind = BRACES[brace_idx ^ 1]; 33 let matching_kind = BRACES[brace_idx ^ 1];
@@ -63,7 +62,7 @@ mod tests {
63 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); 62 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}");
64 63
65 { 64 {
66 mark::check!(pipes_not_braces); 65 cov_mark::check!(pipes_not_braces);
67 do_check( 66 do_check(
68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", 67 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
69 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", 68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index ddbaf22b7..03d71b380 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -5,7 +5,6 @@ use syntax::{
5 algo::find_node_at_offset, 5 algo::find_node_at_offset,
6 ast::{self, AstNode}, 6 ast::{self, AstNode},
7}; 7};
8use test_utils::mark;
9 8
10use crate::NavigationTarget; 9use crate::NavigationTarget;
11 10
@@ -33,7 +32,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
33 .item_list() 32 .item_list()
34 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) 33 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset))
35 { 34 {
36 mark::hit!(test_resolve_parent_module_on_module_decl); 35 cov_mark::hit!(test_resolve_parent_module_on_module_decl);
37 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); 36 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast);
38 } 37 }
39 } 38 }
@@ -64,7 +63,6 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
64#[cfg(test)] 63#[cfg(test)]
65mod tests { 64mod tests {
66 use ide_db::base_db::FileRange; 65 use ide_db::base_db::FileRange;
67 use test_utils::mark;
68 66
69 use crate::fixture; 67 use crate::fixture;
70 68
@@ -92,7 +90,7 @@ $0// empty
92 90
93 #[test] 91 #[test]
94 fn test_resolve_parent_module_on_module_decl() { 92 fn test_resolve_parent_module_on_module_decl() {
95 mark::check!(test_resolve_parent_module_on_module_decl); 93 cov_mark::check!(test_resolve_parent_module_on_module_decl);
96 check( 94 check(
97 r#" 95 r#"
98//- /lib.rs 96//- /lib.rs
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 1919639a3..1e378279d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -14,14 +14,14 @@ use syntax::{
14 ast::{self, NameOwner}, 14 ast::{self, NameOwner},
15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, 15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
16}; 16};
17use test_utils::mark; 17
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; 20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
21 21
22type RenameResult<T> = Result<T, RenameError>; 22type RenameResult<T> = Result<T, RenameError>;
23#[derive(Debug)] 23#[derive(Debug)]
24pub struct RenameError(pub(crate) String); 24pub struct RenameError(String);
25 25
26impl fmt::Display for RenameError { 26impl fmt::Display for RenameError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -47,16 +47,15 @@ pub(crate) fn prepare_rename(
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
48 let source_file = sema.parse(position.file_id); 48 let source_file = sema.parse(position.file_id);
49 let syntax = source_file.syntax(); 49 let syntax = source_file.syntax();
50 let range = match &sema 50 let name_like = sema
51 .find_node_at_offset_with_descend(&syntax, position.offset) 51 .find_node_at_offset_with_descend(&syntax, position.offset)
52 .ok_or_else(|| format_err!("No references found at position"))? 52 .ok_or_else(|| format_err!("No references found at position"))?;
53 { 53 let node = match &name_like {
54 ast::NameLike::Name(it) => it.syntax(), 54 ast::NameLike::Name(it) => it.syntax(),
55 ast::NameLike::NameRef(it) => it.syntax(), 55 ast::NameLike::NameRef(it) => it.syntax(),
56 ast::NameLike::Lifetime(it) => it.syntax(), 56 ast::NameLike::Lifetime(it) => it.syntax(),
57 } 57 };
58 .text_range(); 58 Ok(RangeInfo::new(sema.original_range(node).range, ()))
59 Ok(RangeInfo::new(range, ()))
60} 59}
61 60
62// Feature: Rename 61// Feature: Rename
@@ -94,6 +93,7 @@ pub(crate) fn rename_with_semantics(
94 } 93 }
95} 94}
96 95
96/// Called by the client when it is about to rename a file.
97pub(crate) fn will_rename_file( 97pub(crate) fn will_rename_file(
98 db: &RootDatabase, 98 db: &RootDatabase,
99 file_id: FileId, 99 file_id: FileId,
@@ -226,34 +226,36 @@ fn rename_reference(
226 | (IdentifierKind::Ident, _) 226 | (IdentifierKind::Ident, _)
227 if def_is_lbl_or_lt => 227 if def_is_lbl_or_lt =>
228 { 228 {
229 mark::hit!(rename_not_a_lifetime_ident_ref); 229 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
230 bail!("Invalid name `{}`: not a lifetime identifier", new_name) 230 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
231 } 231 }
232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), 232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime),
233 (IdentifierKind::Lifetime, _) => { 233 (IdentifierKind::Lifetime, _) => {
234 mark::hit!(rename_not_an_ident_ref); 234 cov_mark::hit!(rename_not_an_ident_ref);
235 bail!("Invalid name `{}`: not an identifier", new_name) 235 bail!("Invalid name `{}`: not an identifier", new_name)
236 } 236 }
237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { 237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
238 // no-op 238 // no-op
239 mark::hit!(rename_self_to_self); 239 cov_mark::hit!(rename_self_to_self);
240 return Ok(SourceChange::default()); 240 return Ok(SourceChange::default());
241 } 241 }
242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { 242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
243 mark::hit!(rename_self_to_param); 243 cov_mark::hit!(rename_self_to_param);
244 return rename_self_to_param(sema, local, new_name, ident_kind); 244 return rename_self_to_param(sema, local, new_name, ident_kind);
245 } 245 }
246 (IdentifierKind::ToSelf, Definition::Local(local)) => { 246 (IdentifierKind::ToSelf, Definition::Local(local)) => {
247 mark::hit!(rename_to_self); 247 cov_mark::hit!(rename_to_self);
248 return rename_to_self(sema, local); 248 return rename_to_self(sema, local);
249 } 249 }
250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), 250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), 251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => {
252 cov_mark::hit!(rename_ident)
253 }
252 } 254 }
253 255
254 let usages = def.usages(sema).all(); 256 let usages = def.usages(sema).all();
255 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { 257 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
256 mark::hit!(rename_underscore_multiple); 258 cov_mark::hit!(rename_underscore_multiple);
257 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 259 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
258 } 260 }
259 let mut source_change = SourceChange::default(); 261 let mut source_change = SourceChange::default();
@@ -444,7 +446,7 @@ fn source_edit_from_name_ref(
444 (Some(field_name), Some(init)) => { 446 (Some(field_name), Some(init)) => {
445 if field_name == *name_ref { 447 if field_name == *name_ref {
446 if init.text() == new_name { 448 if init.text() == new_name {
447 mark::hit!(test_rename_field_put_init_shorthand); 449 cov_mark::hit!(test_rename_field_put_init_shorthand);
448 // same names, we can use a shorthand here instead. 450 // same names, we can use a shorthand here instead.
449 // we do not want to erase attributes hence this range start 451 // we do not want to erase attributes hence this range start
450 let s = field_name.syntax().text_range().start(); 452 let s = field_name.syntax().text_range().start();
@@ -453,7 +455,7 @@ fn source_edit_from_name_ref(
453 } 455 }
454 } else if init == *name_ref { 456 } else if init == *name_ref {
455 if field_name.text() == new_name { 457 if field_name.text() == new_name {
456 mark::hit!(test_rename_local_put_init_shorthand); 458 cov_mark::hit!(test_rename_local_put_init_shorthand);
457 // same names, we can use a shorthand here instead. 459 // same names, we can use a shorthand here instead.
458 // we do not want to erase attributes hence this range start 460 // we do not want to erase attributes hence this range start
459 let s = field_name.syntax().text_range().start(); 461 let s = field_name.syntax().text_range().start();
@@ -467,12 +469,12 @@ fn source_edit_from_name_ref(
467 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the 469 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
468 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 470 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
469 (None, Some(_)) if matches!(def, Definition::Field(_)) => { 471 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
470 mark::hit!(test_rename_field_in_field_shorthand); 472 cov_mark::hit!(test_rename_field_in_field_shorthand);
471 let s = name_ref.syntax().text_range().start(); 473 let s = name_ref.syntax().text_range().start();
472 Some((TextRange::empty(s), format!("{}: ", new_name))) 474 Some((TextRange::empty(s), format!("{}: ", new_name)))
473 } 475 }
474 (None, Some(_)) if matches!(def, Definition::Local(_)) => { 476 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
475 mark::hit!(test_rename_local_in_field_shorthand); 477 cov_mark::hit!(test_rename_local_in_field_shorthand);
476 let s = name_ref.syntax().text_range().end(); 478 let s = name_ref.syntax().text_range().end();
477 Some((TextRange::empty(s), format!(": {}", new_name))) 479 Some((TextRange::empty(s), format!(": {}", new_name)))
478 } 480 }
@@ -486,7 +488,7 @@ fn source_edit_from_name_ref(
486 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { 488 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
487 // field name is being renamed 489 // field name is being renamed
488 if pat.name().map_or(false, |it| it.text() == new_name) { 490 if pat.name().map_or(false, |it| it.text() == new_name) {
489 mark::hit!(test_rename_field_put_init_shorthand_pat); 491 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
490 // same names, we can use a shorthand here instead/ 492 // same names, we can use a shorthand here instead/
491 // we do not want to erase attributes hence this range start 493 // we do not want to erase attributes hence this range start
492 let s = field_name.syntax().text_range().start(); 494 let s = field_name.syntax().text_range().start();
@@ -538,11 +540,13 @@ fn source_edit_from_def(
538mod tests { 540mod tests {
539 use expect_test::{expect, Expect}; 541 use expect_test::{expect, Expect};
540 use stdx::trim_indent; 542 use stdx::trim_indent;
541 use test_utils::{assert_eq_text, mark}; 543 use test_utils::assert_eq_text;
542 use text_edit::TextEdit; 544 use text_edit::TextEdit;
543 545
544 use crate::{fixture, FileId}; 546 use crate::{fixture, FileId};
545 547
548 use super::{RangeInfo, RenameError};
549
546 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 550 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
547 let ra_fixture_after = &trim_indent(ra_fixture_after); 551 let ra_fixture_after = &trim_indent(ra_fixture_after);
548 let (analysis, position) = fixture::position(ra_fixture_before); 552 let (analysis, position) = fixture::position(ra_fixture_before);
@@ -588,6 +592,45 @@ mod tests {
588 expect.assert_debug_eq(&source_change) 592 expect.assert_debug_eq(&source_change)
589 } 593 }
590 594
595 fn check_prepare(ra_fixture: &str, expect: Expect) {
596 let (analysis, position) = fixture::position(ra_fixture);
597 let result = analysis
598 .prepare_rename(position)
599 .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err));
600 match result {
601 Ok(RangeInfo { range, info: () }) => {
602 let source = analysis.file_text(position.file_id).unwrap();
603 expect.assert_eq(&format!("{:?}: {}", range, &source[range]))
604 }
605 Err(RenameError(err)) => expect.assert_eq(&err),
606 };
607 }
608
609 #[test]
610 fn test_prepare_rename_namelikes() {
611 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
612 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
613 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]);
614 }
615
616 #[test]
617 fn test_prepare_rename_in_macro() {
618 check_prepare(
619 r"macro_rules! foo {
620 ($ident:ident) => {
621 pub struct $ident;
622 }
623}
624foo!(Foo$0);",
625 expect![[r#"83..86: Foo"#]],
626 );
627 }
628
629 #[test]
630 fn test_prepare_rename_keyword() {
631 check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
632 }
633
591 #[test] 634 #[test]
592 fn test_rename_to_underscore() { 635 fn test_rename_to_underscore() {
593 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); 636 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
@@ -627,7 +670,7 @@ mod tests {
627 670
628 #[test] 671 #[test]
629 fn test_rename_to_invalid_identifier_lifetime() { 672 fn test_rename_to_invalid_identifier_lifetime() {
630 mark::check!(rename_not_an_ident_ref); 673 cov_mark::check!(rename_not_an_ident_ref);
631 check( 674 check(
632 "'foo", 675 "'foo",
633 r#"fn main() { let i$0 = 1; }"#, 676 r#"fn main() { let i$0 = 1; }"#,
@@ -637,7 +680,7 @@ mod tests {
637 680
638 #[test] 681 #[test]
639 fn test_rename_to_invalid_identifier_lifetime2() { 682 fn test_rename_to_invalid_identifier_lifetime2() {
640 mark::check!(rename_not_a_lifetime_ident_ref); 683 cov_mark::check!(rename_not_a_lifetime_ident_ref);
641 check( 684 check(
642 "foo", 685 "foo",
643 r#"fn main<'a>(_: &'a$0 ()) {}"#, 686 r#"fn main<'a>(_: &'a$0 ()) {}"#,
@@ -647,7 +690,7 @@ mod tests {
647 690
648 #[test] 691 #[test]
649 fn test_rename_to_underscore_invalid() { 692 fn test_rename_to_underscore_invalid() {
650 mark::check!(rename_underscore_multiple); 693 cov_mark::check!(rename_underscore_multiple);
651 check( 694 check(
652 "_", 695 "_",
653 r#"fn main(foo$0: ()) {foo;}"#, 696 r#"fn main(foo$0: ()) {foo;}"#,
@@ -666,7 +709,7 @@ mod tests {
666 709
667 #[test] 710 #[test]
668 fn test_rename_for_local() { 711 fn test_rename_for_local() {
669 mark::check!(rename_ident); 712 cov_mark::check!(rename_ident);
670 check( 713 check(
671 "k", 714 "k",
672 r#" 715 r#"
@@ -829,7 +872,7 @@ impl Foo {
829 872
830 #[test] 873 #[test]
831 fn test_rename_field_in_field_shorthand() { 874 fn test_rename_field_in_field_shorthand() {
832 mark::check!(test_rename_field_in_field_shorthand); 875 cov_mark::check!(test_rename_field_in_field_shorthand);
833 check( 876 check(
834 "j", 877 "j",
835 r#" 878 r#"
@@ -855,7 +898,7 @@ impl Foo {
855 898
856 #[test] 899 #[test]
857 fn test_rename_local_in_field_shorthand() { 900 fn test_rename_local_in_field_shorthand() {
858 mark::check!(test_rename_local_in_field_shorthand); 901 cov_mark::check!(test_rename_local_in_field_shorthand);
859 check( 902 check(
860 "j", 903 "j",
861 r#" 904 r#"
@@ -1261,7 +1304,7 @@ fn foo(f: foo::Foo) {
1261 1304
1262 #[test] 1305 #[test]
1263 fn test_parameter_to_self() { 1306 fn test_parameter_to_self() {
1264 mark::check!(rename_to_self); 1307 cov_mark::check!(rename_to_self);
1265 check( 1308 check(
1266 "self", 1309 "self",
1267 r#" 1310 r#"
@@ -1401,7 +1444,7 @@ impl Foo {
1401 1444
1402 #[test] 1445 #[test]
1403 fn test_owned_self_to_parameter() { 1446 fn test_owned_self_to_parameter() {
1404 mark::check!(rename_self_to_param); 1447 cov_mark::check!(rename_self_to_param);
1405 check( 1448 check(
1406 "foo", 1449 "foo",
1407 r#" 1450 r#"
@@ -1454,7 +1497,7 @@ impl Foo {
1454 1497
1455 #[test] 1498 #[test]
1456 fn test_rename_field_put_init_shorthand() { 1499 fn test_rename_field_put_init_shorthand() {
1457 mark::check!(test_rename_field_put_init_shorthand); 1500 cov_mark::check!(test_rename_field_put_init_shorthand);
1458 check( 1501 check(
1459 "bar", 1502 "bar",
1460 r#" 1503 r#"
@@ -1476,7 +1519,7 @@ fn foo(bar: i32) -> Foo {
1476 1519
1477 #[test] 1520 #[test]
1478 fn test_rename_local_put_init_shorthand() { 1521 fn test_rename_local_put_init_shorthand() {
1479 mark::check!(test_rename_local_put_init_shorthand); 1522 cov_mark::check!(test_rename_local_put_init_shorthand);
1480 check( 1523 check(
1481 "i", 1524 "i",
1482 r#" 1525 r#"
@@ -1498,7 +1541,7 @@ fn foo(i: i32) -> Foo {
1498 1541
1499 #[test] 1542 #[test]
1500 fn test_struct_field_pat_into_shorthand() { 1543 fn test_struct_field_pat_into_shorthand() {
1501 mark::check!(test_rename_field_put_init_shorthand_pat); 1544 cov_mark::check!(test_rename_field_put_init_shorthand_pat);
1502 check( 1545 check(
1503 "baz", 1546 "baz",
1504 r#" 1547 r#"
@@ -1610,7 +1653,7 @@ fn foo(foo: Foo) {
1610 1653
1611 #[test] 1654 #[test]
1612 fn test_rename_lifetimes() { 1655 fn test_rename_lifetimes() {
1613 mark::check!(rename_lifetime); 1656 cov_mark::check!(rename_lifetime);
1614 check( 1657 check(
1615 "'yeeee", 1658 "'yeeee",
1616 r#" 1659 r#"
@@ -1698,7 +1741,7 @@ fn foo<'a>() -> &'a () {
1698 1741
1699 #[test] 1742 #[test]
1700 fn test_self_to_self() { 1743 fn test_self_to_self() {
1701 mark::check!(rename_self_to_self); 1744 cov_mark::check!(rename_self_to_self);
1702 check( 1745 check(
1703 "self", 1746 "self",
1704 r#" 1747 r#"
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 65f60891e..280565563 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -9,7 +9,6 @@ use syntax::{
9 ast::{self, AstNode, AttrsOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12use test_utils::mark;
13 12
14use crate::{ 13use crate::{
15 display::{ToNav, TryToNav}, 14 display::{ToNav, TryToNav},
@@ -130,7 +129,9 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module
130 if let hir::ModuleDef::Module(submodule) = def { 129 if let hir::ModuleDef::Module(submodule) = def {
131 match submodule.definition_source(sema.db).value { 130 match submodule.definition_source(sema.db).value {
132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), 131 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules), 132 hir::ModuleSource::SourceFile(_) => {
133 cov_mark::hit!(dont_recurse_in_outline_submodules)
134 }
134 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable 135 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
135 } 136 }
136 } 137 }
@@ -328,7 +329,6 @@ fn has_test_function_or_multiple_test_submodules(
328#[cfg(test)] 329#[cfg(test)]
329mod tests { 330mod tests {
330 use expect_test::{expect, Expect}; 331 use expect_test::{expect, Expect};
331 use test_utils::mark;
332 332
333 use crate::fixture; 333 use crate::fixture;
334 334
@@ -1056,7 +1056,7 @@ mod tests {
1056 1056
1057 #[test] 1057 #[test]
1058 fn dont_recurse_in_outline_submodules() { 1058 fn dont_recurse_in_outline_submodules() {
1059 mark::check!(dont_recurse_in_outline_submodules); 1059 cov_mark::check!(dont_recurse_in_outline_submodules);
1060 check( 1060 check(
1061 r#" 1061 r#"
1062//- /lib.rs 1062//- /lib.rs
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
new file mode 100644
index 000000000..f3638d928
--- /dev/null
+++ b/crates/ide/src/ssr.rs
@@ -0,0 +1,259 @@
1//! This module provides an SSR assist. It is not desirable to include this
2//! assist in ide_assists because that would require the ide_assists crate
3//! depend on the ide_ssr crate.
4
5use ide_assists::{Assist, AssistId, AssistKind, GroupLabel};
6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
7
8pub(crate) fn add_ssr_assist(
9 db: &RootDatabase,
10 base: &mut Vec<Assist>,
11 resolve: bool,
12 frange: FileRange,
13) -> Option<()> {
14 let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?;
15
16 let (source_change_for_file, source_change_for_workspace) = if resolve {
17 let edits = match_finder.edits();
18
19 let source_change_for_file = {
20 let text_edit_for_file = edits.get(&frange.file_id).cloned().unwrap_or_default();
21 SourceChange::from_text_edit(frange.file_id, text_edit_for_file)
22 };
23
24 let source_change_for_workspace = SourceChange::from(match_finder.edits());
25
26 (Some(source_change_for_file), Some(source_change_for_workspace))
27 } else {
28 (None, None)
29 };
30
31 let assists = vec![
32 ("Apply SSR in file", source_change_for_file),
33 ("Apply SSR in workspace", source_change_for_workspace),
34 ];
35
36 for (label, source_change) in assists.into_iter() {
37 let assist = Assist {
38 id: AssistId("ssr", AssistKind::RefactorRewrite),
39 label: Label::new(label),
40 group: Some(GroupLabel("Apply SSR".into())),
41 target: comment_range,
42 source_change,
43 };
44
45 base.push(assist);
46 }
47 Some(())
48}
49
50#[cfg(test)]
51mod tests {
52 use std::sync::Arc;
53
54 use expect_test::expect;
55 use ide_assists::Assist;
56 use ide_db::{
57 base_db::{fixture::WithFixture, salsa::Durability, FileRange},
58 symbol_index::SymbolsDatabase,
59 RootDatabase,
60 };
61 use rustc_hash::FxHashSet;
62
63 use super::add_ssr_assist;
64
65 fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> {
66 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
67 let mut local_roots = FxHashSet::default();
68 local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
69 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
70
71 let mut assists = vec![];
72
73 add_ssr_assist(
74 &db,
75 &mut assists,
76 resolve,
77 FileRange { file_id, range: range_or_offset.into() },
78 );
79
80 assists
81 }
82
83 #[test]
84 fn not_applicable_comment_not_ssr() {
85 let ra_fixture = r#"
86 //- /lib.rs
87
88 // This is foo $0
89 fn foo() {}
90 "#;
91 let resolve = true;
92
93 let assists = get_assists(ra_fixture, resolve);
94
95 assert_eq!(0, assists.len());
96 }
97
98 #[test]
99 fn resolve_edits_true() {
100 let resolve = true;
101 let assists = get_assists(
102 r#"
103 //- /lib.rs
104 mod bar;
105
106 // 2 ==>> 3$0
107 fn foo() { 2 }
108
109 //- /bar.rs
110 fn bar() { 2 }
111 "#,
112 resolve,
113 );
114
115 assert_eq!(2, assists.len());
116 let mut assists = assists.into_iter();
117
118 let apply_in_file_assist = assists.next().unwrap();
119 expect![[r#"
120 Assist {
121 id: AssistId(
122 "ssr",
123 RefactorRewrite,
124 ),
125 label: "Apply SSR in file",
126 group: Some(
127 GroupLabel(
128 "Apply SSR",
129 ),
130 ),
131 target: 10..21,
132 source_change: Some(
133 SourceChange {
134 source_file_edits: {
135 FileId(
136 0,
137 ): TextEdit {
138 indels: [
139 Indel {
140 insert: "3",
141 delete: 33..34,
142 },
143 ],
144 },
145 },
146 file_system_edits: [],
147 is_snippet: false,
148 },
149 ),
150 }
151 "#]]
152 .assert_debug_eq(&apply_in_file_assist);
153
154 let apply_in_workspace_assist = assists.next().unwrap();
155 expect![[r#"
156 Assist {
157 id: AssistId(
158 "ssr",
159 RefactorRewrite,
160 ),
161 label: "Apply SSR in workspace",
162 group: Some(
163 GroupLabel(
164 "Apply SSR",
165 ),
166 ),
167 target: 10..21,
168 source_change: Some(
169 SourceChange {
170 source_file_edits: {
171 FileId(
172 0,
173 ): TextEdit {
174 indels: [
175 Indel {
176 insert: "3",
177 delete: 33..34,
178 },
179 ],
180 },
181 FileId(
182 1,
183 ): TextEdit {
184 indels: [
185 Indel {
186 insert: "3",
187 delete: 11..12,
188 },
189 ],
190 },
191 },
192 file_system_edits: [],
193 is_snippet: false,
194 },
195 ),
196 }
197 "#]]
198 .assert_debug_eq(&apply_in_workspace_assist);
199 }
200
201 #[test]
202 fn resolve_edits_false() {
203 let resolve = false;
204 let assists = get_assists(
205 r#"
206 //- /lib.rs
207 mod bar;
208
209 // 2 ==>> 3$0
210 fn foo() { 2 }
211
212 //- /bar.rs
213 fn bar() { 2 }
214 "#,
215 resolve,
216 );
217
218 assert_eq!(2, assists.len());
219 let mut assists = assists.into_iter();
220
221 let apply_in_file_assist = assists.next().unwrap();
222 expect![[r#"
223 Assist {
224 id: AssistId(
225 "ssr",
226 RefactorRewrite,
227 ),
228 label: "Apply SSR in file",
229 group: Some(
230 GroupLabel(
231 "Apply SSR",
232 ),
233 ),
234 target: 10..21,
235 source_change: None,
236 }
237 "#]]
238 .assert_debug_eq(&apply_in_file_assist);
239
240 let apply_in_workspace_assist = assists.next().unwrap();
241 expect![[r#"
242 Assist {
243 id: AssistId(
244 "ssr",
245 RefactorRewrite,
246 ),
247 label: "Apply SSR in workspace",
248 group: Some(
249 GroupLabel(
250 "Apply SSR",
251 ),
252 ),
253 target: 10..21,
254 source_change: None,
255 }
256 "#]]
257 .assert_debug_eq(&apply_in_workspace_assist);
258 }
259}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 24fcbb584..b0cfdd8b7 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -330,10 +330,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
330 HlTag::Symbol(SymbolKind::Local) 330 HlTag::Symbol(SymbolKind::Local)
331 }; 331 };
332 let mut h = Highlight::new(tag); 332 let mut h = Highlight::new(tag);
333 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 333 let ty = local.ty(db);
334 if local.is_mut(db) || ty.is_mutable_reference() {
334 h |= HlMod::Mutable; 335 h |= HlMod::Mutable;
335 } 336 }
336 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { 337 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
337 h |= HlMod::Callable; 338 h |= HlMod::Callable;
338 } 339 }
339 return h; 340 return h;
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 63cd51b69..978c479de 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -9,7 +9,7 @@ use syntax::{
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TextRange, TextSize, TokenAtOffset, 10 SyntaxToken, TextRange, TextSize, TokenAtOffset,
11}; 11};
12use test_utils::mark; 12
13use text_edit::TextEdit; 13use text_edit::TextEdit;
14 14
15// Feature: On Enter 15// Feature: On Enter
@@ -55,7 +55,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
55 // Continuing single-line non-doc comments (like this one :) ) is annoying 55 // Continuing single-line non-doc comments (like this one :) ) is annoying
56 if prefix == "//" && comment_range.end() == position.offset { 56 if prefix == "//" && comment_range.end() == position.offset {
57 if comment.text().ends_with(' ') { 57 if comment.text().ends_with(' ') {
58 mark::hit!(continues_end_of_line_comment_with_space); 58 cov_mark::hit!(continues_end_of_line_comment_with_space);
59 remove_trailing_whitespace = true; 59 remove_trailing_whitespace = true;
60 } else if !followed_by_comment(&comment) { 60 } else if !followed_by_comment(&comment) {
61 return None; 61 return None;
@@ -109,7 +109,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
109#[cfg(test)] 109#[cfg(test)]
110mod tests { 110mod tests {
111 use stdx::trim_indent; 111 use stdx::trim_indent;
112 use test_utils::{assert_eq_text, mark}; 112 use test_utils::assert_eq_text;
113 113
114 use crate::fixture; 114 use crate::fixture;
115 115
@@ -238,7 +238,7 @@ fn main() {
238 238
239 #[test] 239 #[test]
240 fn continues_end_of_line_comment_with_space() { 240 fn continues_end_of_line_comment_with_space() {
241 mark::check!(continues_end_of_line_comment_with_space); 241 cov_mark::check!(continues_end_of_line_comment_with_space);
242 do_check( 242 do_check(
243 r#" 243 r#"
244fn main() { 244fn main() {