use syntax::{ ast::{self, edit_in_place::AttrsOwnerEdit, make, AstNode, HasAttrs}, T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: generate_derive // // Adds a new `#[derive()]` clause to a struct or enum. // // ``` // struct Point { // x: u32, // y: u32,$0 // } // ``` // -> // ``` // #[derive($0)] // struct Point { // x: u32, // y: u32, // } // ``` pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::()?; let target = nominal.syntax().text_range(); let derive_attr = nominal .attrs() .filter_map(|x| x.as_simple_call()) .filter(|(name, _arg)| name == "derive") .map(|(_name, arg)| arg) .next(); let delimiter = match &derive_attr { None => None, Some(tt) => Some(tt.right_delimiter_token()?), }; acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| { match derive_attr { None => { let derive = make::attr_outer(make::meta_token_tree( make::ext::ident_path("derive"), make::token_tree(T!['('], vec![]).clone_for_update(), )) .clone_for_update(); let nominal = edit.make_mut(nominal); nominal.add_attr(derive.clone()); let delimiter = derive .meta() .expect("make::attr_outer was expected to have Meta") .token_tree() .expect("failed to get token tree out of Meta") .r_paren_token() .expect("make::attr_outer was expected to have a R_PAREN"); edit.add_tabstop_before_token(cap, delimiter); } Some(_) => { // Just move the cursor. edit.add_tabstop_before_token( cap, delimiter.expect("Right delim token could not be found."), ); } }; }) } #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_target}; use super::*; #[test] fn add_derive_new() { check_assist( generate_derive, "struct Foo { a: i32, $0}", "#[derive($0)]\nstruct Foo { a: i32, }", ); check_assist( generate_derive, "struct Foo { $0 a: i32, }", "#[derive($0)]\nstruct Foo { a: i32, }", ); check_assist( generate_derive, " mod m { struct Foo { a: i32,$0 } } ", " mod m { #[derive($0)] struct Foo { a: i32, } } ", ); } #[test] fn add_derive_existing() { check_assist( generate_derive, "#[derive(Clone)]\nstruct Foo { a: i32$0, }", "#[derive(Clone$0)]\nstruct Foo { a: i32, }", ); } #[test] fn add_derive_existing_with_brackets() { check_assist( generate_derive, " #[derive[Clone]] struct Foo { a: i32$0, } ", " #[derive[Clone$0]] struct Foo { a: i32, } ", ); } #[test] fn add_derive_existing_missing_delimiter() { // since `#[derive]` isn't a simple attr call (i.e. `#[derive()]`) // we don't consider it as a proper derive attr and generate a new // one instead check_assist( generate_derive, " #[derive] struct Foo { a: i32$0, }", " #[derive] #[derive($0)] struct Foo { a: i32, }", ); } #[test] fn add_derive_new_with_doc_comment() { check_assist( generate_derive, " /// `Foo` is a pretty important struct. /// It does stuff. struct Foo { a: i32$0, } ", " /// `Foo` is a pretty important struct. /// It does stuff. #[derive($0)] struct Foo { a: i32, } ", ); check_assist( generate_derive, " mod m { /// `Foo` is a pretty important struct. /// It does stuff. struct Foo { a: i32,$0 } } ", " mod m { /// `Foo` is a pretty important struct. /// It does stuff. #[derive($0)] struct Foo { a: i32, } } ", ); } #[test] fn add_derive_target() { check_assist_target( generate_derive, " struct SomeThingIrrelevant; /// `Foo` is a pretty important struct. /// It does stuff. struct Foo { a: i32$0, } struct EvenMoreIrrelevant; ", "/// `Foo` is a pretty important struct. /// It does stuff. struct Foo { a: i32, }", ); } }