2019-04-10 11:15:55 +03:00
|
|
|
use hir::{AdtDef, db::HirDatabase};
|
2019-03-17 19:48:25 +01:00
|
|
|
|
2019-03-18 09:03:10 +01:00
|
|
|
use ra_syntax::ast::{self, AstNode};
|
2019-03-17 19:48:25 +01:00
|
|
|
|
2019-04-21 22:13:52 +03:00
|
|
|
use crate::{AssistCtx, Assist, AssistId, ast_editor::{AstEditor, AstBuilder}};
|
2019-03-17 19:48:25 +01:00
|
|
|
|
|
|
|
pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
|
|
|
let struct_lit = ctx.node_at_offset::<ast::StructLit>()?;
|
2019-04-21 22:13:52 +03:00
|
|
|
let named_field_list = struct_lit.named_field_list()?;
|
2019-03-17 19:48:25 +01:00
|
|
|
|
2019-04-21 22:13:52 +03:00
|
|
|
// Collect all fields from struct definition
|
|
|
|
let mut fields = {
|
|
|
|
let analyzer =
|
|
|
|
hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, struct_lit.syntax(), None);
|
|
|
|
let struct_lit_ty = analyzer.type_of(ctx.db, struct_lit.into())?;
|
2019-03-17 19:37:09 +01:00
|
|
|
let struct_def = match struct_lit_ty.as_adt() {
|
|
|
|
Some((AdtDef::Struct(s), _)) => s,
|
2019-03-20 16:52:29 -03:00
|
|
|
_ => return None,
|
|
|
|
};
|
2019-04-21 22:13:52 +03:00
|
|
|
struct_def.fields(ctx.db)
|
|
|
|
};
|
2019-03-20 16:52:29 -03:00
|
|
|
|
2019-04-21 22:13:52 +03:00
|
|
|
// Filter out existing fields
|
|
|
|
for ast_field in named_field_list.fields() {
|
|
|
|
let name_from_ast = ast_field.name_ref()?.text().to_string();
|
|
|
|
fields.retain(|field| field.name(ctx.db).to_string() != name_from_ast);
|
|
|
|
}
|
|
|
|
if fields.is_empty() {
|
|
|
|
return None;
|
2019-03-20 16:52:29 -03:00
|
|
|
}
|
|
|
|
|
2019-04-21 22:13:52 +03:00
|
|
|
let db = ctx.db;
|
|
|
|
ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| {
|
|
|
|
let mut ast_editor = AstEditor::new(named_field_list);
|
|
|
|
if named_field_list.fields().count() == 0 && fields.len() > 2 {
|
|
|
|
ast_editor.make_multiline();
|
|
|
|
};
|
|
|
|
|
|
|
|
for field in fields {
|
2019-04-22 10:10:05 +03:00
|
|
|
let field = AstBuilder::<ast::NamedField>::from_pieces(
|
|
|
|
&AstBuilder::<ast::NameRef>::new(&field.name(db).to_string()),
|
|
|
|
Some(&AstBuilder::<ast::Expr>::unit()),
|
|
|
|
);
|
2019-04-21 22:13:52 +03:00
|
|
|
ast_editor.append_field(&field);
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|
2019-04-21 22:13:52 +03:00
|
|
|
|
|
|
|
edit.target(struct_lit.syntax().range());
|
|
|
|
edit.set_cursor(struct_lit.syntax().range().start());
|
|
|
|
|
|
|
|
ast_editor.into_text_edit(edit.text_edit_builder());
|
|
|
|
});
|
|
|
|
ctx.build()
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::helpers::{check_assist, check_assist_target};
|
|
|
|
|
|
|
|
use super::fill_struct_fields;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fill_struct_fields_empty_body() {
|
|
|
|
check_assist(
|
|
|
|
fill_struct_fields,
|
|
|
|
r#"
|
|
|
|
struct S<'a, D> {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
c: (i32, i32),
|
|
|
|
d: D,
|
2019-03-20 16:52:29 -03:00
|
|
|
e: &'a str,
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = S<|> {}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
struct S<'a, D> {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
c: (i32, i32),
|
|
|
|
d: D,
|
2019-03-20 16:52:29 -03:00
|
|
|
e: &'a str,
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = <|>S {
|
|
|
|
a: (),
|
|
|
|
b: (),
|
|
|
|
c: (),
|
|
|
|
d: (),
|
2019-03-20 16:52:29 -03:00
|
|
|
e: (),
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fill_struct_fields_target() {
|
|
|
|
check_assist_target(
|
|
|
|
fill_struct_fields,
|
|
|
|
r#"
|
|
|
|
struct S<'a, D> {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
c: (i32, i32),
|
|
|
|
d: D,
|
2019-03-20 16:52:29 -03:00
|
|
|
e: &'a str,
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = S<|> {}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
"S {}",
|
|
|
|
);
|
|
|
|
}
|
2019-03-18 09:03:10 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fill_struct_fields_preserve_self() {
|
|
|
|
check_assist(
|
|
|
|
fill_struct_fields,
|
|
|
|
r#"
|
|
|
|
struct Foo {
|
|
|
|
foo: u8,
|
|
|
|
bar: String,
|
|
|
|
baz: i128,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Foo {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self <|>{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
struct Foo {
|
|
|
|
foo: u8,
|
|
|
|
bar: String,
|
|
|
|
baz: i128,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Foo {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
<|>Self {
|
|
|
|
foo: (),
|
|
|
|
bar: (),
|
|
|
|
baz: (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
2019-03-20 16:52:29 -03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fill_struct_fields_partial() {
|
|
|
|
check_assist(
|
|
|
|
fill_struct_fields,
|
|
|
|
r#"
|
|
|
|
struct S<'a, D> {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
c: (i32, i32),
|
|
|
|
d: D,
|
|
|
|
e: &'a str,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = S {
|
|
|
|
c: (1, 2),
|
|
|
|
e: "foo",<|>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
struct S<'a, D> {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
c: (i32, i32),
|
|
|
|
d: D,
|
|
|
|
e: &'a str,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = <|>S {
|
2019-04-21 22:13:52 +03:00
|
|
|
c: (1, 2),
|
|
|
|
e: "foo",
|
2019-03-20 16:52:29 -03:00
|
|
|
a: (),
|
|
|
|
b: (),
|
|
|
|
d: (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
2019-04-22 00:52:21 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fill_struct_short() {
|
|
|
|
check_assist(
|
|
|
|
fill_struct_fields,
|
|
|
|
r#"
|
|
|
|
struct S {
|
|
|
|
foo: u32,
|
|
|
|
bar: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = S {<|> };
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
struct S {
|
|
|
|
foo: u32,
|
|
|
|
bar: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let s = <|>S { foo: (), bar: () };
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
2019-03-17 19:48:25 +01:00
|
|
|
}
|