aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/join_lines.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/join_lines.rs')
-rw-r--r--crates/ra_ide_api/src/join_lines.rs79
1 files changed, 40 insertions, 39 deletions
diff --git a/crates/ra_ide_api/src/join_lines.rs b/crates/ra_ide_api/src/join_lines.rs
index 8fb3eaa06..57b6f8384 100644
--- a/crates/ra_ide_api/src/join_lines.rs
+++ b/crates/ra_ide_api/src/join_lines.rs
@@ -1,8 +1,8 @@
1use itertools::Itertools; 1use itertools::Itertools;
2use ra_syntax::{ 2use ra_syntax::{
3 SourceFile, TextRange, TextUnit, AstNode, SyntaxNode, 3 SourceFile, TextRange, TextUnit, AstNode, 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,
7 Direction, 7 Direction,
8}; 8};
@@ -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
50fn remove_newline( 50fn 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
116fn has_comma_after(node: &SyntaxNode) -> bool { 117fn 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
123fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> { 124fn 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
143fn join_single_use_tree(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> { 144fn 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"
402use ra_syntax::{ 403use 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"
409use ra_syntax::{ 410use ra_syntax::{
410 algo::<|>find_leaf_at_offset, 411 algo::<|>find_token_at_offset,
411 ast, 412 ast,
412};", 413};",
413 ); 414 );