diff options
-rw-r--r-- | crates/ra_editor/src/typing.rs | 79 |
1 files changed, 64 insertions, 15 deletions
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 3384389d1..6c1a91ffb 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -30,6 +30,7 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | |||
30 | } else { | 30 | } else { |
31 | range | 31 | range |
32 | }; | 32 | }; |
33 | |||
33 | let node = find_covering_node(file.syntax(), range); | 34 | let node = find_covering_node(file.syntax(), range); |
34 | let mut edit = EditBuilder::new(); | 35 | let mut edit = EditBuilder::new(); |
35 | for node in node.descendants() { | 36 | for node in node.descendants() { |
@@ -140,30 +141,71 @@ fn remove_newline( | |||
140 | offset: TextUnit, | 141 | offset: TextUnit, |
141 | ) { | 142 | ) { |
142 | if node.kind() == WHITESPACE && node_text.bytes().filter(|&b| b == b'\n').count() == 1 { | 143 | if node.kind() == WHITESPACE && node_text.bytes().filter(|&b| b == b'\n').count() == 1 { |
144 | // Special case that turns something like: | ||
145 | // | ||
146 | // ``` | ||
147 | // my_function({<|> | ||
148 | // <some-expr> | ||
149 | // }) | ||
150 | // ``` | ||
151 | // | ||
152 | // into `my_function(<some-expr>)` | ||
143 | if join_single_expr_block(edit, node).is_some() { | 153 | if join_single_expr_block(edit, node).is_some() { |
144 | return | 154 | return |
145 | } | 155 | } |
146 | match (node.prev_sibling(), node.next_sibling()) { | 156 | |
147 | (Some(prev), Some(next)) => { | 157 | if let (Some(prev), Some(next)) = (node.prev_sibling(), node.next_sibling()) { |
148 | let range = TextRange::from_to(prev.range().start(), node.range().end()); | 158 | let range = TextRange::from_to(prev.range().start(), node.range().end()); |
149 | if is_trailing_comma(prev.kind(), next.kind()) { | 159 | if is_trailing_comma(prev.kind(), next.kind()) { |
150 | edit.delete(range); | 160 | // Removes: trailing comma, newline (incl. surrounding whitespace) |
151 | } else if no_space_required(prev.kind(), next.kind()) { | 161 | edit.delete(range); |
152 | edit.delete(node.range()); | 162 | } else if no_space_required(prev.kind(), next.kind()) { |
153 | } else if prev.kind() == COMMA && next.kind() == R_CURLY { | 163 | // Removes: newline (incl. surrounding whitespace) |
154 | edit.replace(range, " ".to_string()); | 164 | edit.delete(node.range()); |
165 | } else if prev.kind() == COMMA && next.kind() == R_CURLY { | ||
166 | // Removes: comma, newline (incl. surrounding whitespace) | ||
167 | // Adds: a single whitespace | ||
168 | edit.replace(range, " ".to_string()); | ||
169 | } else if prev.kind() == COMMENT && next.kind() == COMMENT { | ||
170 | // Removes: newline (incl. surrounding whitespace), start of the next comment | ||
171 | |||
172 | // FIXME: I guess it is safe to unwrap here? A comment always has text, right? | ||
173 | let comment_text = next.leaf_text().unwrap().as_str(); | ||
174 | let comment_start_length = comment_start_length(comment_text); | ||
175 | |||
176 | if let Some(newline_pos) = comment_text.find('\n') { | ||
177 | // Special case for multi-line c-like comments: join the comment content but | ||
178 | // keep the leading `/*` | ||
179 | |||
180 | let newline_offset = next.range().start() | ||
181 | + TextUnit::from(newline_pos as u32) | ||
182 | + TextUnit::of_char('\n'); | ||
183 | |||
184 | edit.insert(newline_offset, "/*".to_string()); | ||
185 | edit.delete(TextRange::from_to( | ||
186 | node.range().start(), | ||
187 | next.range().start() + comment_start_length | ||
188 | )); | ||
155 | } else { | 189 | } else { |
156 | edit.replace( | 190 | // Single-line comments |
157 | node.range(), | 191 | edit.delete(TextRange::from_to( |
158 | compute_ws(prev, next).to_string(), | 192 | node.range().start(), |
159 | ); | 193 | next.range().start() + comment_start_length |
194 | )); | ||
160 | } | 195 | } |
161 | return; | 196 | } else { |
197 | // Remove newline but add a computed amount of whitespace characters | ||
198 | edit.replace( | ||
199 | node.range(), | ||
200 | compute_ws(prev, next).to_string(), | ||
201 | ); | ||
162 | } | 202 | } |
163 | _ => (), | 203 | |
204 | return; | ||
164 | } | 205 | } |
165 | } | 206 | } |
166 | 207 | ||
208 | // FIXME: do we ever reach this point? What does it mean to be here? I think we should document it | ||
167 | let suff = &node_text[TextRange::from_to( | 209 | let suff = &node_text[TextRange::from_to( |
168 | offset - node.range().start() + TextUnit::of_char('\n'), | 210 | offset - node.range().start() + TextUnit::of_char('\n'), |
169 | TextUnit::of_str(node_text), | 211 | TextUnit::of_str(node_text), |
@@ -176,6 +218,13 @@ fn remove_newline( | |||
176 | ); | 218 | ); |
177 | } | 219 | } |
178 | 220 | ||
221 | // Return the start length of the comment (e.g. 2 for `//` and 3 for `//!`) | ||
222 | fn comment_start_length(_text: &str) -> TextUnit { | ||
223 | // TODO: use the parser here instead of reimplementing comment parsing? | ||
224 | // Otherwise, reimplement comment parsing :) | ||
225 | return TextUnit::from(2); | ||
226 | } | ||
227 | |||
179 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { | 228 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { |
180 | match (left, right) { | 229 | match (left, right) { |
181 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, | 230 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, |