diff options
-rw-r--r-- | crates/ide/src/references/rename.rs | 376 |
1 files changed, 187 insertions, 189 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 22ddeeae3..878ca1afc 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -122,7 +122,7 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> { | |||
122 | Ok(IdentifierKind::Lifetime) | 122 | Ok(IdentifierKind::Lifetime) |
123 | } | 123 | } |
124 | (SyntaxKind::LIFETIME_IDENT, _) => { | 124 | (SyntaxKind::LIFETIME_IDENT, _) => { |
125 | bail!("Invalid name `{0}`: Cannot rename lifetime to {0}", new_name) | 125 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) |
126 | } | 126 | } |
127 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), | 127 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), |
128 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), | 128 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), |
@@ -162,119 +162,6 @@ fn find_definition( | |||
162 | .ok_or_else(|| format_err!("No references found at position")) | 162 | .ok_or_else(|| format_err!("No references found at position")) |
163 | } | 163 | } |
164 | 164 | ||
165 | fn source_edit_from_references( | ||
166 | _sema: &Semantics<RootDatabase>, | ||
167 | file_id: FileId, | ||
168 | references: &[FileReference], | ||
169 | def: Definition, | ||
170 | new_name: &str, | ||
171 | ) -> (FileId, TextEdit) { | ||
172 | let mut edit = TextEdit::builder(); | ||
173 | for reference in references { | ||
174 | let (range, replacement) = match &reference.name { | ||
175 | // if the ranges differ then the node is inside a macro call, we can't really attempt | ||
176 | // to make special rewrites like shorthand syntax and such, so just rename the node in | ||
177 | // the macro input | ||
178 | ast::NameLike::NameRef(name_ref) | ||
179 | if name_ref.syntax().text_range() == reference.range => | ||
180 | { | ||
181 | source_edit_from_name_ref(name_ref, new_name, def) | ||
182 | } | ||
183 | ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => { | ||
184 | source_edit_from_name(name, new_name) | ||
185 | } | ||
186 | _ => None, | ||
187 | } | ||
188 | .unwrap_or_else(|| (reference.range, new_name.to_string())); | ||
189 | edit.replace(range, replacement); | ||
190 | } | ||
191 | (file_id, edit.finish()) | ||
192 | } | ||
193 | |||
194 | fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { | ||
195 | if let Some(_) = ast::RecordPatField::for_field_name(name) { | ||
196 | if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | ||
197 | return Some(( | ||
198 | TextRange::empty(ident_pat.syntax().text_range().start()), | ||
199 | format!("{}: ", new_name), | ||
200 | )); | ||
201 | } | ||
202 | } | ||
203 | None | ||
204 | } | ||
205 | |||
206 | fn source_edit_from_name_ref( | ||
207 | name_ref: &ast::NameRef, | ||
208 | new_name: &str, | ||
209 | def: Definition, | ||
210 | ) -> Option<(TextRange, String)> { | ||
211 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { | ||
212 | let rcf_name_ref = record_field.name_ref(); | ||
213 | let rcf_expr = record_field.expr(); | ||
214 | match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { | ||
215 | // field: init-expr, check if we can use a field init shorthand | ||
216 | (Some(field_name), Some(init)) => { | ||
217 | if field_name == *name_ref { | ||
218 | if init.text() == new_name { | ||
219 | mark::hit!(test_rename_field_put_init_shorthand); | ||
220 | // same names, we can use a shorthand here instead. | ||
221 | // we do not want to erase attributes hence this range start | ||
222 | let s = field_name.syntax().text_range().start(); | ||
223 | let e = record_field.syntax().text_range().end(); | ||
224 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
225 | } | ||
226 | } else if init == *name_ref { | ||
227 | if field_name.text() == new_name { | ||
228 | mark::hit!(test_rename_local_put_init_shorthand); | ||
229 | // same names, we can use a shorthand here instead. | ||
230 | // we do not want to erase attributes hence this range start | ||
231 | let s = field_name.syntax().text_range().start(); | ||
232 | let e = record_field.syntax().text_range().end(); | ||
233 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
234 | } | ||
235 | } | ||
236 | None | ||
237 | } | ||
238 | // init shorthand | ||
239 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | ||
240 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | ||
241 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { | ||
242 | mark::hit!(test_rename_field_in_field_shorthand); | ||
243 | let s = name_ref.syntax().text_range().start(); | ||
244 | Some((TextRange::empty(s), format!("{}: ", new_name))) | ||
245 | } | ||
246 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { | ||
247 | mark::hit!(test_rename_local_in_field_shorthand); | ||
248 | let s = name_ref.syntax().text_range().end(); | ||
249 | Some((TextRange::empty(s), format!(": {}", new_name))) | ||
250 | } | ||
251 | _ => None, | ||
252 | } | ||
253 | } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
254 | let rcf_name_ref = record_field.name_ref(); | ||
255 | let rcf_pat = record_field.pat(); | ||
256 | match (rcf_name_ref, rcf_pat) { | ||
257 | // field: rename | ||
258 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
259 | // field name is being renamed | ||
260 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
261 | mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
262 | // same names, we can use a shorthand here instead/ | ||
263 | // we do not want to erase attributes hence this range start | ||
264 | let s = field_name.syntax().text_range().start(); | ||
265 | let e = record_field.syntax().text_range().end(); | ||
266 | Some((TextRange::new(s, e), pat.to_string())) | ||
267 | } else { | ||
268 | None | ||
269 | } | ||
270 | } | ||
271 | _ => None, | ||
272 | } | ||
273 | } else { | ||
274 | None | ||
275 | } | ||
276 | } | ||
277 | |||
278 | fn rename_mod( | 165 | fn rename_mod( |
279 | sema: &Semantics<RootDatabase>, | 166 | sema: &Semantics<RootDatabase>, |
280 | module: Module, | 167 | module: Module, |
@@ -308,18 +195,75 @@ fn rename_mod( | |||
308 | TextEdit::replace(name.syntax().text_range(), new_name.to_string()), | 195 | TextEdit::replace(name.syntax().text_range(), new_name.to_string()), |
309 | ), | 196 | ), |
310 | _ => unreachable!(), | 197 | _ => unreachable!(), |
311 | }; | 198 | } |
312 | } | 199 | } |
313 | let def = Definition::ModuleDef(ModuleDef::Module(module)); | 200 | let def = Definition::ModuleDef(ModuleDef::Module(module)); |
314 | let usages = def.usages(sema).all(); | 201 | let usages = def.usages(sema).all(); |
315 | let ref_edits = usages.iter().map(|(&file_id, references)| { | 202 | let ref_edits = usages.iter().map(|(&file_id, references)| { |
316 | source_edit_from_references(sema, file_id, references, def, new_name) | 203 | (file_id, source_edit_from_references(references, def, new_name)) |
317 | }); | 204 | }); |
318 | source_change.extend(ref_edits); | 205 | source_change.extend(ref_edits); |
319 | 206 | ||
320 | Ok(source_change) | 207 | Ok(source_change) |
321 | } | 208 | } |
322 | 209 | ||
210 | fn rename_reference( | ||
211 | sema: &Semantics<RootDatabase>, | ||
212 | def: Definition, | ||
213 | new_name: &str, | ||
214 | ) -> RenameResult<SourceChange> { | ||
215 | let ident_kind = check_identifier(new_name)?; | ||
216 | |||
217 | let def_is_lbl_or_lt = matches!( | ||
218 | def, | ||
219 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
220 | ); | ||
221 | match (ident_kind, def) { | ||
222 | (IdentifierKind::ToSelf, _) | ||
223 | | (IdentifierKind::Underscore, _) | ||
224 | | (IdentifierKind::Ident, _) | ||
225 | if def_is_lbl_or_lt => | ||
226 | { | ||
227 | mark::hit!(rename_not_a_lifetime_ident_ref); | ||
228 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | ||
229 | } | ||
230 | (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), | ||
231 | (IdentifierKind::Lifetime, _) => { | ||
232 | mark::hit!(rename_not_an_ident_ref); | ||
233 | bail!("Invalid name `{}`: not an identifier", new_name) | ||
234 | } | ||
235 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { | ||
236 | // no-op | ||
237 | mark::hit!(rename_self_to_self); | ||
238 | return Ok(SourceChange::default()); | ||
239 | } | ||
240 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { | ||
241 | mark::hit!(rename_self_to_param); | ||
242 | return rename_self_to_param(sema, local, new_name, ident_kind); | ||
243 | } | ||
244 | (IdentifierKind::ToSelf, Definition::Local(local)) => { | ||
245 | mark::hit!(rename_to_self); | ||
246 | return rename_to_self(sema, local); | ||
247 | } | ||
248 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), | ||
249 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), | ||
250 | } | ||
251 | |||
252 | let usages = def.usages(sema).all(); | ||
253 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | ||
254 | mark::hit!(rename_underscore_multiple); | ||
255 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | ||
256 | } | ||
257 | let mut source_change = SourceChange::default(); | ||
258 | source_change.extend(usages.iter().map(|(&file_id, references)| { | ||
259 | (file_id, source_edit_from_references(&references, def, new_name)) | ||
260 | })); | ||
261 | |||
262 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | ||
263 | source_change.insert_source_edit(file_id, edit); | ||
264 | Ok(source_change) | ||
265 | } | ||
266 | |||
323 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { | 267 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { |
324 | if never!(local.is_self(sema.db)) { | 268 | if never!(local.is_self(sema.db)) { |
325 | bail!("rename_to_self invoked on self"); | 269 | bail!("rename_to_self invoked on self"); |
@@ -384,7 +328,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
384 | let usages = def.usages(sema).all(); | 328 | let usages = def.usages(sema).all(); |
385 | let mut source_change = SourceChange::default(); | 329 | let mut source_change = SourceChange::default(); |
386 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 330 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
387 | source_edit_from_references(sema, file_id, references, def, "self") | 331 | (file_id, source_edit_from_references(references, def, "self")) |
388 | })); | 332 | })); |
389 | source_change.insert_source_edit( | 333 | source_change.insert_source_edit( |
390 | file_id.original_file(sema.db), | 334 | file_id.original_file(sema.db), |
@@ -394,29 +338,6 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
394 | Ok(source_change) | 338 | Ok(source_change) |
395 | } | 339 | } |
396 | 340 | ||
397 | fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> { | ||
398 | fn target_type_name(impl_def: &ast::Impl) -> Option<String> { | ||
399 | if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { | ||
400 | return Some(p.path()?.segment()?.name_ref()?.text().to_string()); | ||
401 | } | ||
402 | None | ||
403 | } | ||
404 | |||
405 | let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; | ||
406 | let type_name = target_type_name(&impl_def)?; | ||
407 | |||
408 | let mut replacement_text = String::from(new_name); | ||
409 | replacement_text.push_str(": "); | ||
410 | match (self_param.amp_token(), self_param.mut_token()) { | ||
411 | (None, None) => (), | ||
412 | (Some(_), None) => replacement_text.push('&'), | ||
413 | (_, Some(_)) => replacement_text.push_str("&mut "), | ||
414 | }; | ||
415 | replacement_text.push_str(type_name.as_str()); | ||
416 | |||
417 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) | ||
418 | } | ||
419 | |||
420 | fn rename_self_to_param( | 341 | fn rename_self_to_param( |
421 | sema: &Semantics<RootDatabase>, | 342 | sema: &Semantics<RootDatabase>, |
422 | local: hir::Local, | 343 | local: hir::Local, |
@@ -441,66 +362,143 @@ fn rename_self_to_param( | |||
441 | let mut source_change = SourceChange::default(); | 362 | let mut source_change = SourceChange::default(); |
442 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | 363 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); |
443 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 364 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
444 | source_edit_from_references(sema, file_id, &references, def, new_name) | 365 | (file_id, source_edit_from_references(&references, def, new_name)) |
445 | })); | 366 | })); |
446 | Ok(source_change) | 367 | Ok(source_change) |
447 | } | 368 | } |
448 | 369 | ||
449 | fn rename_reference( | 370 | fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> { |
450 | sema: &Semantics<RootDatabase>, | 371 | fn target_type_name(impl_def: &ast::Impl) -> Option<String> { |
372 | if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { | ||
373 | return Some(p.path()?.segment()?.name_ref()?.text().to_string()); | ||
374 | } | ||
375 | None | ||
376 | } | ||
377 | |||
378 | let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; | ||
379 | let type_name = target_type_name(&impl_def)?; | ||
380 | |||
381 | let mut replacement_text = String::from(new_name); | ||
382 | replacement_text.push_str(": "); | ||
383 | match (self_param.amp_token(), self_param.mut_token()) { | ||
384 | (Some(_), None) => replacement_text.push('&'), | ||
385 | (Some(_), Some(_)) => replacement_text.push_str("&mut "), | ||
386 | (_, _) => (), | ||
387 | }; | ||
388 | replacement_text.push_str(type_name.as_str()); | ||
389 | |||
390 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) | ||
391 | } | ||
392 | |||
393 | fn source_edit_from_references( | ||
394 | references: &[FileReference], | ||
451 | def: Definition, | 395 | def: Definition, |
452 | new_name: &str, | 396 | new_name: &str, |
453 | ) -> RenameResult<SourceChange> { | 397 | ) -> TextEdit { |
454 | let ident_kind = check_identifier(new_name)?; | 398 | let mut edit = TextEdit::builder(); |
455 | 399 | for reference in references { | |
456 | let def_is_lbl_or_lt = matches!( | 400 | let (range, replacement) = match &reference.name { |
457 | def, | 401 | // if the ranges differ then the node is inside a macro call, we can't really attempt |
458 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | 402 | // to make special rewrites like shorthand syntax and such, so just rename the node in |
459 | ); | 403 | // the macro input |
460 | match (ident_kind, def) { | 404 | ast::NameLike::NameRef(name_ref) |
461 | (IdentifierKind::ToSelf, _) | 405 | if name_ref.syntax().text_range() == reference.range => |
462 | | (IdentifierKind::Underscore, _) | 406 | { |
463 | | (IdentifierKind::Ident, _) | 407 | source_edit_from_name_ref(name_ref, new_name, def) |
464 | if def_is_lbl_or_lt => | 408 | } |
465 | { | 409 | ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => { |
466 | mark::hit!(rename_not_a_lifetime_ident_ref); | 410 | source_edit_from_name(name, new_name) |
467 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | 411 | } |
468 | } | 412 | _ => None, |
469 | (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), | ||
470 | (IdentifierKind::Lifetime, _) => { | ||
471 | mark::hit!(rename_not_an_ident_ref); | ||
472 | bail!("Invalid name `{}`: not an identifier", new_name) | ||
473 | } | ||
474 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { | ||
475 | // no-op | ||
476 | mark::hit!(rename_self_to_self); | ||
477 | return Ok(SourceChange::default()); | ||
478 | } | ||
479 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { | ||
480 | mark::hit!(rename_self_to_param); | ||
481 | return rename_self_to_param(sema, local, new_name, ident_kind); | ||
482 | } | ||
483 | (IdentifierKind::ToSelf, Definition::Local(local)) => { | ||
484 | mark::hit!(rename_to_self); | ||
485 | return rename_to_self(sema, local); | ||
486 | } | 413 | } |
487 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), | 414 | .unwrap_or_else(|| (reference.range, new_name.to_string())); |
488 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), | 415 | edit.replace(range, replacement); |
489 | } | 416 | } |
417 | edit.finish() | ||
418 | } | ||
490 | 419 | ||
491 | let usages = def.usages(sema).all(); | 420 | fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { |
492 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | 421 | if let Some(_) = ast::RecordPatField::for_field_name(name) { |
493 | mark::hit!(rename_underscore_multiple); | 422 | if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { |
494 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | 423 | return Some(( |
424 | TextRange::empty(ident_pat.syntax().text_range().start()), | ||
425 | [new_name, ": "].concat(), | ||
426 | )); | ||
427 | } | ||
495 | } | 428 | } |
496 | let mut source_change = SourceChange::default(); | 429 | None |
497 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 430 | } |
498 | source_edit_from_references(sema, file_id, &references, def, new_name) | ||
499 | })); | ||
500 | 431 | ||
501 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | 432 | fn source_edit_from_name_ref( |
502 | source_change.insert_source_edit(file_id, edit); | 433 | name_ref: &ast::NameRef, |
503 | Ok(source_change) | 434 | new_name: &str, |
435 | def: Definition, | ||
436 | ) -> Option<(TextRange, String)> { | ||
437 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { | ||
438 | let rcf_name_ref = record_field.name_ref(); | ||
439 | let rcf_expr = record_field.expr(); | ||
440 | match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { | ||
441 | // field: init-expr, check if we can use a field init shorthand | ||
442 | (Some(field_name), Some(init)) => { | ||
443 | if field_name == *name_ref { | ||
444 | if init.text() == new_name { | ||
445 | mark::hit!(test_rename_field_put_init_shorthand); | ||
446 | // same names, we can use a shorthand here instead. | ||
447 | // we do not want to erase attributes hence this range start | ||
448 | let s = field_name.syntax().text_range().start(); | ||
449 | let e = record_field.syntax().text_range().end(); | ||
450 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
451 | } | ||
452 | } else if init == *name_ref { | ||
453 | if field_name.text() == new_name { | ||
454 | mark::hit!(test_rename_local_put_init_shorthand); | ||
455 | // same names, we can use a shorthand here instead. | ||
456 | // we do not want to erase attributes hence this range start | ||
457 | let s = field_name.syntax().text_range().start(); | ||
458 | let e = record_field.syntax().text_range().end(); | ||
459 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
460 | } | ||
461 | } | ||
462 | None | ||
463 | } | ||
464 | // init shorthand | ||
465 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | ||
466 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | ||
467 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { | ||
468 | mark::hit!(test_rename_field_in_field_shorthand); | ||
469 | let s = name_ref.syntax().text_range().start(); | ||
470 | Some((TextRange::empty(s), format!("{}: ", new_name))) | ||
471 | } | ||
472 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { | ||
473 | mark::hit!(test_rename_local_in_field_shorthand); | ||
474 | let s = name_ref.syntax().text_range().end(); | ||
475 | Some((TextRange::empty(s), format!(": {}", new_name))) | ||
476 | } | ||
477 | _ => None, | ||
478 | } | ||
479 | } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
480 | let rcf_name_ref = record_field.name_ref(); | ||
481 | let rcf_pat = record_field.pat(); | ||
482 | match (rcf_name_ref, rcf_pat) { | ||
483 | // field: rename | ||
484 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
485 | // field name is being renamed | ||
486 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
487 | mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
488 | // same names, we can use a shorthand here instead/ | ||
489 | // we do not want to erase attributes hence this range start | ||
490 | let s = field_name.syntax().text_range().start(); | ||
491 | let e = record_field.syntax().text_range().end(); | ||
492 | Some((TextRange::new(s, e), pat.to_string())) | ||
493 | } else { | ||
494 | None | ||
495 | } | ||
496 | } | ||
497 | _ => None, | ||
498 | } | ||
499 | } else { | ||
500 | None | ||
501 | } | ||
504 | } | 502 | } |
505 | 503 | ||
506 | fn source_edit_from_def( | 504 | fn source_edit_from_def( |