diff options
Diffstat (limited to 'crates/libeditor/src/typing.rs')
-rw-r--r-- | crates/libeditor/src/typing.rs | 81 |
1 files changed, 81 insertions, 0 deletions
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 | } | ||