Skip to content

Commit a4598bb

Browse files
committed
Use ast::make API
1 parent 11e3fd7 commit a4598bb

File tree

2 files changed

+72
-67
lines changed

2 files changed

+72
-67
lines changed

crates/ra_assists/src/handlers/add_function.rs

Lines changed: 52 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use ra_syntax::{
22
ast::{self, AstNode},
3-
SmolStr, SyntaxKind, SyntaxNode, TextUnit,
3+
SyntaxKind, SyntaxNode, TextUnit,
44
};
55

66
use crate::{Assist, AssistCtx, AssistId};
7-
use ast::{ArgListOwner, CallExpr, Expr};
7+
use ast::{edit::IndentLevel, ArgListOwner, CallExpr, Expr};
88
use hir::HirDisplay;
9-
use ra_fmt::leading_indent;
109
use rustc_hash::{FxHashMap, FxHashSet};
1110

1211
// Assist: add_function
@@ -53,73 +52,66 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
5352
ctx.add_assist(AssistId("add_function"), "Add function", |edit| {
5453
edit.target(call.syntax().text_range());
5554

56-
let function_template = function_builder.render();
57-
edit.set_cursor(function_template.cursor_offset);
58-
edit.insert(function_template.insert_offset, function_template.fn_text);
55+
if let Some(function_template) = function_builder.render() {
56+
let empty_line_after_previous_element = "\n\n";
57+
edit.set_cursor(
58+
function_template.cursor_offset
59+
+ TextUnit::of_str(empty_line_after_previous_element),
60+
);
61+
edit.insert(function_template.insert_offset, empty_line_after_previous_element);
62+
edit.insert(function_template.insert_offset, function_template.fn_def.to_string());
63+
}
5964
})
6065
}
6166

6267
struct FunctionTemplate {
6368
insert_offset: TextUnit,
6469
cursor_offset: TextUnit,
65-
fn_text: String,
70+
fn_def: ast::FnDef,
6671
}
6772

6873
struct FunctionBuilder {
69-
start_offset: TextUnit,
70-
fn_name: String,
71-
fn_generics: String,
72-
fn_args: String,
73-
indent: String,
74+
append_fn_at: SyntaxNode,
75+
fn_name: ast::Name,
76+
type_params: Option<ast::TypeParamList>,
77+
params: ast::ParamList,
7478
}
7579

