diff options
Diffstat (limited to 'crates/ra_syntax/src/algo.rs')
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 138 |
1 files changed, 109 insertions, 29 deletions
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index ffdbdc767..4d463a3ef 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::ops::RangeInclusive; | 3 | use std::{ |
4 | fmt, | ||
5 | ops::{self, RangeInclusive}, | ||
6 | }; | ||
4 | 7 | ||
5 | use itertools::Itertools; | 8 | use itertools::Itertools; |
6 | use ra_text_edit::TextEditBuilder; | 9 | use ra_text_edit::TextEditBuilder; |
@@ -222,44 +225,121 @@ fn _replace_children( | |||
222 | with_children(parent, new_children) | 225 | with_children(parent, new_children) |
223 | } | 226 | } |
224 | 227 | ||
225 | /// Replaces descendants in the node, according to the mapping. | 228 | #[derive(Default)] |
226 | /// | 229 | pub struct SyntaxRewriter<'a> { |
227 | /// This is a type-unsafe low-level editing API, if you need to use it, prefer | 230 | f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>, |
228 | /// to create a type-safe abstraction on top of it instead. | 231 | //FIXME: add debug_assertions that all elements are in fact from the same file. |
229 | pub fn replace_descendants( | 232 | replacements: FxHashMap<SyntaxElement, Replacement>, |
230 | parent: &SyntaxNode, | ||
231 | map: impl Fn(&SyntaxElement) -> Option<SyntaxElement>, | ||
232 | ) -> SyntaxNode { | ||
233 | _replace_descendants(parent, &map) | ||
234 | } | 233 | } |
235 | 234 | ||
236 | fn _replace_descendants( | 235 | impl fmt::Debug for SyntaxRewriter<'_> { |
237 | parent: &SyntaxNode, | 236 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
238 | map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>, | 237 | f.debug_struct("SyntaxRewriter").field("replacements", &self.replacements).finish() |
239 | ) -> SyntaxNode { | 238 | } |
240 | // FIXME: this could be made much faster. | 239 | } |
241 | let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::<Vec<_>>(); | ||
242 | return with_children(parent, new_children); | ||
243 | 240 | ||
244 | fn go( | 241 | impl<'a> SyntaxRewriter<'a> { |
245 | map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>, | 242 | pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> { |
246 | element: SyntaxElement, | 243 | SyntaxRewriter { f: Some(Box::new(f)), replacements: FxHashMap::default() } |
247 | ) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { | 244 | } |
248 | if let Some(replacement) = map(&element) { | 245 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { |
246 | let what = what.clone().into(); | ||
247 | let replacement = Replacement::Delete; | ||
248 | self.replacements.insert(what, replacement); | ||
249 | } | ||
250 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | ||
251 | let what = what.clone().into(); | ||
252 | let replacement = Replacement::Single(with.clone().into()); | ||
253 | self.replacements.insert(what, replacement); | ||
254 | } | ||
255 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | ||
256 | self.replace(what.syntax(), with.syntax()) | ||
257 | } | ||
258 | |||
259 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | ||
260 | if self.f.is_none() && self.replacements.is_empty() { | ||
261 | return node.clone(); | ||
262 | } | ||
263 | self.rewrite_children(node) | ||
264 | } | ||
265 | |||
266 | pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N { | ||
267 | N::cast(self.rewrite(node.syntax())).unwrap() | ||
268 | } | ||
269 | |||
270 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { | ||
271 | assert!(self.f.is_none()); | ||
272 | self.replacements | ||
273 | .keys() | ||
274 | .map(|element| match element { | ||
275 | SyntaxElement::Node(it) => it.clone(), | ||
276 | SyntaxElement::Token(it) => it.parent(), | ||
277 | }) | ||
278 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) | ||
279 | } | ||
280 | |||
281 | fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> { | ||
282 | if let Some(f) = &self.f { | ||
283 | assert!(self.replacements.is_empty()); | ||
284 | return f(element).map(Replacement::Single); | ||
285 | } | ||
286 | self.replacements.get(element).cloned() | ||
287 | } | ||
288 | |||
289 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | ||
290 | // FIXME: this could be made much faster. | ||
291 | let new_children = | ||
292 | node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>(); | ||
293 | with_children(node, new_children) | ||
294 | } | ||
295 | |||
296 | fn rewrite_self( | ||
297 | &self, | ||
298 | element: &SyntaxElement, | ||
299 | ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> { | ||
300 | if let Some(replacement) = self.replacement(&element) { | ||
249 | return match replacement { | 301 | return match replacement { |
250 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | 302 | Replacement::Single(NodeOrToken::Node(it)) => { |
251 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 303 | Some(NodeOrToken::Node(it.green().clone())) |
304 | } | ||
305 | Replacement::Single(NodeOrToken::Token(it)) => { | ||
306 | Some(NodeOrToken::Token(it.green().clone())) | ||
307 | } | ||
308 | Replacement::Delete => None, | ||
252 | }; | 309 | }; |
253 | } | 310 | } |
254 | match element { | 311 | let res = match element { |
255 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 312 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), |
256 | NodeOrToken::Node(it) => { | 313 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), |
257 | NodeOrToken::Node(_replace_descendants(&it, map).green().clone()) | 314 | }; |
258 | } | 315 | Some(res) |
259 | } | 316 | } |
317 | } | ||
318 | |||
319 | impl<'a> ops::AddAssign for SyntaxRewriter<'_> { | ||
320 | fn add_assign(&mut self, rhs: SyntaxRewriter) { | ||
321 | assert!(rhs.f.is_none()); | ||
322 | self.replacements.extend(rhs.replacements) | ||
260 | } | 323 | } |
261 | } | 324 | } |
262 | 325 | ||
326 | #[derive(Clone, Debug)] | ||
327 | enum Replacement { | ||
328 | Delete, | ||
329 | Single(SyntaxElement), | ||
330 | } | ||
331 | |||
332 | /// Replaces descendants in the node, according to the mapping. | ||
333 | /// | ||
334 | /// This is a type-unsafe low-level editing API, if you need to use it, prefer | ||
335 | /// to create a type-safe abstraction on top of it instead. | ||
336 | pub fn _replace_descendants( | ||
337 | parent: &SyntaxNode, | ||
338 | map: impl Fn(&SyntaxElement) -> Option<SyntaxElement>, | ||
339 | ) -> SyntaxNode { | ||
340 | SyntaxRewriter::from_fn(map).rewrite(parent) | ||
341 | } | ||
342 | |||
263 | fn with_children( | 343 | fn with_children( |
264 | parent: &SyntaxNode, | 344 | parent: &SyntaxNode, |
265 | new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | 345 | new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, |