aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/Cargo.toml2
-rw-r--r--crates/ide/src/display/short_label.rs6
-rw-r--r--crates/ide/src/hover.rs62
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/references/rename.rs55
-rw-r--r--crates/ide/src/ssr.rs259
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs5
7 files changed, 363 insertions, 36 deletions
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f7c5efaf3..107bd8432 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -27,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
27ide_db = { path = "../ide_db", version = "0.0.0" } 27ide_db = { path = "../ide_db", version = "0.0.0" }
28cfg = { path = "../cfg", version = "0.0.0" } 28cfg = { path = "../cfg", version = "0.0.0" }
29profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
30test_utils = { path = "../test_utils", version = "0.0.0" }
31ide_assists = { path = "../ide_assists", version = "0.0.0" } 30ide_assists = { path = "../ide_assists", version = "0.0.0" }
32ide_ssr = { path = "../ide_ssr", version = "0.0.0" } 31ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
33ide_completion = { path = "../ide_completion", version = "0.0.0" } 32ide_completion = { path = "../ide_completion", version = "0.0.0" }
@@ -37,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" }
37hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
38 37
39[dev-dependencies] 38[dev-dependencies]
39test_utils = { path = "../test_utils" }
40expect-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 5d1cc2052..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,
@@ -366,7 +367,7 @@ fn hover_for_definition(
366 .and_then(|fd| hover_for_builtin(fd, it)) 367 .and_then(|fd| hover_for_builtin(fd, it))
367 .or_else(|| Some(Markup::fenced_block(&it.name()))), 368 .or_else(|| Some(Markup::fenced_block(&it.name()))),
368 }, 369 },
369 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => hover_for_local(it, db),
370 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
371 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 {
372 Adt::Struct(it) => from_def_source(db, it, mod_path), 373 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -405,6 +406,29 @@ fn hover_for_definition(
405 } 406 }
406} 407}
407 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
408fn hover_for_keyword( 432fn hover_for_keyword(
409 sema: &Semantics<RootDatabase>, 433 sema: &Semantics<RootDatabase>,
410 links_in_hover: bool, 434 links_in_hover: bool,
@@ -574,7 +598,7 @@ fn main() {
574 *iter* 598 *iter*
575 599
576 ```rust 600 ```rust
577 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>>
578 ``` 602 ```
579 "#]], 603 "#]],
580 ); 604 );
@@ -798,7 +822,7 @@ fn main() {
798 ``` 822 ```
799 823
800 ```rust 824 ```rust
801 const foo: u32 = 123 825 const foo: u32
802 ``` 826 ```
803 "#]], 827 "#]],
804 ); 828 );
@@ -831,7 +855,7 @@ fn main() {
831 *zz* 855 *zz*
832 856
833 ```rust 857 ```rust
834 Test<i32, u8> 858 let zz: Test<i32, u8>
835 ``` 859 ```
836 "#]], 860 "#]],
837 ); 861 );
@@ -870,7 +894,7 @@ fn main() { let b$0ar = Some(12); }
870 *bar* 894 *bar*
871 895
872 ```rust 896 ```rust
873 Option<i32> 897 let bar: Option<i32>
874 ``` 898 ```
875 "#]], 899 "#]],
876 ); 900 );
@@ -938,7 +962,7 @@ fn main() {
938 *foo* 962 *foo*
939 963
940 ```rust 964 ```rust
941 i32 965 foo: i32
942 ``` 966 ```
943 "#]], 967 "#]],
944 ) 968 )
@@ -952,7 +976,7 @@ fn main() {
952 *foo* 976 *foo*
953 977
954 ```rust 978 ```rust
955 i32 979 foo: i32
956 ``` 980 ```
957 "#]], 981 "#]],
958 ) 982 )
@@ -966,7 +990,7 @@ fn main() {
966 *foo* 990 *foo*
967 991
968 ```rust 992 ```rust
969 i32 993 foo: i32
970 ``` 994 ```
971 "#]], 995 "#]],
972 ) 996 )
@@ -980,7 +1004,7 @@ fn main() {
980 *foo* 1004 *foo*
981 1005
982 ```rust 1006 ```rust
983 i32 1007 foo: i32
984 ``` 1008 ```
985 "#]], 1009 "#]],
986 ) 1010 )
@@ -1000,7 +1024,7 @@ fn main() {
1000 *_x* 1024 *_x*
1001 1025
1002 ```rust 1026 ```rust
1003 impl Deref<Target = u8> + DerefMut<Target = u8> 1027 _x: impl Deref<Target = u8> + DerefMut<Target = u8>
1004 ``` 1028 ```
1005 "#]], 1029 "#]],
1006 ) 1030 )
@@ -1022,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); }
1022 *foo_test* 1046 *foo_test*
1023 1047
1024 ```rust 1048 ```rust
1025 Thing 1049 let foo_test: Thing
1026 ``` 1050 ```
1027 "#]], 1051 "#]],
1028 ) 1052 )
@@ -1081,7 +1105,7 @@ fn main() {
1081 ``` 1105 ```
1082 1106
1083 ```rust 1107 ```rust
1084 const C: u32 = 1 1108 const C: u32
1085 ``` 1109 ```
1086 "#]], 1110 "#]],
1087 ) 1111 )
@@ -1182,7 +1206,7 @@ fn y() {
1182 *x* 1206 *x*
1183 1207
1184 ```rust 1208 ```rust
1185 i32 1209 let x: i32
1186 ``` 1210 ```
1187 "#]], 1211 "#]],
1188 ) 1212 )
@@ -1259,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1259 *bar* 1283 *bar*
1260 1284
1261 ```rust 1285 ```rust
1262 u32 1286 bar: u32
1263 ``` 1287 ```
1264 "#]], 1288 "#]],
1265 ); 1289 );
@@ -1277,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1277 *bar* 1301 *bar*
1278 1302
1279 ```rust 1303 ```rust
1280 u32 1304 bar: u32
1281 ``` 1305 ```
1282 "#]], 1306 "#]],
1283 ); 1307 );
@@ -3302,7 +3326,7 @@ fn main() {
3302 *f* 3326 *f*
3303 3327
3304 ```rust 3328 ```rust
3305 &i32 3329 f: &i32
3306 ``` 3330 ```
3307 "#]], 3331 "#]],
3308 ); 3332 );
@@ -3321,7 +3345,7 @@ impl Foo {
3321 *self* 3345 *self*
3322 3346
3323 ```rust 3347 ```rust
3324 &Foo 3348 self: &Foo
3325 ``` 3349 ```
3326 "#]], 3350 "#]],
3327 ); 3351 );
@@ -3341,7 +3365,7 @@ impl Foo {
3341 *self* 3365 *self*
3342 3366
3343 ```rust 3367 ```rust
3344 Arc<Foo> 3368 self: Arc<Foo>
3345 ``` 3369 ```
3346 "#]], 3370 "#]],
3347 ); 3371 );
@@ -3537,7 +3561,7 @@ fn foo() {
3537 ``` 3561 ```
3538 3562
3539 ```rust 3563 ```rust
3540 const FOO: usize = 3 3564 const FOO: usize
3541 ``` 3565 ```
3542 3566
3543 --- 3567 ---
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index f83ed65d5..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::{
@@ -502,7 +504,11 @@ impl Analysis {
502 resolve: bool, 504 resolve: bool,
503 frange: FileRange, 505 frange: FileRange,
504 ) -> Cancelable<Vec<Assist>> { 506 ) -> Cancelable<Vec<Assist>> {
505 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 })
506 } 512 }
507 513
508 /// 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/references/rename.rs b/crates/ide/src/references/rename.rs
index 05c73de88..1e378279d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -21,7 +21,7 @@ use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceCh
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,
@@ -545,6 +545,8 @@ mod tests {
545 545
546 use crate::{fixture, FileId}; 546 use crate::{fixture, FileId};
547 547
548 use super::{RangeInfo, RenameError};
549
548 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) {
549 let ra_fixture_after = &trim_indent(ra_fixture_after); 551 let ra_fixture_after = &trim_indent(ra_fixture_after);
550 let (analysis, position) = fixture::position(ra_fixture_before); 552 let (analysis, position) = fixture::position(ra_fixture_before);
@@ -590,6 +592,45 @@ mod tests {
590 expect.assert_debug_eq(&source_change) 592 expect.assert_debug_eq(&source_change)
591 } 593 }
592 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
593 #[test] 634 #[test]
594 fn test_rename_to_underscore() { 635 fn test_rename_to_underscore() {
595 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; }"#);
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;