aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/ast_editor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/ast_editor.rs')
-rw-r--r--crates/ra_assists/src/ast_editor.rs245
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 @@
1use std::{iter, ops::RangeInclusive};
2
3use arrayvec::ArrayVec;
4use rustc_hash::FxHashMap;
5
6use ra_fmt::leading_indent;
7use ra_syntax::{
8 algo,
9 ast::{self, make::tokens, TypeBoundsOwner},
10 AstNode, Direction, InsertPosition, SyntaxElement,
11 SyntaxKind::*,
12 T,
13};
14use ra_text_edit::TextEditBuilder;
15
16pub struct AstEditor<N: AstNode> {
17 original_ast: N,
18 ast: N,
19}
20
21impl<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
104impl 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
182impl 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
215impl 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
232impl 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}