aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorTimo Freiberg <[email protected]>2020-03-31 22:02:48 +0100
committerTimo Freiberg <[email protected]>2020-04-01 22:06:14 +0100
commit10667753c71242c75e70e7b8c46486f37685c186 (patch)
treee32cd2b6965f9f2ed6cda7f84b426bb8de435b35 /crates
parente5fc42cbc114bcfa5a4fac48a16b70fce8d438ae (diff)
Use ast::make API in add_function assist
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs115
-rw-r--r--crates/ra_syntax/src/ast/make.rs25
2 files changed, 73 insertions, 67 deletions
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index f6721d9df..488bae08f 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -1,12 +1,11 @@
1use ra_syntax::{ 1use ra_syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SmolStr, SyntaxKind, SyntaxNode, TextUnit, 3 SyntaxKind, SyntaxNode, TextUnit,
4}; 4};
5 5
6use crate::{Assist, AssistCtx, AssistId}; 6use crate::{Assist, AssistCtx, AssistId};
7use ast::{ArgListOwner, CallExpr, Expr}; 7use ast::{edit::IndentLevel, ArgListOwner, CallExpr, Expr};
8use hir::HirDisplay; 8use hir::HirDisplay;
9use ra_fmt::leading_indent;
10use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
11 10
12// Assist: add_function 11// Assist: add_function
@@ -53,73 +52,62 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
53 ctx.add_assist(AssistId("add_function"), "Add function", |edit| { 52 ctx.add_assist(AssistId("add_function"), "Add function", |edit| {
54 edit.target(call.syntax().text_range()); 53 edit.target(call.syntax().text_range());
55 54
56 let function_template = function_builder.render(); 55 if let Some(function_template) = function_builder.render() {
57 edit.set_cursor(function_template.cursor_offset); 56 edit.set_cursor(function_template.cursor_offset);
58 edit.insert(function_template.insert_offset, function_template.fn_text); 57 edit.insert(function_template.insert_offset, function_template.fn_def.to_string());
58 }
59 }) 59 })
60} 60}
61 61
62struct FunctionTemplate { 62struct FunctionTemplate {
63 insert_offset: TextUnit, 63 insert_offset: TextUnit,
64 cursor_offset: TextUnit, 64 cursor_offset: TextUnit,
65 fn_text: String, 65 fn_def: ast::SourceFile,
66} 66}
67 67
68struct FunctionBuilder { 68struct FunctionBuilder {
69 start_offset: TextUnit, 69 append_fn_at: SyntaxNode,
70 fn_name: String, 70 fn_name: ast::Name,
71 fn_generics: String, 71 type_params: Option<ast::TypeParamList>,
72 fn_args: String, 72 params: ast::ParamList,
73 indent: String,
74} 73}
75 74
76impl FunctionBuilder { 75impl FunctionBuilder {
77 fn from_call(ctx: &AssistCtx, call: &ast::CallExpr) -> Option<Self> { 76 fn from_call(ctx: &AssistCtx, call: &ast::CallExpr) -> Option<Self> {
78 let (start, indent) = next_space_for_fn(&call)?; 77 let append_fn_at = next_space_for_fn(&call)?;
79 let fn_name = fn_name(&call)?; 78 let fn_name = fn_name(&call)?;
80 let fn_generics = fn_generics(&call)?; 79 let (type_params, params) = fn_args(ctx, &call)?;
81 let fn_args = fn_args(ctx, &call)?; 80 Some(Self { append_fn_at, fn_name, type_params, params })
82 let indent = if let Some(i) = &indent { i.to_string() } else { String::new() }; 81 }
83 Some(Self { start_offset: start, fn_name, fn_generics, fn_args, indent }) 82 fn render(self) -> Option<FunctionTemplate> {
84 } 83 let placeholder_expr = ast::make::expr_unimplemented();
85 fn render(&self) -> FunctionTemplate { 84 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr));
86 let mut fn_buf = String::with_capacity(128); 85 let fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body);
87 fn_buf.push_str("\n\n"); 86 let fn_def = ast::make::add_newlines(2, fn_def);
88 fn_buf.push_str(&self.indent); 87 let fn_def = IndentLevel::from_node(&self.append_fn_at).increase_indent(fn_def);
89 fn_buf.push_str("fn "); 88 let insert_offset = self.append_fn_at.text_range().end();
90 fn_buf.push_str(&self.fn_name); 89 let cursor_offset_from_fn_start = fn_def
91 fn_buf.push_str(&self.fn_generics); 90 .syntax()
92 fn_buf.push_str(&self.fn_args); 91 .descendants()
93 fn_buf.push_str(" {\n"); 92 .find_map(ast::MacroCall::cast)?
94 fn_buf.push_str(&self.indent); 93 .syntax()
95 fn_buf.push_str(" "); 94 .text_range()
96 95 .start();
97 // We take the offset here to put the cursor in front of the `unimplemented!()` body 96 let cursor_offset = insert_offset + cursor_offset_from_fn_start;
98 let offset = TextUnit::of_str(&fn_buf); 97 Some(FunctionTemplate { insert_offset, cursor_offset, fn_def })
99 98 }
100 fn_buf.push_str("unimplemented!()\n"); 99}
101 fn_buf.push_str(&self.indent); 100
102 fn_buf.push_str("}"); 101fn fn_name(call: &CallExpr) -> Option<ast::Name> {
103 102 let name = call.expr()?.syntax().to_string();
104 let cursor_pos = self.start_offset + offset; 103 Some(ast::make::name(&name))
105 FunctionTemplate { 104}
106 fn_text: fn_buf, 105
107 cursor_offset: cursor_pos, 106/// Computes the type variables and arguments required for the generated function
108 insert_offset: self.start_offset, 107fn fn_args(
109 } 108 ctx: &AssistCtx,
110 } 109 call: &CallExpr,
111} 110) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
112
113fn fn_name(call: &CallExpr) -> Option<String> {
114 Some(call.expr()?.syntax().to_string())
115}
116
117fn fn_generics(_call: &CallExpr) -> Option<String> {
118 // TODO
119 Some("".into())
120}
121
122fn fn_args(ctx: &AssistCtx, call: &CallExpr) -> Option<String> {
123 let mut arg_names = Vec::new(); 111 let mut arg_names = Vec::new();
124 let mut arg_types = Vec::new(); 112 let mut arg_types = Vec::new();
125 for arg in call.arg_list()?.args() { 113 for arg in call.arg_list()?.args() {
@@ -134,15 +122,8 @@ fn fn_args(ctx: &AssistCtx, call: &CallExpr) -> Option<String> {
134 }); 122 });
135 } 123 }
136 deduplicate_arg_names(&mut arg_names); 124 deduplicate_arg_names(&mut arg_names);
137 Some(format!( 125 let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty));
138 "({})", 126 Some((None, ast::make::param_list(params)))
139 arg_names
140 .into_iter()
141 .zip(arg_types)
142 .map(|(name, ty)| format!("{}: {}", name, ty))
143 .collect::<Vec<_>>()
144 .join(", ")
145 ))
146} 127}
147 128
148/// Makes duplicate argument names unique by appending incrementing numbers. 129/// Makes duplicate argument names unique by appending incrementing numbers.
@@ -203,7 +184,7 @@ fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> {
203/// directly after the current block 184/// directly after the current block
204/// We want to write the generated function directly after 185/// We want to write the generated function directly after
205/// fns, impls or macro calls, but inside mods 186/// fns, impls or macro calls, but inside mods
206fn next_space_for_fn(expr: &CallExpr) -> Option<(TextUnit, Option<SmolStr>)> { 187fn next_space_for_fn(expr: &CallExpr) -> Option<SyntaxNode> {
207 let mut ancestors = expr.syntax().ancestors().peekable(); 188 let mut ancestors = expr.syntax().ancestors().peekable();
208 let mut last_ancestor: Option<SyntaxNode> = None; 189 let mut last_ancestor: Option<SyntaxNode> = None;
209 while let Some(next_ancestor) = ancestors.next() { 190 while let Some(next_ancestor) = ancestors.next() {
@@ -220,7 +201,7 @@ fn next_space_for_fn(expr: &CallExpr) -> Option<(TextUnit, Option<SmolStr>)> {
220 } 201 }
221 last_ancestor = Some(next_ancestor); 202 last_ancestor = Some(next_ancestor);
222 } 203 }
223 last_ancestor.map(|a| (a.text_range().end(), leading_indent(&a))) 204 last_ancestor
224} 205}
225 206
226#[cfg(test)] 207#[cfg(test)]
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 0c908573d..dbb9c8a9b 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -269,6 +269,31 @@ pub fn unreachable_macro_call() -> ast::MacroCall {
269 ast_from_text(&format!("unreachable!()")) 269 ast_from_text(&format!("unreachable!()"))
270} 270}
271 271
272pub fn param(name: String, ty: String) -> ast::Param {
273 ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty))
274}
275
276pub 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
281pub 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
292pub fn add_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile {
293 let newlines = "\n".repeat(amount_of_newlines);
294 ast_from_text(&format!("{}{}", newlines, t.syntax()))
295}
296
272fn ast_from_text<N: AstNode>(text: &str) -> N { 297fn ast_from_text<N: AstNode>(text: &str) -> N {
273 let parse = SourceFile::parse(text); 298 let parse = SourceFile::parse(text);
274 let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); 299 let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap();