aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor')
-rw-r--r--crates/libeditor/src/code_actions.rs1
-rw-r--r--crates/libeditor/src/lib.rs4
-rw-r--r--crates/libeditor/src/typing.rs81
-rw-r--r--crates/libeditor/tests/test.rs49
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
15use {TextUnit, EditBuilder, Edit}; 15use {TextUnit, EditBuilder, Edit};
16 16
17#[derive(Debug)]
17pub struct ActionResult { 18pub 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;
7mod line_index; 7mod line_index;
8mod edit; 8mod edit;
9mod code_actions; 9mod code_actions;
10mod typing;
10 11
11use libsyntax2::{ 12use 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};
17pub use libsyntax2::{ParsedFile, TextRange, TextUnit}; 18pub 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 @@
1use libsyntax2::{
2 TextUnit, TextRange, SyntaxNodeRef,
3 ast,
4 algo::{
5 walk::preorder,
6 find_covering_node,
7 },
8};
9
10use {ActionResult, EditBuilder};
11
12pub 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
55fn 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
65fn 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]
182fn 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"
192fn foo() {
193 <|>foo(1,
194 )
195}
196", r"
197fn foo() {
198 <|>foo(1, )
199}
200");
201}
202
203#[test]
204fn 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"
216fn foo() {
217 <|>foo(1,
218 2,
219 3,
220 <|>)
221}
222", r"
223fn foo() {
224 foo(1, 2, 3, )
225}
226");
227}
228
180fn file(text: &str) -> ParsedFile { 229fn file(text: &str) -> ParsedFile {
181 ParsedFile::parse(text) 230 ParsedFile::parse(text)
182} 231}