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