aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide_db/src/helpers/merge_imports.rs4
-rw-r--r--crates/syntax/src/algo.rs106
-rw-r--r--crates/syntax/src/ast/edit.rs51
3 files changed, 7 insertions, 154 deletions
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 475ef99b5..8fb40e837 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -42,10 +42,12 @@ pub fn try_merge_imports(
42 return None; 42 return None;
43 } 43 }
44 44
45 let lhs = lhs.clone_subtree().clone_for_update();
45 let lhs_tree = lhs.use_tree()?; 46 let lhs_tree = lhs.use_tree()?;
46 let rhs_tree = rhs.use_tree()?; 47 let rhs_tree = rhs.use_tree()?;
47 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; 48 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
48 Some(lhs.with_use_tree(merged).clone_for_update()) 49 ted::replace(lhs_tree.syntax(), merged.syntax());
50 Some(lhs)
49} 51}
50 52
51pub fn try_merge_trees( 53pub fn try_merge_trees(
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 825ea3185..241713c48 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{fmt, hash::BuildHasherDefault, ops::RangeInclusive}; 3use std::{hash::BuildHasherDefault, ops::RangeInclusive};
4 4
5use indexmap::IndexMap; 5use indexmap::IndexMap;
6use itertools::Itertools; 6use itertools::Itertools;
@@ -330,110 +330,6 @@ fn _replace_children(
330 with_children(parent, new_children) 330 with_children(parent, new_children)
331} 331}
332 332
333#[derive(Debug, PartialEq, Eq, Hash)]
334enum InsertPos {
335 FirstChildOf(SyntaxNode),
336 After(SyntaxElement),
337}
338
339#[derive(Default)]
340pub(crate) struct SyntaxRewriter<'a> {
341 //FIXME: add debug_assertions that all elements are in fact from the same file.
342 replacements: FxHashMap<SyntaxElement, Replacement>,
343 insertions: IndexMap<InsertPos, Vec<SyntaxElement>>,
344 _pd: std::marker::PhantomData<&'a ()>,
345}
346
347impl fmt::Debug for SyntaxRewriter<'_> {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 f.debug_struct("SyntaxRewriter")
350 .field("replacements", &self.replacements)
351 .field("insertions", &self.insertions)
352 .finish()
353 }
354}
355
356impl SyntaxRewriter<'_> {
357 pub(crate) fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) {
358 let what = what.clone().into();
359 let replacement = Replacement::Single(with.clone().into());
360 self.replacements.insert(what, replacement);
361 }
362
363 pub(crate) fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
364 let _p = profile::span("rewrite");
365
366 if self.replacements.is_empty() && self.insertions.is_empty() {
367 return node.clone();
368 }
369 let green = self.rewrite_children(node);
370 with_green(node, green)
371 }
372
373 pub(crate) fn rewrite_ast<N: AstNode>(self, node: &N) -> N {
374 N::cast(self.rewrite(node.syntax())).unwrap()
375 }
376
377 fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> {
378 self.replacements.get(element).cloned()
379 }
380
381 fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> {
382 self.insertions.get(pos).map(|insertions| insertions.iter().cloned())
383 }
384
385 fn rewrite_children(&self, node: &SyntaxNode) -> rowan::GreenNode {
386 let _p = profile::span("rewrite_children");
387
388 // FIXME: this could be made much faster.
389 let mut new_children = Vec::new();
390 if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) {
391 new_children.extend(elements.map(element_to_green));
392 }
393 for child in node.children_with_tokens() {
394 self.rewrite_self(&mut new_children, &child);
395 }
396
397 rowan::GreenNode::new(rowan::SyntaxKind(node.kind() as u16), new_children)
398 }
399
400 fn rewrite_self(
401 &self,
402 acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
403 element: &SyntaxElement,
404 ) {
405 let _p = profile::span("rewrite_self");
406
407 if let Some(replacement) = self.replacement(&element) {
408 match replacement {
409 Replacement::Single(element) => acc.push(element_to_green(element)),
410 };
411 } else {
412 match element {
413 NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().to_owned())),
414 NodeOrToken::Node(it) => {
415 acc.push(NodeOrToken::Node(self.rewrite_children(it)));
416 }
417 }
418 }
419 if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) {
420 acc.extend(elements.map(element_to_green));
421 }
422 }
423}
424
425fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
426 match element {
427 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().into_owned()),
428 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()),
429 }
430}
431
432#[derive(Clone, Debug)]
433enum Replacement {
434 Single(SyntaxElement),
435}
436
437fn with_children( 333fn with_children(
438 parent: &SyntaxNode, 334 parent: &SyntaxNode,
439 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, 335 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 78c0dc102..10ec94cd2 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -2,18 +2,18 @@
2//! immutable, all function here return a fresh copy of the tree, instead of 2//! immutable, all function here return a fresh copy of the tree, instead of
3//! doing an in-place modification. 3//! doing an in-place modification.
4use std::{ 4use std::{
5 array, fmt, iter, 5 fmt, iter,
6 ops::{self, RangeInclusive}, 6 ops::{self, RangeInclusive},
7}; 7};
8 8
9use arrayvec::ArrayVec; 9use arrayvec::ArrayVec;
10 10
11use crate::{ 11use crate::{
12 algo::{self, SyntaxRewriter}, 12 algo,
13 ast::{ 13 ast::{
14 self, 14 self,
15 make::{self, tokens}, 15 make::{self, tokens},
16 AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, 16 AstNode, TypeBoundsOwner,
17 }, 17 },
18 ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind, 18 ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind,
19 SyntaxKind::{ATTR, COMMENT, WHITESPACE}, 19 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -46,18 +46,6 @@ impl ast::Fn {
46 to_insert.push(body.syntax().clone().into()); 46 to_insert.push(body.syntax().clone().into());
47 self.replace_children(single_node(old_body_or_semi), to_insert) 47 self.replace_children(single_node(old_body_or_semi), to_insert)
48 } 48 }
49
50 #[must_use]
51 pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn {
52 if let Some(old) = self.generic_param_list() {
53 return self.replace_descendant(old, generic_args);
54 }
55
56 let anchor = self.name().expect("The function must have a name").syntax().clone();
57
58 let to_insert = [generic_args.syntax().clone().into()];
59 self.insert_children(InsertPosition::After(anchor.into()), array::IntoIter::new(to_insert))
60 }
61} 49}
62 50
63fn make_multiline<N>(node: N) -> N 51fn make_multiline<N>(node: N) -> N
@@ -313,33 +301,7 @@ impl ast::PathSegment {
313 } 301 }
314} 302}
315 303
316impl ast::Use {
317 #[must_use]
318 pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::Use {
319 if let Some(old) = self.use_tree() {
320 return self.replace_descendant(old, use_tree);
321 }
322 self.clone()
323 }
324}
325
326impl ast::UseTree { 304impl ast::UseTree {
327 #[must_use]
328 pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
329 if let Some(old) = self.path() {
330 return self.replace_descendant(old, path);
331 }
332 self.clone()
333 }
334
335 #[must_use]
336 pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
337 if let Some(old) = self.use_tree_list() {
338 return self.replace_descendant(old, use_tree_list);
339 }
340 self.clone()
341 }
342
343 /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. 305 /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items.
344 #[must_use] 306 #[must_use]
345 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { 307 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
@@ -664,13 +626,6 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
664 let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert); 626 let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert);
665 Self::cast(new_syntax).unwrap() 627 Self::cast(new_syntax).unwrap()
666 } 628 }
667
668 #[must_use]
669 fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self {
670 let mut rewriter = SyntaxRewriter::default();
671 rewriter.replace(old.syntax(), new.syntax());
672 rewriter.rewrite_ast(self)
673 }
674 fn indent_level(&self) -> IndentLevel { 629 fn indent_level(&self) -> IndentLevel {
675 IndentLevel::from_node(self.syntax()) 630 IndentLevel::from_node(self.syntax())
676 } 631 }