diff options
Diffstat (limited to 'crates/ra_assists/src/ast_editor.rs')
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 245 |
1 files changed, 0 insertions, 245 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs deleted file mode 100644 index 72c8c478a..000000000 --- a/crates/ra_assists/src/ast_editor.rs +++ /dev/null | |||
@@ -1,245 +0,0 @@ | |||
1 | use std::{iter, ops::RangeInclusive}; | ||
2 | |||
3 | use arrayvec::ArrayVec; | ||
4 | use rustc_hash::FxHashMap; | ||
5 | |||
6 | use ra_fmt::leading_indent; | ||
7 | use ra_syntax::{ | ||
8 | algo, | ||
9 | ast::{self, make::tokens, TypeBoundsOwner}, | ||
10 | AstNode, Direction, InsertPosition, SyntaxElement, | ||
11 | SyntaxKind::*, | ||
12 | T, | ||
13 | }; | ||
14 | use ra_text_edit::TextEditBuilder; | ||
15 | |||
16 | pub struct AstEditor<N: AstNode> { | ||
17 | original_ast: N, | ||
18 | ast: N, | ||
19 | } | ||
20 | |||
21 | impl<N: AstNode> AstEditor<N> { | ||
22 | pub fn new(node: N) -> AstEditor<N> | ||
23 | where | ||
24 | N: Clone, | ||
25 | { | ||
26 | AstEditor { original_ast: node.clone(), ast: node } | ||
27 | } | ||
28 | |||
29 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { | ||
30 | for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) { | ||
31 | builder.replace(from.text_range(), to.to_string()) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | pub fn ast(&self) -> &N { | ||
36 | &self.ast | ||
37 | } | ||
38 | |||
39 | pub fn replace_descendants<T: AstNode>( | ||
40 | &mut self, | ||
41 | replacement_map: impl Iterator<Item = (T, T)>, | ||
42 | ) -> &mut Self { | ||
43 | let map = replacement_map | ||
44 | .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) | ||
45 | .collect::<FxHashMap<_, _>>(); | ||
46 | let new_syntax = algo::replace_descendants(self.ast.syntax(), &map); | ||
47 | self.ast = N::cast(new_syntax).unwrap(); | ||
48 | self | ||
49 | } | ||
50 | |||
51 | #[must_use] | ||
52 | fn insert_children( | ||
53 | &self, | ||
54 | position: InsertPosition<SyntaxElement>, | ||
55 | mut to_insert: impl Iterator<Item = SyntaxElement>, | ||
56 | ) -> N { | ||
57 | let new_syntax = algo::insert_children(self.ast().syntax(), position, &mut to_insert); | ||
58 | N::cast(new_syntax).unwrap() | ||
59 | } | ||
60 | |||
61 | #[must_use] | ||
62 | fn replace_children( | ||
63 | &self, | ||
64 | to_delete: RangeInclusive<SyntaxElement>, | ||
65 | mut to_insert: impl Iterator<Item = SyntaxElement>, | ||
66 | ) -> N { | ||
67 | let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert); | ||
68 | N::cast(new_syntax).unwrap() | ||
69 | } | ||
70 | |||
71 | fn do_make_multiline(&mut self) { | ||
72 | let l_curly = | ||
73 | match self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
74 | Some(it) => it, | ||
75 | None => return, | ||
76 | }; | ||
77 | let sibling = match l_curly.next_sibling_or_token() { | ||
78 | Some(it) => it, | ||
79 | None => return, | ||
80 | }; | ||
81 | let existing_ws = match sibling.as_token() { | ||
82 | None => None, | ||
83 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
84 | Some(ws) => { | ||
85 | if ws.text().contains('\n') { | ||
86 | return; | ||
87 | } | ||
88 | Some(ws.clone()) | ||
89 | } | ||
90 | }; | ||
91 | |||
92 | let indent = leading_indent(self.ast().syntax()).unwrap_or("".into()); | ||
93 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
94 | let to_insert = iter::once(ws.ws().into()); | ||
95 | self.ast = match existing_ws { | ||
96 | None => self.insert_children(InsertPosition::After(l_curly), to_insert), | ||
97 | Some(ws) => { | ||
98 | self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert) | ||
99 | } | ||
100 | }; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | impl AstEditor<ast::RecordFieldList> { | ||
105 | pub fn append_field(&mut self, field: &ast::RecordField) { | ||
106 | self.insert_field(InsertPosition::Last, field) | ||
107 | } | ||
108 | |||
109 | pub fn insert_field( | ||
110 | &mut self, | ||
111 | position: InsertPosition<&'_ ast::RecordField>, | ||
112 | field: &ast::RecordField, | ||
113 | ) { | ||
114 | let is_multiline = self.ast().syntax().text().contains_char('\n'); | ||
115 | let ws; | ||
116 | let space = if is_multiline { | ||
117 | ws = tokens::WsBuilder::new(&format!( | ||
118 | "\n{} ", | ||
119 | leading_indent(self.ast().syntax()).unwrap_or("".into()) | ||
120 | )); | ||
121 | ws.ws() | ||
122 | } else { | ||
123 | tokens::single_space() | ||
124 | }; | ||
125 | |||
126 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | ||
127 | to_insert.push(space.into()); | ||
128 | to_insert.push(field.syntax().clone().into()); | ||
129 | to_insert.push(tokens::comma().into()); | ||
130 | |||
131 | macro_rules! after_l_curly { | ||
132 | () => {{ | ||
133 | let anchor = match self.l_curly() { | ||
134 | Some(it) => it, | ||
135 | None => return, | ||
136 | }; | ||
137 | InsertPosition::After(anchor) | ||
138 | }}; | ||
139 | } | ||
140 | |||
141 | macro_rules! after_field { | ||
142 | ($anchor:expr) => { | ||
143 | if let Some(comma) = $anchor | ||
144 | .syntax() | ||
145 | .siblings_with_tokens(Direction::Next) | ||
146 | .find(|it| it.kind() == T![,]) | ||
147 | { | ||
148 | InsertPosition::After(comma) | ||
149 | } else { | ||
150 | to_insert.insert(0, tokens::comma().into()); | ||
151 | InsertPosition::After($anchor.syntax().clone().into()) | ||
152 | } | ||
153 | }; | ||
154 | }; | ||
155 | |||
156 | let position = match position { | ||
157 | InsertPosition::First => after_l_curly!(), | ||
158 | InsertPosition::Last => { | ||
159 | if !is_multiline { | ||
160 | // don't insert comma before curly | ||
161 | to_insert.pop(); | ||
162 | } | ||
163 | match self.ast().fields().last() { | ||
164 | Some(it) => after_field!(it), | ||
165 | None => after_l_curly!(), | ||
166 | } | ||
167 | } | ||
168 | InsertPosition::Before(anchor) => { | ||
169 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
170 | } | ||
171 | InsertPosition::After(anchor) => after_field!(anchor), | ||
172 | }; | ||
173 | |||
174 | self.ast = self.insert_children(position, to_insert.iter().cloned()); | ||
175 | } | ||
176 | |||
177 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
178 | self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
179 | } | ||
180 | } | ||
181 | |||
182 | impl AstEditor<ast::ItemList> { | ||
183 | pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) { | ||
184 | if !self.ast().syntax().text().contains_char('\n') { | ||
185 | self.do_make_multiline(); | ||
186 | } | ||
187 | items.for_each(|it| self.append_item(it)); | ||
188 | } | ||
189 | |||
190 | pub fn append_item(&mut self, item: ast::ImplItem) { | ||
191 | let (indent, position) = match self.ast().impl_items().last() { | ||
192 | Some(it) => ( | ||
193 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
194 | InsertPosition::After(it.syntax().clone().into()), | ||
195 | ), | ||
196 | None => match self.l_curly() { | ||
197 | Some(it) => ( | ||
198 | " ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(), | ||
199 | InsertPosition::After(it), | ||
200 | ), | ||
201 | None => return, | ||
202 | }, | ||
203 | }; | ||
204 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
205 | let to_insert: ArrayVec<[SyntaxElement; 2]> = | ||
206 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
207 | self.ast = self.insert_children(position, to_insert.into_iter()); | ||
208 | } | ||
209 | |||
210 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
211 | self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
212 | } | ||
213 | } | ||
214 | |||
215 | impl AstEditor<ast::ImplItem> { | ||
216 | pub fn strip_attrs_and_docs(&mut self) { | ||
217 | while let Some(start) = self | ||
218 | .ast() | ||
219 | .syntax() | ||
220 | .children_with_tokens() | ||
221 | .find(|it| it.kind() == ATTR || it.kind() == COMMENT) | ||
222 | { | ||
223 | let end = match &start.next_sibling_or_token() { | ||
224 | Some(el) if el.kind() == WHITESPACE => el.clone(), | ||
225 | Some(_) | None => start.clone(), | ||
226 | }; | ||
227 | self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty()); | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | impl AstEditor<ast::TypeParam> { | ||
233 | pub fn remove_bounds(&mut self) -> &mut Self { | ||
234 | let colon = match self.ast.colon_token() { | ||
235 | Some(it) => it, | ||
236 | None => return self, | ||
237 | }; | ||
238 | let end = match self.ast.type_bound_list() { | ||
239 | Some(it) => it.syntax().clone().into(), | ||
240 | None => colon.clone().into(), | ||
241 | }; | ||
242 | self.ast = self.replace_children(RangeInclusive::new(colon.into(), end), iter::empty()); | ||
243 | self | ||
244 | } | ||
245 | } | ||