diff options
Diffstat (limited to 'crates/libeditor/src/typing.rs')
-rw-r--r-- | crates/libeditor/src/typing.rs | 348 |
1 files changed, 0 insertions, 348 deletions
diff --git a/crates/libeditor/src/typing.rs b/crates/libeditor/src/typing.rs deleted file mode 100644 index 826b16181..000000000 --- a/crates/libeditor/src/typing.rs +++ /dev/null | |||
@@ -1,348 +0,0 @@ | |||
1 | use std::mem; | ||
2 | |||
3 | use libsyntax2::{ | ||
4 | TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind, | ||
5 | ast, | ||
6 | algo::{ | ||
7 | walk::preorder, | ||
8 | find_covering_node, | ||
9 | }, | ||
10 | text_utils::{intersect, contains_offset_nonstrict}, | ||
11 | SyntaxKind::*, | ||
12 | }; | ||
13 | |||
14 | use {LocalEdit, EditBuilder, find_node_at_offset}; | ||
15 | |||
16 | pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | ||
17 | let range = if range.is_empty() { | ||
18 | let syntax = file.syntax(); | ||
19 | let text = syntax.text().slice(range.start()..); | ||
20 | let pos = match text.find('\n') { | ||
21 | None => return LocalEdit { | ||
22 | edit: EditBuilder::new().finish(), | ||
23 | cursor_position: None | ||
24 | }, | ||
25 | Some(pos) => pos | ||
26 | }; | ||
27 | TextRange::offset_len( | ||
28 | range.start() + pos, | ||
29 | TextUnit::of_char('\n'), | ||
30 | ) | ||
31 | } else { | ||
32 | range | ||
33 | }; | ||
34 | let node = find_covering_node(file.syntax(), range); | ||
35 | let mut edit = EditBuilder::new(); | ||
36 | for node in preorder(node) { | ||
37 | let text = match node.leaf_text() { | ||
38 | Some(text) => text, | ||
39 | None => continue, | ||
40 | }; | ||
41 | let range = match intersect(range, node.range()) { | ||
42 | Some(range) => range, | ||
43 | None => continue, | ||
44 | } - node.range().start(); | ||
45 | for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') { | ||
46 | let pos: TextUnit = (pos as u32).into(); | ||
47 | let off = node.range().start() + range.start() + pos; | ||
48 | if !edit.invalidates_offset(off) { | ||
49 | remove_newline(&mut edit, node, text.as_str(), off); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | LocalEdit { | ||
55 | edit: edit.finish(), | ||
56 | cursor_position: None, | ||
57 | } | ||
58 | } | ||
59 | |||
60 | pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { | ||
61 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | ||
62 | if let_stmt.has_semi() { | ||
63 | return None; | ||
64 | } | ||
65 | if let Some(expr) = let_stmt.initializer() { | ||
66 | let expr_range = expr.syntax().range(); | ||
67 | if contains_offset_nonstrict(expr_range, offset) && offset != expr_range.start() { | ||
68 | return None; | ||
69 | } | ||
70 | if file.syntax().text().slice(offset..expr_range.start()).contains('\n') { | ||
71 | return None; | ||
72 | } | ||
73 | } else { | ||
74 | return None; | ||
75 | } | ||
76 | let offset = let_stmt.syntax().range().end(); | ||
77 | let mut edit = EditBuilder::new(); | ||
78 | edit.insert(offset, ";".to_string()); | ||
79 | Some(LocalEdit { | ||
80 | edit: edit.finish(), | ||
81 | cursor_position: None, | ||
82 | }) | ||
83 | } | ||
84 | |||
85 | fn remove_newline( | ||
86 | edit: &mut EditBuilder, | ||
87 | node: SyntaxNodeRef, | ||
88 | node_text: &str, | ||
89 | offset: TextUnit, | ||
90 | ) { | ||
91 | if node.kind() == WHITESPACE && node_text.bytes().filter(|&b| b == b'\n').count() == 1 { | ||
92 | if join_single_expr_block(edit, node).is_some() { | ||
93 | return | ||
94 | } | ||
95 | match (node.prev_sibling(), node.next_sibling()) { | ||
96 | (Some(prev), Some(next)) => { | ||
97 | let range = TextRange::from_to(prev.range().start(), node.range().end()); | ||
98 | if is_trailing_comma(prev.kind(), next.kind()) { | ||
99 | edit.delete(range); | ||
100 | } else if no_space_required(prev.kind(), next.kind()) { | ||
101 | edit.delete(node.range()); | ||
102 | } else if prev.kind() == COMMA && next.kind() == R_CURLY { | ||
103 | edit.replace(range, " ".to_string()); | ||
104 | } else { | ||
105 | edit.replace( | ||
106 | node.range(), | ||
107 | compute_ws(prev, next).to_string(), | ||
108 | ); | ||
109 | } | ||
110 | return; | ||
111 | } | ||
112 | _ => (), | ||
113 | } | ||
114 | } | ||
115 | |||
116 | let suff = &node_text[TextRange::from_to( | ||
117 | offset - node.range().start() + TextUnit::of_char('\n'), | ||
118 | TextUnit::of_str(node_text), | ||
119 | )]; | ||
120 | let spaces = suff.bytes().take_while(|&b| b == b' ').count(); | ||
121 | |||
122 | edit.replace( | ||
123 | TextRange::offset_len(offset, ((spaces + 1) as u32).into()), | ||
124 | " ".to_string(), | ||
125 | ); | ||
126 | } | ||
127 | |||
128 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { | ||
129 | match (left, right) { | ||
130 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, | ||
131 | _ => false | ||
132 | } | ||
133 | } | ||
134 | |||
135 | fn no_space_required(left: SyntaxKind, right: SyntaxKind) -> bool { | ||
136 | match (left, right) { | ||
137 | (_, DOT) => true, | ||
138 | _ => false | ||
139 | } | ||
140 | } | ||
141 | |||
142 | fn join_single_expr_block( | ||
143 | edit: &mut EditBuilder, | ||
144 | node: SyntaxNodeRef, | ||
145 | ) -> Option<()> { | ||
146 | let block = ast::Block::cast(node.parent()?)?; | ||
147 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; | ||
148 | let expr = single_expr(block)?; | ||
149 | edit.replace( | ||
150 | block_expr.syntax().range(), | ||
151 | expr.syntax().text().to_string(), | ||
152 | ); | ||
153 | Some(()) | ||
154 | } | ||
155 | |||
156 | fn single_expr(block: ast::Block) -> Option<ast::Expr> { | ||
157 | let mut res = None; | ||
158 | for child in block.syntax().children() { | ||
159 | if let Some(expr) = ast::Expr::cast(child) { | ||
160 | if expr.syntax().text().contains('\n') { | ||
161 | return None; | ||
162 | } | ||
163 | if mem::replace(&mut res, Some(expr)).is_some() { | ||
164 | return None; | ||
165 | } | ||
166 | } else { | ||
167 | match child.kind() { | ||
168 | WHITESPACE | L_CURLY | R_CURLY => (), | ||
169 | _ => return None, | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | res | ||
174 | } | ||
175 | |||
176 | fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | ||
177 | match left.kind() { | ||
178 | L_PAREN | L_BRACK => return "", | ||
179 | _ => (), | ||
180 | } | ||
181 | match right.kind() { | ||
182 | R_PAREN | R_BRACK => return "", | ||
183 | _ => (), | ||
184 | } | ||
185 | " " | ||
186 | } | ||
187 | |||
188 | #[cfg(test)] | ||
189 | mod tests { | ||
190 | use super::*; | ||
191 | use test_utils::{check_action, extract_range, extract_offset}; | ||
192 | |||
193 | fn check_join_lines(before: &str, after: &str) { | ||
194 | check_action(before, after, |file, offset| { | ||
195 | let range = TextRange::offset_len(offset, 0.into()); | ||
196 | let res = join_lines(file, range); | ||
197 | Some(res) | ||
198 | }) | ||
199 | } | ||
200 | |||
201 | #[test] | ||
202 | fn test_join_lines_comma() { | ||
203 | check_join_lines(r" | ||
204 | fn foo() { | ||
205 | <|>foo(1, | ||
206 | ) | ||
207 | } | ||
208 | ", r" | ||
209 | fn foo() { | ||
210 | <|>foo(1) | ||
211 | } | ||
212 | "); | ||
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn test_join_lines_lambda_block() { | ||
217 | check_join_lines(r" | ||
218 | pub fn reparse(&self, edit: &AtomEdit) -> File { | ||
219 | <|>self.incremental_reparse(edit).unwrap_or_else(|| { | ||
220 | self.full_reparse(edit) | ||
221 | }) | ||
222 | } | ||
223 | ", r" | ||
224 | pub fn reparse(&self, edit: &AtomEdit) -> File { | ||
225 | <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) | ||
226 | } | ||
227 | "); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn test_join_lines_block() { | ||
232 | check_join_lines(r" | ||
233 | fn foo() { | ||
234 | foo(<|>{ | ||
235 | 92 | ||
236 | }) | ||
237 | }", r" | ||
238 | fn foo() { | ||
239 | foo(<|>92) | ||
240 | }"); | ||
241 | } | ||
242 | |||
243 | fn check_join_lines_sel(before: &str, after: &str) { | ||
244 | let (sel, before) = extract_range(before); | ||
245 | let file = File::parse(&before); | ||
246 | let result = join_lines(&file, sel); | ||
247 | let actual = result.edit.apply(&before); | ||
248 | assert_eq_text!(after, &actual); | ||
249 | } | ||
250 | |||
251 | #[test] | ||
252 | fn test_join_lines_selection_fn_args() { | ||
253 | check_join_lines_sel(r" | ||
254 | fn foo() { | ||
255 | <|>foo(1, | ||
256 | 2, | ||
257 | 3, | ||
258 | <|>) | ||
259 | } | ||
260 | ", r" | ||
261 | fn foo() { | ||
262 | foo(1, 2, 3) | ||
263 | } | ||
264 | "); | ||
265 | } | ||
266 | |||
267 | #[test] | ||
268 | fn test_join_lines_selection_struct() { | ||
269 | check_join_lines_sel(r" | ||
270 | struct Foo <|>{ | ||
271 | f: u32, | ||
272 | }<|> | ||
273 | ", r" | ||
274 | struct Foo { f: u32 } | ||
275 | "); | ||
276 | } | ||
277 | |||
278 | #[test] | ||
279 | fn test_join_lines_selection_dot_chain() { | ||
280 | check_join_lines_sel(r" | ||
281 | fn foo() { | ||
282 | join(<|>type_params.type_params() | ||
283 | .filter_map(|it| it.name()) | ||
284 | .map(|it| it.text())<|>) | ||
285 | }", r" | ||
286 | fn foo() { | ||
287 | join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text())) | ||
288 | }"); | ||
289 | } | ||
290 | |||
291 | #[test] | ||
292 | fn test_join_lines_selection_lambda_block_body() { | ||
293 | check_join_lines_sel(r" | ||
294 | pub fn handle_find_matching_brace() { | ||
295 | params.offsets | ||
296 | .map(|offset| <|>{ | ||
297 | world.analysis().matching_brace(&file, offset).unwrap_or(offset) | ||
298 | }<|>) | ||
299 | .collect(); | ||
300 | }", r" | ||
301 | pub fn handle_find_matching_brace() { | ||
302 | params.offsets | ||
303 | .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset)) | ||
304 | .collect(); | ||
305 | }"); | ||
306 | } | ||
307 | |||
308 | #[test] | ||
309 | fn test_on_eq_typed() { | ||
310 | fn do_check(before: &str, after: &str) { | ||
311 | let (offset, before) = extract_offset(before); | ||
312 | let file = File::parse(&before); | ||
313 | let result = on_eq_typed(&file, offset).unwrap(); | ||
314 | let actual = result.edit.apply(&before); | ||
315 | assert_eq_text!(after, &actual); | ||
316 | } | ||
317 | |||
318 | // do_check(r" | ||
319 | // fn foo() { | ||
320 | // let foo =<|> | ||
321 | // } | ||
322 | // ", r" | ||
323 | // fn foo() { | ||
324 | // let foo =; | ||
325 | // } | ||
326 | // "); | ||
327 | do_check(r" | ||
328 | fn foo() { | ||
329 | let foo =<|> 1 + 1 | ||
330 | } | ||
331 | ", r" | ||
332 | fn foo() { | ||
333 | let foo = 1 + 1; | ||
334 | } | ||
335 | "); | ||
336 | // do_check(r" | ||
337 | // fn foo() { | ||
338 | // let foo =<|> | ||
339 | // let bar = 1; | ||
340 | // } | ||
341 | // ", r" | ||
342 | // fn foo() { | ||
343 | // let foo =; | ||
344 | // let bar = 1; | ||
345 | // } | ||
346 | // "); | ||
347 | } | ||
348 | } | ||