aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax/src')
-rw-r--r--crates/syntax/src/algo.rs582
-rw-r--r--crates/syntax/src/ast/make.rs3
-rw-r--r--crates/syntax/src/ast/node_ext.rs14
-rw-r--r--crates/syntax/src/display.rs83
-rw-r--r--crates/syntax/src/lib.rs1
5 files changed, 630 insertions, 53 deletions
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index ea199f9b8..065035fe6 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -2,11 +2,14 @@
2 2
3use std::{ 3use std::{
4 fmt, 4 fmt,
5 hash::BuildHasherDefault,
5 ops::{self, RangeInclusive}, 6 ops::{self, RangeInclusive},
6}; 7};
7 8
9use indexmap::IndexMap;
8use itertools::Itertools; 10use itertools::Itertools;
9use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
12use test_utils::mark;
10use text_edit::TextEditBuilder; 13use text_edit::TextEditBuilder;
11 14
12use crate::{ 15use crate::{
@@ -106,42 +109,56 @@ pub enum InsertPosition<T> {
106 After(T), 109 After(T),
107} 110}
108 111
112type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
113
114#[derive(Debug)]
109pub struct TreeDiff { 115pub struct TreeDiff {
110 replacements: FxHashMap<SyntaxElement, SyntaxElement>, 116 replacements: FxHashMap<SyntaxElement, SyntaxElement>,
117 deletions: Vec<SyntaxElement>,
118 // the vec as well as the indexmap are both here to preserve order
119 insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>,
111} 120}
112 121
113impl TreeDiff { 122impl TreeDiff {
114 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { 123 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
124 for (anchor, to) in self.insertions.iter() {
125 to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string()));
126 }
115 for (from, to) in self.replacements.iter() { 127 for (from, to) in self.replacements.iter() {
116 builder.replace(from.text_range(), to.to_string()) 128 builder.replace(from.text_range(), to.to_string())
117 } 129 }
130 for text_range in self.deletions.iter().map(SyntaxElement::text_range) {
131 builder.delete(text_range);
132 }
118 } 133 }
119 134
120 pub fn is_empty(&self) -> bool { 135 pub fn is_empty(&self) -> bool {
121 self.replacements.is_empty() 136 self.replacements.is_empty() && self.deletions.is_empty() && self.insertions.is_empty()
122 } 137 }
123} 138}
124 139
125/// Finds minimal the diff, which, applied to `from`, will result in `to`. 140/// Finds minimal the diff, which, applied to `from`, will result in `to`.
126/// 141///
127/// Specifically, returns a map whose keys are descendants of `from` and values 142/// Specifically, returns a structure that consists of a replacements, insertions and deletions
128/// are descendants of `to`, such that `replace_descendants(from, map) == to`. 143/// such that applying this map on `from` will result in `to`.
129/// 144///
130/// A trivial solution is a singleton map `{ from: to }`, but this function 145/// This function tries to find a fine-grained diff.
131/// tries to find a more fine-grained diff.
132pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { 146pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
133 let mut buf = FxHashMap::default(); 147 let mut diff = TreeDiff {
134 // FIXME: this is both horrible inefficient and gives larger than 148 replacements: FxHashMap::default(),
135 // necessary diff. I bet there's a cool algorithm to diff trees properly. 149 insertions: FxIndexMap::default(),
136 go(&mut buf, from.clone().into(), to.clone().into()); 150 deletions: Vec::new(),
137 return TreeDiff { replacements: buf }; 151 };
138 152 let (from, to) = (from.clone().into(), to.clone().into());
139 fn go( 153
140 buf: &mut FxHashMap<SyntaxElement, SyntaxElement>, 154 // FIXME: this is horrible inefficient. I bet there's a cool algorithm to diff trees properly.
141 lhs: SyntaxElement, 155 if !syntax_element_eq(&from, &to) {
142 rhs: SyntaxElement, 156 go(&mut diff, from, to);
143 ) { 157 }
144 if lhs.kind() == rhs.kind() 158 return diff;
159
160 fn syntax_element_eq(lhs: &SyntaxElement, rhs: &SyntaxElement) -> bool {
161 lhs.kind() == rhs.kind()
145 && lhs.text_range().len() == rhs.text_range().len() 162 && lhs.text_range().len() == rhs.text_range().len()
146 && match (&lhs, &rhs) { 163 && match (&lhs, &rhs) {
147 (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => { 164 (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => {
@@ -150,18 +167,47 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
150 (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), 167 (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
151 _ => false, 168 _ => false,
152 } 169 }
153 { 170 }
154 return; 171
155 } 172 fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) {
156 if let (Some(lhs), Some(rhs)) = (lhs.as_node(), rhs.as_node()) { 173 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) {
157 if lhs.children_with_tokens().count() == rhs.children_with_tokens().count() { 174 Some((lhs, rhs)) => (lhs, rhs),
158 for (lhs, rhs) in lhs.children_with_tokens().zip(rhs.children_with_tokens()) { 175 _ => {
159 go(buf, lhs, rhs) 176 mark::hit!(diff_node_token_replace);
160 } 177 diff.replacements.insert(lhs, rhs);
161 return; 178 return;
162 } 179 }
180 };
181
182 let mut rhs_children = rhs.children_with_tokens();
183 let mut lhs_children = lhs.children_with_tokens();
184 let mut last_lhs = None;
185 loop {
186 let lhs_child = lhs_children.next();
187 match (lhs_child.clone(), rhs_children.next()) {
188 (None, None) => break,
189 (None, Some(element)) => match last_lhs.clone() {
190 Some(prev) => {
191 mark::hit!(diff_insert);
192 diff.insertions.entry(prev).or_insert_with(Vec::new).push(element);
193 }
194 // first iteration, this means we got no anchor element to insert after
195 // therefor replace the parent node instead
196 None => {
197 mark::hit!(diff_replace_parent);
198 diff.replacements.insert(lhs.clone().into(), rhs.clone().into());
199 break;
200 }
201 },
202 (Some(element), None) => {
203 mark::hit!(diff_delete);
204 diff.deletions.push(element);
205 }
206 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {}
207 (Some(lhs_ele), Some(rhs_ele)) => go(diff, lhs_ele, rhs_ele),
208 }
209 last_lhs = lhs_child.or(last_lhs);
163 } 210 }
164 buf.insert(lhs, rhs);
165 } 211 }
166} 212}
167 213
@@ -243,11 +289,19 @@ fn _replace_children(
243 with_children(parent, new_children) 289 with_children(parent, new_children)
244} 290}
245 291
292#[derive(Debug, PartialEq, Eq, Hash)]
293enum InsertPos {
294 FirstChildOf(SyntaxNode),
295 // Before(SyntaxElement),
296 After(SyntaxElement),
297}
298
246#[derive(Default)] 299#[derive(Default)]
247pub struct SyntaxRewriter<'a> { 300pub struct SyntaxRewriter<'a> {
248 f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>, 301 f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>,
249 //FIXME: add debug_assertions that all elements are in fact from the same file. 302 //FIXME: add debug_assertions that all elements are in fact from the same file.
250 replacements: FxHashMap<SyntaxElement, Replacement>, 303 replacements: FxHashMap<SyntaxElement, Replacement>,
304 insertions: IndexMap<InsertPos, Vec<SyntaxElement>>,
251} 305}
252 306
253impl fmt::Debug for SyntaxRewriter<'_> { 307impl fmt::Debug for SyntaxRewriter<'_> {
@@ -258,13 +312,96 @@ impl fmt::Debug for SyntaxRewriter<'_> {
258 312
259impl<'a> SyntaxRewriter<'a> { 313impl<'a> SyntaxRewriter<'a> {
260 pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> { 314 pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> {
261 SyntaxRewriter { f: Some(Box::new(f)), replacements: FxHashMap::default() } 315 SyntaxRewriter {
316 f: Some(Box::new(f)),
317 replacements: FxHashMap::default(),
318 insertions: IndexMap::default(),
319 }
262 } 320 }
263 pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { 321 pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) {
264 let what = what.clone().into(); 322 let what = what.clone().into();
265 let replacement = Replacement::Delete; 323 let replacement = Replacement::Delete;
266 self.replacements.insert(what, replacement); 324 self.replacements.insert(what, replacement);
267 } 325 }
326 pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>(
327 &mut self,
328 before: &T,
329 what: &U,
330 ) {
331 let before = before.clone().into();
332 let pos = match before.prev_sibling_or_token() {
333 Some(sibling) => InsertPos::After(sibling),
334 None => match before.parent() {
335 Some(parent) => InsertPos::FirstChildOf(parent),
336 None => return,
337 },
338 };
339 self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into());
340 }
341 pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>(
342 &mut self,
343 after: &T,
344 what: &U,
345 ) {
346 self.insertions
347 .entry(InsertPos::After(after.clone().into()))
348 .or_insert_with(Vec::new)
349 .push(what.clone().into());
350 }
351 pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>(
352 &mut self,
353 parent: &T,
354 what: &U,
355 ) {
356 self.insertions
357 .entry(InsertPos::FirstChildOf(parent.clone().into()))
358 .or_insert_with(Vec::new)
359 .push(what.clone().into());
360 }
361 pub fn insert_many_before<
362 T: Clone + Into<SyntaxElement>,
363 U: IntoIterator<Item = SyntaxElement>,
364 >(
365 &mut self,
366 before: &T,
367 what: U,
368 ) {
369 let before = before.clone().into();
370 let pos = match before.prev_sibling_or_token() {
371 Some(sibling) => InsertPos::After(sibling),
372 None => match before.parent() {
373 Some(parent) => InsertPos::FirstChildOf(parent),
374 None => return,
375 },
376 };
377 self.insertions.entry(pos).or_insert_with(Vec::new).extend(what);
378 }
379 pub fn insert_many_after<
380 T: Clone + Into<SyntaxElement>,
381 U: IntoIterator<Item = SyntaxElement>,
382 >(
383 &mut self,
384 after: &T,
385 what: U,
386 ) {
387 self.insertions
388 .entry(InsertPos::After(after.clone().into()))
389 .or_insert_with(Vec::new)
390 .extend(what);
391 }
392 pub fn insert_many_as_first_children<
393 T: Clone + Into<SyntaxNode>,
394 U: IntoIterator<Item = SyntaxElement>,
395 >(
396 &mut self,
397 parent: &T,
398 what: U,
399 ) {
400 self.insertions
401 .entry(InsertPos::FirstChildOf(parent.clone().into()))
402 .or_insert_with(Vec::new)
403 .extend(what)
404 }
268 pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { 405 pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) {
269 let what = what.clone().into(); 406 let what = what.clone().into();
270 let replacement = Replacement::Single(with.clone().into()); 407 let replacement = Replacement::Single(with.clone().into());
@@ -284,7 +421,7 @@ impl<'a> SyntaxRewriter<'a> {
284 } 421 }
285 422
286 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { 423 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
287 if self.f.is_none() && self.replacements.is_empty() { 424 if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() {
288 return node.clone(); 425 return node.clone();
289 } 426 }
290 self.rewrite_children(node) 427 self.rewrite_children(node)
@@ -300,14 +437,22 @@ impl<'a> SyntaxRewriter<'a> {
300 /// 437 ///
301 /// Returns `None` when there are no replacements. 438 /// Returns `None` when there are no replacements.
302 pub fn rewrite_root(&self) -> Option<SyntaxNode> { 439 pub fn rewrite_root(&self) -> Option<SyntaxNode> {
440 fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode {
441 match element {
442 SyntaxElement::Node(it) => it.clone(),
443 SyntaxElement::Token(it) => it.parent(),
444 }
445 }
446
303 assert!(self.f.is_none()); 447 assert!(self.f.is_none());
304 self.replacements 448 self.replacements
305 .keys() 449 .keys()
306 .map(|element| match element { 450 .map(element_to_node_or_parent)
307 SyntaxElement::Node(it) => it.clone(), 451 .chain(self.insertions.keys().map(|pos| match pos {
308 SyntaxElement::Token(it) => it.parent(), 452 InsertPos::FirstChildOf(it) => it.clone(),
309 }) 453 InsertPos::After(it) => element_to_node_or_parent(it),
310 // If we only have one replacement, we must return its parent node, since `rewrite` does 454 }))
455 // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does
311 // not replace the node passed to it. 456 // not replace the node passed to it.
312 .map(|it| it.parent().unwrap_or(it)) 457 .map(|it| it.parent().unwrap_or(it))
313 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) 458 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
@@ -321,9 +466,16 @@ impl<'a> SyntaxRewriter<'a> {
321 self.replacements.get(element).cloned() 466 self.replacements.get(element).cloned()
322 } 467 }
323 468
469 fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> {
470 self.insertions.get(pos).map(|insertions| insertions.iter().cloned())
471 }
472
324 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { 473 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode {
325 // FIXME: this could be made much faster. 474 // FIXME: this could be made much faster.
326 let mut new_children = Vec::new(); 475 let mut new_children = Vec::new();
476 if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) {
477 new_children.extend(elements.map(element_to_green));
478 }
327 for child in node.children_with_tokens() { 479 for child in node.children_with_tokens() {
328 self.rewrite_self(&mut new_children, &child); 480 self.rewrite_self(&mut new_children, &child);
329 } 481 }
@@ -337,34 +489,45 @@ impl<'a> SyntaxRewriter<'a> {
337 ) { 489 ) {
338 if let Some(replacement) = self.replacement(&element) { 490 if let Some(replacement) = self.replacement(&element) {
339 match replacement { 491 match replacement {
340 Replacement::Single(NodeOrToken::Node(it)) => { 492 Replacement::Single(element) => acc.push(element_to_green(element)),
341 acc.push(NodeOrToken::Node(it.green().clone()))
342 }
343 Replacement::Single(NodeOrToken::Token(it)) => {
344 acc.push(NodeOrToken::Token(it.green().clone()))
345 }
346 Replacement::Many(replacements) => { 493 Replacement::Many(replacements) => {
347 acc.extend(replacements.iter().map(|it| match it { 494 acc.extend(replacements.into_iter().map(element_to_green))
348 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
349 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
350 }))
351 } 495 }
352 Replacement::Delete => (), 496 Replacement::Delete => (),
353 }; 497 };
354 return; 498 } else {
499 match element {
500 NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().clone())),
501 NodeOrToken::Node(it) => {
502 acc.push(NodeOrToken::Node(self.rewrite_children(it).green().clone()));
503 }
504 }
505 }
506 if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) {
507 acc.extend(elements.map(element_to_green));
355 } 508 }
356 let res = match element { 509 }
357 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), 510}
358 NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), 511
359 }; 512fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
360 acc.push(res) 513 match element {
514 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
515 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
361 } 516 }
362} 517}
363 518
364impl ops::AddAssign for SyntaxRewriter<'_> { 519impl ops::AddAssign for SyntaxRewriter<'_> {
365 fn add_assign(&mut self, rhs: SyntaxRewriter) { 520 fn add_assign(&mut self, rhs: SyntaxRewriter) {
366 assert!(rhs.f.is_none()); 521 assert!(rhs.f.is_none());
367 self.replacements.extend(rhs.replacements) 522 self.replacements.extend(rhs.replacements);
523 for (pos, insertions) in rhs.insertions.into_iter() {
524 match self.insertions.entry(pos) {
525 indexmap::map::Entry::Occupied(mut occupied) => {
526 occupied.get_mut().extend(insertions)
527 }
528 indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)),
529 }
530 }
368 } 531 }
369} 532}
370 533
@@ -404,3 +567,322 @@ fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, row
404 NodeOrToken::Token(it) => it.green().clone().into(), 567 NodeOrToken::Token(it) => it.green().clone().into(),
405 } 568 }
406} 569}
570
571#[cfg(test)]
572mod tests {
573 use expect_test::{expect, Expect};
574 use itertools::Itertools;
575 use parser::SyntaxKind;
576 use test_utils::mark;
577 use text_edit::TextEdit;
578
579 use crate::{AstNode, SyntaxElement};
580
581 #[test]
582 fn replace_node_token() {
583 mark::check!(diff_node_token_replace);
584 check_diff(
585 r#"use node;"#,
586 r#"ident"#,
587 expect![[r#"
588 insertions:
589
590
591
592 replacements:
593
594 Line 0: Token([email protected] "use") -> ident
595
596 deletions:
597
598 Line 1: " "
599 Line 1: node
600 Line 1: ;
601 "#]],
602 );
603 }
604
605 #[test]
606 fn insert() {
607 mark::check!(diff_insert);
608 check_diff(
609 r#"use foo;"#,
610 r#"use foo;
611use bar;"#,
612 expect![[r#"
613 insertions:
614
615 Line 0: Node([email protected])
616 -> "\n"
617 -> use bar;
618
619 replacements:
620
621
622
623 deletions:
624
625
626 "#]],
627 );
628 }
629
630 #[test]
631 fn replace_parent() {
632 mark::check!(diff_replace_parent);
633 check_diff(
634 r#""#,
635 r#"use foo::bar;"#,
636 expect![[r#"
637 insertions:
638
639
640
641 replacements:
642
643 Line 0: Node([email protected]) -> use foo::bar;
644
645 deletions:
646
647
648 "#]],
649 );
650 }
651
652 #[test]
653 fn delete() {
654 mark::check!(diff_delete);
655 check_diff(
656 r#"use foo;
657 use bar;"#,
658 r#"use foo;"#,
659 expect![[r#"
660 insertions:
661
662
663
664 replacements:
665
666
667
668 deletions:
669
670 Line 1: "\n "
671 Line 2: use bar;
672 "#]],
673 );
674 }
675
676 #[test]
677 fn insert_use() {
678 check_diff(
679 r#"
680use expect_test::{expect, Expect};
681
682use crate::AstNode;
683"#,
684 r#"
685use expect_test::{expect, Expect};
686use text_edit::TextEdit;
687
688use crate::AstNode;
689"#,
690 expect![[r#"
691 insertions:
692
693 Line 4: Token([email protected] "\n")
694 -> use crate::AstNode;
695 -> "\n"
696
697 replacements:
698
699 Line 2: Token([email protected] "\n\n") -> "\n"
700 Line 4: Token([email protected] "crate") -> text_edit
701 Line 4: Token([email protected] "AstNode") -> TextEdit
702 Line 4: Token([email protected] "\n") -> "\n\n"
703
704 deletions:
705
706
707 "#]],
708 )
709 }
710
711 #[test]
712 fn remove_use() {
713 check_diff(
714 r#"
715use expect_test::{expect, Expect};
716use text_edit::TextEdit;
717
718use crate::AstNode;
719"#,
720 r#"
721use expect_test::{expect, Expect};
722
723use crate::AstNode;
724"#,
725 expect![[r#"
726 insertions:
727
728
729
730 replacements:
731
732 Line 2: Token([email protected] "\n") -> "\n\n"
733 Line 3: Node([email protected]) -> crate
734 Line 3: Token([email protected] "TextEdit") -> AstNode
735 Line 3: Token([email protected] "\n\n") -> "\n"
736
737 deletions:
738
739 Line 4: use crate::AstNode;
740 Line 5: "\n"
741 "#]],
742 )
743 }
744
745 #[test]
746 fn merge_use() {
747 check_diff(
748 r#"
749use std::{
750 fmt,
751 hash::BuildHasherDefault,
752 ops::{self, RangeInclusive},
753};
754"#,
755 r#"
756use std::fmt;
757use std::hash::BuildHasherDefault;
758use std::ops::{self, RangeInclusive};
759"#,
760 expect![[r#"
761 insertions:
762
763 Line 2: Node([email protected])
764 -> ::
765 -> fmt
766 Line 6: Token([email protected] "\n")
767 -> use std::hash::BuildHasherDefault;
768 -> "\n"
769 -> use std::ops::{self, RangeInclusive};
770 -> "\n"
771
772 replacements:
773
774 Line 2: Token([email protected] "std") -> std
775
776 deletions:
777
778 Line 2: ::
779 Line 2: {
780 fmt,
781 hash::BuildHasherDefault,
782 ops::{self, RangeInclusive},
783 }
784 "#]],
785 )
786 }
787
788 #[test]
789 fn early_return_assist() {
790 check_diff(
791 r#"
792fn main() {
793 if let Ok(x) = Err(92) {
794 foo(x);
795 }
796}
797 "#,
798 r#"
799fn main() {
800 let x = match Err(92) {
801 Ok(it) => it,
802 _ => return,
803 };
804 foo(x);
805}
806 "#,
807 expect![[r#"
808 insertions:
809
810 Line 3: Node([email protected])
811 -> " "
812 -> match Err(92) {
813 Ok(it) => it,
814 _ => return,
815 }
816 -> ;
817 Line 5: Token([email protected] "}")
818 -> "\n"
819 -> }
820
821 replacements:
822
823 Line 3: Token([email protected] "if") -> let
824 Line 3: Token([email protected] "let") -> x
825 Line 3: Node([email protected]) -> =
826 Line 5: Token([email protected] "\n") -> "\n "
827 Line 5: Token([email protected] "}") -> foo(x);
828
829 deletions:
830
831 Line 3: " "
832 Line 3: Ok(x)
833 Line 3: " "
834 Line 3: =
835 Line 3: " "
836 Line 3: Err(92)
837 "#]],
838 )
839 }
840
841 fn check_diff(from: &str, to: &str, expected_diff: Expect) {
842 let from_node = crate::SourceFile::parse(from).tree().syntax().clone();
843 let to_node = crate::SourceFile::parse(to).tree().syntax().clone();
844 let diff = super::diff(&from_node, &to_node);
845
846 let line_number =
847 |syn: &SyntaxElement| from[..syn.text_range().start().into()].lines().count();
848
849 let fmt_syntax = |syn: &SyntaxElement| match syn.kind() {
850 SyntaxKind::WHITESPACE => format!("{:?}", syn.to_string()),
851 _ => format!("{}", syn),
852 };
853
854 let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| {
855 f(&format!(
856 "Line {}: {:?}\n-> {}",
857 line_number(k),
858 k,
859 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
860 ))
861 });
862
863 let replacements = diff
864 .replacements
865 .iter()
866 .sorted_by_key(|(syntax, _)| syntax.text_range().start())
867 .format_with("\n", |(k, v), f| {
868 f(&format!("Line {}: {:?} -> {}", line_number(k), k, fmt_syntax(v)))
869 });
870
871 let deletions = diff
872 .deletions
873 .iter()
874 .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), &fmt_syntax(v))));
875
876 let actual = format!(
877 "insertions:\n\n{}\n\nreplacements:\n\n{}\n\ndeletions:\n\n{}\n",
878 insertions, replacements, deletions
879 );
880 expected_diff.assert_eq(&actual);
881
882 let mut from = from.to_owned();
883 let mut text_edit = TextEdit::builder();
884 diff.into_text_edit(&mut text_edit);
885 text_edit.finish().apply(&mut from);
886 assert_eq!(&*from, to, "diff did not turn `from` to `to`");
887 }
888}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 74dbdfaf7..5b06cb767 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -172,6 +172,9 @@ pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
172pub fn expr_method_call(receiver: ast::Expr, method: &str, arg_list: ast::ArgList) -> ast::Expr { 172pub fn expr_method_call(receiver: ast::Expr, method: &str, arg_list: ast::ArgList) -> ast::Expr {
173 expr_from_text(&format!("{}.{}{}", receiver, method, arg_list)) 173 expr_from_text(&format!("{}.{}{}", receiver, method, arg_list))
174} 174}
175pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
176 expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) })
177}
175fn expr_from_text(text: &str) -> ast::Expr { 178fn expr_from_text(text: &str) -> ast::Expr {
176 ast_from_text(&format!("const C: () = {};", text)) 179 ast_from_text(&format!("const C: () = {};", text))
177} 180}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 50c1c157d..c5cd1c504 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -7,7 +7,7 @@ use itertools::Itertools;
7use parser::SyntaxKind; 7use parser::SyntaxKind;
8 8
9use crate::{ 9use crate::{
10 ast::{self, support, AstNode, NameOwner, SyntaxNode}, 10 ast::{self, support, token_ext::HasStringValue, AstNode, AstToken, NameOwner, SyntaxNode},
11 SmolStr, SyntaxElement, SyntaxToken, T, 11 SmolStr, SyntaxElement, SyntaxToken, T,
12}; 12};
13 13
@@ -53,8 +53,16 @@ impl ast::Attr {
53 pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> { 53 pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> {
54 let lit = self.literal()?; 54 let lit = self.literal()?;
55 let key = self.simple_name()?; 55 let key = self.simple_name()?;
56 // FIXME: escape? raw string? 56 let value_token = lit.syntax().first_token()?;
57 let value = lit.syntax().first_token()?.text().trim_matches('"').into(); 57
58 let value: SmolStr = if let Some(s) = ast::String::cast(value_token.clone()) {
59 s.value()?.into()
60 } else if let Some(s) = ast::RawString::cast(value_token) {
61 s.value()?.into()
62 } else {
63 return None;
64 };
65
58 Some((key, value)) 66 Some((key, value))
59 } 67 }
60 68
diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs
new file mode 100644
index 000000000..8d2c7eae4
--- /dev/null
+++ b/crates/syntax/src/display.rs
@@ -0,0 +1,83 @@
1//! This module contains utilities for turning SyntaxNodes and HIR types
2//! into types that may be used to render in a UI.
3
4use crate::{
5 ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
6 SyntaxKind::{ATTR, COMMENT},
7};
8
9use ast::VisibilityOwner;
10use stdx::format_to;
11
12pub fn function_declaration(node: &ast::Fn) -> String {
13 let mut buf = String::new();
14 if let Some(vis) = node.visibility() {
15 format_to!(buf, "{} ", vis);
16 }
17 if node.async_token().is_some() {
18 format_to!(buf, "async ");
19 }
20 if node.const_token().is_some() {
21 format_to!(buf, "const ");
22 }
23 if node.unsafe_token().is_some() {
24 format_to!(buf, "unsafe ");
25 }
26 if let Some(abi) = node.abi() {
27 // Keyword `extern` is included in the string.
28 format_to!(buf, "{} ", abi);
29 }
30 if let Some(name) = node.name() {
31 format_to!(buf, "fn {}", name)
32 }
33 if let Some(type_params) = node.generic_param_list() {
34 format_to!(buf, "{}", type_params);
35 }
36 if let Some(param_list) = node.param_list() {
37 let params: Vec<String> = param_list
38 .self_param()
39 .into_iter()
40 .map(|self_param| self_param.to_string())
41 .chain(param_list.params().map(|param| param.to_string()))
42 .collect();
43 // Useful to inline parameters
44 format_to!(buf, "({})", params.join(", "));
45 }
46 if let Some(ret_type) = node.ret_type() {
47 if ret_type.ty().is_some() {
48 format_to!(buf, " {}", ret_type);
49 }
50 }
51 if let Some(where_clause) = node.where_clause() {
52 format_to!(buf, "\n{}", where_clause);
53 }
54 buf
55}
56
57pub fn const_label(node: &ast::Const) -> String {
58 let label: String = node
59 .syntax()
60 .children_with_tokens()
61 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
62 .map(|node| node.to_string())
63 .collect();
64
65 label.trim().to_owned()
66}
67
68pub fn type_label(node: &ast::TypeAlias) -> String {
69 let label: String = node
70 .syntax()
71 .children_with_tokens()
72 .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
73 .map(|node| node.to_string())
74 .collect();
75
76 label.trim().to_owned()
77}
78
79pub fn macro_label(node: &ast::MacroCall) -> String {
80 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
81 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
82 format!("{}macro_rules! {}", vis, name)
83}
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 7f8da66af..849a1cdd6 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -32,6 +32,7 @@ mod ptr;
32#[cfg(test)] 32#[cfg(test)]
33mod tests; 33mod tests;
34 34
35pub mod display;
35pub mod algo; 36pub mod algo;
36pub mod ast; 37pub mod ast;
37#[doc(hidden)] 38#[doc(hidden)]