diff options
Diffstat (limited to 'crates/syntax/src/ted.rs')
-rw-r--r-- | crates/syntax/src/ted.rs | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs new file mode 100644 index 000000000..76f950ef9 --- /dev/null +++ b/crates/syntax/src/ted.rs | |||
@@ -0,0 +1,126 @@ | |||
1 | //! Primitive tree editor, ed for trees. | ||
2 | //! | ||
3 | //! The `_raw`-suffixed functions insert elements as is, unsuffixed versions fix | ||
4 | //! up elements around the edges. | ||
5 | use std::ops::RangeInclusive; | ||
6 | |||
7 | use parser::T; | ||
8 | |||
9 | use crate::{ast::make, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken}; | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | pub struct Position { | ||
13 | repr: PositionRepr, | ||
14 | } | ||
15 | |||
16 | #[derive(Debug)] | ||
17 | enum PositionRepr { | ||
18 | FirstChild(SyntaxNode), | ||
19 | After(SyntaxElement), | ||
20 | } | ||
21 | |||
22 | impl Position { | ||
23 | pub fn after(elem: impl Into<SyntaxElement>) -> Position { | ||
24 | let repr = PositionRepr::After(elem.into()); | ||
25 | Position { repr } | ||
26 | } | ||
27 | pub fn before(elem: impl Into<SyntaxElement>) -> Position { | ||
28 | let elem = elem.into(); | ||
29 | let repr = match elem.prev_sibling_or_token() { | ||
30 | Some(it) => PositionRepr::After(it), | ||
31 | None => PositionRepr::FirstChild(elem.parent().unwrap()), | ||
32 | }; | ||
33 | Position { repr } | ||
34 | } | ||
35 | pub fn first_child_of(node: impl Into<SyntaxNode>) -> Position { | ||
36 | let repr = PositionRepr::FirstChild(node.into()); | ||
37 | Position { repr } | ||
38 | } | ||
39 | pub fn last_child_of(node: impl Into<SyntaxNode>) -> Position { | ||
40 | let node = node.into(); | ||
41 | let repr = match node.last_child_or_token() { | ||
42 | Some(it) => PositionRepr::After(it), | ||
43 | None => PositionRepr::FirstChild(node), | ||
44 | }; | ||
45 | Position { repr } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | pub fn insert(position: Position, elem: impl Into<SyntaxElement>) { | ||
50 | insert_all(position, vec![elem.into()]) | ||
51 | } | ||
52 | pub fn insert_raw(position: Position, elem: impl Into<SyntaxElement>) { | ||
53 | insert_all_raw(position, vec![elem.into()]) | ||
54 | } | ||
55 | pub fn insert_all(position: Position, mut elements: Vec<SyntaxElement>) { | ||
56 | if let Some(first) = elements.first() { | ||
57 | if let Some(ws) = ws_before(&position, first) { | ||
58 | elements.insert(0, ws.into()) | ||
59 | } | ||
60 | } | ||
61 | if let Some(last) = elements.last() { | ||
62 | if let Some(ws) = ws_after(&position, last) { | ||
63 | elements.push(ws.into()) | ||
64 | } | ||
65 | } | ||
66 | insert_all_raw(position, elements) | ||
67 | } | ||
68 | pub fn insert_all_raw(position: Position, elements: Vec<SyntaxElement>) { | ||
69 | let (parent, index) = match position.repr { | ||
70 | PositionRepr::FirstChild(parent) => (parent, 0), | ||
71 | PositionRepr::After(child) => (child.parent().unwrap(), child.index() + 1), | ||
72 | }; | ||
73 | parent.splice_children(index..index, elements); | ||
74 | } | ||
75 | |||
76 | pub fn remove(elem: impl Into<SyntaxElement>) { | ||
77 | let elem = elem.into(); | ||
78 | remove_all(elem.clone()..=elem) | ||
79 | } | ||
80 | pub fn remove_all(range: RangeInclusive<SyntaxElement>) { | ||
81 | replace_all(range, Vec::new()) | ||
82 | } | ||
83 | |||
84 | pub fn replace(old: impl Into<SyntaxElement>, new: impl Into<SyntaxElement>) { | ||
85 | let old = old.into(); | ||
86 | replace_all(old.clone()..=old, vec![new.into()]) | ||
87 | } | ||
88 | pub fn replace_all(range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) { | ||
89 | let start = range.start().index(); | ||
90 | let end = range.end().index(); | ||
91 | let parent = range.start().parent().unwrap(); | ||
92 | parent.splice_children(start..end + 1, new) | ||
93 | } | ||
94 | |||
95 | pub fn append_child(node: impl Into<SyntaxNode>, child: impl Into<SyntaxElement>) { | ||
96 | let position = Position::last_child_of(node); | ||
97 | insert(position, child) | ||
98 | } | ||
99 | pub fn append_child_raw(node: impl Into<SyntaxNode>, child: impl Into<SyntaxElement>) { | ||
100 | let position = Position::last_child_of(node); | ||
101 | insert_raw(position, child) | ||
102 | } | ||
103 | |||
104 | fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> { | ||
105 | let prev = match &position.repr { | ||
106 | PositionRepr::FirstChild(_) => return None, | ||
107 | PositionRepr::After(it) => it, | ||
108 | }; | ||
109 | ws_between(prev, new) | ||
110 | } | ||
111 | fn ws_after(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> { | ||
112 | let next = match &position.repr { | ||
113 | PositionRepr::FirstChild(parent) => parent.first_child_or_token()?, | ||
114 | PositionRepr::After(sibling) => sibling.next_sibling_or_token()?, | ||
115 | }; | ||
116 | ws_between(new, &next) | ||
117 | } | ||
118 | fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken> { | ||
119 | if left.kind() == SyntaxKind::WHITESPACE || right.kind() == SyntaxKind::WHITESPACE { | ||
120 | return None; | ||
121 | } | ||
122 | if right.kind() == T![;] || right.kind() == T![,] { | ||
123 | return None; | ||
124 | } | ||
125 | Some(make::tokens::single_space().into()) | ||
126 | } | ||