Add getter/setter assists
Finish implementing `generate_setter` assists Make `generate_impl_text` util generic generate getter methods Fix getter / setter naming It's now in-line with the Rust API naming guidelines: https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter apply clippy Improve examples
This commit is contained in:
parent
876c4519e3
commit
e8d7bcc355
@ -4,7 +4,7 @@
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
utils::{find_impl_block, find_struct_impl},
|
||||
utils::{find_impl_block, find_struct_impl, generate_impl_text},
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
@ -82,7 +82,7 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&parent_enum, &buf);
|
||||
buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf);
|
||||
parent_enum.syntax().text_range().end()
|
||||
});
|
||||
|
||||
@ -91,16 +91,6 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
|
||||
)
|
||||
}
|
||||
|
||||
// Generates the surrounding `impl Type { <code> }` including type and lifetime
|
||||
// parameters
|
||||
fn generate_impl_text(strukt: &ast::Enum, code: &str) -> String {
|
||||
let mut buf = String::with_capacity(code.len());
|
||||
buf.push_str("\n\nimpl ");
|
||||
buf.push_str(strukt.name().unwrap().text());
|
||||
format_to!(buf, " {{\n{}\n}}", code);
|
||||
buf
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::mark;
|
||||
|
156
crates/assists/src/handlers/generate_getter.rs
Normal file
156
crates/assists/src/handlers/generate_getter.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use stdx::{format_to, to_lower_snake_case};
|
||||
use syntax::ast::VisibilityOwner;
|
||||
use syntax::ast::{self, AstNode, NameOwner};
|
||||
|
||||
use crate::{
|
||||
utils::{find_impl_block, find_struct_impl, generate_impl_text},
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: generate_getter
|
||||
//
|
||||
// Generate a getter method.
|
||||
//
|
||||
// ```
|
||||
// struct Person {
|
||||
// nam$0e: String,
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Person {
|
||||
// name: String,
|
||||
// }
|
||||
//
|
||||
// impl Person {
|
||||
// /// Get a reference to the person's name.
|
||||
// fn name(&self) -> &String {
|
||||
// &self.name
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
|
||||
|
||||
let strukt_name = strukt.name()?;
|
||||
let field_name = field.name()?;
|
||||
let field_ty = field.ty()?;
|
||||
|
||||
// Return early if we've found an existing fn
|
||||
let fn_name = to_lower_snake_case(&field_name.to_string());
|
||||
let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
|
||||
|
||||
let target = field.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("generate_getter", AssistKind::Generate),
|
||||
"Generate a getter method",
|
||||
target,
|
||||
|builder| {
|
||||
let mut buf = String::with_capacity(512);
|
||||
|
||||
let fn_name_spaced = fn_name.replace('_', " ");
|
||||
let strukt_name_spaced =
|
||||
to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
|
||||
|
||||
if impl_def.is_some() {
|
||||
buf.push('\n');
|
||||
}
|
||||
|
||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
|
||||
format_to!(
|
||||
buf,
|
||||
" /// Get a reference to the {}'s {}.
|
||||
{}fn {}(&self) -> &{} {{
|
||||
&self.{}
|
||||
}}",
|
||||
strukt_name_spaced,
|
||||
fn_name_spaced,
|
||||
vis,
|
||||
fn_name,
|
||||
field_ty,
|
||||
fn_name,
|
||||
);
|
||||
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
builder.insert(start_offset, buf);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn check_not_applicable(ra_fixture: &str) {
|
||||
check_assist_not_applicable(generate_getter, ra_fixture)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_getter_from_field() {
|
||||
check_assist(
|
||||
generate_getter,
|
||||
r#"
|
||||
struct Context<T: Clone> {
|
||||
dat$0a: T,
|
||||
}"#,
|
||||
r#"
|
||||
struct Context<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Context<T> {
|
||||
/// Get a reference to the context's data.
|
||||
fn data(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_getter_already_implemented() {
|
||||
check_not_applicable(
|
||||
r#"
|
||||
struct Context<T: Clone> {
|
||||
dat$0a: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Context<T> {
|
||||
fn data(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_getter_from_field_with_visibility_marker() {
|
||||
check_assist(
|
||||
generate_getter,
|
||||
r#"
|
||||
pub(crate) struct Context<T: Clone> {
|
||||
dat$0a: T,
|
||||
}"#,
|
||||
r#"
|
||||
pub(crate) struct Context<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Context<T> {
|
||||
/// Get a reference to the context's data.
|
||||
pub(crate) fn data(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
}
|
159
crates/assists/src/handlers/generate_getter_mut.rs
Normal file
159
crates/assists/src/handlers/generate_getter_mut.rs
Normal file
@ -0,0 +1,159 @@
|
||||
use stdx::{format_to, to_lower_snake_case};
|
||||
use syntax::ast::VisibilityOwner;
|
||||
use syntax::ast::{self, AstNode, NameOwner};
|
||||
|
||||
use crate::{
|
||||
utils::{find_impl_block, find_struct_impl, generate_impl_text},
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: generate_getter_mut
|
||||
//
|
||||
// Generate a mut getter method.
|
||||
//
|
||||
// ```
|
||||
// struct Person {
|
||||
// nam$0e: String,
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Person {
|
||||
// name: String,
|
||||
// }
|
||||
//
|
||||
// impl Person {
|
||||
// /// Get a mutable reference to the person's name.
|
||||
// fn name_mut(&mut self) -> &mut String {
|
||||
// &mut self.name
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
|
||||
|
||||
let strukt_name = strukt.name()?;
|
||||
let field_name = field.name()?;
|
||||
let field_ty = field.ty()?;
|
||||
|
||||
// Return early if we've found an existing fn
|
||||
let fn_name = to_lower_snake_case(&field_name.to_string());
|
||||
let impl_def = find_struct_impl(
|
||||
&ctx,
|
||||
&ast::Adt::Struct(strukt.clone()),
|
||||
format!("{}_mut", fn_name).as_str(),
|
||||
)?;
|
||||
|
||||
let target = field.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("generate_getter_mut", AssistKind::Generate),
|
||||
"Generate a mut getter method",
|
||||
target,
|
||||
|builder| {
|
||||
let mut buf = String::with_capacity(512);
|
||||
let fn_name_spaced = fn_name.replace('_', " ");
|
||||
let strukt_name_spaced =
|
||||
to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
|
||||
|
||||
if impl_def.is_some() {
|
||||
buf.push('\n');
|
||||
}
|
||||
|
||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
|
||||
format_to!(
|
||||
buf,
|
||||
" /// Get a mutable reference to the {}'s {}.
|
||||
{}fn {}_mut(&mut self) -> &mut {} {{
|
||||
&mut self.{}
|
||||
}}",
|
||||
strukt_name_spaced,
|
||||
fn_name_spaced,
|
||||
vis,
|
||||
fn_name,
|
||||
field_ty,
|
||||
fn_name,
|
||||
);
|
||||
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
builder.insert(start_offset, buf);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn check_not_applicable(ra_fixture: &str) {
|
||||
check_assist_not_applicable(generate_getter_mut, ra_fixture)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_getter_mut_from_field() {
|
||||
check_assist(
|
||||
generate_getter_mut,
|
||||
r#"
|
||||
struct Context<T: Clone> {
|
||||
dat$0a: T,
|
||||
}"#,
|
||||
r#"
|
||||
struct Context<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Context<T> {
|
||||
/// Get a mutable reference to the context's data.
|
||||
fn data_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_getter_mut_already_implemented() {
|
||||
check_not_applicable(
|
||||
r#"
|
||||
struct Context<T: Clone> {
|
||||
dat$0a: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Context<T> {
|
||||
fn data_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_getter_mut_from_field_with_visibility_marker() {
|
||||
check_assist(
|
||||
generate_getter_mut,
|
||||
r#"
|
||||
pub(crate) struct Context<T: Clone> {
|
||||
dat$0a: T,
|
||||
}"#,
|
||||
r#"
|
||||
pub(crate) struct Context<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Context<T> {
|
||||
/// Get a mutable reference to the context's data.
|
||||
pub(crate) fn data_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
use ast::Adt;
|
||||
use itertools::Itertools;
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
|
||||
SmolStr,
|
||||
};
|
||||
use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
|
||||
|
||||
use crate::{
|
||||
utils::{find_impl_block, find_struct_impl},
|
||||
utils::{find_impl_block, find_struct_impl, generate_impl_text},
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
@ -28,7 +26,6 @@
|
||||
// impl<T: Clone> Ctx<T> {
|
||||
// fn $0new(data: T) -> Self { Self { data } }
|
||||
// }
|
||||
//
|
||||
// ```
|
||||
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||
@ -40,7 +37,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
};
|
||||
|
||||
// Return early if we've found an existing new fn
|
||||
let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
|
||||
let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?;
|
||||
|
||||
let target = strukt.syntax().text_range();
|
||||
acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
|
||||
@ -63,7 +60,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&strukt, &buf);
|
||||
buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf);
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
@ -77,32 +74,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
})
|
||||
}
|
||||
|
||||
// Generates the surrounding `impl Type { <code> }` including type and lifetime
|
||||
// parameters
|
||||
fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
|
||||
let type_params = strukt.generic_param_list();
|
||||
let mut buf = String::with_capacity(code.len());
|
||||
buf.push_str("\n\nimpl");
|
||||
if let Some(type_params) = &type_params {
|
||||
format_to!(buf, "{}", type_params.syntax());
|
||||
}
|
||||
buf.push(' ');
|
||||
buf.push_str(strukt.name().unwrap().text());
|
||||
if let Some(type_params) = type_params {
|
||||
let lifetime_params = type_params
|
||||
.lifetime_params()
|
||||
.filter_map(|it| it.lifetime())
|
||||
.map(|it| SmolStr::from(it.text()));
|
||||
let type_params =
|
||||
type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text()));
|
||||
format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
|
||||
}
|
||||
|
||||
format_to!(buf, " {{\n{}\n}}\n", code);
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||
@ -120,8 +91,7 @@ fn test_generate_new() {
|
||||
|
||||
impl Foo {
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
check_assist(
|
||||
generate_new,
|
||||
@ -130,8 +100,7 @@ fn $0new() -> Self { Self { } }
|
||||
|
||||
impl<T: Clone> Foo<T> {
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
check_assist(
|
||||
generate_new,
|
||||
@ -140,8 +109,7 @@ fn $0new() -> Self { Self { } }
|
||||
|
||||
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
check_assist(
|
||||
generate_new,
|
||||
@ -150,8 +118,7 @@ fn $0new() -> Self { Self { } }
|
||||
|
||||
impl Foo {
|
||||
fn $0new(baz: String) -> Self { Self { baz } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
check_assist(
|
||||
generate_new,
|
||||
@ -160,8 +127,7 @@ fn $0new(baz: String) -> Self { Self { baz } }
|
||||
|
||||
impl Foo {
|
||||
fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
|
||||
// Check that visibility modifiers don't get brought in for fields
|
||||
@ -172,8 +138,7 @@ fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
||||
|
||||
impl Foo {
|
||||
fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
|
||||
// Check that it reuses existing impls
|
||||
@ -240,8 +205,7 @@ fn baz() -> i32 {
|
||||
|
||||
impl Foo {
|
||||
pub fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
check_assist(
|
||||
generate_new,
|
||||
@ -250,8 +214,7 @@ pub fn $0new() -> Self { Self { } }
|
||||
|
||||
impl Foo {
|
||||
pub(crate) fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
@ -322,8 +285,7 @@ impl<T> Source<T> {
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||
}
|
||||
}
|
||||
"##,
|
||||
}"##,
|
||||
r##"
|
||||
pub struct AstId<N: AstNode> {
|
||||
file_id: HirFileId,
|
||||
@ -347,8 +309,7 @@ pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||
}
|
||||
}
|
||||
"##,
|
||||
}"##,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
162
crates/assists/src/handlers/generate_setter.rs
Normal file
162
crates/assists/src/handlers/generate_setter.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use stdx::{format_to, to_lower_snake_case};
|
||||
use syntax::ast::VisibilityOwner;
|
||||
use syntax::ast::{self, AstNode, NameOwner};
|
||||
|
||||
use crate::{
|
||||
utils::{find_impl_block, find_struct_impl, generate_impl_text},
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: generate_setter
|
||||
//
|
||||
// Generate a setter method.
|
||||
//
|
||||
// ```
|
||||
// struct Person {
|
||||
// nam$0e: String,
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Person {
|
||||
// name: String,
|
||||
// }
|
||||
//
|
||||
// impl Person {
|
||||
// /// Set the person's name.
|
||||
// fn set_name(&mut self, name: String) {
|
||||
// self.name = name;
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
|
||||
|
||||
let strukt_name = strukt.name()?;
|
||||
let field_name = field.name()?;
|
||||
let field_ty = field.ty()?;
|
||||
|
||||
// Return early if we've found an existing fn
|
||||
let fn_name = to_lower_snake_case(&field_name.to_string());
|
||||
let impl_def = find_struct_impl(
|
||||
&ctx,
|
||||
&ast::Adt::Struct(strukt.clone()),
|
||||
format!("set_{}", fn_name).as_str(),
|
||||
)?;
|
||||
|
||||
let target = field.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("generate_setter", AssistKind::Generate),
|
||||
"Generate a setter method",
|
||||
target,
|
||||
|builder| {
|
||||
let mut buf = String::with_capacity(512);
|
||||
|
||||
let fn_name_spaced = fn_name.replace('_', " ");
|
||||
let strukt_name_spaced =
|
||||
to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
|
||||
|
||||
if impl_def.is_some() {
|
||||
buf.push('\n');
|
||||
}
|
||||
|
||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
|
||||
format_to!(
|
||||
buf,
|
||||
" /// Set the {}'s {}.
|
||||
{}fn set_{}(&mut self, {}: {}) {{
|
||||
self.{} = {};
|
||||
}}",
|
||||
strukt_name_spaced,
|
||||
fn_name_spaced,
|
||||
vis,
|
||||
fn_name,
|
||||
fn_name,
|
||||
field_ty,
|
||||
fn_name,
|
||||
fn_name,
|
||||
);
|
||||
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
builder.insert(start_offset, buf);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn check_not_applicable(ra_fixture: &str) {
|
||||
check_assist_not_applicable(generate_setter, ra_fixture)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_setter_from_field() {
|
||||
check_assist(
|
||||
generate_setter,
|
||||
r#"
|
||||
struct Person<T: Clone> {
|
||||
dat$0a: T,
|
||||
}"#,
|
||||
r#"
|
||||
struct Person<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Person<T> {
|
||||
/// Set the person's data.
|
||||
fn set_data(&mut self, data: T) {
|
||||
self.data = data;
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_setter_already_implemented() {
|
||||
check_not_applicable(
|
||||
r#"
|
||||
struct Person<T: Clone> {
|
||||
dat$0a: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Person<T> {
|
||||
fn set_data(&mut self, data: T) {
|
||||
self.data = data;
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_setter_from_field_with_visibility_marker() {
|
||||
check_assist(
|
||||
generate_setter,
|
||||
r#"
|
||||
pub(crate) struct Person<T: Clone> {
|
||||
dat$0a: T,
|
||||
}"#,
|
||||
r#"
|
||||
pub(crate) struct Person<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Person<T> {
|
||||
/// Set the person's data.
|
||||
pub(crate) fn set_data(&mut self, data: T) {
|
||||
self.data = data;
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -130,8 +130,11 @@ mod handlers {
|
||||
mod generate_enum_match_method;
|
||||
mod generate_from_impl_for_enum;
|
||||
mod generate_function;
|
||||
mod generate_getter;
|
||||
mod generate_getter_mut;
|
||||
mod generate_impl;
|
||||
mod generate_new;
|
||||
mod generate_setter;
|
||||
mod infer_function_return_type;
|
||||
mod inline_function;
|
||||
mod inline_local_variable;
|
||||
@ -189,8 +192,11 @@ pub(crate) fn all() -> &'static [Handler] {
|
||||
generate_enum_match_method::generate_enum_match_method,
|
||||
generate_from_impl_for_enum::generate_from_impl_for_enum,
|
||||
generate_function::generate_function,
|
||||
generate_getter::generate_getter,
|
||||
generate_getter_mut::generate_getter_mut,
|
||||
generate_impl::generate_impl,
|
||||
generate_new::generate_new,
|
||||
generate_setter::generate_setter,
|
||||
infer_function_return_type::infer_function_return_type,
|
||||
inline_function::inline_function,
|
||||
inline_local_variable::inline_local_variable,
|
||||
|
@ -169,6 +169,9 @@ fn assist_order_field_struct() {
|
||||
let mut assists = assists.iter();
|
||||
|
||||
assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
|
||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
|
||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
|
||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
|
||||
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
|
||||
}
|
||||
|
||||
|
@ -533,6 +533,54 @@ fn bar(arg: &str, baz: Baz) ${0:-> ()} {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_generate_getter() {
|
||||
check_doc_test(
|
||||
"generate_getter",
|
||||
r#####"
|
||||
struct Person {
|
||||
nam$0e: String,
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Person {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Person {
|
||||
/// Get a reference to the person's name.
|
||||
fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_generate_getter_mut() {
|
||||
check_doc_test(
|
||||
"generate_getter_mut",
|
||||
r#####"
|
||||
struct Person {
|
||||
nam$0e: String,
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Person {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Person {
|
||||
/// Get a mutable reference to the person's name.
|
||||
fn name_mut(&mut self) -> &mut String {
|
||||
&mut self.name
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_generate_impl() {
|
||||
check_doc_test(
|
||||
@ -571,7 +619,30 @@ struct Ctx<T: Clone> {
|
||||
impl<T: Clone> Ctx<T> {
|
||||
fn $0new(data: T) -> Self { Self { data } }
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_generate_setter() {
|
||||
check_doc_test(
|
||||
"generate_setter",
|
||||
r#####"
|
||||
struct Person {
|
||||
nam$0e: String,
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Person {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Person {
|
||||
/// Set the person's name.
|
||||
fn set_name(&mut self, name: String) {
|
||||
self.name = name;
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
@ -5,12 +5,13 @@
|
||||
use hir::{Adt, HasSource};
|
||||
use ide_db::{helpers::SnippetCap, RootDatabase};
|
||||
use itertools::Itertools;
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
ast::edit::AstNodeEdit,
|
||||
ast::AttrsOwner,
|
||||
ast::NameOwner,
|
||||
ast::{self, edit, make, ArgListOwner},
|
||||
AstNode, Direction,
|
||||
ast::{self, edit, make, ArgListOwner, GenericParamsOwner},
|
||||
AstNode, Direction, SmolStr,
|
||||
SyntaxKind::*,
|
||||
SyntaxNode, TextSize, T,
|
||||
};
|
||||
@ -354,3 +355,29 @@ pub(crate) fn find_impl_block(impl_def: ast::Impl, buf: &mut String) -> Option<T
|
||||
.end();
|
||||
Some(start)
|
||||
}
|
||||
|
||||
// Generates the surrounding `impl Type { <code> }` including type and lifetime
|
||||
// parameters
|
||||
pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
|
||||
let type_params = adt.generic_param_list();
|
||||
let mut buf = String::with_capacity(code.len());
|
||||
buf.push_str("\n\nimpl");
|
||||
if let Some(type_params) = &type_params {
|
||||
format_to!(buf, "{}", type_params.syntax());
|
||||
}
|
||||
buf.push(' ');
|
||||
buf.push_str(adt.name().unwrap().text());
|
||||
if let Some(type_params) = type_params {
|
||||
let lifetime_params = type_params
|
||||
.lifetime_params()
|
||||
.filter_map(|it| it.lifetime())
|
||||
.map(|it| SmolStr::from(it.text()));
|
||||
let type_params =
|
||||
type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text()));
|
||||
format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
|
||||
}
|
||||
|
||||
format_to!(buf, " {{\n{}\n}}", code);
|
||||
|
||||
buf
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user