diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/generate_enum_match_method.rs | 41 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_getter.rs | 40 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_getter_mut.rs | 40 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_new.rs | 4 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_setter.rs | 40 | ||||
-rw-r--r-- | crates/assists/src/handlers/inline_local_variable.rs | 109 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 24 | ||||
-rw-r--r-- | crates/ide/src/call_hierarchy.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 370 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 197 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 119 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 49 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 30 |
14 files changed, 631 insertions, 444 deletions
diff --git a/crates/assists/src/handlers/generate_enum_match_method.rs b/crates/assists/src/handlers/generate_enum_match_method.rs index c3ff38b66..aeb887e71 100644 --- a/crates/assists/src/handlers/generate_enum_match_method.rs +++ b/crates/assists/src/handlers/generate_enum_match_method.rs | |||
@@ -4,7 +4,7 @@ use syntax::ast::{self, AstNode, NameOwner}; | |||
4 | use test_utils::mark; | 4 | use test_utils::mark; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | utils::{find_impl_block, find_struct_impl, generate_impl_text}, | 7 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
8 | AssistContext, AssistId, AssistKind, Assists, | 8 | AssistContext, AssistId, AssistKind, Assists, |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -80,7 +80,7 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) | |||
80 | ); | 80 | ); |
81 | 81 | ||
82 | let start_offset = impl_def | 82 | let start_offset = impl_def |
83 | .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) | 83 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
84 | .unwrap_or_else(|| { | 84 | .unwrap_or_else(|| { |
85 | buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf); | 85 | buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf); |
86 | parent_enum.syntax().text_range().end() | 86 | parent_enum.syntax().text_range().end() |
@@ -200,4 +200,41 @@ impl Variant { | |||
200 | }"#, | 200 | }"#, |
201 | ); | 201 | ); |
202 | } | 202 | } |
203 | |||
204 | #[test] | ||
205 | fn test_multiple_generate_enum_match_from_variant() { | ||
206 | check_assist( | ||
207 | generate_enum_match_method, | ||
208 | r#" | ||
209 | enum Variant { | ||
210 | Undefined, | ||
211 | Minor, | ||
212 | Major$0, | ||
213 | } | ||
214 | |||
215 | impl Variant { | ||
216 | /// Returns `true` if the variant is [`Minor`]. | ||
217 | fn is_minor(&self) -> bool { | ||
218 | matches!(self, Self::Minor) | ||
219 | } | ||
220 | }"#, | ||
221 | r#"enum Variant { | ||
222 | Undefined, | ||
223 | Minor, | ||
224 | Major, | ||
225 | } | ||
226 | |||
227 | impl Variant { | ||
228 | /// Returns `true` if the variant is [`Minor`]. | ||
229 | fn is_minor(&self) -> bool { | ||
230 | matches!(self, Self::Minor) | ||
231 | } | ||
232 | |||
233 | /// Returns `true` if the variant is [`Major`]. | ||
234 | fn is_major(&self) -> bool { | ||
235 | matches!(self, Self::Major) | ||
236 | } | ||
237 | }"#, | ||
238 | ); | ||
239 | } | ||
203 | } | 240 | } |
diff --git a/crates/assists/src/handlers/generate_getter.rs b/crates/assists/src/handlers/generate_getter.rs index b63dfce41..fbcf8b069 100644 --- a/crates/assists/src/handlers/generate_getter.rs +++ b/crates/assists/src/handlers/generate_getter.rs | |||
@@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner; | |||
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | utils::{find_impl_block, find_struct_impl, generate_impl_text}, | 6 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
7 | AssistContext, AssistId, AssistKind, Assists, | 7 | AssistContext, AssistId, AssistKind, Assists, |
8 | }; | 8 | }; |
9 | 9 | ||
@@ -73,7 +73,7 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
73 | ); | 73 | ); |
74 | 74 | ||
75 | let start_offset = impl_def | 75 | let start_offset = impl_def |
76 | .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) | 76 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
77 | .unwrap_or_else(|| { | 77 | .unwrap_or_else(|| { |
78 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); | 78 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
79 | strukt.syntax().text_range().end() | 79 | strukt.syntax().text_range().end() |
@@ -153,4 +153,40 @@ impl<T: Clone> Context<T> { | |||
153 | }"#, | 153 | }"#, |
154 | ); | 154 | ); |
155 | } | 155 | } |
156 | |||
157 | #[test] | ||
158 | fn test_multiple_generate_getter() { | ||
159 | check_assist( | ||
160 | generate_getter, | ||
161 | r#" | ||
162 | struct Context<T: Clone> { | ||
163 | data: T, | ||
164 | cou$0nt: usize, | ||
165 | } | ||
166 | |||
167 | impl<T: Clone> Context<T> { | ||
168 | /// Get a reference to the context's data. | ||
169 | fn data(&self) -> &T { | ||
170 | &self.data | ||
171 | } | ||
172 | }"#, | ||
173 | r#" | ||
174 | struct Context<T: Clone> { | ||
175 | data: T, | ||
176 | count: usize, | ||
177 | } | ||
178 | |||
179 | impl<T: Clone> Context<T> { | ||
180 | /// Get a reference to the context's data. | ||
181 | fn data(&self) -> &T { | ||
182 | &self.data | ||
183 | } | ||
184 | |||
185 | /// Get a reference to the context's count. | ||
186 | fn count(&self) -> &usize { | ||
187 | &self.count | ||
188 | } | ||
189 | }"#, | ||
190 | ); | ||
191 | } | ||
156 | } | 192 | } |
diff --git a/crates/assists/src/handlers/generate_getter_mut.rs b/crates/assists/src/handlers/generate_getter_mut.rs index b5085035e..bf0d99881 100644 --- a/crates/assists/src/handlers/generate_getter_mut.rs +++ b/crates/assists/src/handlers/generate_getter_mut.rs | |||
@@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner; | |||
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | utils::{find_impl_block, find_struct_impl, generate_impl_text}, | 6 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
7 | AssistContext, AssistId, AssistKind, Assists, | 7 | AssistContext, AssistId, AssistKind, Assists, |
8 | }; | 8 | }; |
9 | 9 | ||
@@ -76,7 +76,7 @@ pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
76 | ); | 76 | ); |
77 | 77 | ||
78 | let start_offset = impl_def | 78 | let start_offset = impl_def |
79 | .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) | 79 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
80 | .unwrap_or_else(|| { | 80 | .unwrap_or_else(|| { |
81 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); | 81 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
82 | strukt.syntax().text_range().end() | 82 | strukt.syntax().text_range().end() |
@@ -156,4 +156,40 @@ impl<T: Clone> Context<T> { | |||
156 | }"#, | 156 | }"#, |
157 | ); | 157 | ); |
158 | } | 158 | } |
159 | |||
160 | #[test] | ||
161 | fn test_multiple_generate_getter_mut() { | ||
162 | check_assist( | ||
163 | generate_getter_mut, | ||
164 | r#" | ||
165 | struct Context<T: Clone> { | ||
166 | data: T, | ||
167 | cou$0nt: usize, | ||
168 | } | ||
169 | |||
170 | impl<T: Clone> Context<T> { | ||
171 | /// Get a mutable reference to the context's data. | ||
172 | fn data_mut(&mut self) -> &mut T { | ||
173 | &mut self.data | ||
174 | } | ||
175 | }"#, | ||
176 | r#" | ||
177 | struct Context<T: Clone> { | ||
178 | data: T, | ||
179 | count: usize, | ||
180 | } | ||
181 | |||
182 | impl<T: Clone> Context<T> { | ||
183 | /// Get a mutable reference to the context's data. | ||
184 | fn data_mut(&mut self) -> &mut T { | ||
185 | &mut self.data | ||
186 | } | ||
187 | |||
188 | /// Get a mutable reference to the context's count. | ||
189 | fn count_mut(&mut self) -> &mut usize { | ||
190 | &mut self.count | ||
191 | } | ||
192 | }"#, | ||
193 | ); | ||
194 | } | ||
159 | } | 195 | } |
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs index c29077225..8ce5930b7 100644 --- a/crates/assists/src/handlers/generate_new.rs +++ b/crates/assists/src/handlers/generate_new.rs | |||
@@ -4,7 +4,7 @@ use stdx::format_to; | |||
4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; | 4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | utils::{find_impl_block, find_struct_impl, generate_impl_text}, | 7 | utils::{find_impl_block_start, find_struct_impl, generate_impl_text}, |
8 | AssistContext, AssistId, AssistKind, Assists, | 8 | AssistContext, AssistId, AssistKind, Assists, |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -58,7 +58,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
58 | format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); | 58 | format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); |
59 | 59 | ||
60 | let start_offset = impl_def | 60 | let start_offset = impl_def |
61 | .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) | 61 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) |
62 | .unwrap_or_else(|| { | 62 | .unwrap_or_else(|| { |
63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); | 63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); |
64 | strukt.syntax().text_range().end() | 64 | strukt.syntax().text_range().end() |
diff --git a/crates/assists/src/handlers/generate_setter.rs b/crates/assists/src/handlers/generate_setter.rs index c9043a162..b655f9b9c 100644 --- a/crates/assists/src/handlers/generate_setter.rs +++ b/crates/assists/src/handlers/generate_setter.rs | |||
@@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner; | |||
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | utils::{find_impl_block, find_struct_impl, generate_impl_text}, | 6 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
7 | AssistContext, AssistId, AssistKind, Assists, | 7 | AssistContext, AssistId, AssistKind, Assists, |
8 | }; | 8 | }; |
9 | 9 | ||
@@ -79,7 +79,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
79 | ); | 79 | ); |
80 | 80 | ||
81 | let start_offset = impl_def | 81 | let start_offset = impl_def |
82 | .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) | 82 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
83 | .unwrap_or_else(|| { | 83 | .unwrap_or_else(|| { |
84 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); | 84 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
85 | strukt.syntax().text_range().end() | 85 | strukt.syntax().text_range().end() |
@@ -159,4 +159,40 @@ impl<T: Clone> Person<T> { | |||
159 | }"#, | 159 | }"#, |
160 | ); | 160 | ); |
161 | } | 161 | } |
162 | |||
163 | #[test] | ||
164 | fn test_multiple_generate_setter() { | ||
165 | check_assist( | ||
166 | generate_setter, | ||
167 | r#" | ||
168 | struct Context<T: Clone> { | ||
169 | data: T, | ||
170 | cou$0nt: usize, | ||
171 | } | ||
172 | |||
173 | impl<T: Clone> Context<T> { | ||
174 | /// Set the context's data. | ||
175 | fn set_data(&mut self, data: T) { | ||
176 | self.data = data; | ||
177 | } | ||
178 | }"#, | ||
179 | r#" | ||
180 | struct Context<T: Clone> { | ||
181 | data: T, | ||
182 | count: usize, | ||
183 | } | ||
184 | |||
185 | impl<T: Clone> Context<T> { | ||
186 | /// Set the context's data. | ||
187 | fn set_data(&mut self, data: T) { | ||
188 | self.data = data; | ||
189 | } | ||
190 | |||
191 | /// Set the context's count. | ||
192 | fn set_count(&mut self, count: usize) { | ||
193 | self.count = count; | ||
194 | } | ||
195 | }"#, | ||
196 | ); | ||
197 | } | ||
162 | } | 198 | } |
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs index 0e63a60e8..8d28431cf 100644 --- a/crates/assists/src/handlers/inline_local_variable.rs +++ b/crates/assists/src/handlers/inline_local_variable.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ide_db::{ | 1 | use std::collections::HashMap; |
2 | defs::Definition, | 2 | |
3 | search::{FileReference, ReferenceKind}, | 3 | use ide_db::{defs::Definition, search::FileReference}; |
4 | }; | ||
5 | use syntax::{ | 4 | use syntax::{ |
6 | ast::{self, AstNode, AstToken}, | 5 | ast::{self, AstNode, AstToken}, |
7 | TextRange, | 6 | TextRange, |
@@ -68,44 +67,51 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
68 | 67 | ||
69 | let wrap_in_parens = usages | 68 | let wrap_in_parens = usages |
70 | .references | 69 | .references |
71 | .values() | 70 | .iter() |
72 | .flatten() | 71 | .map(|(&file_id, refs)| { |
73 | .map(|&FileReference { range, .. }| { | 72 | refs.iter() |
74 | let usage_node = | 73 | .map(|&FileReference { range, .. }| { |
75 | ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?; | 74 | let usage_node = ctx |
76 | let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); | 75 | .covering_node_for_range(range) |
77 | let usage_parent = match usage_parent_option { | 76 | .ancestors() |
78 | Some(u) => u, | 77 | .find_map(ast::PathExpr::cast)?; |
79 | None => return Ok(false), | 78 | let usage_parent_option = |
80 | }; | 79 | usage_node.syntax().parent().and_then(ast::Expr::cast); |
81 | 80 | let usage_parent = match usage_parent_option { | |
82 | Ok(!matches!( | 81 | Some(u) => u, |
83 | (&initializer_expr, usage_parent), | 82 | None => return Ok(false), |
84 | (ast::Expr::CallExpr(_), _) | 83 | }; |
85 | | (ast::Expr::IndexExpr(_), _) | 84 | |
86 | | (ast::Expr::MethodCallExpr(_), _) | 85 | Ok(!matches!( |
87 | | (ast::Expr::FieldExpr(_), _) | 86 | (&initializer_expr, usage_parent), |
88 | | (ast::Expr::TryExpr(_), _) | 87 | (ast::Expr::CallExpr(_), _) |
89 | | (ast::Expr::RefExpr(_), _) | 88 | | (ast::Expr::IndexExpr(_), _) |
90 | | (ast::Expr::Literal(_), _) | 89 | | (ast::Expr::MethodCallExpr(_), _) |
91 | | (ast::Expr::TupleExpr(_), _) | 90 | | (ast::Expr::FieldExpr(_), _) |
92 | | (ast::Expr::ArrayExpr(_), _) | 91 | | (ast::Expr::TryExpr(_), _) |
93 | | (ast::Expr::ParenExpr(_), _) | 92 | | (ast::Expr::RefExpr(_), _) |
94 | | (ast::Expr::PathExpr(_), _) | 93 | | (ast::Expr::Literal(_), _) |
95 | | (ast::Expr::BlockExpr(_), _) | 94 | | (ast::Expr::TupleExpr(_), _) |
96 | | (ast::Expr::EffectExpr(_), _) | 95 | | (ast::Expr::ArrayExpr(_), _) |
97 | | (_, ast::Expr::CallExpr(_)) | 96 | | (ast::Expr::ParenExpr(_), _) |
98 | | (_, ast::Expr::TupleExpr(_)) | 97 | | (ast::Expr::PathExpr(_), _) |
99 | | (_, ast::Expr::ArrayExpr(_)) | 98 | | (ast::Expr::BlockExpr(_), _) |
100 | | (_, ast::Expr::ParenExpr(_)) | 99 | | (ast::Expr::EffectExpr(_), _) |
101 | | (_, ast::Expr::ForExpr(_)) | 100 | | (_, ast::Expr::CallExpr(_)) |
102 | | (_, ast::Expr::WhileExpr(_)) | 101 | | (_, ast::Expr::TupleExpr(_)) |
103 | | (_, ast::Expr::BreakExpr(_)) | 102 | | (_, ast::Expr::ArrayExpr(_)) |
104 | | (_, ast::Expr::ReturnExpr(_)) | 103 | | (_, ast::Expr::ParenExpr(_)) |
105 | | (_, ast::Expr::MatchExpr(_)) | 104 | | (_, ast::Expr::ForExpr(_)) |
106 | )) | 105 | | (_, ast::Expr::WhileExpr(_)) |
106 | | (_, ast::Expr::BreakExpr(_)) | ||
107 | | (_, ast::Expr::ReturnExpr(_)) | ||
108 | | (_, ast::Expr::MatchExpr(_)) | ||
109 | )) | ||
110 | }) | ||
111 | .collect::<Result<_, _>>() | ||
112 | .map(|b| (file_id, b)) | ||
107 | }) | 113 | }) |
108 | .collect::<Result<Vec<_>, _>>()?; | 114 | .collect::<Result<HashMap<_, Vec<_>>, _>>()?; |
109 | 115 | ||
110 | let init_str = initializer_expr.syntax().text().to_string(); | 116 | let init_str = initializer_expr.syntax().text().to_string(); |
111 | let init_in_paren = format!("({})", &init_str); | 117 | let init_in_paren = format!("({})", &init_str); |
@@ -117,16 +123,19 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
117 | target, | 123 | target, |
118 | move |builder| { | 124 | move |builder| { |
119 | builder.delete(delete_range); | 125 | builder.delete(delete_range); |
120 | for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens) | 126 | for (file_id, references) in usages.references { |
121 | { | 127 | for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) { |
122 | let replacement = | 128 | let replacement = |
123 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 129 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
124 | match reference.kind { | 130 | match reference.name.as_name_ref() { |
125 | ReferenceKind::FieldShorthandForLocal => { | 131 | Some(name_ref) |
126 | mark::hit!(inline_field_shorthand); | 132 | if ast::RecordExprField::for_field_name(name_ref).is_some() => |
127 | builder.insert(reference.range.end(), format!(": {}", replacement)) | 133 | { |
134 | mark::hit!(inline_field_shorthand); | ||
135 | builder.insert(reference.range.end(), format!(": {}", replacement)); | ||
136 | } | ||
137 | _ => builder.replace(reference.range, replacement), | ||
128 | } | 138 | } |
129 | _ => builder.replace(reference.range, replacement), | ||
130 | } | 139 | } |
131 | } | 140 | } |
132 | }, | 141 | }, |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 643dade23..5dd32aef1 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -279,7 +279,7 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { | |||
279 | // | 279 | // |
280 | // FIXME: change the new fn checking to a more semantic approach when that's more | 280 | // FIXME: change the new fn checking to a more semantic approach when that's more |
281 | // viable (e.g. we process proc macros, etc) | 281 | // viable (e.g. we process proc macros, etc) |
282 | // FIXME: this partially overlaps with `find_impl_block` | 282 | // FIXME: this partially overlaps with `find_impl_block_*` |
283 | pub(crate) fn find_struct_impl( | 283 | pub(crate) fn find_struct_impl( |
284 | ctx: &AssistContext, | 284 | ctx: &AssistContext, |
285 | strukt: &ast::Adt, | 285 | strukt: &ast::Adt, |
@@ -343,17 +343,25 @@ fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool { | |||
343 | 343 | ||
344 | /// Find the start of the `impl` block for the given `ast::Impl`. | 344 | /// Find the start of the `impl` block for the given `ast::Impl`. |
345 | // | 345 | // |
346 | // FIXME: add a way to find the end of the `impl` block. | ||
347 | // FIXME: this partially overlaps with `find_struct_impl` | 346 | // FIXME: this partially overlaps with `find_struct_impl` |
348 | pub(crate) fn find_impl_block(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> { | 347 | pub(crate) fn find_impl_block_start(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> { |
349 | buf.push('\n'); | 348 | buf.push('\n'); |
350 | let start = impl_def | 349 | let start = impl_def.assoc_item_list().and_then(|it| it.l_curly_token())?.text_range().end(); |
351 | .syntax() | 350 | Some(start) |
352 | .descendants_with_tokens() | 351 | } |
353 | .find(|t| t.kind() == T!['{'])? | 352 | |
353 | /// Find the end of the `impl` block for the given `ast::Impl`. | ||
354 | // | ||
355 | // FIXME: this partially overlaps with `find_struct_impl` | ||
356 | pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> { | ||
357 | buf.push('\n'); | ||
358 | let end = impl_def | ||
359 | .assoc_item_list() | ||
360 | .and_then(|it| it.r_curly_token())? | ||
361 | .prev_sibling_or_token()? | ||
354 | .text_range() | 362 | .text_range() |
355 | .end(); | 363 | .end(); |
356 | Some(start) | 364 | Some(end) |
357 | } | 365 | } |
358 | 366 | ||
359 | // Generates the surrounding `impl Type { <code> }` including type and lifetime | 367 | // Generates the surrounding `impl Type { <code> }` including type and lifetime |
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index b10a0a78b..b848945d7 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -47,11 +47,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
47 | 47 | ||
48 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
49 | 49 | ||
50 | for (&file_id, references) in refs.references().iter() { | 50 | for (file_id, references) in refs.references { |
51 | let file = sema.parse(file_id); | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | 52 | let file = file.syntax(); |
53 | for reference in references { | 53 | for (r_range, _) in references { |
54 | let token = file.token_at_offset(reference.range.start()).next()?; | 54 | let token = file.token_at_offset(r_range.start()).next()?; |
55 | let token = sema.descend_into_macros(token); | 55 | let token = sema.descend_into_macros(token); |
56 | let syntax = token.parent(); | 56 | let syntax = token.parent(); |
57 | 57 | ||
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
61 | let def = sema.to_def(&fn_)?; | 61 | let def = sema.to_def(&fn_)?; |
62 | def.try_to_nav(sema.db) | 62 | def.try_to_nav(sema.db) |
63 | }) { | 63 | }) { |
64 | let relative_range = reference.range; | 64 | let relative_range = r_range; |
65 | calls.add(&nav, relative_range); | 65 | calls.add(&nav, relative_range); |
66 | } | 66 | } |
67 | } | 67 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 989e94a31..592b12925 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -73,7 +73,7 @@ pub use crate::{ | |||
73 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 73 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
74 | markup::Markup, | 74 | markup::Markup, |
75 | prime_caches::PrimeCachesProgress, | 75 | prime_caches::PrimeCachesProgress, |
76 | references::{rename::RenameError, Declaration, ReferenceSearchResult}, | 76 | references::{rename::RenameError, ReferenceSearchResult}, |
77 | runnables::{Runnable, RunnableKind, TestId}, | 77 | runnables::{Runnable, RunnableKind, TestId}, |
78 | syntax_highlighting::{ | 78 | syntax_highlighting::{ |
79 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, | 79 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, |
@@ -94,7 +94,7 @@ pub use ide_db::{ | |||
94 | call_info::CallInfo, | 94 | call_info::CallInfo, |
95 | label::Label, | 95 | label::Label, |
96 | line_index::{LineCol, LineIndex}, | 96 | line_index::{LineCol, LineIndex}, |
97 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope}, | 97 | search::{ReferenceAccess, SearchScope}, |
98 | source_change::{FileSystemEdit, SourceChange}, | 98 | source_change::{FileSystemEdit, SourceChange}, |
99 | symbol_index::Query, | 99 | symbol_index::Query, |
100 | RootDatabase, | 100 | RootDatabase, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 6999dacee..c7cefb3b6 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -11,14 +11,14 @@ | |||
11 | 11 | ||
12 | pub(crate) mod rename; | 12 | pub(crate) mod rename; |
13 | 13 | ||
14 | use either::Either; | ||
15 | use hir::Semantics; | 14 | use hir::Semantics; |
16 | use ide_db::{ | 15 | use ide_db::{ |
17 | base_db::FileId, | 16 | base_db::FileId, |
18 | defs::{Definition, NameClass, NameRefClass}, | 17 | defs::{Definition, NameClass, NameRefClass}, |
19 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, | 18 | search::{ReferenceAccess, SearchScope}, |
20 | RootDatabase, | 19 | RootDatabase, |
21 | }; | 20 | }; |
21 | use rustc_hash::FxHashMap; | ||
22 | use syntax::{ | 22 | use syntax::{ |
23 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
24 | ast::{self, NameOwner}, | 24 | ast::{self, NameOwner}, |
@@ -29,52 +29,16 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget}; | |||
29 | 29 | ||
30 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone)] |
31 | pub struct ReferenceSearchResult { | 31 | pub struct ReferenceSearchResult { |
32 | declaration: Declaration, | 32 | pub declaration: Declaration, |
33 | references: UsageSearchResult, | 33 | pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>, |
34 | } | 34 | } |
35 | 35 | ||
36 | #[derive(Debug, Clone)] | 36 | #[derive(Debug, Clone)] |
37 | pub struct Declaration { | 37 | pub struct Declaration { |
38 | pub nav: NavigationTarget, | 38 | pub nav: NavigationTarget, |
39 | pub kind: ReferenceKind, | ||
40 | pub access: Option<ReferenceAccess>, | 39 | pub access: Option<ReferenceAccess>, |
41 | } | 40 | } |
42 | 41 | ||
43 | impl ReferenceSearchResult { | ||
44 | pub fn references(&self) -> &UsageSearchResult { | ||
45 | &self.references | ||
46 | } | ||
47 | |||
48 | pub fn references_with_declaration(mut self) -> UsageSearchResult { | ||
49 | let decl_ref = FileReference { | ||
50 | range: self.declaration.nav.focus_or_full_range(), | ||
51 | kind: self.declaration.kind, | ||
52 | access: self.declaration.access, | ||
53 | }; | ||
54 | let file_id = self.declaration.nav.file_id; | ||
55 | self.references.references.entry(file_id).or_default().push(decl_ref); | ||
56 | self.references | ||
57 | } | ||
58 | |||
59 | /// Total number of references | ||
60 | /// At least 1 since all valid references should | ||
61 | /// Have a declaration | ||
62 | pub fn len(&self) -> usize { | ||
63 | self.references.len() + 1 | ||
64 | } | ||
65 | } | ||
66 | |||
67 | // allow turning ReferenceSearchResult into an iterator | ||
68 | // over References | ||
69 | impl IntoIterator for ReferenceSearchResult { | ||
70 | type Item = (FileId, Vec<FileReference>); | ||
71 | type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>; | ||
72 | |||
73 | fn into_iter(self) -> Self::IntoIter { | ||
74 | self.references_with_declaration().into_iter() | ||
75 | } | ||
76 | } | ||
77 | |||
78 | pub(crate) fn find_all_refs( | 42 | pub(crate) fn find_all_refs( |
79 | sema: &Semantics<RootDatabase>, | 43 | sema: &Semantics<RootDatabase>, |
80 | position: FilePosition, | 44 | position: FilePosition, |
@@ -83,83 +47,72 @@ pub(crate) fn find_all_refs( | |||
83 | let _p = profile::span("find_all_refs"); | 47 | let _p = profile::span("find_all_refs"); |
84 | let syntax = sema.parse(position.file_id).syntax().clone(); | 48 | let syntax = sema.parse(position.file_id).syntax().clone(); |
85 | 49 | ||
86 | let (opt_name, search_kind) = if let Some(name) = | 50 | let (opt_name, ctor_filter): (_, Option<fn(&_) -> bool>) = if let Some(name) = |
87 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | 51 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) |
88 | { | 52 | { |
89 | (Some(name), ReferenceKind::StructLiteral) | 53 | ( |
54 | Some(name), | ||
55 | Some(|name_ref| is_record_lit_name_ref(name_ref) || is_call_expr_name_ref(name_ref)), | ||
56 | ) | ||
90 | } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position) | 57 | } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position) |
91 | { | 58 | { |
92 | (Some(name), ReferenceKind::EnumLiteral) | 59 | (Some(name), Some(is_enum_lit_name_ref)) |
93 | } else { | 60 | } else { |
94 | ( | 61 | (sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None) |
95 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), | ||
96 | ReferenceKind::Other, | ||
97 | ) | ||
98 | }; | 62 | }; |
99 | 63 | ||
100 | let def = find_name(&sema, &syntax, position, opt_name)?; | 64 | let def = find_def(&sema, &syntax, position, opt_name)?; |
101 | 65 | ||
102 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 66 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
103 | usages | 67 | if let Some(ctor_filter) = ctor_filter { |
104 | .references | 68 | // filter for constructor-literals |
105 | .values_mut() | 69 | usages.references.values_mut().for_each(|it| { |
106 | .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); | 70 | it.retain(|reference| reference.name.as_name_ref().map_or(false, ctor_filter)); |
107 | usages.references.retain(|_, it| !it.is_empty()); | 71 | }); |
108 | 72 | usages.references.retain(|_, it| !it.is_empty()); | |
73 | } | ||
109 | let nav = def.try_to_nav(sema.db)?; | 74 | let nav = def.try_to_nav(sema.db)?; |
110 | let decl_range = nav.focus_or_full_range(); | 75 | let decl_range = nav.focus_or_full_range(); |
111 | 76 | ||
112 | let mut kind = ReferenceKind::Other; | 77 | let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) }; |
113 | if let Definition::Local(local) = def { | 78 | let references = usages |
114 | match local.source(sema.db).value { | 79 | .into_iter() |
115 | Either::Left(pat) => { | 80 | .map(|(file_id, refs)| { |
116 | if matches!( | 81 | (file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect()) |
117 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | 82 | }) |
118 | Some(pat_field) if pat_field.name_ref().is_none() | 83 | .collect(); |
119 | ) { | ||
120 | kind = ReferenceKind::FieldShorthandForLocal; | ||
121 | } | ||
122 | } | ||
123 | Either::Right(_) => kind = ReferenceKind::SelfParam, | ||
124 | } | ||
125 | } else if matches!( | ||
126 | def, | ||
127 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
128 | ) { | ||
129 | kind = ReferenceKind::Lifetime; | ||
130 | }; | ||
131 | |||
132 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | ||
133 | 84 | ||
134 | Some(ReferenceSearchResult { declaration, references: usages }) | 85 | Some(ReferenceSearchResult { declaration, references }) |
135 | } | 86 | } |
136 | 87 | ||
137 | fn find_name( | 88 | fn find_def( |
138 | sema: &Semantics<RootDatabase>, | 89 | sema: &Semantics<RootDatabase>, |
139 | syntax: &SyntaxNode, | 90 | syntax: &SyntaxNode, |
140 | position: FilePosition, | 91 | position: FilePosition, |
141 | opt_name: Option<ast::Name>, | 92 | opt_name: Option<ast::Name>, |
142 | ) -> Option<Definition> { | 93 | ) -> Option<Definition> { |
143 | let def = if let Some(name) = opt_name { | 94 | if let Some(name) = opt_name { |
144 | NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) | 95 | let class = NameClass::classify(sema, &name)?; |
96 | Some(class.referenced_or_defined(sema.db)) | ||
145 | } else if let Some(lifetime) = | 97 | } else if let Some(lifetime) = |
146 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) | 98 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) |
147 | { | 99 | { |
148 | if let Some(def) = | 100 | let def = if let Some(def) = |
149 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) | 101 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) |
150 | { | 102 | { |
151 | def | 103 | def |
152 | } else { | 104 | } else { |
153 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) | 105 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) |
154 | } | 106 | }; |
107 | Some(def) | ||
155 | } else if let Some(name_ref) = | 108 | } else if let Some(name_ref) = |
156 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) | 109 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) |
157 | { | 110 | { |
158 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db) | 111 | let class = NameRefClass::classify(sema, &name_ref)?; |
112 | Some(class.referenced(sema.db)) | ||
159 | } else { | 113 | } else { |
160 | return None; | 114 | None |
161 | }; | 115 | } |
162 | Some(def) | ||
163 | } | 116 | } |
164 | 117 | ||
165 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 118 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
@@ -235,6 +188,43 @@ fn get_enum_def_name_for_struct_literal_search( | |||
235 | None | 188 | None |
236 | } | 189 | } |
237 | 190 | ||
191 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
192 | name_ref | ||
193 | .syntax() | ||
194 | .ancestors() | ||
195 | .find_map(ast::CallExpr::cast) | ||
196 | .and_then(|c| match c.expr()? { | ||
197 | ast::Expr::PathExpr(p) => { | ||
198 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
199 | } | ||
200 | _ => None, | ||
201 | }) | ||
202 | .unwrap_or(false) | ||
203 | } | ||
204 | |||
205 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
206 | name_ref | ||
207 | .syntax() | ||
208 | .ancestors() | ||
209 | .find_map(ast::RecordExpr::cast) | ||
210 | .and_then(|l| l.path()) | ||
211 | .and_then(|p| p.segment()) | ||
212 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
213 | .unwrap_or(false) | ||
214 | } | ||
215 | |||
216 | fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
217 | name_ref | ||
218 | .syntax() | ||
219 | .ancestors() | ||
220 | .find_map(ast::PathExpr::cast) | ||
221 | .and_then(|p| p.path()) | ||
222 | .and_then(|p| p.qualifier()) | ||
223 | .and_then(|p| p.segment()) | ||
224 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
225 | .unwrap_or(false) | ||
226 | } | ||
227 | |||
238 | #[cfg(test)] | 228 | #[cfg(test)] |
239 | mod tests { | 229 | mod tests { |
240 | use expect_test::{expect, Expect}; | 230 | use expect_test::{expect, Expect}; |
@@ -259,9 +249,9 @@ fn main() { | |||
259 | } | 249 | } |
260 | "#, | 250 | "#, |
261 | expect![[r#" | 251 | expect![[r#" |
262 | Foo Struct FileId(0) 0..26 7..10 Other | 252 | Foo Struct FileId(0) 0..26 7..10 |
263 | 253 | ||
264 | FileId(0) 101..104 StructLiteral | 254 | FileId(0) 101..104 |
265 | "#]], | 255 | "#]], |
266 | ); | 256 | ); |
267 | } | 257 | } |
@@ -277,10 +267,10 @@ struct Foo$0 {} | |||
277 | } | 267 | } |
278 | "#, | 268 | "#, |
279 | expect![[r#" | 269 | expect![[r#" |
280 | Foo Struct FileId(0) 0..13 7..10 Other | 270 | Foo Struct FileId(0) 0..13 7..10 |
281 | 271 | ||
282 | FileId(0) 41..44 Other | 272 | FileId(0) 41..44 |
283 | FileId(0) 54..57 StructLiteral | 273 | FileId(0) 54..57 |
284 | "#]], | 274 | "#]], |
285 | ); | 275 | ); |
286 | } | 276 | } |
@@ -296,9 +286,9 @@ struct Foo<T> $0{} | |||
296 | } | 286 | } |
297 | "#, | 287 | "#, |
298 | expect![[r#" | 288 | expect![[r#" |
299 | Foo Struct FileId(0) 0..16 7..10 Other | 289 | Foo Struct FileId(0) 0..16 7..10 |
300 | 290 | ||
301 | FileId(0) 64..67 StructLiteral | 291 | FileId(0) 64..67 |
302 | "#]], | 292 | "#]], |
303 | ); | 293 | ); |
304 | } | 294 | } |
@@ -315,9 +305,9 @@ fn main() { | |||
315 | } | 305 | } |
316 | "#, | 306 | "#, |
317 | expect![[r#" | 307 | expect![[r#" |
318 | Foo Struct FileId(0) 0..16 7..10 Other | 308 | Foo Struct FileId(0) 0..16 7..10 |
319 | 309 | ||
320 | FileId(0) 54..57 StructLiteral | 310 | FileId(0) 54..57 |
321 | "#]], | 311 | "#]], |
322 | ); | 312 | ); |
323 | } | 313 | } |
@@ -336,9 +326,9 @@ fn main() { | |||
336 | } | 326 | } |
337 | "#, | 327 | "#, |
338 | expect![[r#" | 328 | expect![[r#" |
339 | Foo Enum FileId(0) 0..26 5..8 Other | 329 | Foo Enum FileId(0) 0..26 5..8 |
340 | 330 | ||
341 | FileId(0) 63..66 EnumLiteral | 331 | FileId(0) 63..66 |
342 | "#]], | 332 | "#]], |
343 | ); | 333 | ); |
344 | } | 334 | } |
@@ -357,10 +347,10 @@ fn main() { | |||
357 | } | 347 | } |
358 | "#, | 348 | "#, |
359 | expect![[r#" | 349 | expect![[r#" |
360 | Foo Enum FileId(0) 0..26 5..8 Other | 350 | Foo Enum FileId(0) 0..26 5..8 |
361 | 351 | ||
362 | FileId(0) 50..53 Other | 352 | FileId(0) 50..53 |
363 | FileId(0) 63..66 EnumLiteral | 353 | FileId(0) 63..66 |
364 | "#]], | 354 | "#]], |
365 | ); | 355 | ); |
366 | } | 356 | } |
@@ -379,9 +369,9 @@ fn main() { | |||
379 | } | 369 | } |
380 | "#, | 370 | "#, |
381 | expect![[r#" | 371 | expect![[r#" |
382 | Foo Enum FileId(0) 0..32 5..8 Other | 372 | Foo Enum FileId(0) 0..32 5..8 |
383 | 373 | ||
384 | FileId(0) 73..76 EnumLiteral | 374 | FileId(0) 73..76 |
385 | "#]], | 375 | "#]], |
386 | ); | 376 | ); |
387 | } | 377 | } |
@@ -400,9 +390,9 @@ fn main() { | |||
400 | } | 390 | } |
401 | "#, | 391 | "#, |
402 | expect![[r#" | 392 | expect![[r#" |
403 | Foo Enum FileId(0) 0..33 5..8 Other | 393 | Foo Enum FileId(0) 0..33 5..8 |
404 | 394 | ||
405 | FileId(0) 70..73 EnumLiteral | 395 | FileId(0) 70..73 |
406 | "#]], | 396 | "#]], |
407 | ); | 397 | ); |
408 | } | 398 | } |
@@ -423,12 +413,12 @@ fn main() { | |||
423 | i = 5; | 413 | i = 5; |
424 | }"#, | 414 | }"#, |
425 | expect![[r#" | 415 | expect![[r#" |
426 | i Local FileId(0) 20..25 24..25 Other Write | 416 | i Local FileId(0) 20..25 24..25 Write |
427 | 417 | ||
428 | FileId(0) 50..51 Other Write | 418 | FileId(0) 50..51 Write |
429 | FileId(0) 54..55 Other Read | 419 | FileId(0) 54..55 Read |
430 | FileId(0) 76..77 Other Write | 420 | FileId(0) 76..77 Write |
431 | FileId(0) 94..95 Other Write | 421 | FileId(0) 94..95 Write |
432 | "#]], | 422 | "#]], |
433 | ); | 423 | ); |
434 | } | 424 | } |
@@ -447,10 +437,10 @@ fn bar() { | |||
447 | } | 437 | } |
448 | "#, | 438 | "#, |
449 | expect![[r#" | 439 | expect![[r#" |
450 | spam Local FileId(0) 19..23 19..23 Other | 440 | spam Local FileId(0) 19..23 19..23 |
451 | 441 | ||
452 | FileId(0) 34..38 Other Read | 442 | FileId(0) 34..38 Read |
453 | FileId(0) 41..45 Other Read | 443 | FileId(0) 41..45 Read |
454 | "#]], | 444 | "#]], |
455 | ); | 445 | ); |
456 | } | 446 | } |
@@ -462,9 +452,9 @@ fn bar() { | |||
462 | fn foo(i : u32) -> u32 { i$0 } | 452 | fn foo(i : u32) -> u32 { i$0 } |
463 | "#, | 453 | "#, |
464 | expect![[r#" | 454 | expect![[r#" |
465 | i ValueParam FileId(0) 7..8 7..8 Other | 455 | i ValueParam FileId(0) 7..8 7..8 |
466 | 456 | ||
467 | FileId(0) 25..26 Other Read | 457 | FileId(0) 25..26 Read |
468 | "#]], | 458 | "#]], |
469 | ); | 459 | ); |
470 | } | 460 | } |
@@ -476,9 +466,9 @@ fn foo(i : u32) -> u32 { i$0 } | |||
476 | fn foo(i$0 : u32) -> u32 { i } | 466 | fn foo(i$0 : u32) -> u32 { i } |
477 | "#, | 467 | "#, |
478 | expect![[r#" | 468 | expect![[r#" |
479 | i ValueParam FileId(0) 7..8 7..8 Other | 469 | i ValueParam FileId(0) 7..8 7..8 |
480 | 470 | ||
481 | FileId(0) 25..26 Other Read | 471 | FileId(0) 25..26 Read |
482 | "#]], | 472 | "#]], |
483 | ); | 473 | ); |
484 | } | 474 | } |
@@ -497,9 +487,9 @@ fn main(s: Foo) { | |||
497 | } | 487 | } |
498 | "#, | 488 | "#, |
499 | expect![[r#" | 489 | expect![[r#" |
500 | spam Field FileId(0) 17..30 21..25 Other | 490 | spam Field FileId(0) 17..30 21..25 |
501 | 491 | ||
502 | FileId(0) 67..71 Other Read | 492 | FileId(0) 67..71 Read |
503 | "#]], | 493 | "#]], |
504 | ); | 494 | ); |
505 | } | 495 | } |
@@ -514,7 +504,7 @@ impl Foo { | |||
514 | } | 504 | } |
515 | "#, | 505 | "#, |
516 | expect![[r#" | 506 | expect![[r#" |
517 | f Function FileId(0) 27..43 30..31 Other | 507 | f Function FileId(0) 27..43 30..31 |
518 | 508 | ||
519 | "#]], | 509 | "#]], |
520 | ); | 510 | ); |
@@ -531,7 +521,7 @@ enum Foo { | |||
531 | } | 521 | } |
532 | "#, | 522 | "#, |
533 | expect![[r#" | 523 | expect![[r#" |
534 | B Variant FileId(0) 22..23 22..23 Other | 524 | B Variant FileId(0) 22..23 22..23 |
535 | 525 | ||
536 | "#]], | 526 | "#]], |
537 | ); | 527 | ); |
@@ -548,7 +538,7 @@ enum Foo { | |||
548 | } | 538 | } |
549 | "#, | 539 | "#, |
550 | expect![[r#" | 540 | expect![[r#" |
551 | field Field FileId(0) 26..35 26..31 Other | 541 | field Field FileId(0) 26..35 26..31 |
552 | 542 | ||
553 | "#]], | 543 | "#]], |
554 | ); | 544 | ); |
@@ -589,10 +579,10 @@ fn f() { | |||
589 | } | 579 | } |
590 | "#, | 580 | "#, |
591 | expect![[r#" | 581 | expect![[r#" |
592 | Foo Struct FileId(1) 17..51 28..31 Other | 582 | Foo Struct FileId(1) 17..51 28..31 |
593 | 583 | ||
594 | FileId(0) 53..56 StructLiteral | 584 | FileId(0) 53..56 |
595 | FileId(2) 79..82 StructLiteral | 585 | FileId(2) 79..82 |
596 | "#]], | 586 | "#]], |
597 | ); | 587 | ); |
598 | } | 588 | } |
@@ -619,9 +609,9 @@ pub struct Foo { | |||
619 | } | 609 | } |
620 | "#, | 610 | "#, |
621 | expect![[r#" | 611 | expect![[r#" |
622 | foo Module FileId(1) 0..35 Other | 612 | foo Module FileId(1) 0..35 |
623 | 613 | ||
624 | FileId(0) 14..17 Other | 614 | FileId(0) 14..17 |
625 | "#]], | 615 | "#]], |
626 | ); | 616 | ); |
627 | } | 617 | } |
@@ -647,10 +637,10 @@ pub(super) struct Foo$0 { | |||
647 | } | 637 | } |
648 | "#, | 638 | "#, |
649 | expect![[r#" | 639 | expect![[r#" |
650 | Foo Struct FileId(2) 0..41 18..21 Other | 640 | Foo Struct FileId(2) 0..41 18..21 |
651 | 641 | ||
652 | FileId(1) 20..23 Other | 642 | FileId(1) 20..23 |
653 | FileId(1) 47..50 StructLiteral | 643 | FileId(1) 47..50 |
654 | "#]], | 644 | "#]], |
655 | ); | 645 | ); |
656 | } | 646 | } |
@@ -675,10 +665,10 @@ pub(super) struct Foo$0 { | |||
675 | code, | 665 | code, |
676 | None, | 666 | None, |
677 | expect![[r#" | 667 | expect![[r#" |
678 | quux Function FileId(0) 19..35 26..30 Other | 668 | quux Function FileId(0) 19..35 26..30 |
679 | 669 | ||
680 | FileId(1) 16..20 StructLiteral | 670 | FileId(1) 16..20 |
681 | FileId(2) 16..20 StructLiteral | 671 | FileId(2) 16..20 |
682 | "#]], | 672 | "#]], |
683 | ); | 673 | ); |
684 | 674 | ||
@@ -686,9 +676,9 @@ pub(super) struct Foo$0 { | |||
686 | code, | 676 | code, |
687 | Some(SearchScope::single_file(FileId(2))), | 677 | Some(SearchScope::single_file(FileId(2))), |
688 | expect![[r#" | 678 | expect![[r#" |
689 | quux Function FileId(0) 19..35 26..30 Other | 679 | quux Function FileId(0) 19..35 26..30 |
690 | 680 | ||
691 | FileId(2) 16..20 StructLiteral | 681 | FileId(2) 16..20 |
692 | "#]], | 682 | "#]], |
693 | ); | 683 | ); |
694 | } | 684 | } |
@@ -706,10 +696,10 @@ fn foo() { | |||
706 | } | 696 | } |
707 | "#, | 697 | "#, |
708 | expect![[r#" | 698 | expect![[r#" |
709 | m1 Macro FileId(0) 0..46 29..31 Other | 699 | m1 Macro FileId(0) 0..46 29..31 |
710 | 700 | ||
711 | FileId(0) 63..65 StructLiteral | 701 | FileId(0) 63..65 |
712 | FileId(0) 73..75 StructLiteral | 702 | FileId(0) 73..75 |
713 | "#]], | 703 | "#]], |
714 | ); | 704 | ); |
715 | } | 705 | } |
@@ -724,10 +714,10 @@ fn foo() { | |||
724 | } | 714 | } |
725 | "#, | 715 | "#, |
726 | expect![[r#" | 716 | expect![[r#" |
727 | i Local FileId(0) 19..24 23..24 Other Write | 717 | i Local FileId(0) 19..24 23..24 Write |
728 | 718 | ||
729 | FileId(0) 34..35 Other Write | 719 | FileId(0) 34..35 Write |
730 | FileId(0) 38..39 Other Read | 720 | FileId(0) 38..39 Read |
731 | "#]], | 721 | "#]], |
732 | ); | 722 | ); |
733 | } | 723 | } |
@@ -746,10 +736,10 @@ fn foo() { | |||
746 | } | 736 | } |
747 | "#, | 737 | "#, |
748 | expect![[r#" | 738 | expect![[r#" |
749 | f Field FileId(0) 15..21 15..16 Other | 739 | f Field FileId(0) 15..21 15..16 |
750 | 740 | ||
751 | FileId(0) 55..56 RecordFieldExprOrPat Read | 741 | FileId(0) 55..56 Read |
752 | FileId(0) 68..69 Other Write | 742 | FileId(0) 68..69 Write |
753 | "#]], | 743 | "#]], |
754 | ); | 744 | ); |
755 | } | 745 | } |
@@ -764,9 +754,9 @@ fn foo() { | |||
764 | } | 754 | } |
765 | "#, | 755 | "#, |
766 | expect![[r#" | 756 | expect![[r#" |
767 | i Local FileId(0) 19..20 19..20 Other | 757 | i Local FileId(0) 19..20 19..20 |
768 | 758 | ||
769 | FileId(0) 26..27 Other Write | 759 | FileId(0) 26..27 Write |
770 | "#]], | 760 | "#]], |
771 | ); | 761 | ); |
772 | } | 762 | } |
@@ -788,9 +778,9 @@ fn main() { | |||
788 | } | 778 | } |
789 | "#, | 779 | "#, |
790 | expect![[r#" | 780 | expect![[r#" |
791 | new Function FileId(0) 54..81 61..64 Other | 781 | new Function FileId(0) 54..81 61..64 |
792 | 782 | ||
793 | FileId(0) 126..129 StructLiteral | 783 | FileId(0) 126..129 |
794 | "#]], | 784 | "#]], |
795 | ); | 785 | ); |
796 | } | 786 | } |
@@ -810,10 +800,10 @@ use crate::f; | |||
810 | fn g() { f(); } | 800 | fn g() { f(); } |
811 | "#, | 801 | "#, |
812 | expect![[r#" | 802 | expect![[r#" |
813 | f Function FileId(0) 22..31 25..26 Other | 803 | f Function FileId(0) 22..31 25..26 |
814 | 804 | ||
815 | FileId(1) 11..12 Other | 805 | FileId(1) 11..12 |
816 | FileId(1) 24..25 StructLiteral | 806 | FileId(1) 24..25 |
817 | "#]], | 807 | "#]], |
818 | ); | 808 | ); |
819 | } | 809 | } |
@@ -833,9 +823,9 @@ fn f(s: S) { | |||
833 | } | 823 | } |
834 | "#, | 824 | "#, |
835 | expect![[r#" | 825 | expect![[r#" |
836 | field Field FileId(0) 15..24 15..20 Other | 826 | field Field FileId(0) 15..24 15..20 |
837 | 827 | ||
838 | FileId(0) 68..73 FieldShorthandForField Read | 828 | FileId(0) 68..73 Read |
839 | "#]], | 829 | "#]], |
840 | ); | 830 | ); |
841 | } | 831 | } |
@@ -857,9 +847,9 @@ fn f(e: En) { | |||
857 | } | 847 | } |
858 | "#, | 848 | "#, |
859 | expect![[r#" | 849 | expect![[r#" |
860 | field Field FileId(0) 32..41 32..37 Other | 850 | field Field FileId(0) 32..41 32..37 |
861 | 851 | ||
862 | FileId(0) 102..107 FieldShorthandForField Read | 852 | FileId(0) 102..107 Read |
863 | "#]], | 853 | "#]], |
864 | ); | 854 | ); |
865 | } | 855 | } |
@@ -881,9 +871,9 @@ fn f() -> m::En { | |||
881 | } | 871 | } |
882 | "#, | 872 | "#, |
883 | expect![[r#" | 873 | expect![[r#" |
884 | field Field FileId(0) 56..65 56..61 Other | 874 | field Field FileId(0) 56..65 56..61 |
885 | 875 | ||
886 | FileId(0) 125..130 RecordFieldExprOrPat Read | 876 | FileId(0) 125..130 Read |
887 | "#]], | 877 | "#]], |
888 | ); | 878 | ); |
889 | } | 879 | } |
@@ -906,10 +896,10 @@ impl Foo { | |||
906 | } | 896 | } |
907 | "#, | 897 | "#, |
908 | expect![[r#" | 898 | expect![[r#" |
909 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 899 | self SelfParam FileId(0) 47..51 47..51 |
910 | 900 | ||
911 | FileId(0) 71..75 Other Read | 901 | FileId(0) 71..75 Read |
912 | FileId(0) 152..156 Other Read | 902 | FileId(0) 152..156 Read |
913 | "#]], | 903 | "#]], |
914 | ); | 904 | ); |
915 | } | 905 | } |
@@ -927,9 +917,9 @@ impl Foo { | |||
927 | } | 917 | } |
928 | "#, | 918 | "#, |
929 | expect![[r#" | 919 | expect![[r#" |
930 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 920 | self SelfParam FileId(0) 47..51 47..51 |
931 | 921 | ||
932 | FileId(0) 63..67 Other Read | 922 | FileId(0) 63..67 Read |
933 | "#]], | 923 | "#]], |
934 | ); | 924 | ); |
935 | } | 925 | } |
@@ -945,7 +935,7 @@ impl Foo { | |||
945 | let mut actual = String::new(); | 935 | let mut actual = String::new(); |
946 | { | 936 | { |
947 | let decl = refs.declaration; | 937 | let decl = refs.declaration; |
948 | format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); | 938 | format_to!(actual, "{}", decl.nav.debug_render()); |
949 | if let Some(access) = decl.access { | 939 | if let Some(access) = decl.access { |
950 | format_to!(actual, " {:?}", access) | 940 | format_to!(actual, " {:?}", access) |
951 | } | 941 | } |
@@ -953,9 +943,9 @@ impl Foo { | |||
953 | } | 943 | } |
954 | 944 | ||
955 | for (file_id, references) in refs.references { | 945 | for (file_id, references) in refs.references { |
956 | for r in references { | 946 | for (range, access) in references { |
957 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); | 947 | format_to!(actual, "{:?} {:?}", file_id, range); |
958 | if let Some(access) = r.access { | 948 | if let Some(access) = access { |
959 | format_to!(actual, " {:?}", access); | 949 | format_to!(actual, " {:?}", access); |
960 | } | 950 | } |
961 | actual += "\n"; | 951 | actual += "\n"; |
@@ -976,13 +966,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
976 | } | 966 | } |
977 | "#, | 967 | "#, |
978 | expect![[r#" | 968 | expect![[r#" |
979 | 'a LifetimeParam FileId(0) 55..57 55..57 Lifetime | 969 | 'a LifetimeParam FileId(0) 55..57 55..57 |
980 | 970 | ||
981 | FileId(0) 63..65 Lifetime | 971 | FileId(0) 63..65 |
982 | FileId(0) 71..73 Lifetime | 972 | FileId(0) 71..73 |
983 | FileId(0) 82..84 Lifetime | 973 | FileId(0) 82..84 |
984 | FileId(0) 95..97 Lifetime | 974 | FileId(0) 95..97 |
985 | FileId(0) 106..108 Lifetime | 975 | FileId(0) 106..108 |
986 | "#]], | 976 | "#]], |
987 | ); | 977 | ); |
988 | } | 978 | } |
@@ -994,10 +984,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
994 | type Foo<'a, T> where T: 'a$0 = &'a T; | 984 | type Foo<'a, T> where T: 'a$0 = &'a T; |
995 | "#, | 985 | "#, |
996 | expect![[r#" | 986 | expect![[r#" |
997 | 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime | 987 | 'a LifetimeParam FileId(0) 9..11 9..11 |
998 | 988 | ||
999 | FileId(0) 25..27 Lifetime | 989 | FileId(0) 25..27 |
1000 | FileId(0) 31..33 Lifetime | 990 | FileId(0) 31..33 |
1001 | "#]], | 991 | "#]], |
1002 | ); | 992 | ); |
1003 | } | 993 | } |
@@ -1016,11 +1006,11 @@ impl<'a> Foo<'a> for &'a () { | |||
1016 | } | 1006 | } |
1017 | "#, | 1007 | "#, |
1018 | expect![[r#" | 1008 | expect![[r#" |
1019 | 'a LifetimeParam FileId(0) 47..49 47..49 Lifetime | 1009 | 'a LifetimeParam FileId(0) 47..49 47..49 |
1020 | 1010 | ||
1021 | FileId(0) 55..57 Lifetime | 1011 | FileId(0) 55..57 |
1022 | FileId(0) 64..66 Lifetime | 1012 | FileId(0) 64..66 |
1023 | FileId(0) 89..91 Lifetime | 1013 | FileId(0) 89..91 |
1024 | "#]], | 1014 | "#]], |
1025 | ); | 1015 | ); |
1026 | } | 1016 | } |
@@ -1036,9 +1026,9 @@ fn main() { | |||
1036 | } | 1026 | } |
1037 | "#, | 1027 | "#, |
1038 | expect![[r#" | 1028 | expect![[r#" |
1039 | a Local FileId(0) 59..60 59..60 Other | 1029 | a Local FileId(0) 59..60 59..60 |
1040 | 1030 | ||
1041 | FileId(0) 80..81 Other Read | 1031 | FileId(0) 80..81 Read |
1042 | "#]], | 1032 | "#]], |
1043 | ); | 1033 | ); |
1044 | } | 1034 | } |
@@ -1054,9 +1044,9 @@ fn main() { | |||
1054 | } | 1044 | } |
1055 | "#, | 1045 | "#, |
1056 | expect![[r#" | 1046 | expect![[r#" |
1057 | a Local FileId(0) 59..60 59..60 Other | 1047 | a Local FileId(0) 59..60 59..60 |
1058 | 1048 | ||
1059 | FileId(0) 80..81 Other Read | 1049 | FileId(0) 80..81 Read |
1060 | "#]], | 1050 | "#]], |
1061 | ); | 1051 | ); |
1062 | } | 1052 | } |
@@ -1075,10 +1065,10 @@ fn foo<'a>() -> &'a () { | |||
1075 | } | 1065 | } |
1076 | "#, | 1066 | "#, |
1077 | expect![[r#" | 1067 | expect![[r#" |
1078 | 'a Label FileId(0) 29..32 29..31 Lifetime | 1068 | 'a Label FileId(0) 29..32 29..31 |
1079 | 1069 | ||
1080 | FileId(0) 80..82 Lifetime | 1070 | FileId(0) 80..82 |
1081 | FileId(0) 108..110 Lifetime | 1071 | FileId(0) 108..110 |
1082 | "#]], | 1072 | "#]], |
1083 | ); | 1073 | ); |
1084 | } | 1074 | } |
@@ -1092,9 +1082,9 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1092 | } | 1082 | } |
1093 | "#, | 1083 | "#, |
1094 | expect![[r#" | 1084 | expect![[r#" |
1095 | FOO ConstParam FileId(0) 7..23 13..16 Other | 1085 | FOO ConstParam FileId(0) 7..23 13..16 |
1096 | 1086 | ||
1097 | FileId(0) 42..45 Other | 1087 | FileId(0) 42..45 |
1098 | "#]], | 1088 | "#]], |
1099 | ); | 1089 | ); |
1100 | } | 1090 | } |
@@ -1108,9 +1098,9 @@ trait Foo { | |||
1108 | } | 1098 | } |
1109 | "#, | 1099 | "#, |
1110 | expect![[r#" | 1100 | expect![[r#" |
1111 | Self TypeParam FileId(0) 6..9 6..9 Other | 1101 | Self TypeParam FileId(0) 6..9 6..9 |
1112 | 1102 | ||
1113 | FileId(0) 26..30 Other | 1103 | FileId(0) 26..30 |
1114 | "#]], | 1104 | "#]], |
1115 | ); | 1105 | ); |
1116 | } | 1106 | } |
@@ -1131,9 +1121,9 @@ impl Foo { | |||
1131 | 1121 | ||
1132 | "#, | 1122 | "#, |
1133 | expect![[r#" | 1123 | expect![[r#" |
1134 | Bar Variant FileId(0) 11..16 11..14 Other | 1124 | Bar Variant FileId(0) 11..16 11..14 |
1135 | 1125 | ||
1136 | FileId(0) 89..92 Other | 1126 | FileId(0) 89..92 |
1137 | "#]], | 1127 | "#]], |
1138 | ); | 1128 | ); |
1139 | } | 1129 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index ebb1ce7dd..b04214291 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -4,9 +4,9 @@ use std::fmt::{self, Display}; | |||
4 | use either::Either; | 4 | use either::Either; |
5 | use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; | 5 | use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; |
6 | use ide_db::{ | 6 | use ide_db::{ |
7 | base_db::{AnchoredPathBuf, FileId, FileRange}, | 7 | base_db::{AnchoredPathBuf, FileId}, |
8 | defs::{Definition, NameClass, NameRefClass}, | 8 | defs::{Definition, NameClass, NameRefClass}, |
9 | search::FileReference, | 9 | search::{FileReference, NameLike}, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use stdx::never; | 12 | use stdx::never; |
@@ -17,10 +17,7 @@ use syntax::{ | |||
17 | use test_utils::mark; | 17 | use test_utils::mark; |
18 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; |
21 | display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange, | ||
22 | TextRange, | ||
23 | }; | ||
24 | 21 | ||
25 | type RenameResult<T> = Result<T, RenameError>; | 22 | type RenameResult<T> = Result<T, RenameError>; |
26 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -41,6 +38,8 @@ macro_rules! bail { | |||
41 | ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} | 38 | ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} |
42 | } | 39 | } |
43 | 40 | ||
41 | /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is | ||
42 | /// being targeted for a rename. | ||
44 | pub(crate) fn prepare_rename( | 43 | pub(crate) fn prepare_rename( |
45 | db: &RootDatabase, | 44 | db: &RootDatabase, |
46 | position: FilePosition, | 45 | position: FilePosition, |
@@ -123,12 +122,6 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> { | |||
123 | } | 122 | } |
124 | } | 123 | } |
125 | 124 | ||
126 | enum NameLike { | ||
127 | Name(ast::Name), | ||
128 | NameRef(ast::NameRef), | ||
129 | Lifetime(ast::Lifetime), | ||
130 | } | ||
131 | |||
132 | fn find_name_like( | 125 | fn find_name_like( |
133 | sema: &Semantics<RootDatabase>, | 126 | sema: &Semantics<RootDatabase>, |
134 | syntax: &SyntaxNode, | 127 | syntax: &SyntaxNode, |
@@ -171,72 +164,97 @@ fn find_definition( | |||
171 | } | 164 | } |
172 | 165 | ||
173 | fn source_edit_from_references( | 166 | fn source_edit_from_references( |
174 | sema: &Semantics<RootDatabase>, | 167 | _sema: &Semantics<RootDatabase>, |
175 | file_id: FileId, | 168 | file_id: FileId, |
176 | references: &[FileReference], | 169 | references: &[FileReference], |
170 | def: Definition, | ||
177 | new_name: &str, | 171 | new_name: &str, |
178 | ) -> (FileId, TextEdit) { | 172 | ) -> (FileId, TextEdit) { |
179 | let mut edit = TextEdit::builder(); | 173 | let mut edit = TextEdit::builder(); |
180 | for reference in references { | 174 | for reference in references { |
181 | let mut replacement_text = String::new(); | 175 | let (range, replacement) = match &reference.name { |
182 | let range = match reference.kind { | 176 | NameLike::Name(_) => (None, format!("{}", new_name)), |
183 | ReferenceKind::FieldShorthandForField => { | 177 | NameLike::NameRef(name_ref) => source_edit_from_name_ref(name_ref, new_name, def), |
184 | mark::hit!(test_rename_struct_field_for_shorthand); | 178 | NameLike::Lifetime(_) => (None, format!("{}", new_name)), |
185 | replacement_text.push_str(new_name); | ||
186 | replacement_text.push_str(": "); | ||
187 | TextRange::new(reference.range.start(), reference.range.start()) | ||
188 | } | ||
189 | ReferenceKind::FieldShorthandForLocal => { | ||
190 | mark::hit!(test_rename_local_for_field_shorthand); | ||
191 | replacement_text.push_str(": "); | ||
192 | replacement_text.push_str(new_name); | ||
193 | TextRange::new(reference.range.end(), reference.range.end()) | ||
194 | } | ||
195 | ReferenceKind::RecordFieldExprOrPat => { | ||
196 | mark::hit!(test_rename_field_expr_pat); | ||
197 | replacement_text.push_str(new_name); | ||
198 | edit_text_range_for_record_field_expr_or_pat( | ||
199 | sema, | ||
200 | FileRange { file_id, range: reference.range }, | ||
201 | new_name, | ||
202 | ) | ||
203 | } | ||
204 | _ => { | ||
205 | replacement_text.push_str(new_name); | ||
206 | reference.range | ||
207 | } | ||
208 | }; | 179 | }; |
209 | edit.replace(range, replacement_text); | 180 | // FIXME: Some(range) will be incorrect when we are inside macros |
181 | edit.replace(range.unwrap_or(reference.range), replacement); | ||
210 | } | 182 | } |
211 | (file_id, edit.finish()) | 183 | (file_id, edit.finish()) |
212 | } | 184 | } |
213 | 185 | ||
214 | fn edit_text_range_for_record_field_expr_or_pat( | 186 | fn source_edit_from_name_ref( |
215 | sema: &Semantics<RootDatabase>, | 187 | name_ref: &ast::NameRef, |
216 | file_range: FileRange, | ||
217 | new_name: &str, | 188 | new_name: &str, |
218 | ) -> TextRange { | 189 | def: Definition, |
219 | let source_file = sema.parse(file_range.file_id); | 190 | ) -> (Option<TextRange>, String) { |
220 | let file_syntax = source_file.syntax(); | 191 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { |
221 | let original_range = file_range.range; | 192 | let rcf_name_ref = record_field.name_ref(); |
222 | 193 | let rcf_expr = record_field.expr(); | |
223 | syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range) | 194 | match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { |
224 | .and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) { | 195 | // field: init-expr, check if we can use a field init shorthand |
225 | Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()), | 196 | (Some(field_name), Some(init)) => { |
226 | _ => None, | 197 | if field_name == *name_ref { |
227 | }) | 198 | if init.text() == new_name { |
228 | .or_else(|| { | 199 | mark::hit!(test_rename_field_put_init_shorthand); |
229 | syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range) | 200 | // same names, we can use a shorthand here instead |
230 | .and_then(|field_pat| match field_pat.pat() { | 201 | // we do not want to erase attributes hence this range start |
231 | Some(ast::Pat::IdentPat(pat)) | 202 | let s = field_name.syntax().text_range().start(); |
232 | if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) => | 203 | let e = record_field.syntax().text_range().end(); |
233 | { | 204 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); |
234 | Some(field_pat.syntax().text_range()) | ||
235 | } | 205 | } |
236 | _ => None, | 206 | } else if init == *name_ref { |
237 | }) | 207 | if field_name.text() == new_name { |
238 | }) | 208 | mark::hit!(test_rename_local_put_init_shorthand); |
239 | .unwrap_or(original_range) | 209 | // same names, we can use a shorthand here instead |
210 | // we do not want to erase attributes hence this range start | ||
211 | let s = field_name.syntax().text_range().start(); | ||
212 | let e = record_field.syntax().text_range().end(); | ||
213 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | // init shorthand | ||
218 | (None, Some(_)) => { | ||
219 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | ||
220 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | ||
221 | match def { | ||
222 | Definition::Field(_) => { | ||
223 | mark::hit!(test_rename_field_in_field_shorthand); | ||
224 | let s = name_ref.syntax().text_range().start(); | ||
225 | return (Some(TextRange::empty(s)), format!("{}: ", new_name)); | ||
226 | } | ||
227 | Definition::Local(_) => { | ||
228 | mark::hit!(test_rename_local_in_field_shorthand); | ||
229 | let s = name_ref.syntax().text_range().end(); | ||
230 | return (Some(TextRange::empty(s)), format!(": {}", new_name)); | ||
231 | } | ||
232 | _ => {} | ||
233 | } | ||
234 | } | ||
235 | _ => {} | ||
236 | } | ||
237 | } | ||
238 | if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
239 | let rcf_name_ref = record_field.name_ref(); | ||
240 | let rcf_pat = record_field.pat(); | ||
241 | match (rcf_name_ref, rcf_pat) { | ||
242 | // field: rename | ||
243 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
244 | // field name is being renamed | ||
245 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
246 | mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
247 | // same names, we can use a shorthand here instead | ||
248 | // we do not want to erase attributes hence this range start | ||
249 | let s = field_name.syntax().text_range().start(); | ||
250 | let e = record_field.syntax().text_range().end(); | ||
251 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); | ||
252 | } | ||
253 | } | ||
254 | _ => {} | ||
255 | } | ||
256 | } | ||
257 | (None, format!("{}", new_name)) | ||
240 | } | 258 | } |
241 | 259 | ||
242 | fn rename_mod( | 260 | fn rename_mod( |
@@ -277,7 +295,7 @@ fn rename_mod( | |||
277 | let def = Definition::ModuleDef(ModuleDef::Module(module)); | 295 | let def = Definition::ModuleDef(ModuleDef::Module(module)); |
278 | let usages = def.usages(sema).all(); | 296 | let usages = def.usages(sema).all(); |
279 | let ref_edits = usages.iter().map(|(&file_id, references)| { | 297 | let ref_edits = usages.iter().map(|(&file_id, references)| { |
280 | source_edit_from_references(sema, file_id, references, new_name) | 298 | source_edit_from_references(sema, file_id, references, def, new_name) |
281 | }); | 299 | }); |
282 | source_change.extend(ref_edits); | 300 | source_change.extend(ref_edits); |
283 | 301 | ||
@@ -346,7 +364,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
346 | let usages = def.usages(sema).all(); | 364 | let usages = def.usages(sema).all(); |
347 | let mut source_change = SourceChange::default(); | 365 | let mut source_change = SourceChange::default(); |
348 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 366 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
349 | source_edit_from_references(sema, file_id, references, "self") | 367 | source_edit_from_references(sema, file_id, references, def, "self") |
350 | })); | 368 | })); |
351 | source_change.insert_source_edit( | 369 | source_change.insert_source_edit( |
352 | file_id.original_file(sema.db), | 370 | file_id.original_file(sema.db), |
@@ -403,7 +421,7 @@ fn rename_self_to_param( | |||
403 | let mut source_change = SourceChange::default(); | 421 | let mut source_change = SourceChange::default(); |
404 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | 422 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); |
405 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 423 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
406 | source_edit_from_references(sema, file_id, &references, new_name) | 424 | source_edit_from_references(sema, file_id, &references, def, new_name) |
407 | })); | 425 | })); |
408 | Ok(source_change) | 426 | Ok(source_change) |
409 | } | 427 | } |
@@ -457,7 +475,7 @@ fn rename_reference( | |||
457 | } | 475 | } |
458 | let mut source_change = SourceChange::default(); | 476 | let mut source_change = SourceChange::default(); |
459 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 477 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
460 | source_edit_from_references(sema, file_id, &references, new_name) | 478 | source_edit_from_references(sema, file_id, &references, def, new_name) |
461 | })); | 479 | })); |
462 | 480 | ||
463 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | 481 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; |
@@ -545,10 +563,8 @@ mod tests { | |||
545 | 563 | ||
546 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { | 564 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { |
547 | let (analysis, position) = fixture::position(ra_fixture); | 565 | let (analysis, position) = fixture::position(ra_fixture); |
548 | let source_change = analysis | 566 | let source_change = |
549 | .rename(position, new_name) | 567 | analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError"); |
550 | .unwrap() | ||
551 | .expect("Expect returned RangeInfo to be Some, but was None"); | ||
552 | expect.assert_debug_eq(&source_change) | 568 | expect.assert_debug_eq(&source_change) |
553 | } | 569 | } |
554 | 570 | ||
@@ -792,8 +808,8 @@ impl Foo { | |||
792 | } | 808 | } |
793 | 809 | ||
794 | #[test] | 810 | #[test] |
795 | fn test_rename_struct_field_for_shorthand() { | 811 | fn test_rename_field_in_field_shorthand() { |
796 | mark::check!(test_rename_struct_field_for_shorthand); | 812 | mark::check!(test_rename_field_in_field_shorthand); |
797 | check( | 813 | check( |
798 | "j", | 814 | "j", |
799 | r#" | 815 | r#" |
@@ -818,8 +834,8 @@ impl Foo { | |||
818 | } | 834 | } |
819 | 835 | ||
820 | #[test] | 836 | #[test] |
821 | fn test_rename_local_for_field_shorthand() { | 837 | fn test_rename_local_in_field_shorthand() { |
822 | mark::check!(test_rename_local_for_field_shorthand); | 838 | mark::check!(test_rename_local_in_field_shorthand); |
823 | check( | 839 | check( |
824 | "j", | 840 | "j", |
825 | r#" | 841 | r#" |
@@ -1417,8 +1433,8 @@ impl Foo { | |||
1417 | } | 1433 | } |
1418 | 1434 | ||
1419 | #[test] | 1435 | #[test] |
1420 | fn test_initializer_use_field_init_shorthand() { | 1436 | fn test_rename_field_put_init_shorthand() { |
1421 | mark::check!(test_rename_field_expr_pat); | 1437 | mark::check!(test_rename_field_put_init_shorthand); |
1422 | check( | 1438 | check( |
1423 | "bar", | 1439 | "bar", |
1424 | r#" | 1440 | r#" |
@@ -1439,7 +1455,30 @@ fn foo(bar: i32) -> Foo { | |||
1439 | } | 1455 | } |
1440 | 1456 | ||
1441 | #[test] | 1457 | #[test] |
1458 | fn test_rename_local_put_init_shorthand() { | ||
1459 | mark::check!(test_rename_local_put_init_shorthand); | ||
1460 | check( | ||
1461 | "i", | ||
1462 | r#" | ||
1463 | struct Foo { i: i32 } | ||
1464 | |||
1465 | fn foo(bar$0: i32) -> Foo { | ||
1466 | Foo { i: bar } | ||
1467 | } | ||
1468 | "#, | ||
1469 | r#" | ||
1470 | struct Foo { i: i32 } | ||
1471 | |||
1472 | fn foo(i: i32) -> Foo { | ||
1473 | Foo { i } | ||
1474 | } | ||
1475 | "#, | ||
1476 | ); | ||
1477 | } | ||
1478 | |||
1479 | #[test] | ||
1442 | fn test_struct_field_destructure_into_shorthand() { | 1480 | fn test_struct_field_destructure_into_shorthand() { |
1481 | mark::check!(test_rename_field_put_init_shorthand_pat); | ||
1443 | check( | 1482 | check( |
1444 | "baz", | 1483 | "baz", |
1445 | r#" | 1484 | r#" |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index b9ba0aed5..38b20f2dc 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -53,24 +53,36 @@ impl IntoIterator for UsageSearchResult { | |||
53 | } | 53 | } |
54 | 54 | ||
55 | #[derive(Debug, Clone)] | 55 | #[derive(Debug, Clone)] |
56 | pub enum NameLike { | ||
57 | NameRef(ast::NameRef), | ||
58 | Name(ast::Name), | ||
59 | Lifetime(ast::Lifetime), | ||
60 | } | ||
61 | |||
62 | impl NameLike { | ||
63 | pub fn as_name_ref(&self) -> Option<&ast::NameRef> { | ||
64 | match self { | ||
65 | NameLike::NameRef(name_ref) => Some(name_ref), | ||
66 | _ => None, | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | mod __ { | ||
72 | use super::{ | ||
73 | ast::{Lifetime, Name, NameRef}, | ||
74 | NameLike, | ||
75 | }; | ||
76 | stdx::impl_from!(NameRef, Name, Lifetime for NameLike); | ||
77 | } | ||
78 | |||
79 | #[derive(Debug, Clone)] | ||
56 | pub struct FileReference { | 80 | pub struct FileReference { |
57 | pub range: TextRange, | 81 | pub range: TextRange, |
58 | pub kind: ReferenceKind, | 82 | pub name: NameLike, |
59 | pub access: Option<ReferenceAccess>, | 83 | pub access: Option<ReferenceAccess>, |
60 | } | 84 | } |
61 | 85 | ||
62 | #[derive(Debug, Clone, PartialEq)] | ||
63 | pub enum ReferenceKind { | ||
64 | FieldShorthandForField, | ||
65 | FieldShorthandForLocal, | ||
66 | StructLiteral, | ||
67 | RecordFieldExprOrPat, | ||
68 | SelfParam, | ||
69 | EnumLiteral, | ||
70 | Lifetime, | ||
71 | Other, | ||
72 | } | ||
73 | |||
74 | #[derive(Debug, Copy, Clone, PartialEq)] | 86 | #[derive(Debug, Copy, Clone, PartialEq)] |
75 | pub enum ReferenceAccess { | 87 | pub enum ReferenceAccess { |
76 | Read, | 88 | Read, |
@@ -369,8 +381,11 @@ impl<'a> FindUsages<'a> { | |||
369 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 381 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
370 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 382 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
371 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); | 383 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
372 | let reference = | 384 | let reference = FileReference { |
373 | FileReference { range, kind: ReferenceKind::Lifetime, access: None }; | 385 | range, |
386 | name: NameLike::Lifetime(lifetime.clone()), | ||
387 | access: None, | ||
388 | }; | ||
374 | sink(file_id, reference) | 389 | sink(file_id, reference) |
375 | } | 390 | } |
376 | _ => false, // not a usage | 391 | _ => false, // not a usage |
@@ -384,19 +399,12 @@ impl<'a> FindUsages<'a> { | |||
384 | ) -> bool { | 399 | ) -> bool { |
385 | match NameRefClass::classify(self.sema, &name_ref) { | 400 | match NameRefClass::classify(self.sema, &name_ref) { |
386 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 401 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
387 | let kind = if is_record_field_expr_or_pat(&name_ref) { | ||
388 | ReferenceKind::RecordFieldExprOrPat | ||
389 | } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { | ||
390 | ReferenceKind::StructLiteral | ||
391 | } else if is_enum_lit_name_ref(&name_ref) { | ||
392 | ReferenceKind::EnumLiteral | ||
393 | } else { | ||
394 | ReferenceKind::Other | ||
395 | }; | ||
396 | |||
397 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 402 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
398 | let reference = | 403 | let reference = FileReference { |
399 | FileReference { range, kind, access: reference_access(&def, &name_ref) }; | 404 | range, |
405 | name: NameLike::NameRef(name_ref.clone()), | ||
406 | access: reference_access(&def, &name_ref), | ||
407 | }; | ||
400 | sink(file_id, reference) | 408 | sink(file_id, reference) |
401 | } | 409 | } |
402 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 410 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
@@ -404,12 +412,12 @@ impl<'a> FindUsages<'a> { | |||
404 | let reference = match self.def { | 412 | let reference = match self.def { |
405 | Definition::Field(_) if &field == self.def => FileReference { | 413 | Definition::Field(_) if &field == self.def => FileReference { |
406 | range, | 414 | range, |
407 | kind: ReferenceKind::FieldShorthandForField, | 415 | name: NameLike::NameRef(name_ref.clone()), |
408 | access: reference_access(&field, &name_ref), | 416 | access: reference_access(&field, &name_ref), |
409 | }, | 417 | }, |
410 | Definition::Local(l) if &local == l => FileReference { | 418 | Definition::Local(l) if &local == l => FileReference { |
411 | range, | 419 | range, |
412 | kind: ReferenceKind::FieldShorthandForLocal, | 420 | name: NameLike::NameRef(name_ref.clone()), |
413 | access: reference_access(&Definition::Local(local), &name_ref), | 421 | access: reference_access(&Definition::Local(local), &name_ref), |
414 | }, | 422 | }, |
415 | _ => return false, // not a usage | 423 | _ => return false, // not a usage |
@@ -433,7 +441,7 @@ impl<'a> FindUsages<'a> { | |||
433 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 441 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
434 | let reference = FileReference { | 442 | let reference = FileReference { |
435 | range, | 443 | range, |
436 | kind: ReferenceKind::FieldShorthandForField, | 444 | name: NameLike::Name(name.clone()), |
437 | // FIXME: mutable patterns should have `Write` access | 445 | // FIXME: mutable patterns should have `Write` access |
438 | access: Some(ReferenceAccess::Read), | 446 | access: Some(ReferenceAccess::Read), |
439 | }; | 447 | }; |
@@ -473,54 +481,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen | |||
473 | // Default Locals and Fields to read | 481 | // Default Locals and Fields to read |
474 | mode.or(Some(ReferenceAccess::Read)) | 482 | mode.or(Some(ReferenceAccess::Read)) |
475 | } | 483 | } |
476 | |||
477 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
478 | name_ref | ||
479 | .syntax() | ||
480 | .ancestors() | ||
481 | .find_map(ast::CallExpr::cast) | ||
482 | .and_then(|c| match c.expr()? { | ||
483 | ast::Expr::PathExpr(p) => { | ||
484 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
485 | } | ||
486 | _ => None, | ||
487 | }) | ||
488 | .unwrap_or(false) | ||
489 | } | ||
490 | |||
491 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
492 | name_ref | ||
493 | .syntax() | ||
494 | .ancestors() | ||
495 | .find_map(ast::RecordExpr::cast) | ||
496 | .and_then(|l| l.path()) | ||
497 | .and_then(|p| p.segment()) | ||
498 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
499 | .unwrap_or(false) | ||
500 | } | ||
501 | |||
502 | fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool { | ||
503 | if let Some(parent) = name_ref.syntax().parent() { | ||
504 | match_ast! { | ||
505 | match parent { | ||
506 | ast::RecordExprField(it) => true, | ||
507 | ast::RecordPatField(_it) => true, | ||
508 | _ => false, | ||
509 | } | ||
510 | } | ||
511 | } else { | ||
512 | false | ||
513 | } | ||
514 | } | ||
515 | |||
516 | fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
517 | name_ref | ||
518 | .syntax() | ||
519 | .ancestors() | ||
520 | .find_map(ast::PathExpr::cast) | ||
521 | .and_then(|p| p.path()) | ||
522 | .and_then(|p| p.qualifier()) | ||
523 | .and_then(|p| p.segment()) | ||
524 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
525 | .unwrap_or(false) | ||
526 | } | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 5a6501216..8898c12e3 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -827,18 +827,23 @@ pub(crate) fn handle_references( | |||
827 | Some(refs) => refs, | 827 | Some(refs) => refs, |
828 | }; | 828 | }; |
829 | 829 | ||
830 | let locations = if params.context.include_declaration { | 830 | let decl = if params.context.include_declaration { |
831 | refs.references_with_declaration() | 831 | Some(FileRange { |
832 | .file_ranges() | 832 | file_id: refs.declaration.nav.file_id, |
833 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | 833 | range: refs.declaration.nav.focus_or_full_range(), |
834 | .collect() | 834 | }) |
835 | } else { | 835 | } else { |
836 | // Only iterate over the references if include_declaration was false | 836 | None |
837 | refs.references() | ||
838 | .file_ranges() | ||
839 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | ||
840 | .collect() | ||
841 | }; | 837 | }; |
838 | let locations = refs | ||
839 | .references | ||
840 | .into_iter() | ||
841 | .flat_map(|(file_id, refs)| { | ||
842 | refs.into_iter().map(move |(range, _)| FileRange { file_id, range }) | ||
843 | }) | ||
844 | .chain(decl) | ||
845 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | ||
846 | .collect(); | ||
842 | 847 | ||
843 | Ok(Some(locations)) | 848 | Ok(Some(locations)) |
844 | } | 849 | } |
@@ -1214,8 +1219,11 @@ pub(crate) fn handle_code_lens_resolve( | |||
1214 | .find_all_refs(position, None) | 1219 | .find_all_refs(position, None) |
1215 | .unwrap_or(None) | 1220 | .unwrap_or(None) |
1216 | .map(|r| { | 1221 | .map(|r| { |
1217 | r.references() | 1222 | r.references |
1218 | .file_ranges() | 1223 | .into_iter() |
1224 | .flat_map(|(file_id, ranges)| { | ||
1225 | ranges.into_iter().map(move |(range, _)| FileRange { file_id, range }) | ||
1226 | }) | ||
1219 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | 1227 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) |
1220 | .collect_vec() | 1228 | .collect_vec() |
1221 | }) | 1229 | }) |
@@ -1259,17 +1267,26 @@ pub(crate) fn handle_document_highlight( | |||
1259 | Some(refs) => refs, | 1267 | Some(refs) => refs, |
1260 | }; | 1268 | }; |
1261 | 1269 | ||
1270 | let decl = if refs.declaration.nav.file_id == position.file_id { | ||
1271 | Some(DocumentHighlight { | ||
1272 | range: to_proto::range(&line_index, refs.declaration.nav.focus_or_full_range()), | ||
1273 | kind: refs.declaration.access.map(to_proto::document_highlight_kind), | ||
1274 | }) | ||
1275 | } else { | ||
1276 | None | ||
1277 | }; | ||
1278 | |||
1262 | let res = refs | 1279 | let res = refs |
1263 | .references_with_declaration() | ||
1264 | .references | 1280 | .references |
1265 | .get(&position.file_id) | 1281 | .get(&position.file_id) |
1266 | .map(|file_refs| { | 1282 | .map(|file_refs| { |
1267 | file_refs | 1283 | file_refs |
1268 | .into_iter() | 1284 | .into_iter() |
1269 | .map(|r| DocumentHighlight { | 1285 | .map(|&(range, access)| DocumentHighlight { |
1270 | range: to_proto::range(&line_index, r.range), | 1286 | range: to_proto::range(&line_index, range), |
1271 | kind: r.access.map(to_proto::document_highlight_kind), | 1287 | kind: access.map(to_proto::document_highlight_kind), |
1272 | }) | 1288 | }) |
1289 | .chain(decl) | ||
1273 | .collect() | 1290 | .collect() |
1274 | }) | 1291 | }) |
1275 | .unwrap_or_default(); | 1292 | .unwrap_or_default(); |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 5c8cf900f..b105cb0e0 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -274,10 +274,7 @@ impl ast::Struct { | |||
274 | 274 | ||
275 | impl ast::RecordExprField { | 275 | impl ast::RecordExprField { |
276 | pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> { | 276 | pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> { |
277 | let candidate = | 277 | let candidate = Self::for_name_ref(field_name)?; |
278 | field_name.syntax().parent().and_then(ast::RecordExprField::cast).or_else(|| { | ||
279 | field_name.syntax().ancestors().nth(4).and_then(ast::RecordExprField::cast) | ||
280 | })?; | ||
281 | if candidate.field_name().as_ref() == Some(field_name) { | 278 | if candidate.field_name().as_ref() == Some(field_name) { |
282 | Some(candidate) | 279 | Some(candidate) |
283 | } else { | 280 | } else { |
@@ -285,6 +282,13 @@ impl ast::RecordExprField { | |||
285 | } | 282 | } |
286 | } | 283 | } |
287 | 284 | ||
285 | pub fn for_name_ref(name_ref: &ast::NameRef) -> Option<ast::RecordExprField> { | ||
286 | let syn = name_ref.syntax(); | ||
287 | syn.parent() | ||
288 | .and_then(ast::RecordExprField::cast) | ||
289 | .or_else(|| syn.ancestors().nth(4).and_then(ast::RecordExprField::cast)) | ||
290 | } | ||
291 | |||
288 | /// Deals with field init shorthand | 292 | /// Deals with field init shorthand |
289 | pub fn field_name(&self) -> Option<ast::NameRef> { | 293 | pub fn field_name(&self) -> Option<ast::NameRef> { |
290 | if let Some(name_ref) = self.name_ref() { | 294 | if let Some(name_ref) = self.name_ref() { |
@@ -294,6 +298,7 @@ impl ast::RecordExprField { | |||
294 | } | 298 | } |
295 | } | 299 | } |
296 | 300 | ||
301 | #[derive(Debug, Clone, PartialEq)] | ||
297 | pub enum NameOrNameRef { | 302 | pub enum NameOrNameRef { |
298 | Name(ast::Name), | 303 | Name(ast::Name), |
299 | NameRef(ast::NameRef), | 304 | NameRef(ast::NameRef), |
@@ -309,6 +314,23 @@ impl fmt::Display for NameOrNameRef { | |||
309 | } | 314 | } |
310 | 315 | ||
311 | impl ast::RecordPatField { | 316 | impl ast::RecordPatField { |
317 | pub fn for_field_name_ref(field_name: &ast::NameRef) -> Option<ast::RecordPatField> { | ||
318 | let candidate = field_name.syntax().parent().and_then(ast::RecordPatField::cast)?; | ||
319 | match candidate.field_name()? { | ||
320 | NameOrNameRef::NameRef(name_ref) if name_ref == *field_name => Some(candidate), | ||
321 | _ => None, | ||
322 | } | ||
323 | } | ||
324 | |||
325 | pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> { | ||
326 | let candidate = | ||
327 | field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?; | ||
328 | match candidate.field_name()? { | ||
329 | NameOrNameRef::Name(name) if name == *field_name => Some(candidate), | ||
330 | _ => None, | ||
331 | } | ||
332 | } | ||
333 | |||
312 | /// Deals with field init shorthand | 334 | /// Deals with field init shorthand |
313 | pub fn field_name(&self) -> Option<NameOrNameRef> { | 335 | pub fn field_name(&self) -> Option<NameOrNameRef> { |
314 | if let Some(name_ref) = self.name_ref() { | 336 | if let Some(name_ref) = self.name_ref() { |