diff options
Diffstat (limited to 'crates/syntax/src')
-rw-r--r-- | crates/syntax/src/algo.rs | 130 | ||||
-rw-r--r-- | crates/syntax/src/ast.rs | 19 | ||||
-rw-r--r-- | crates/syntax/src/ast/expr_ext.rs | 12 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 4 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 10 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 73 | ||||
-rw-r--r-- | crates/syntax/src/parsing/text_tree_sink.rs | 27 |
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 | ||
112 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; | 112 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; |
113 | 113 | ||
114 | #[derive(Debug, Hash, PartialEq, Eq)] | ||
115 | enum TreeDiffInsertPos { | ||
116 | After(SyntaxElement), | ||
117 | AsFirstChild(SyntaxElement), | ||
118 | } | ||
119 | |||
114 | #[derive(Debug)] | 120 | #[derive(Debug)] |
115 | pub struct TreeDiff { | 121 | pub 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 | ||
122 | impl TreeDiff { | 128 | impl 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 | ||
332 | impl fmt::Debug for SyntaxRewriter<'_> { | 350 | impl 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] |
118 | fn test_doc_comment_of_items() { | 118 | fn 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] |
133 | fn 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] | ||
133 | fn test_doc_comment_of_statics() { | 148 | fn 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 | ||
28 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | ||
29 | ast_from_text(&format!("impl {} for {} {{}}", trait_, ty)) | ||
30 | } | ||
31 | |||
28 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { | 32 | pub 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 | ||
15 | impl ast::Comment { | 15 | impl 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 | ||
58 | const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { | 57 | impl 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 | ||
71 | fn 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 | ||
83 | impl ast::Whitespace { | 78 | impl 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; | |||
5 | use parser::{ParseError, TreeSink}; | 5 | use parser::{ParseError, TreeSink}; |
6 | 6 | ||
7 | use crate::{ | 7 | use 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 | _ => (), |