aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api_light/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api_light/src')
-rw-r--r--crates/ra_ide_api_light/src/lib.rs3
-rw-r--r--crates/ra_ide_api_light/src/typing.rs407
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
6mod structure; 6mod structure;
7mod typing;
8 7
9use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
10use ra_text_edit::TextEditBuilder;
11use ra_syntax::{ 9use 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
18pub use crate::{ 16pub 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 @@
1use 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};
7use ra_fmt::leading_indent;
8use crate::{LocalEdit, TextEditBuilder};
9
10pub 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
35fn 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
55pub 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
82pub 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)]
120mod 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"
152fn foo() {
153 let foo <|> 1 + 1
154}
155",
156 r"
157fn 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<|>
380fn foo() {
381}
382",
383 r"
384/// Some docs
385/// <|>
386fn foo() {
387}
388",
389 );
390 do_check(
391 r"
392impl S {
393 /// Some<|> docs.
394 fn foo() {}
395}
396",
397 r"
398impl S {
399 /// Some
400 /// <|> docs.
401 fn foo() {}
402}
403",
404 );
405 do_check_noop(r"<|>//! docz");
406 }
407}