mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 09:48:10 +00:00
implement field stuff too
This commit is contained in:
parent
a624e2ea8d
commit
3d1ca786f6
1 changed files with 106 additions and 22 deletions
|
@ -1,11 +1,13 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use ide_db::{helpers::FamousDefs, RootDatabase};
|
||||
use syntax::{
|
||||
ast::{self, NameOwner},
|
||||
AstNode,
|
||||
AstNode, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
assist_context::{AssistBuilder, AssistContext, Assists},
|
||||
utils::generate_trait_impl_text,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
@ -36,11 +38,15 @@ use crate::{
|
|||
// }
|
||||
// ```
|
||||
pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
|
||||
}
|
||||
|
||||
fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
|
||||
|
||||
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
|
||||
cov_mark::hit!(test_add_deref_impl_already_exists);
|
||||
cov_mark::hit!(test_add_record_deref_impl_already_exists);
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -51,7 +57,38 @@ pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<(
|
|||
AssistId("generate_deref", AssistKind::Generate),
|
||||
format!("Generate `Deref` impl using `{}`", field_name),
|
||||
target,
|
||||
|edit| {
|
||||
|edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()),
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||
let field = ctx.find_node_at_offset::<ast::TupleField>()?;
|
||||
let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
|
||||
let field_list_index =
|
||||
field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
|
||||
|
||||
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
|
||||
cov_mark::hit!(test_add_field_deref_impl_already_exists);
|
||||
return None;
|
||||
}
|
||||
|
||||
let field_type = field.ty()?;
|
||||
let target = field.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("generate_deref", AssistKind::Generate),
|
||||
format!("Generate `Deref` impl using `{}`", field.syntax()),
|
||||
target,
|
||||
|edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index),
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_edit(
|
||||
edit: &mut AssistBuilder,
|
||||
strukt: ast::Struct,
|
||||
field_type_syntax: &SyntaxNode,
|
||||
field_name: impl Display,
|
||||
) {
|
||||
let start_offset = strukt.syntax().text_range().end();
|
||||
let impl_code = format!(
|
||||
r#" type Target = {0};
|
||||
|
@ -59,18 +96,11 @@ pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<(
|
|||
fn deref(&self) -> &Self::Target {{
|
||||
&self.{1}
|
||||
}}"#,
|
||||
field_type.syntax(),
|
||||
field_name.syntax()
|
||||
field_type_syntax, field_name
|
||||
);
|
||||
let strukt_adt = ast::Adt::Struct(strukt);
|
||||
// Q for reviewer: Is there a better way to specify the trait_text, e.g.
|
||||
// - can I have it auto `use std::ops::Deref`, and then just use `Deref` as the trait text?
|
||||
// Or is there a helper that might detect if `std::ops::Deref` has been used, and pick `Deref`,
|
||||
// otherwise, pick `std::ops::Deref` for the trait_text.
|
||||
let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
|
||||
edit.insert(start_offset, deref_impl);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn existing_deref_impl(
|
||||
|
@ -97,7 +127,7 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_generate_deref() {
|
||||
fn test_generate_record_deref() {
|
||||
check_assist(
|
||||
generate_deref,
|
||||
r#"struct A { }
|
||||
|
@ -115,6 +145,43 @@ impl std::ops::Deref for B {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_field_deref_idx_0() {
|
||||
check_assist(
|
||||
generate_deref,
|
||||
r#"struct A { }
|
||||
struct B($0A);"#,
|
||||
r#"struct A { }
|
||||
struct B(A);
|
||||
|
||||
impl std::ops::Deref for B {
|
||||
type Target = A;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_generate_field_deref_idx_1() {
|
||||
check_assist(
|
||||
generate_deref,
|
||||
r#"struct A { }
|
||||
struct B(u8, $0A);"#,
|
||||
r#"struct A { }
|
||||
struct B(u8, A);
|
||||
|
||||
impl std::ops::Deref for B {
|
||||
type Target = A;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
fn check_not_applicable(ra_fixture: &str) {
|
||||
let fixture = format!(
|
||||
"//- /main.rs crate:main deps:core,std\n{}\n{}",
|
||||
|
@ -125,8 +192,8 @@ impl std::ops::Deref for B {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_deref_not_applicable_if_already_impl() {
|
||||
cov_mark::check!(test_add_deref_impl_already_exists);
|
||||
fn test_generate_record_deref_not_applicable_if_already_impl() {
|
||||
cov_mark::check!(test_add_record_deref_impl_already_exists);
|
||||
check_not_applicable(
|
||||
r#"struct A { }
|
||||
struct B { $0a: A }
|
||||
|
@ -137,6 +204,23 @@ impl std::ops::Deref for B {
|
|||
fn deref(&self) -> &Self::Target {
|
||||
&self.a
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_field_deref_not_applicable_if_already_impl() {
|
||||
cov_mark::check!(test_add_field_deref_impl_already_exists);
|
||||
check_not_applicable(
|
||||
r#"struct A { }
|
||||
struct B($0A)
|
||||
|
||||
impl std::ops::Deref for B {
|
||||
type Target = A;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue