aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax/src')
-rw-r--r--crates/syntax/src/algo.rs125
-rw-r--r--crates/syntax/src/ast.rs19
-rw-r--r--crates/syntax/src/ast/token_ext.rs51
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs27
4 files changed, 142 insertions, 80 deletions
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 30adb7217..320c430c9 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -111,18 +111,28 @@ pub enum InsertPosition<T> {
111 111
112type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; 112type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
113 113
114#[derive(Debug, Hash, PartialEq, Eq)]
115enum TreeDiffInsertPos {
116 After(SyntaxElement),
117 AsFirstChild(SyntaxElement),
118}
119
114#[derive(Debug)] 120#[derive(Debug)]
115pub struct TreeDiff { 121pub struct TreeDiff {
116 replacements: FxHashMap<SyntaxElement, SyntaxElement>, 122 replacements: FxHashMap<SyntaxElement, SyntaxElement>,
117 deletions: Vec<SyntaxElement>, 123 deletions: Vec<SyntaxElement>,
118 // the vec as well as the indexmap are both here to preserve order 124 // the vec as well as the indexmap are both here to preserve order
119 insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>, 125 insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>,
120} 126}
121 127
122impl TreeDiff { 128impl TreeDiff {
123 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { 129 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
124 for (anchor, to) in self.insertions.iter() { 130 for (anchor, to) in self.insertions.iter() {
125 to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string())); 131 let offset = match anchor {
132 TreeDiffInsertPos::After(it) => it.text_range().end(),
133 TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(),
134 };
135 to.iter().for_each(|to| builder.insert(offset, to.to_string()));
126 } 136 }
127 for (from, to) in self.replacements.iter() { 137 for (from, to) in self.replacements.iter() {
128 builder.replace(from.text_range(), to.to_string()) 138 builder.replace(from.text_range(), to.to_string())
@@ -188,19 +198,20 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
188 let lhs_child = lhs_children.next(); 198 let lhs_child = lhs_children.next();
189 match (lhs_child.clone(), rhs_children.next()) { 199 match (lhs_child.clone(), rhs_children.next()) {
190 (None, None) => break, 200 (None, None) => break,
191 (None, Some(element)) => match last_lhs.clone() { 201 (None, Some(element)) => {
192 Some(prev) => { 202 let insert_pos = match last_lhs.clone() {
193 mark::hit!(diff_insert); 203 Some(prev) => {
194 diff.insertions.entry(prev).or_insert_with(Vec::new).push(element); 204 mark::hit!(diff_insert);
195 } 205 TreeDiffInsertPos::After(prev)
196 // first iteration, this means we got no anchor element to insert after 206 }
197 // therefor replace the parent node instead 207 // first iteration, insert into out parent as the first child
198 None => { 208 None => {
199 mark::hit!(diff_replace_parent); 209 mark::hit!(diff_insert_as_first_child);
200 diff.replacements.insert(lhs.clone().into(), rhs.clone().into()); 210 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
201 break; 211 }
202 } 212 };
203 }, 213 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element);
214 }
204 (Some(element), None) => { 215 (Some(element), None) => {
205 mark::hit!(diff_delete); 216 mark::hit!(diff_delete);
206 diff.deletions.push(element); 217 diff.deletions.push(element);
@@ -224,8 +235,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
224 } 235 }
225 } 236 }
226 let drain = look_ahead_scratch.drain(..); 237 let drain = look_ahead_scratch.drain(..);
227 if let Some(prev) = last_lhs.clone().filter(|_| insert) { 238 if insert {
228 diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain); 239 let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
240 TreeDiffInsertPos::After(prev)
241 } else {
242 mark::hit!(insert_first_child);
243 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
244 };
245
246 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).extend(drain);
229 rhs_children = rhs_children_clone; 247 rhs_children = rhs_children_clone;
230 } else { 248 } else {
231 go(diff, lhs_ele, rhs_ele) 249 go(diff, lhs_ele, rhs_ele)
@@ -632,18 +650,19 @@ mod tests {
632 650
633 #[test] 651 #[test]
634 fn replace_parent() { 652 fn replace_parent() {
635 mark::check!(diff_replace_parent); 653 mark::check!(diff_insert_as_first_child);
636 check_diff( 654 check_diff(
637 r#""#, 655 r#""#,
638 r#"use foo::bar;"#, 656 r#"use foo::bar;"#,
639 expect![[r#" 657 expect![[r#"
640 insertions: 658 insertions:
641 659
642 660 Line 0: AsFirstChild(Node([email protected]))
661 -> use foo::bar;
643 662
644 replacements: 663 replacements:
645 664
646 Line 0: Node([email protected]) -> use foo::bar; 665
647 666
648 deletions: 667 deletions:
649 668
@@ -666,7 +685,7 @@ use baz;"#,
666 expect![[r#" 685 expect![[r#"
667 insertions: 686 insertions:
668 687
669 Line 2: Node([email protected]) 688 Line 2: After(Node([email protected]))
670 -> "\n" 689 -> "\n"
671 -> use baz; 690 -> use baz;
672 691
@@ -694,7 +713,7 @@ use baz;"#,
694 expect![[r#" 713 expect![[r#"
695 insertions: 714 insertions:
696 715
697 Line 2: Token([email protected] "\n") 716 Line 2: After(Token([email protected] "\n"))
698 -> use bar; 717 -> use bar;
699 -> "\n" 718 -> "\n"
700 719
@@ -722,7 +741,7 @@ use baz;"#,
722 expect![[r#" 741 expect![[r#"
723 insertions: 742 insertions:
724 743
725 Line 0: Token([email protected] "\n") 744 Line 0: After(Token([email protected] "\n"))
726 -> use foo; 745 -> use foo;
727 -> "\n" 746 -> "\n"
728 747
@@ -738,6 +757,36 @@ use baz;"#,
738 } 757 }
739 758
740 #[test] 759 #[test]
760 fn first_child_insertion() {
761 mark::check!(insert_first_child);
762 check_diff(
763 r#"fn main() {
764 stdi
765 }"#,
766 r#"use foo::bar;
767
768 fn main() {
769 stdi
770 }"#,
771 expect![[r#"
772 insertions:
773
774 Line 0: AsFirstChild(Node([email protected]))
775 -> use foo::bar;
776 -> "\n\n "
777
778 replacements:
779
780
781
782 deletions:
783
784
785 "#]],
786 );
787 }
788
789 #[test]
741 fn delete_last() { 790 fn delete_last() {
742 mark::check!(diff_delete); 791 mark::check!(diff_delete);
743 check_diff( 792 check_diff(
@@ -779,7 +828,7 @@ use crate::AstNode;
779 expect![[r#" 828 expect![[r#"
780 insertions: 829 insertions:
781 830
782 Line 1: Node([email protected]) 831 Line 1: After(Node([email protected]))
783 -> "\n\n" 832 -> "\n\n"
784 -> use crate::AstNode; 833 -> use crate::AstNode;
785 834
@@ -845,10 +894,10 @@ use std::ops::{self, RangeInclusive};
845 expect![[r#" 894 expect![[r#"
846 insertions: 895 insertions:
847 896
848 Line 2: Node([email protected]) 897 Line 2: After(Node([email protected]))
849 -> :: 898 -> ::
850 -> fmt 899 -> fmt
851 Line 6: Token([email protected] "\n") 900 Line 6: After(Token([email protected] "\n"))
852 -> use std::hash::BuildHasherDefault; 901 -> use std::hash::BuildHasherDefault;
853 -> "\n" 902 -> "\n"
854 -> use std::ops::{self, RangeInclusive}; 903 -> use std::ops::{self, RangeInclusive};
@@ -892,14 +941,14 @@ fn main() {
892 expect![[r#" 941 expect![[r#"
893 insertions: 942 insertions:
894 943
895 Line 3: Node([email protected]) 944 Line 3: After(Node([email protected]))
896 -> " " 945 -> " "
897 -> match Err(92) { 946 -> match Err(92) {
898 Ok(it) => it, 947 Ok(it) => it,
899 _ => return, 948 _ => return,
900 } 949 }
901 -> ; 950 -> ;
902 Line 3: Node([email protected]) 951 Line 3: After(Node([email protected]))
903 -> "\n " 952 -> "\n "
904 -> foo(x); 953 -> foo(x);
905 954
@@ -934,14 +983,18 @@ fn main() {
934 _ => format!("{}", syn), 983 _ => format!("{}", syn),
935 }; 984 };
936 985
937 let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| { 986 let insertions =
938 f(&format!( 987 diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
939 "Line {}: {:?}\n-> {}", 988 f(&format!(
940 line_number(k), 989 "Line {}: {:?}\n-> {}",
941 k, 990 line_number(match k {
942 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) 991 super::TreeDiffInsertPos::After(syn) => syn,
943 )) 992 super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
944 }); 993 }),
994 k,
995 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
996 ))
997 });
945 998
946 let replacements = diff 999 let replacements = diff
947 .replacements 1000 .replacements
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 8a0e3d27b..7844f9ed6 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -115,10 +115,10 @@ fn test_doc_comment_none() {
115} 115}
116 116
117#[test] 117#[test]
118fn test_doc_comment_of_items() { 118fn test_outer_doc_comment_of_items() {
119 let file = SourceFile::parse( 119 let file = SourceFile::parse(
120 r#" 120 r#"
121 //! doc 121 /// doc
122 // non-doc 122 // non-doc
123 mod foo {} 123 mod foo {}
124 "#, 124 "#,
@@ -130,6 +130,21 @@ fn test_doc_comment_of_items() {
130} 130}
131 131
132#[test] 132#[test]
133fn test_inner_doc_comment_of_items() {
134 let file = SourceFile::parse(
135 r#"
136 //! doc
137 // non-doc
138 mod foo {}
139 "#,
140 )
141 .ok()
142 .unwrap();
143 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
144 assert!(module.doc_comment_text().is_none());
145}
146
147#[test]
133fn test_doc_comment_of_statics() { 148fn test_doc_comment_of_statics() {
134 let file = SourceFile::parse( 149 let file = SourceFile::parse(
135 r#" 150 r#"
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index e4e512f2e..2661c753e 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -14,16 +14,15 @@ use crate::{
14 14
15impl ast::Comment { 15impl ast::Comment {
16 pub fn kind(&self) -> CommentKind { 16 pub fn kind(&self) -> CommentKind {
17 kind_by_prefix(self.text()) 17 CommentKind::from_text(self.text())
18 } 18 }
19 19
20 pub fn prefix(&self) -> &'static str { 20 pub fn prefix(&self) -> &'static str {
21 for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() { 21 let &(prefix, _kind) = CommentKind::BY_PREFIX
22 if *k == self.kind() && self.text().starts_with(prefix) { 22 .iter()
23 return prefix; 23 .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix))
24 } 24 .unwrap();
25 } 25 prefix
26 unreachable!()
27 } 26 }
28} 27}
29 28
@@ -55,29 +54,25 @@ pub enum CommentPlacement {
55 Outer, 54 Outer,
56} 55}
57 56
58const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { 57impl CommentKind {
59 use {CommentPlacement::*, CommentShape::*}; 58 const BY_PREFIX: [(&'static str, CommentKind); 8] = [
60 &[ 59 ("/**/", CommentKind { shape: CommentShape::Block, doc: None }),
61 ("////", CommentKind { shape: Line, doc: None }), 60 ("////", CommentKind { shape: CommentShape::Line, doc: None }),
62 ("///", CommentKind { shape: Line, doc: Some(Outer) }), 61 ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }),
63 ("//!", CommentKind { shape: Line, doc: Some(Inner) }), 62 ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }),
64 ("/**", CommentKind { shape: Block, doc: Some(Outer) }), 63 ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }),
65 ("/*!", CommentKind { shape: Block, doc: Some(Inner) }), 64 ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }),
66 ("//", CommentKind { shape: Line, doc: None }), 65 ("//", CommentKind { shape: CommentShape::Line, doc: None }),
67 ("/*", CommentKind { shape: Block, doc: None }), 66 ("/*", CommentKind { shape: CommentShape::Block, doc: None }),
68 ] 67 ];
69};
70 68
71fn kind_by_prefix(text: &str) -> CommentKind { 69 pub(crate) fn from_text(text: &str) -> CommentKind {
72 if text == "/**/" { 70 let &(_prefix, kind) = CommentKind::BY_PREFIX
73 return CommentKind { shape: CommentShape::Block, doc: None }; 71 .iter()
74 } 72 .find(|&(prefix, _kind)| text.starts_with(prefix))
75 for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() { 73 .unwrap();
76 if text.starts_with(prefix) { 74 kind
77 return *kind;
78 }
79 } 75 }
80 panic!("bad comment text: {:?}", text)
81} 76}
82 77
83impl ast::Whitespace { 78impl ast::Whitespace {
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index c1b5f246d..997bc5d28 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -5,6 +5,7 @@ use std::mem;
5use parser::{ParseError, TreeSink}; 5use parser::{ParseError, TreeSink};
6 6
7use crate::{ 7use crate::{
8 ast,
8 parsing::Token, 9 parsing::Token,
9 syntax_node::GreenNode, 10 syntax_node::GreenNode,
10 SmolStr, SyntaxError, 11 SmolStr, SyntaxError,
@@ -153,24 +154,22 @@ fn n_attached_trivias<'a>(
153 154
154 while let Some((i, (kind, text))) = trivias.next() { 155 while let Some((i, (kind, text))) = trivias.next() {
155 match kind { 156 match kind {
156 WHITESPACE => { 157 WHITESPACE if text.contains("\n\n") => {
157 if text.contains("\n\n") { 158 // we check whether the next token is a doc-comment
158 // we check whether the next token is a doc-comment 159 // and skip the whitespace in this case
159 // and skip the whitespace in this case 160 if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) {
160 if let Some((peek_kind, peek_text)) = 161 let comment_kind = ast::CommentKind::from_text(peek_text);
161 trivias.peek().map(|(_, pair)| pair) 162 if comment_kind.doc == Some(ast::CommentPlacement::Outer) {
162 { 163 continue;
163 if *peek_kind == COMMENT
164 && peek_text.starts_with("///")
165 && !peek_text.starts_with("////")
166 {
167 continue;
168 }
169 } 164 }
170 break;
171 } 165 }
166 break;
172 } 167 }
173 COMMENT => { 168 COMMENT => {
169 let comment_kind = ast::CommentKind::from_text(text);
170 if comment_kind.doc == Some(ast::CommentPlacement::Inner) {
171 break;
172 }
174 res = i + 1; 173 res = i + 1;
175 } 174 }
176 _ => (), 175 _ => (),