diff options
Diffstat (limited to 'crates/ra_ide_api/src/join_lines.rs')
-rw-r--r-- | crates/ra_ide_api/src/join_lines.rs | 81 |
1 files changed, 41 insertions, 40 deletions
diff --git a/crates/ra_ide_api/src/join_lines.rs b/crates/ra_ide_api/src/join_lines.rs index 8fb3eaa06..598717311 100644 --- a/crates/ra_ide_api/src/join_lines.rs +++ b/crates/ra_ide_api/src/join_lines.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | use itertools::Itertools; | 1 | use itertools::Itertools; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | SourceFile, TextRange, TextUnit, AstNode, SyntaxNode, | 3 | SourceFile, TextRange, TextUnit, SyntaxNode, SyntaxElement, SyntaxToken, |
4 | SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK}, | 4 | SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK}, |
5 | algo::{find_covering_node, non_trivia_sibling}, | 5 | algo::{find_covering_element, non_trivia_sibling}, |
6 | ast, | 6 | ast::{self, AstNode, AstToken}, |
7 | Direction, | 7 | Direction, |
8 | }; | 8 | }; |
9 | use ra_fmt::{ | 9 | use ra_fmt::{ |
@@ -24,22 +24,22 @@ pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { | |||
24 | range | 24 | range |
25 | }; | 25 | }; |
26 | 26 | ||
27 | let node = find_covering_node(file.syntax(), range); | 27 | let node = match find_covering_element(file.syntax(), range) { |
28 | SyntaxElement::Node(node) => node, | ||
29 | SyntaxElement::Token(token) => token.parent(), | ||
30 | }; | ||
28 | let mut edit = TextEditBuilder::default(); | 31 | let mut edit = TextEditBuilder::default(); |
29 | for node in node.descendants() { | 32 | for token in node.descendants_with_tokens().filter_map(|it| it.as_token()) { |
30 | let text = match node.leaf_text() { | 33 | let range = match range.intersection(&token.range()) { |
31 | Some(text) => text, | ||
32 | None => continue, | ||
33 | }; | ||
34 | let range = match range.intersection(&node.range()) { | ||
35 | Some(range) => range, | 34 | Some(range) => range, |
36 | None => continue, | 35 | None => continue, |
37 | } - node.range().start(); | 36 | } - token.range().start(); |
37 | let text = token.text(); | ||
38 | for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') { | 38 | for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') { |
39 | let pos: TextUnit = (pos as u32).into(); | 39 | let pos: TextUnit = (pos as u32).into(); |
40 | let off = node.range().start() + range.start() + pos; | 40 | let off = token.range().start() + range.start() + pos; |
41 | if !edit.invalidates_offset(off) { | 41 | if !edit.invalidates_offset(off) { |
42 | remove_newline(&mut edit, node, text.as_str(), off); | 42 | remove_newline(&mut edit, token, off); |
43 | } | 43 | } |
44 | } | 44 | } |
45 | } | 45 | } |
@@ -47,17 +47,12 @@ pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { | |||
47 | edit.finish() | 47 | edit.finish() |
48 | } | 48 | } |
49 | 49 | ||
50 | fn remove_newline( | 50 | fn remove_newline(edit: &mut TextEditBuilder, token: SyntaxToken, offset: TextUnit) { |
51 | edit: &mut TextEditBuilder, | 51 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { |
52 | node: &SyntaxNode, | ||
53 | node_text: &str, | ||
54 | offset: TextUnit, | ||
55 | ) { | ||
56 | if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 { | ||
57 | // The node is either the first or the last in the file | 52 | // The node is either the first or the last in the file |
58 | let suff = &node_text[TextRange::from_to( | 53 | let suff = &token.text()[TextRange::from_to( |
59 | offset - node.range().start() + TextUnit::of_char('\n'), | 54 | offset - token.range().start() + TextUnit::of_char('\n'), |
60 | TextUnit::of_str(node_text), | 55 | TextUnit::of_str(token.text()), |
61 | )]; | 56 | )]; |
62 | let spaces = suff.bytes().take_while(|&b| b == b' ').count(); | 57 | let spaces = suff.bytes().take_while(|&b| b == b' ').count(); |
63 | 58 | ||
@@ -74,7 +69,7 @@ fn remove_newline( | |||
74 | // ``` | 69 | // ``` |
75 | // | 70 | // |
76 | // into `my_function(<some-expr>)` | 71 | // into `my_function(<some-expr>)` |
77 | if join_single_expr_block(edit, node).is_some() { | 72 | if join_single_expr_block(edit, token).is_some() { |
78 | return; | 73 | return; |
79 | } | 74 | } |
80 | // ditto for | 75 | // ditto for |
@@ -84,44 +79,50 @@ fn remove_newline( | |||
84 | // bar | 79 | // bar |
85 | // }; | 80 | // }; |
86 | // ``` | 81 | // ``` |
87 | if join_single_use_tree(edit, node).is_some() { | 82 | if join_single_use_tree(edit, token).is_some() { |
88 | return; | 83 | return; |
89 | } | 84 | } |
90 | 85 | ||
91 | // The node is between two other nodes | 86 | // The node is between two other nodes |
92 | let prev = node.prev_sibling().unwrap(); | 87 | let prev = token.prev_sibling_or_token().unwrap(); |
93 | let next = node.next_sibling().unwrap(); | 88 | let next = token.next_sibling_or_token().unwrap(); |
94 | if is_trailing_comma(prev.kind(), next.kind()) { | 89 | if is_trailing_comma(prev.kind(), next.kind()) { |
95 | // Removes: trailing comma, newline (incl. surrounding whitespace) | 90 | // Removes: trailing comma, newline (incl. surrounding whitespace) |
96 | edit.delete(TextRange::from_to(prev.range().start(), node.range().end())); | 91 | edit.delete(TextRange::from_to(prev.range().start(), token.range().end())); |
97 | } else if prev.kind() == COMMA && next.kind() == R_CURLY { | 92 | } else if prev.kind() == COMMA && next.kind() == R_CURLY { |
98 | // Removes: comma, newline (incl. surrounding whitespace) | 93 | // Removes: comma, newline (incl. surrounding whitespace) |
99 | let space = if let Some(left) = prev.prev_sibling() { compute_ws(left, next) } else { " " }; | 94 | let space = if let Some(left) = prev.prev_sibling_or_token() { |
95 | compute_ws(left.kind(), next.kind()) | ||
96 | } else { | ||
97 | " " | ||
98 | }; | ||
100 | edit.replace( | 99 | edit.replace( |
101 | TextRange::from_to(prev.range().start(), node.range().end()), | 100 | TextRange::from_to(prev.range().start(), token.range().end()), |
102 | space.to_string(), | 101 | space.to_string(), |
103 | ); | 102 | ); |
104 | } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) { | 103 | } else if let (Some(_), Some(next)) = |
104 | (prev.as_token().and_then(ast::Comment::cast), next.as_token().and_then(ast::Comment::cast)) | ||
105 | { | ||
105 | // Removes: newline (incl. surrounding whitespace), start of the next comment | 106 | // Removes: newline (incl. surrounding whitespace), start of the next comment |
106 | edit.delete(TextRange::from_to( | 107 | edit.delete(TextRange::from_to( |
107 | node.range().start(), | 108 | token.range().start(), |
108 | next.syntax().range().start() + TextUnit::of_str(next.prefix()), | 109 | next.syntax().range().start() + TextUnit::of_str(next.prefix()), |
109 | )); | 110 | )); |
110 | } else { | 111 | } else { |
111 | // Remove newline but add a computed amount of whitespace characters | 112 | // Remove newline but add a computed amount of whitespace characters |
112 | edit.replace(node.range(), compute_ws(prev, next).to_string()); | 113 | edit.replace(token.range(), compute_ws(prev.kind(), next.kind()).to_string()); |
113 | } | 114 | } |
114 | } | 115 | } |
115 | 116 | ||
116 | fn has_comma_after(node: &SyntaxNode) -> bool { | 117 | fn has_comma_after(node: &SyntaxNode) -> bool { |
117 | match non_trivia_sibling(node, Direction::Next) { | 118 | match non_trivia_sibling(node.into(), Direction::Next) { |
118 | Some(n) => n.kind() == COMMA, | 119 | Some(n) => n.kind() == COMMA, |
119 | _ => false, | 120 | _ => false, |
120 | } | 121 | } |
121 | } | 122 | } |
122 | 123 | ||
123 | fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> { | 124 | fn join_single_expr_block(edit: &mut TextEditBuilder, token: SyntaxToken) -> Option<()> { |
124 | let block = ast::Block::cast(node.parent()?)?; | 125 | let block = ast::Block::cast(token.parent())?; |
125 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; | 126 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; |
126 | let expr = extract_trivial_expression(block)?; | 127 | let expr = extract_trivial_expression(block)?; |
127 | 128 | ||
@@ -140,8 +141,8 @@ fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Opti | |||
140 | Some(()) | 141 | Some(()) |
141 | } | 142 | } |
142 | 143 | ||
143 | fn join_single_use_tree(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> { | 144 | fn join_single_use_tree(edit: &mut TextEditBuilder, token: SyntaxToken) -> Option<()> { |
144 | let use_tree_list = ast::UseTreeList::cast(node.parent()?)?; | 145 | let use_tree_list = ast::UseTreeList::cast(token.parent())?; |
145 | let (tree,) = use_tree_list.use_trees().collect_tuple()?; | 146 | let (tree,) = use_tree_list.use_trees().collect_tuple()?; |
146 | edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string()); | 147 | edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string()); |
147 | Some(()) | 148 | Some(()) |
@@ -401,13 +402,13 @@ use ra_syntax::{ | |||
401 | r" | 402 | r" |
402 | use ra_syntax::{ | 403 | use ra_syntax::{ |
403 | algo::<|>{ | 404 | algo::<|>{ |
404 | find_leaf_at_offset, | 405 | find_token_at_offset, |
405 | }, | 406 | }, |
406 | ast, | 407 | ast, |
407 | };", | 408 | };", |
408 | r" | 409 | r" |
409 | use ra_syntax::{ | 410 | use ra_syntax::{ |
410 | algo::<|>find_leaf_at_offset, | 411 | algo::<|>find_token_at_offset, |
411 | ast, | 412 | ast, |
412 | };", | 413 | };", |
413 | ); | 414 | ); |