7680
impl FunctionBuilder {
7781
fn from_call(ctx: &AssistCtx, call: &ast::CallExpr) -> Option<Self> {
78-
let (start, indent) = next_space_for_fn(&call)?;
82+
let append_fn_at = next_space_for_fn(&call)?;
7983
let fn_name = fn_name(&call)?;
80-
let fn_generics = fn_generics(&call)?;
81-
let fn_args = fn_args(ctx, &call)?;
82-
let indent = if let Some(i) = &indent { i.to_string() } else { String::new() };
83-
Some(Self { start_offset: start, fn_name, fn_generics, fn_args, indent })
84-
}
85-
fn render(&self) -> FunctionTemplate {
86-
let mut fn_buf = String::with_capacity(128);
87-
fn_buf.push_str("\n\n");
88-
fn_buf.push_str(&self.indent);
89-
fn_buf.push_str("fn ");
90-
fn_buf.push_str(&self.fn_name);
91-
fn_buf.push_str(&self.fn_generics);
92-
fn_buf.push_str(&self.fn_args);
93-
fn_buf.push_str(" {\n");
94-
fn_buf.push_str(&self.indent);
95-
fn_buf.push_str(" ");
96-
97-
// We take the offset here to put the cursor in front of the `unimplemented!()` body
98-
let offset = TextUnit::of_str(&fn_buf);
99-
100-
fn_buf.push_str("unimplemented!()\n");
101-
fn_buf.push_str(&self.indent);
102-
fn_buf.push_str("}");
103-
104-
let cursor_pos = self.start_offset + offset;
105-
FunctionTemplate {
106-
fn_text: fn_buf,
107-
cursor_offset: cursor_pos,
108-
insert_offset: self.start_offset,
109-
}
110-
}
111-
}
112-
113-
fn fn_name(call: &CallExpr) -> Option<String> {
114-
Some(call.expr()?.syntax().to_string())
115-
}
116-
117-
fn fn_generics(_call: &CallExpr) -> Option<String> {
118-
// TODO
119-
Some("".into())
120-
}
121-
122-
fn fn_args(ctx: &AssistCtx, call: &CallExpr) -> Option<String> {
84+
let (type_params, params) = fn_args(ctx, &call)?;
85+
Some(Self { append_fn_at, fn_name, type_params, params })
86+
}
87+
fn render(self) -> Option<FunctionTemplate> {
88+
let placeholder_expr = ast::make::expr_unimplemented();
89+
let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr));
90+
let fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body);
91+
let fn_def = IndentLevel::from_node(&self.append_fn_at).increase_indent(fn_def);
92+
let insert_offset = self.append_fn_at.text_range().end();
93+
let cursor_offset_from_fn_start = fn_def
94+
.syntax()
95+
.descendants()
96+
.find_map(ast::MacroCall::cast)?
97+
.syntax()
98+
.text_range()
99+
.start();
100+
let cursor_offset = insert_offset + cursor_offset_from_fn_start;
101+
Some(FunctionTemplate { insert_offset, cursor_offset, fn_def })
102+
}
103+
}
104+
105+
fn fn_name(call: &CallExpr) -> Option<ast::Name> {
106+
let name = call.expr()?.syntax().to_string();
107+
Some(ast::make::name(&name))
108+
}
109+
110+
/// Computes the type variables and arguments required for the generated function
111+
fn fn_args(
112+
ctx: &AssistCtx,
113+
call: &CallExpr,
114+
) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
123115
let mut arg_names = Vec::new();
124116
let mut arg_types = Vec::new();
125117
for arg in call.arg_list()?.args() {
@@ -134,15 +126,8 @@ fn fn_args(ctx: &AssistCtx, call: &CallExpr) -> Option<String> {
134126
});
135127
}
136128
deduplicate_arg_names(&mut arg_names);
137-
Some(format!(
138-
"({})",
139-
arg_names
140-
.into_iter()
141-
.zip(arg_types)
142-
.map(|(name, ty)| format!("{}: {}", name, ty))
143-
.collect::<Vec<_>>()
144-
.join(", ")
145-
))
129+
let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty));
130+
Some((None, ast::make::param_list(params)))
146131
}
147132

148133
/// Makes duplicate argument names unique by appending incrementing numbers.
@@ -203,7 +188,7 @@ fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> {
203188
/// directly after the current block
204189
/// We want to write the generated function directly after
205190
/// fns, impls or macro calls, but inside mods
206-
fn next_space_for_fn(expr: &CallExpr) -> Option<(TextUnit, Option<SmolStr>)> {
191+
fn next_space_for_fn(expr: &CallExpr) -> Option<SyntaxNode> {
207192
let mut ancestors = expr.syntax().ancestors().peekable();
208193
let mut last_ancestor: Option<SyntaxNode> = None;
209194
while let Some(next_ancestor) = ancestors.next() {
@@ -220,7 +205,7 @@ fn next_space_for_fn(expr: &CallExpr) -> Option<(TextUnit, Option<SmolStr>)> {
220205
}
221206
last_ancestor = Some(next_ancestor);
222207
}
223-
last_ancestor.map(|a| (a.text_range().end(), leading_indent(&a)))
208+
last_ancestor
224209
}
225210

226211
#[cfg(test)]

crates/ra_syntax/src/ast/make.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,26 @@ pub fn unreachable_macro_call() -> ast::MacroCall {
269269
ast_from_text(&format!("unreachable!()"))
270270
}
271271

272+
pub fn param(name: String, ty: String) -> ast::Param {
273+
ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty))
274+
}
275+
276+
pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList {
277+
let args = pats.into_iter().join(", ");
278+
ast_from_text(&format!("fn f({}) {{ }}", args))
279+
}
280+
281+
pub fn fn_def(
282+
fn_name: ast::Name,
283+
type_params: Option<ast::TypeParamList>,
284+
params: ast::ParamList,
285+
body: ast::BlockExpr,
286+
) -> ast::FnDef {
287+
let type_params =
288+
if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() };
289+
ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body))
290+
}
291+
272292
fn ast_from_text<N: AstNode>(text: &str) -> N {
273293
let parse = SourceFile::parse(text);
274294
let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap();

0 commit comments

Comments
 (0)