diff options
Diffstat (limited to 'crates/libeditor')
-rw-r--r-- | crates/libeditor/src/code_actions.rs | 1 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/libeditor/src/typing.rs | 81 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 49 |
4 files changed, 134 insertions, 1 deletions
diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 6c41923dd..c25ee973c 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs | |||
@@ -14,6 +14,7 @@ use libsyntax2::{ | |||
14 | 14 | ||
15 | use {TextUnit, EditBuilder, Edit}; | 15 | use {TextUnit, EditBuilder, Edit}; |
16 | 16 | ||
17 | #[derive(Debug)] | ||
17 | pub struct ActionResult { | 18 | pub struct ActionResult { |
18 | pub edit: Edit, | 19 | pub edit: Edit, |
19 | pub cursor_position: Option<TextUnit>, | 20 | pub cursor_position: Option<TextUnit>, |
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 6bae7a3fa..f8bc73ae9 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -7,11 +7,12 @@ mod symbols; | |||
7 | mod line_index; | 7 | mod line_index; |
8 | mod edit; | 8 | mod edit; |
9 | mod code_actions; | 9 | mod code_actions; |
10 | mod typing; | ||
10 | 11 | ||
11 | use libsyntax2::{ | 12 | use libsyntax2::{ |
12 | ast::{self, NameOwner}, | 13 | ast::{self, NameOwner}, |
13 | AstNode, | 14 | AstNode, |
14 | algo::{walk, find_leaf_at_offset}, | 15 | algo::{walk, find_leaf_at_offset, find_covering_node}, |
15 | SyntaxKind::{self, *}, | 16 | SyntaxKind::{self, *}, |
16 | }; | 17 | }; |
17 | pub use libsyntax2::{ParsedFile, TextRange, TextUnit}; | 18 | pub use libsyntax2::{ParsedFile, TextRange, TextUnit}; |
@@ -24,6 +25,7 @@ pub use self::{ | |||
24 | ActionResult, find_node, | 25 | ActionResult, find_node, |
25 | flip_comma, add_derive, add_impl, | 26 | flip_comma, add_derive, add_impl, |
26 | }, | 27 | }, |
28 | typing::join_lines, | ||
27 | }; | 29 | }; |
28 | 30 | ||
29 | #[derive(Debug)] | 31 | #[derive(Debug)] |
diff --git a/crates/libeditor/src/typing.rs b/crates/libeditor/src/typing.rs new file mode 100644 index 000000000..f49dd0fdc --- /dev/null +++ b/crates/libeditor/src/typing.rs | |||
@@ -0,0 +1,81 @@ | |||
1 | use libsyntax2::{ | ||
2 | TextUnit, TextRange, SyntaxNodeRef, | ||
3 | ast, | ||
4 | algo::{ | ||
5 | walk::preorder, | ||
6 | find_covering_node, | ||
7 | }, | ||
8 | }; | ||
9 | |||
10 | use {ActionResult, EditBuilder}; | ||
11 | |||
12 | pub fn join_lines(file: &ast::ParsedFile, range: TextRange) -> ActionResult { | ||
13 | let range = if range.is_empty() { | ||
14 | let text = file.syntax().text(); | ||
15 | let text = &text[TextRange::from_to(range.start(), TextUnit::of_str(&text))]; | ||
16 | let pos = text.bytes().take_while(|&b| b != b'\n').count(); | ||
17 | if pos == text.len() { | ||
18 | return ActionResult { | ||
19 | edit: EditBuilder::new().finish(), | ||
20 | cursor_position: None | ||
21 | }; | ||
22 | } | ||
23 | let pos: TextUnit = (pos as u32).into(); | ||
24 | TextRange::offset_len( | ||
25 | range.start() + pos, | ||
26 | TextUnit::of_char('\n'), | ||
27 | ) | ||
28 | } else { | ||
29 | range | ||
30 | }; | ||
31 | let node = find_covering_node(file.syntax(), range); | ||
32 | let mut edit = EditBuilder::new(); | ||
33 | for node in preorder(node) { | ||
34 | let text = match node.leaf_text() { | ||
35 | Some(text) => text, | ||
36 | None => continue, | ||
37 | }; | ||
38 | let range = match intersect(range, node.range()) { | ||
39 | Some(range) => range, | ||
40 | None => continue, | ||
41 | } - node.range().start(); | ||
42 | for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') { | ||
43 | let pos: TextUnit = (pos as u32).into(); | ||
44 | let off = node.range().start() + range.start() + pos; | ||
45 | remove_newline(&mut edit, node, text.as_str(), off); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | ActionResult { | ||
50 | edit: edit.finish(), | ||
51 | cursor_position: None, | ||
52 | } | ||
53 | } | ||
54 | |||
55 | fn intersect(r1: TextRange, r2: TextRange) -> Option<TextRange> { | ||
56 | let start = r1.start().max(r2.start()); | ||
57 | let end = r1.end().min(r2.end()); | ||
58 | if start <= end { | ||
59 | Some(TextRange::from_to(start, end)) | ||
60 | } else { | ||
61 | None | ||
62 | } | ||
63 | } | ||
64 | |||
65 | fn remove_newline( | ||
66 | edit: &mut EditBuilder, | ||
67 | node: SyntaxNodeRef, | ||
68 | node_text: &str, | ||
69 | offset: TextUnit, | ||
70 | ) { | ||
71 | let suff = &node_text[TextRange::from_to( | ||
72 | offset - node.range().start() + TextUnit::of_char('\n'), | ||
73 | TextUnit::of_str(node_text), | ||
74 | )]; | ||
75 | let spaces = suff.bytes().take_while(|&b| b == b' ').count(); | ||
76 | |||
77 | edit.replace( | ||
78 | TextRange::offset_len(offset, ((spaces + 1) as u32).into()), | ||
79 | " ".to_string(), | ||
80 | ); | ||
81 | } | ||
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index 42926ffc8..6aa260a86 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -8,6 +8,7 @@ use libeditor::{ | |||
8 | ParsedFile, TextUnit, TextRange, ActionResult, | 8 | ParsedFile, TextUnit, TextRange, ActionResult, |
9 | highlight, runnables, extend_selection, file_structure, | 9 | highlight, runnables, extend_selection, file_structure, |
10 | flip_comma, add_derive, add_impl, matching_brace, | 10 | flip_comma, add_derive, add_impl, matching_brace, |
11 | join_lines, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | #[test] | 14 | #[test] |
@@ -177,6 +178,54 @@ fn test_matching_brace() { | |||
177 | ); | 178 | ); |
178 | } | 179 | } |
179 | 180 | ||
181 | #[test] | ||
182 | fn test_join_lines_cursor() { | ||
183 | fn do_check(before: &str, after: &str) { | ||
184 | check_action(before, after, |file, offset| { | ||
185 | let range = TextRange::offset_len(offset, 0.into()); | ||
186 | let res = join_lines(file, range); | ||
187 | Some(res) | ||
188 | }) | ||
189 | } | ||
190 | |||
191 | do_check(r" | ||
192 | fn foo() { | ||
193 | <|>foo(1, | ||
194 | ) | ||
195 | } | ||
196 | ", r" | ||
197 | fn foo() { | ||
198 | <|>foo(1, ) | ||
199 | } | ||
200 | "); | ||
201 | } | ||
202 | |||
203 | #[test] | ||
204 | fn test_join_lines_selection() { | ||
205 | fn do_check(before: &str, after: &str) { | ||
206 | let (sel_start, before) = extract_cursor(before); | ||
207 | let (sel_end, before) = extract_cursor(&before); | ||
208 | let sel = TextRange::from_to(sel_start, sel_end); | ||
209 | let file = file(&before); | ||
210 | let result = join_lines(&file, sel); | ||
211 | let actual = result.edit.apply(&before); | ||
212 | assert_eq_text!(after, &actual); | ||
213 | } | ||
214 | |||
215 | do_check(r" | ||
216 | fn foo() { | ||
217 | <|>foo(1, | ||
218 | 2, | ||
219 | 3, | ||
220 | <|>) | ||
221 | } | ||
222 | ", r" | ||
223 | fn foo() { | ||
224 | foo(1, 2, 3, ) | ||
225 | } | ||
226 | "); | ||
227 | } | ||
228 | |||
180 | fn file(text: &str) -> ParsedFile { | 229 | fn file(text: &str) -> ParsedFile { |
181 | ParsedFile::parse(text) | 230 | ParsedFile::parse(text) |
182 | } | 231 | } |