diff options
Diffstat (limited to 'crates/ra_ide_api_light/src')
-rw-r--r-- | crates/ra_ide_api_light/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide_api_light/src/typing.rs | 407 |
2 files changed, 0 insertions, 410 deletions
diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs index f21a91e18..0d928745f 100644 --- a/crates/ra_ide_api_light/src/lib.rs +++ b/crates/ra_ide_api_light/src/lib.rs | |||
@@ -4,10 +4,8 @@ | |||
4 | //! an edit or some auxiliary info. | 4 | //! an edit or some auxiliary info. |
5 | 5 | ||
6 | mod structure; | 6 | mod structure; |
7 | mod typing; | ||
8 | 7 | ||
9 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
10 | use ra_text_edit::TextEditBuilder; | ||
11 | use ra_syntax::{ | 9 | use ra_syntax::{ |
12 | SourceFile, SyntaxNode, TextRange, TextUnit, Direction, | 10 | SourceFile, SyntaxNode, TextRange, TextUnit, Direction, |
13 | algo::find_leaf_at_offset, | 11 | algo::find_leaf_at_offset, |
@@ -17,7 +15,6 @@ use ra_syntax::{ | |||
17 | 15 | ||
18 | pub use crate::{ | 16 | pub use crate::{ |
19 | structure::{file_structure, StructureNode}, | 17 | structure::{file_structure, StructureNode}, |
20 | typing::{on_enter, on_dot_typed, on_eq_typed}, | ||
21 | }; | 18 | }; |
22 | 19 | ||
23 | #[derive(Debug)] | 20 | #[derive(Debug)] |
diff --git a/crates/ra_ide_api_light/src/typing.rs b/crates/ra_ide_api_light/src/typing.rs deleted file mode 100644 index c69270333..000000000 --- a/crates/ra_ide_api_light/src/typing.rs +++ /dev/null | |||
@@ -1,407 +0,0 @@ | |||
1 | use ra_syntax::{ | ||
2 | AstNode, SourceFile, SyntaxKind::*, | ||
3 | SyntaxNode, TextUnit, TextRange, | ||
4 | algo::{find_node_at_offset, find_leaf_at_offset, LeafAtOffset}, | ||
5 | ast::{self, AstToken}, | ||
6 | }; | ||
7 | use ra_fmt::leading_indent; | ||
8 | use crate::{LocalEdit, TextEditBuilder}; | ||
9 | |||
10 | pub fn on_enter(file: &SourceFile, offset: TextUnit) -> Option<LocalEdit> { | ||
11 | let comment = | ||
12 | find_leaf_at_offset(file.syntax(), offset).left_biased().and_then(ast::Comment::cast)?; | ||
13 | |||
14 | if let ast::CommentFlavor::Multiline = comment.flavor() { | ||
15 | return None; | ||
16 | } | ||
17 | |||
18 | let prefix = comment.prefix(); | ||
19 | if offset < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1) { | ||
20 | return None; | ||
21 | } | ||
22 | |||
23 | let indent = node_indent(file, comment.syntax())?; | ||
24 | let inserted = format!("\n{}{} ", indent, prefix); | ||
25 | let cursor_position = offset + TextUnit::of_str(&inserted); | ||
26 | let mut edit = TextEditBuilder::default(); | ||
27 | edit.insert(offset, inserted); | ||
28 | Some(LocalEdit { | ||
29 | label: "on enter".to_string(), | ||
30 | edit: edit.finish(), | ||
31 | cursor_position: Some(cursor_position), | ||
32 | }) | ||
33 | } | ||
34 | |||
35 | fn node_indent<'a>(file: &'a SourceFile, node: &SyntaxNode) -> Option<&'a str> { | ||
36 | let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) { | ||
37 | LeafAtOffset::Between(l, r) => { | ||
38 | assert!(r == node); | ||
39 | l | ||
40 | } | ||
41 | LeafAtOffset::Single(n) => { | ||
42 | assert!(n == node); | ||
43 | return Some(""); | ||
44 | } | ||
45 | LeafAtOffset::None => unreachable!(), | ||
46 | }; | ||
47 | if ws.kind() != WHITESPACE { | ||
48 | return None; | ||
49 | } | ||
50 | let text = ws.leaf_text().unwrap(); | ||
51 | let pos = text.as_str().rfind('\n').map(|it| it + 1).unwrap_or(0); | ||
52 | Some(&text[pos..]) | ||
53 | } | ||
54 | |||
55 | pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<LocalEdit> { | ||
56 | assert_eq!(file.syntax().text().char_at(eq_offset), Some('=')); | ||
57 | let let_stmt: &ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?; | ||
58 | if let_stmt.has_semi() { | ||
59 | return None; | ||
60 | } | ||
61 | if let Some(expr) = let_stmt.initializer() { | ||
62 | let expr_range = expr.syntax().range(); | ||
63 | if expr_range.contains(eq_offset) && eq_offset != expr_range.start() { | ||
64 | return None; | ||
65 | } | ||
66 | if file.syntax().text().slice(eq_offset..expr_range.start()).contains('\n') { | ||
67 | return None; | ||
68 | } | ||
69 | } else { | ||
70 | return None; | ||
71 | } | ||
72 | let offset = let_stmt.syntax().range().end(); | ||
73 | let mut edit = TextEditBuilder::default(); | ||
74 | edit.insert(offset, ";".to_string()); | ||
75 | Some(LocalEdit { | ||
76 | label: "add semicolon".to_string(), | ||
77 | edit: edit.finish(), | ||
78 | cursor_position: None, | ||
79 | }) | ||
80 | } | ||
81 | |||
82 | pub fn on_dot_typed(file: &SourceFile, dot_offset: TextUnit) -> Option<LocalEdit> { | ||
83 | assert_eq!(file.syntax().text().char_at(dot_offset), Some('.')); | ||
84 | |||
85 | let whitespace = find_leaf_at_offset(file.syntax(), dot_offset) | ||
86 | .left_biased() | ||
87 | .and_then(ast::Whitespace::cast)?; | ||
88 | |||
89 | let current_indent = { | ||
90 | let text = whitespace.text(); | ||
91 | let newline = text.rfind('\n')?; | ||
92 | &text[newline + 1..] | ||
93 | }; | ||
94 | let current_indent_len = TextUnit::of_str(current_indent); | ||
95 | |||
96 | // Make sure dot is a part of call chain | ||
97 | let field_expr = whitespace.syntax().parent().and_then(ast::FieldExpr::cast)?; | ||
98 | let prev_indent = leading_indent(field_expr.syntax())?; | ||
99 | let target_indent = format!(" {}", prev_indent); | ||
100 | let target_indent_len = TextUnit::of_str(&target_indent); | ||
101 | if current_indent_len == target_indent_len { | ||
102 | return None; | ||
103 | } | ||
104 | let mut edit = TextEditBuilder::default(); | ||
105 | edit.replace( | ||
106 | TextRange::from_to(dot_offset - current_indent_len, dot_offset), | ||
107 | target_indent.into(), | ||
108 | ); | ||
109 | let res = LocalEdit { | ||
110 | label: "reindent dot".to_string(), | ||
111 | edit: edit.finish(), | ||
112 | cursor_position: Some( | ||
113 | dot_offset + target_indent_len - current_indent_len + TextUnit::of_char('.'), | ||
114 | ), | ||
115 | }; | ||
116 | Some(res) | ||
117 | } | ||
118 | |||
119 | #[cfg(test)] | ||
120 | mod tests { | ||
121 | use test_utils::{add_cursor, assert_eq_text, extract_offset}; | ||
122 | |||
123 | use super::*; | ||
124 | |||
125 | #[test] | ||
126 | fn test_on_eq_typed() { | ||
127 | fn type_eq(before: &str, after: &str) { | ||
128 | let (offset, before) = extract_offset(before); | ||
129 | let mut edit = TextEditBuilder::default(); | ||
130 | edit.insert(offset, "=".to_string()); | ||
131 | let before = edit.finish().apply(&before); | ||
132 | let file = SourceFile::parse(&before); | ||
133 | if let Some(result) = on_eq_typed(&file, offset) { | ||
134 | let actual = result.edit.apply(&before); | ||
135 | assert_eq_text!(after, &actual); | ||
136 | } else { | ||
137 | assert_eq_text!(&before, after) | ||
138 | }; | ||
139 | } | ||
140 | |||
141 | // do_check(r" | ||
142 | // fn foo() { | ||
143 | // let foo =<|> | ||
144 | // } | ||
145 | // ", r" | ||
146 | // fn foo() { | ||
147 | // let foo =; | ||
148 | // } | ||
149 | // "); | ||
150 | type_eq( | ||
151 | r" | ||
152 | fn foo() { | ||
153 | let foo <|> 1 + 1 | ||
154 | } | ||
155 | ", | ||
156 | r" | ||
157 | fn foo() { | ||
158 | let foo = 1 + 1; | ||
159 | } | ||
160 | ", | ||
161 | ); | ||
162 | // do_check(r" | ||
163 | // fn foo() { | ||
164 | // let foo =<|> | ||
165 | // let bar = 1; | ||
166 | // } | ||
167 | // ", r" | ||
168 | // fn foo() { | ||
169 | // let foo =; | ||
170 | // let bar = 1; | ||
171 | // } | ||
172 | // "); | ||
173 | } | ||
174 | |||
175 | fn type_dot(before: &str, after: &str) { | ||
176 | let (offset, before) = extract_offset(before); | ||
177 | let mut edit = TextEditBuilder::default(); | ||
178 | edit.insert(offset, ".".to_string()); | ||
179 | let before = edit.finish().apply(&before); | ||
180 | let file = SourceFile::parse(&before); | ||
181 | if let Some(result) = on_dot_typed(&file, offset) { | ||
182 | let actual = result.edit.apply(&before); | ||
183 | assert_eq_text!(after, &actual); | ||
184 | } else { | ||
185 | assert_eq_text!(&before, after) | ||
186 | }; | ||
187 | } | ||
188 | |||
189 | #[test] | ||
190 | fn indents_new_chain_call() { | ||
191 | type_dot( | ||
192 | r" | ||
193 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
194 | self.child_impl(db, name) | ||
195 | <|> | ||
196 | } | ||
197 | ", | ||
198 | r" | ||
199 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
200 | self.child_impl(db, name) | ||
201 | . | ||
202 | } | ||
203 | ", | ||
204 | ); | ||
205 | type_dot( | ||
206 | r" | ||
207 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
208 | self.child_impl(db, name) | ||
209 | <|> | ||
210 | } | ||
211 | ", | ||
212 | r" | ||
213 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
214 | self.child_impl(db, name) | ||
215 | . | ||
216 | } | ||
217 | ", | ||
218 | ) | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn indents_new_chain_call_with_semi() { | ||
223 | type_dot( | ||
224 | r" | ||
225 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
226 | self.child_impl(db, name) | ||
227 | <|>; | ||
228 | } | ||
229 | ", | ||
230 | r" | ||
231 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
232 | self.child_impl(db, name) | ||
233 | .; | ||
234 | } | ||
235 | ", | ||
236 | ); | ||
237 | type_dot( | ||
238 | r" | ||
239 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
240 | self.child_impl(db, name) | ||
241 | <|>; | ||
242 | } | ||
243 | ", | ||
244 | r" | ||
245 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
246 | self.child_impl(db, name) | ||
247 | .; | ||
248 | } | ||
249 | ", | ||
250 | ) | ||
251 | } | ||
252 | |||
253 | #[test] | ||
254 | fn indents_continued_chain_call() { | ||
255 | type_dot( | ||
256 | r" | ||
257 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
258 | self.child_impl(db, name) | ||
259 | .first() | ||
260 | <|> | ||
261 | } | ||
262 | ", | ||
263 | r" | ||
264 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
265 | self.child_impl(db, name) | ||
266 | .first() | ||
267 | . | ||
268 | } | ||
269 | ", | ||
270 | ); | ||
271 | type_dot( | ||
272 | r" | ||
273 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
274 | self.child_impl(db, name) | ||
275 | .first() | ||
276 | <|> | ||
277 | } | ||
278 | ", | ||
279 | r" | ||
280 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
281 | self.child_impl(db, name) | ||
282 | .first() | ||
283 | . | ||
284 | } | ||
285 | ", | ||
286 | ); | ||
287 | } | ||
288 | |||
289 | #[test] | ||
290 | fn indents_middle_of_chain_call() { | ||
291 | type_dot( | ||
292 | r" | ||
293 | fn source_impl() { | ||
294 | let var = enum_defvariant_list().unwrap() | ||
295 | <|> | ||
296 | .nth(92) | ||
297 | .unwrap(); | ||
298 | } | ||
299 | ", | ||
300 | r" | ||
301 | fn source_impl() { | ||
302 | let var = enum_defvariant_list().unwrap() | ||
303 | . | ||
304 | .nth(92) | ||
305 | .unwrap(); | ||
306 | } | ||
307 | ", | ||
308 | ); | ||
309 | type_dot( | ||
310 | r" | ||
311 | fn source_impl() { | ||
312 | let var = enum_defvariant_list().unwrap() | ||
313 | <|> | ||
314 | .nth(92) | ||
315 | .unwrap(); | ||
316 | } | ||
317 | ", | ||
318 | r" | ||
319 | fn source_impl() { | ||
320 | let var = enum_defvariant_list().unwrap() | ||
321 | . | ||
322 | .nth(92) | ||
323 | .unwrap(); | ||
324 | } | ||
325 | ", | ||
326 | ); | ||
327 | } | ||
328 | |||
329 | #[test] | ||
330 | fn dont_indent_freestanding_dot() { | ||
331 | type_dot( | ||
332 | r" | ||
333 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
334 | <|> | ||
335 | } | ||
336 | ", | ||
337 | r" | ||
338 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
339 | . | ||
340 | } | ||
341 | ", | ||
342 | ); | ||
343 | type_dot( | ||
344 | r" | ||
345 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
346 | <|> | ||
347 | } | ||
348 | ", | ||
349 | r" | ||
350 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
351 | . | ||
352 | } | ||
353 | ", | ||
354 | ); | ||
355 | } | ||
356 | |||
357 | #[test] | ||
358 | fn test_on_enter() { | ||
359 | fn apply_on_enter(before: &str) -> Option<String> { | ||
360 | let (offset, before) = extract_offset(before); | ||
361 | let file = SourceFile::parse(&before); | ||
362 | let result = on_enter(&file, offset)?; | ||
363 | let actual = result.edit.apply(&before); | ||
364 | let actual = add_cursor(&actual, result.cursor_position.unwrap()); | ||
365 | Some(actual) | ||
366 | } | ||
367 | |||
368 | fn do_check(before: &str, after: &str) { | ||
369 | let actual = apply_on_enter(before).unwrap(); | ||
370 | assert_eq_text!(after, &actual); | ||
371 | } | ||
372 | |||
373 | fn do_check_noop(text: &str) { | ||
374 | assert!(apply_on_enter(text).is_none()) | ||
375 | } | ||
376 | |||
377 | do_check( | ||
378 | r" | ||
379 | /// Some docs<|> | ||
380 | fn foo() { | ||
381 | } | ||
382 | ", | ||
383 | r" | ||
384 | /// Some docs | ||
385 | /// <|> | ||
386 | fn foo() { | ||
387 | } | ||
388 | ", | ||
389 | ); | ||
390 | do_check( | ||
391 | r" | ||
392 | impl S { | ||
393 | /// Some<|> docs. | ||
394 | fn foo() {} | ||
395 | } | ||
396 | ", | ||
397 | r" | ||
398 | impl S { | ||
399 | /// Some | ||
400 | /// <|> docs. | ||
401 | fn foo() {} | ||
402 | } | ||
403 | ", | ||
404 | ); | ||
405 | do_check_noop(r"<|>//! docz"); | ||
406 | } | ||
407 | } | ||