diff options
author | Aleksey Kladov <[email protected]> | 2020-05-19 22:12:01 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-05-20 00:30:12 +0100 |
commit | e6fc0bdffb213f6e94c5bb4081e6d175ccbd518f (patch) | |
tree | 13e97077b33a25d1923d946dfbcf636fea838f9f | |
parent | 8eb3272ad6f774bccb967ee640b72a9a17273e7b (diff) |
Moderate cleanup of add_function
-rw-r--r-- | crates/ra_assists/src/handlers/add_function.rs | 80 | ||||
-rw-r--r-- | crates/ra_assists/src/utils.rs | 8 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 36 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 44 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 25 |
5 files changed, 128 insertions, 65 deletions
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 69fede00f..a0709630d 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -4,13 +4,13 @@ use ra_syntax::{ | |||
4 | ast::{ | 4 | ast::{ |
5 | self, | 5 | self, |
6 | edit::{AstNodeEdit, IndentLevel}, | 6 | edit::{AstNodeEdit, IndentLevel}, |
7 | ArgListOwner, AstNode, ModuleItemOwner, | 7 | make, ArgListOwner, AstNode, ModuleItemOwner, |
8 | }, | 8 | }, |
9 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
10 | }; | 10 | }; |
11 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
12 | 12 | ||
13 | use crate::{utils::render_snippet, AssistContext, AssistId, Assists}; | 13 | use crate::{assist_config::SnippetCap, utils::render_snippet, AssistContext, AssistId, Assists}; |
14 | 14 | ||
15 | // Assist: add_function | 15 | // Assist: add_function |
16 | // | 16 | // |
@@ -61,27 +61,33 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
61 | acc.add(AssistId("add_function"), "Add function", target, |builder| { | 61 | acc.add(AssistId("add_function"), "Add function", target, |builder| { |
62 | let function_template = function_builder.render(); | 62 | let function_template = function_builder.render(); |
63 | builder.set_file(function_template.file); | 63 | builder.set_file(function_template.file); |
64 | let new_fn = function_template.to_string(ctx.config.snippet_cap); | ||
64 | match ctx.config.snippet_cap { | 65 | match ctx.config.snippet_cap { |
65 | Some(cap) => { | 66 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), |
66 | let snippet = render_snippet( | 67 | None => builder.insert(function_template.insert_offset, new_fn), |
67 | function_template.fn_def.syntax(), | ||
68 | function_template.placeholder_expr.syntax(), | ||
69 | ); | ||
70 | builder.insert_snippet(cap, function_template.insert_offset, snippet) | ||
71 | } | ||
72 | None => builder | ||
73 | .insert(function_template.insert_offset, function_template.fn_def.to_string()), | ||
74 | } | 68 | } |
75 | }) | 69 | }) |
76 | } | 70 | } |
77 | 71 | ||
78 | struct FunctionTemplate { | 72 | struct FunctionTemplate { |
79 | insert_offset: TextSize, | 73 | insert_offset: TextSize, |
80 | fn_def: ast::SourceFile, | ||
81 | placeholder_expr: ast::MacroCall, | 74 | placeholder_expr: ast::MacroCall, |
75 | leading_ws: String, | ||
76 | fn_def: ast::FnDef, | ||
77 | trailing_ws: String, | ||
82 | file: FileId, | 78 | file: FileId, |
83 | } | 79 | } |
84 | 80 | ||
81 | impl FunctionTemplate { | ||
82 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | ||
83 | let f = match cap { | ||
84 | Some(cap) => render_snippet(cap, self.fn_def.syntax(), self.placeholder_expr.syntax()), | ||
85 | None => self.fn_def.to_string(), | ||
86 | }; | ||
87 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | ||
88 | } | ||
89 | } | ||
90 | |||
85 | struct FunctionBuilder { | 91 | struct FunctionBuilder { |
86 | target: GeneratedFunctionTarget, | 92 | target: GeneratedFunctionTarget, |
87 | fn_name: ast::Name, | 93 | fn_name: ast::Name, |
@@ -119,33 +125,41 @@ impl FunctionBuilder { | |||
119 | } | 125 | } |
120 | 126 | ||
121 | fn render(self) -> FunctionTemplate { | 127 | fn render(self) -> FunctionTemplate { |
122 | let placeholder_expr = ast::make::expr_todo(); | 128 | let placeholder_expr = make::expr_todo(); |
123 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); | 129 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
124 | let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); | 130 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
125 | if self.needs_pub { | 131 | let mut fn_def = |
126 | fn_def = ast::make::add_pub_crate_modifier(fn_def); | 132 | make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); |
127 | } | 133 | let leading_ws; |
128 | 134 | let trailing_ws; | |
129 | let (fn_def, insert_offset) = match self.target { | 135 | |
136 | let insert_offset = match self.target { | ||
130 | GeneratedFunctionTarget::BehindItem(it) => { | 137 | GeneratedFunctionTarget::BehindItem(it) => { |
131 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 138 | let indent = IndentLevel::from_node(&it); |
132 | let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); | 139 | leading_ws = format!("\n\n{}", indent); |
133 | (indented, it.text_range().end()) | 140 | fn_def = fn_def.indent(indent); |
141 | trailing_ws = String::new(); | ||
142 | it.text_range().end() | ||
134 | } | 143 | } |
135 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 144 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
136 | let indent_once = IndentLevel(1); | ||
137 | let indent = IndentLevel::from_node(it.syntax()); | 145 | let indent = IndentLevel::from_node(it.syntax()); |
138 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 146 | leading_ws = format!("\n{}", indent + 1); |
139 | let fn_def = fn_def.indent(indent_once); | 147 | fn_def = fn_def.indent(indent + 1); |
140 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 148 | trailing_ws = format!("\n{}", indent); |
141 | let fn_def = fn_def.indent(indent); | 149 | it.syntax().text_range().start() + TextSize::of('{') |
142 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | ||
143 | } | 150 | } |
144 | }; | 151 | }; |
145 | 152 | ||
146 | let placeholder_expr = | 153 | let placeholder_expr = |
147 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 154 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
148 | FunctionTemplate { insert_offset, placeholder_expr, fn_def, file: self.file } | 155 | FunctionTemplate { |
156 | insert_offset, | ||
157 | placeholder_expr, | ||
158 | leading_ws, | ||
159 | fn_def, | ||
160 | trailing_ws, | ||
161 | file: self.file, | ||
162 | } | ||
149 | } | 163 | } |
150 | } | 164 | } |
151 | 165 | ||
@@ -165,7 +179,7 @@ impl GeneratedFunctionTarget { | |||
165 | 179 | ||
166 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 180 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
167 | let name = call.segment()?.syntax().to_string(); | 181 | let name = call.segment()?.syntax().to_string(); |
168 | Some(ast::make::name(&name)) | 182 | Some(make::name(&name)) |
169 | } | 183 | } |
170 | 184 | ||
171 | /// Computes the type variables and arguments required for the generated function | 185 | /// Computes the type variables and arguments required for the generated function |
@@ -187,8 +201,8 @@ fn fn_args( | |||
187 | }); | 201 | }); |
188 | } | 202 | } |
189 | deduplicate_arg_names(&mut arg_names); | 203 | deduplicate_arg_names(&mut arg_names); |
190 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); | 204 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); |
191 | Some((None, ast::make::param_list(params))) | 205 | Some((None, make::param_list(params))) |
192 | } | 206 | } |
193 | 207 | ||
194 | /// Makes duplicate argument names unique by appending incrementing numbers. | 208 | /// Makes duplicate argument names unique by appending incrementing numbers. |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index bb9749b06..8a26a6808 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -11,9 +11,15 @@ use ra_syntax::{ | |||
11 | }; | 11 | }; |
12 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
13 | 13 | ||
14 | use crate::assist_config::SnippetCap; | ||
15 | |||
14 | pub(crate) use insert_use::insert_use_statement; | 16 | pub(crate) use insert_use::insert_use_statement; |
15 | 17 | ||
16 | pub(crate) fn render_snippet(node: &SyntaxNode, placeholder: &SyntaxNode) -> String { | 18 | pub(crate) fn render_snippet( |
19 | _cap: SnippetCap, | ||
20 | node: &SyntaxNode, | ||
21 | placeholder: &SyntaxNode, | ||
22 | ) -> String { | ||
17 | assert!(placeholder.ancestors().any(|it| it == *node)); | 23 | assert!(placeholder.ancestors().any(|it| it == *node)); |
18 | let range = placeholder.text_range() - node.text_range().start(); | 24 | let range = placeholder.text_range() - node.text_range().start(); |
19 | let range: ops::Range<usize> = range.into(); | 25 | let range: ops::Range<usize> = range.into(); |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 2a8dac757..664894d1f 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> { | |||
266 | let replacement = Replacement::Single(with.clone().into()); | 266 | let replacement = Replacement::Single(with.clone().into()); |
267 | self.replacements.insert(what, replacement); | 267 | self.replacements.insert(what, replacement); |
268 | } | 268 | } |
269 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
270 | &mut self, | ||
271 | what: &T, | ||
272 | with: Vec<SyntaxElement>, | ||
273 | ) { | ||
274 | let what = what.clone().into(); | ||
275 | let replacement = Replacement::Many(with); | ||
276 | self.replacements.insert(what, replacement); | ||
277 | } | ||
269 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | 278 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { |
270 | self.replace(what.syntax(), with.syntax()) | 279 | self.replace(what.syntax(), with.syntax()) |
271 | } | 280 | } |
@@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> { | |||
302 | 311 | ||
303 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 312 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
304 | // FIXME: this could be made much faster. | 313 | // FIXME: this could be made much faster. |
305 | let new_children = | 314 | let mut new_children = Vec::new(); |
306 | node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>(); | 315 | for child in node.children_with_tokens() { |
316 | self.rewrite_self(&mut new_children, &child); | ||
317 | } | ||
307 | with_children(node, new_children) | 318 | with_children(node, new_children) |
308 | } | 319 | } |
309 | 320 | ||
310 | fn rewrite_self( | 321 | fn rewrite_self( |
311 | &self, | 322 | &self, |
323 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | ||
312 | element: &SyntaxElement, | 324 | element: &SyntaxElement, |
313 | ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> { | 325 | ) { |
314 | if let Some(replacement) = self.replacement(&element) { | 326 | if let Some(replacement) = self.replacement(&element) { |
315 | return match replacement { | 327 | match replacement { |
316 | Replacement::Single(NodeOrToken::Node(it)) => { | 328 | Replacement::Single(NodeOrToken::Node(it)) => { |
317 | Some(NodeOrToken::Node(it.green().clone())) | 329 | acc.push(NodeOrToken::Node(it.green().clone())) |
318 | } | 330 | } |
319 | Replacement::Single(NodeOrToken::Token(it)) => { | 331 | Replacement::Single(NodeOrToken::Token(it)) => { |
320 | Some(NodeOrToken::Token(it.green().clone())) | 332 | acc.push(NodeOrToken::Token(it.green().clone())) |
321 | } | 333 | } |
322 | Replacement::Delete => None, | 334 | Replacement::Many(replacements) => { |
335 | acc.extend(replacements.iter().map(|it| match it { | ||
336 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
337 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
338 | })) | ||
339 | } | ||
340 | Replacement::Delete => (), | ||
323 | }; | 341 | }; |
342 | return; | ||
324 | } | 343 | } |
325 | let res = match element { | 344 | let res = match element { |
326 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 345 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), |
327 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), | 346 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), |
328 | }; | 347 | }; |
329 | Some(res) | 348 | acc.push(res) |
330 | } | 349 | } |
331 | } | 350 | } |
332 | 351 | ||
@@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> { | |||
341 | enum Replacement { | 360 | enum Replacement { |
342 | Delete, | 361 | Delete, |
343 | Single(SyntaxElement), | 362 | Single(SyntaxElement), |
363 | Many(Vec<SyntaxElement>), | ||
344 | } | 364 | } |
345 | 365 | ||
346 | fn with_children( | 366 | fn with_children( |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 24a1e1d91..29eb3fcb9 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | //! This module contains functions for editing syntax trees. As the trees are | 1 | //! This module contains functions for editing syntax trees. As the trees are |
2 | //! immutable, all function here return a fresh copy of the tree, instead of | 2 | //! immutable, all function here return a fresh copy of the tree, instead of |
3 | //! doing an in-place modification. | 3 | //! doing an in-place modification. |
4 | use std::{iter, ops::RangeInclusive}; | 4 | use std::{ |
5 | fmt, iter, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
5 | 8 | ||
6 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
7 | 10 | ||
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel { | |||
437 | } | 440 | } |
438 | } | 441 | } |
439 | 442 | ||
443 | impl fmt::Display for IndentLevel { | ||
444 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
445 | let spaces = " "; | ||
446 | let buf; | ||
447 | let len = self.0 as usize * 4; | ||
448 | let indent = if len <= spaces.len() { | ||
449 | &spaces[..len] | ||
450 | } else { | ||
451 | buf = iter::repeat(' ').take(len).collect::<String>(); | ||
452 | &buf | ||
453 | }; | ||
454 | fmt::Display::fmt(indent, f) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl ops::Add<u8> for IndentLevel { | ||
459 | type Output = IndentLevel; | ||
460 | fn add(self, rhs: u8) -> IndentLevel { | ||
461 | IndentLevel(self.0 + rhs) | ||
462 | } | ||
463 | } | ||
464 | |||
440 | impl IndentLevel { | 465 | impl IndentLevel { |
441 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { | 466 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { |
442 | let first_token = match node.first_token() { | 467 | let first_token = match node.first_token() { |
@@ -453,6 +478,14 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 478 | IndentLevel(0) |
454 | } | 479 | } |
455 | 480 | ||
481 | /// XXX: this intentionally doesn't change the indent of the very first token. | ||
482 | /// Ie, in something like | ||
483 | /// ``` | ||
484 | /// fn foo() { | ||
485 | /// 92 | ||
486 | /// } | ||
487 | /// ``` | ||
488 | /// if you indent the block, the `{` token would stay put. | ||
456 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 489 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
457 | let mut rewriter = SyntaxRewriter::default(); | 490 | let mut rewriter = SyntaxRewriter::default(); |
458 | node.descendants_with_tokens() | 491 | node.descendants_with_tokens() |
@@ -463,12 +496,7 @@ impl IndentLevel { | |||
463 | text.contains('\n') | 496 | text.contains('\n') |
464 | }) | 497 | }) |
465 | .for_each(|ws| { | 498 | .for_each(|ws| { |
466 | let new_ws = make::tokens::whitespace(&format!( | 499 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); |
467 | "{}{:width$}", | ||
468 | ws.syntax().text(), | ||
469 | "", | ||
470 | width = self.0 as usize * 4 | ||
471 | )); | ||
472 | rewriter.replace(ws.syntax(), &new_ws) | 500 | rewriter.replace(ws.syntax(), &new_ws) |
473 | }); | 501 | }); |
474 | rewriter.rewrite(&node) | 502 | rewriter.rewrite(&node) |
@@ -485,7 +513,7 @@ impl IndentLevel { | |||
485 | }) | 513 | }) |
486 | .for_each(|ws| { | 514 | .for_each(|ws| { |
487 | let new_ws = make::tokens::whitespace( | 515 | let new_ws = make::tokens::whitespace( |
488 | &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), | 516 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
489 | ); | 517 | ); |
490 | rewriter.replace(ws.syntax(), &new_ws) | 518 | rewriter.replace(ws.syntax(), &new_ws) |
491 | }); | 519 | }); |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index d0e960fb4..b275780ec 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -277,7 +277,12 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList | |||
277 | ast_from_text(&format!("fn f({}) {{ }}", args)) | 277 | ast_from_text(&format!("fn f({}) {{ }}", args)) |
278 | } | 278 | } |
279 | 279 | ||
280 | pub fn visibility_pub_crate() -> ast::Visibility { | ||
281 | ast_from_text("pub(crate) struct S") | ||
282 | } | ||
283 | |||
280 | pub fn fn_def( | 284 | pub fn fn_def( |
285 | visibility: Option<ast::Visibility>, | ||
281 | fn_name: ast::Name, | 286 | fn_name: ast::Name, |
282 | type_params: Option<ast::TypeParamList>, | 287 | type_params: Option<ast::TypeParamList>, |
283 | params: ast::ParamList, | 288 | params: ast::ParamList, |
@@ -285,21 +290,11 @@ pub fn fn_def( | |||
285 | ) -> ast::FnDef { | 290 | ) -> ast::FnDef { |
286 | let type_params = | 291 | let type_params = |
287 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | 292 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; |
288 | ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) | 293 | let visibility = match visibility { |
289 | } | 294 | None => String::new(), |
290 | 295 | Some(it) => format!("{} ", it), | |
291 | pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | 296 | }; |
292 | let newlines = "\n".repeat(amount_of_newlines); | 297 | ast_from_text(&format!("{}fn {}{}{} {}", visibility, fn_name, type_params, params, body)) |
293 | ast_from_text(&format!("{}{}", newlines, t.syntax())) | ||
294 | } | ||
295 | |||
296 | pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | ||
297 | let newlines = "\n".repeat(amount_of_newlines); | ||
298 | ast_from_text(&format!("{}{}", t.syntax(), newlines)) | ||
299 | } | ||
300 | |||
301 | pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef { | ||
302 | ast_from_text(&format!("pub(crate) {}", fn_def)) | ||
303 | } | 298 | } |
304 | 299 | ||
305 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 300 | fn ast_from_text<N: AstNode>(text: &str) -> N { |