aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax/src')
-rw-r--r--crates/syntax/src/algo.rs130
-rw-r--r--crates/syntax/src/ast.rs19
-rw-r--r--crates/syntax/src/ast/expr_ext.rs12
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
-rw-r--r--crates/syntax/src/ast/token_ext.rs73
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs27
7 files changed, 185 insertions, 90 deletions
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 9dc7182bd..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)
@@ -331,7 +349,10 @@ pub struct SyntaxRewriter<'a> {
331 349
332impl fmt::Debug for SyntaxRewriter<'_> { 350impl fmt::Debug for SyntaxRewriter<'_> {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 f.debug_struct("SyntaxRewriter").field("replacements", &self.replacements).finish() 352 f.debug_struct("SyntaxRewriter")
353 .field("replacements", &self.replacements)
354 .field("insertions", &self.insertions)
355 .finish()
335 } 356 }
336} 357}
337 358
@@ -629,18 +650,19 @@ mod tests {
629 650
630 #[test] 651 #[test]
631 fn replace_parent() { 652 fn replace_parent() {
632 mark::check!(diff_replace_parent); 653 mark::check!(diff_insert_as_first_child);
633 check_diff( 654 check_diff(
634 r#""#, 655 r#""#,
635 r#"use foo::bar;"#, 656 r#"use foo::bar;"#,
636 expect![[r#" 657 expect![[r#"
637 insertions: 658 insertions:
638 659
639 660 Line 0: AsFirstChild(Node([email protected]))
661 -> use foo::bar;
640 662
641 replacements: 663 replacements:
642 664
643 Line 0: Node([email protected]) -> use foo::bar; 665
644 666
645 deletions: 667 deletions:
646 668
@@ -663,7 +685,7 @@ use baz;"#,
663 expect![[r#" 685 expect![[r#"
664 insertions: 686 insertions:
665 687
666 Line 2: Node([email protected]) 688 Line 2: After(Node([email protected]))
667 -> "\n" 689 -> "\n"
668 -> use baz; 690 -> use baz;
669 691
@@ -691,7 +713,7 @@ use baz;"#,
691 expect![[r#" 713 expect![[r#"
692 insertions: 714 insertions:
693 715
694 Line 2: Token([email protected] "\n") 716 Line 2: After(Token([email protected] "\n"))
695 -> use bar; 717 -> use bar;
696 -> "\n" 718 -> "\n"
697 719
@@ -719,7 +741,7 @@ use baz;"#,
719 expect![[r#" 741 expect![[r#"
720 insertions: 742 insertions:
721 743
722 Line 0: Token([email protected] "\n") 744 Line 0: After(Token([email protected] "\n"))
723 -> use foo; 745 -> use foo;
724 -> "\n" 746 -> "\n"
725 747
@@ -735,6 +757,36 @@ use baz;"#,
735 } 757 }
736 758
737 #[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]
738 fn delete_last() { 790 fn delete_last() {
739 mark::check!(diff_delete); 791 mark::check!(diff_delete);
740 check_diff( 792 check_diff(
@@ -776,7 +828,7 @@ use crate::AstNode;
776 expect![[r#" 828 expect![[r#"
777 insertions: 829 insertions:
778 830
779 Line 1: Node([email protected]) 831 Line 1: After(Node([email protected]))
780 -> "\n\n" 832 -> "\n\n"
781 -> use crate::AstNode; 833 -> use crate::AstNode;
782 834
@@ -842,10 +894,10 @@ use std::ops::{self, RangeInclusive};
842 expect![[r#" 894 expect![[r#"
843 insertions: 895 insertions:
844 896
845 Line 2: Node([email protected]) 897 Line 2: After(Node([email protected]))
846 -> :: 898 -> ::
847 -> fmt 899 -> fmt
848 Line 6: Token([email protected] "\n") 900 Line 6: After(Token([email protected] "\n"))
849 -> use std::hash::BuildHasherDefault; 901 -> use std::hash::BuildHasherDefault;
850 -> "\n" 902 -> "\n"
851 -> use std::ops::{self, RangeInclusive}; 903 -> use std::ops::{self, RangeInclusive};
@@ -889,14 +941,14 @@ fn main() {
889 expect![[r#" 941 expect![[r#"
890 insertions: 942 insertions:
891 943
892 Line 3: Node([email protected]) 944 Line 3: After(Node([email protected]))
893 -> " " 945 -> " "
894 -> match Err(92) { 946 -> match Err(92) {
895 Ok(it) => it, 947 Ok(it) => it,
896 _ => return, 948 _ => return,
897 } 949 }
898 -> ; 950 -> ;
899 Line 3: Node([email protected]) 951 Line 3: After(Node([email protected]))
900 -> "\n " 952 -> "\n "
901 -> foo(x); 953 -> foo(x);
902 954
@@ -931,14 +983,18 @@ fn main() {
931 _ => format!("{}", syn), 983 _ => format!("{}", syn),
932 }; 984 };
933 985
934 let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| { 986 let insertions =
935 f(&format!( 987 diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
936 "Line {}: {:?}\n-> {}", 988 f(&format!(
937 line_number(k), 989 "Line {}: {:?}\n-> {}",
938 k, 990 line_number(match k {
939 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) 991 super::TreeDiffInsertPos::After(syn) => syn,
940 )) 992 super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
941 }); 993 }),
994 k,
995 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
996 ))
997 });
942 998
943 let replacements = diff 999 let replacements = diff
944 .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/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 9253c97d0..e4a9b945c 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -22,6 +22,18 @@ impl ast::Expr {
22 _ => false, 22 _ => false,
23 } 23 }
24 } 24 }
25
26 pub fn name_ref(&self) -> Option<ast::NameRef> {
27 if let ast::Expr::PathExpr(expr) = self {
28 let path = expr.path()?;
29 let segment = path.segment()?;
30 let name_ref = segment.name_ref()?;
31 if path.qualifier().is_none() {
32 return Some(name_ref);
33 }
34 }
35 None
36 }
25} 37}
26 38
27#[derive(Debug, Clone, PartialEq, Eq)] 39#[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index b1578820f..876659a2b 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -25,6 +25,10 @@ pub fn assoc_item_list() -> ast::AssocItemList {
25 ast_from_text("impl C for D {};") 25 ast_from_text("impl C for D {};")
26} 26}
27 27
28pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
29 ast_from_text(&format!("impl {} for {} {{}}", trait_, ty))
30}
31
28pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { 32pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
29 ast_from_text(&format!("use {};", name_ref)) 33 ast_from_text(&format!("use {};", name_ref))
30} 34}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index ce35ac01a..b70b840b8 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -203,15 +203,7 @@ impl ast::RecordExprField {
203 if let Some(name_ref) = self.name_ref() { 203 if let Some(name_ref) = self.name_ref() {
204 return Some(name_ref); 204 return Some(name_ref);
205 } 205 }
206 if let Some(ast::Expr::PathExpr(expr)) = self.expr() { 206 self.expr()?.name_ref()
207 let path = expr.path()?;
208 let segment = path.segment()?;
209 let name_ref = segment.name_ref()?;
210 if path.qualifier().is_none() {
211 return Some(name_ref);
212 }
213 }
214 None
215 } 207 }
216} 208}
217 209
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index e4e512f2e..ac0326420 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 {
@@ -336,10 +331,22 @@ pub trait HasFormatSpecifier: AstToken {
336 } 331 }
337 c if c == '_' || c.is_alphabetic() => { 332 c if c == '_' || c.is_alphabetic() => {
338 read_identifier(&mut chars, &mut callback); 333 read_identifier(&mut chars, &mut callback);
334
335 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
336 == Some('?')
337 {
338 skip_char_and_emit(
339 &mut chars,
340 FormatSpecifier::QuestionMark,
341 &mut callback,
342 );
343 }
344
339 // can be either width (indicated by dollar sign, or type in which case 345 // can be either width (indicated by dollar sign, or type in which case
340 // the next sign has to be `}`) 346 // the next sign has to be `}`)
341 let next = 347 let next =
342 chars.peek().and_then(|next| next.1.as_ref().ok()).copied(); 348 chars.peek().and_then(|next| next.1.as_ref().ok()).copied();
349
343 match next { 350 match next {
344 Some('$') => skip_char_and_emit( 351 Some('$') => skip_char_and_emit(
345 &mut chars, 352 &mut chars,
@@ -422,6 +429,16 @@ pub trait HasFormatSpecifier: AstToken {
422 } 429 }
423 c if c == '_' || c.is_alphabetic() => { 430 c if c == '_' || c.is_alphabetic() => {
424 read_identifier(&mut chars, &mut callback); 431 read_identifier(&mut chars, &mut callback);
432
433 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
434 == Some('?')
435 {
436 skip_char_and_emit(
437 &mut chars,
438 FormatSpecifier::QuestionMark,
439 &mut callback,
440 );
441 }
425 } 442 }
426 _ => {} 443 _ => {}
427 } 444 }
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 _ => (),