|
| 1 | +use hir::{self, HasCrate, HirDisplay}; |
| 2 | +use stdx::format_to; |
| 3 | +use syntax::ast::{self, AstNode, HasName, HasVisibility}; |
| 4 | + |
| 5 | +use crate::{ |
| 6 | + utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
| 7 | + AssistContext, AssistId, AssistKind, Assists, GroupLabel, |
| 8 | +}; |
| 9 | + |
| 10 | +// Assist: generate_setter |
| 11 | +// |
| 12 | +// Generate a setter method. |
| 13 | +// |
| 14 | +// ``` |
| 15 | +// struct Person { |
| 16 | +// nam$0e: String, |
| 17 | +// } |
| 18 | +// ``` |
| 19 | +// -> |
| 20 | +// ``` |
| 21 | +// struct Person { |
| 22 | +// name: String, |
| 23 | +// } |
| 24 | +// |
| 25 | +// impl Person { |
| 26 | +// /// Set the person's name. |
| 27 | +// fn set_name(&mut self, name: String) { |
| 28 | +// self.name = name; |
| 29 | +// } |
| 30 | +// } |
| 31 | +// ``` |
| 32 | +pub(crate) fn generate_delegate(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
| 33 | + let strukt = ctx.find_node_at_offset::<ast::Struct>()?; |
| 34 | + let field = ctx.find_node_at_offset::<ast::RecordField>()?; |
| 35 | + |
| 36 | + let field_name = field.name()?; |
| 37 | + let field_ty = field.ty()?; |
| 38 | + |
| 39 | + let sema_field_ty = ctx.sema.resolve_type(&field_ty)?; |
| 40 | + let krate = sema_field_ty.krate(ctx.db()); |
| 41 | + let mut methods = vec![]; |
| 42 | + sema_field_ty.iterate_assoc_items(ctx.db(), krate, |item| { |
| 43 | + if let hir::AssocItem::Function(f) = item { |
| 44 | + if f.self_param(ctx.db()).is_some() { |
| 45 | + methods.push(f) |
| 46 | + } |
| 47 | + } |
| 48 | + Some(()) |
| 49 | + }); |
| 50 | + |
| 51 | + let target = field_ty.syntax().text_range(); |
| 52 | + for method in methods { |
| 53 | + let impl_def = find_struct_impl( |
| 54 | + ctx, |
| 55 | + &ast::Adt::Struct(strukt.clone()), |
| 56 | + &method.name(ctx.db()).to_string(), |
| 57 | + )?; |
| 58 | + acc.add_group( |
| 59 | + &GroupLabel("Generate delegate".to_owned()), |
| 60 | + AssistId("generate_delegate", AssistKind::Generate), |
| 61 | + format!("Generate a delegate method for '{}'", method.name(ctx.db())), |
| 62 | + target, |
| 63 | + |builder| { |
| 64 | + let mut buf = String::with_capacity(512); |
| 65 | + |
| 66 | + let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); |
| 67 | + let return_type = method.ret_type(ctx.db()); |
| 68 | + let return_type = if return_type.is_unit() || return_type.is_unknown() { |
| 69 | + String::new() |
| 70 | + } else { |
| 71 | + let module = match ctx.sema.scope(strukt.syntax()).module() { |
| 72 | + Some(m) => m, |
| 73 | + None => return, |
| 74 | + }; |
| 75 | + match return_type.display_source_code(ctx.db(), module.into()) { |
| 76 | + Ok(rt) => format!("-> {}", rt), |
| 77 | + Err(_) => return, |
| 78 | + } |
| 79 | + }; |
| 80 | + |
| 81 | + format_to!( |
| 82 | + buf, |
| 83 | + "{}fn {}(&self) {} {{ |
| 84 | + self.{}.{}() |
| 85 | + }}", |
| 86 | + vis, |
| 87 | + method.name(ctx.db()), |
| 88 | + return_type, |
| 89 | + field_name, |
| 90 | + method.name(ctx.db()) |
| 91 | + ); |
| 92 | + |
| 93 | + let start_offset = impl_def |
| 94 | + .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
| 95 | + .unwrap_or_else(|| { |
| 96 | + buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
| 97 | + strukt.syntax().text_range().end() |
| 98 | + }); |
| 99 | + |
| 100 | + builder.insert(start_offset, buf); |
| 101 | + }, |
| 102 | + )?; |
| 103 | + } |
| 104 | + Some(()) |
| 105 | +} |
| 106 | + |
| 107 | +#[cfg(test)] |
| 108 | +mod tests { |
| 109 | + use crate::tests::check_assist; |
| 110 | + |
| 111 | + use super::*; |
| 112 | + |
| 113 | + #[test] |
| 114 | + fn test_generate_setter_from_field() { |
| 115 | + check_assist( |
| 116 | + generate_delegate, |
| 117 | + r#" |
| 118 | +struct Age(u8); |
| 119 | +impl Age { |
| 120 | + fn age(&self) -> u8 { |
| 121 | + self.0 |
| 122 | + |
| 123 | + } |
| 124 | +} |
| 125 | +
|
| 126 | +struct Person { |
| 127 | + ag$0e: Age, |
| 128 | +} |
| 129 | +"#, |
| 130 | + r#" |
| 131 | +struct Age(u8); |
| 132 | +impl Age { |
| 133 | + fn age(&self) -> u8 { |
| 134 | + self.0 |
| 135 | + } |
| 136 | +} |
| 137 | +
|
| 138 | +struct Person { |
| 139 | + age: Age, |
| 140 | +} |
| 141 | +
|
| 142 | +impl Person { |
| 143 | + fn age(&self) -> u8 { |
| 144 | + self.age.age() |
| 145 | + } |
| 146 | +}"#, |
| 147 | + ); |
| 148 | + } |
| 149 | +} |
0 commit comments