1pub mod dive;
4
5use std::borrow::Cow;
6use std::collections::VecDeque;
7use std::fmt;
8use std::iter;
9
10use itertools::Either;
11use rowan::Direction;
12use rowan::GreenNodeBuilder;
13use rowan::GreenNodeData;
14use strum::VariantArray;
15
16use super::Diagnostic;
17use super::grammar;
18use super::lexer::Lexer;
19use super::parser::Event;
20use crate::parser::Parser;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArray)]
31#[repr(u16)]
32pub enum SyntaxKind {
33 Unknown,
35 Unparsed,
39 Whitespace,
41 Comment,
43 Version,
45 Float,
47 Integer,
49 Ident,
51 SingleQuote,
53 DoubleQuote,
55 OpenHeredoc,
57 CloseHeredoc,
59 ArrayTypeKeyword,
61 BooleanTypeKeyword,
63 FileTypeKeyword,
65 FloatTypeKeyword,
67 IntTypeKeyword,
69 MapTypeKeyword,
71 ObjectTypeKeyword,
73 PairTypeKeyword,
75 StringTypeKeyword,
77 AfterKeyword,
79 AliasKeyword,
81 AsKeyword,
83 CallKeyword,
85 CommandKeyword,
87 ElseKeyword,
89 EnvKeyword,
91 FalseKeyword,
93 IfKeyword,
95 InKeyword,
97 ImportKeyword,
99 InputKeyword,
101 MetaKeyword,
103 NoneKeyword,
105 NullKeyword,
107 ObjectKeyword,
109 OutputKeyword,
111 ParameterMetaKeyword,
113 RuntimeKeyword,
115 ScatterKeyword,
117 StructKeyword,
119 TaskKeyword,
121 ThenKeyword,
123 TrueKeyword,
125 VersionKeyword,
127 WorkflowKeyword,
129 DirectoryTypeKeyword,
131 HintsKeyword,
133 RequirementsKeyword,
135 OpenBrace,
137 CloseBrace,
139 OpenBracket,
141 CloseBracket,
143 Assignment,
145 Colon,
147 Comma,
149 OpenParen,
151 CloseParen,
153 QuestionMark,
155 Exclamation,
157 Plus,
159 Minus,
161 LogicalOr,
163 LogicalAnd,
165 Asterisk,
167 Exponentiation,
169 Slash,
171 Percent,
173 Equal,
175 NotEqual,
177 LessEqual,
179 GreaterEqual,
181 Less,
183 Greater,
185 Dot,
187 LiteralStringText,
189 LiteralCommandText,
191 PlaceholderOpen,
193
194 #[doc(hidden)]
202 Abandoned,
203 RootNode,
205 VersionStatementNode,
207 ImportStatementNode,
209 ImportAliasNode,
211 StructDefinitionNode,
213 TaskDefinitionNode,
215 WorkflowDefinitionNode,
217 UnboundDeclNode,
219 BoundDeclNode,
221 InputSectionNode,
223 OutputSectionNode,
225 CommandSectionNode,
227 RequirementsSectionNode,
229 RequirementsItemNode,
231 TaskHintsSectionNode,
233 WorkflowHintsSectionNode,
235 TaskHintsItemNode,
237 WorkflowHintsItemNode,
239 WorkflowHintsObjectNode,
241 WorkflowHintsObjectItemNode,
243 WorkflowHintsArrayNode,
245 RuntimeSectionNode,
247 RuntimeItemNode,
249 PrimitiveTypeNode,
251 MapTypeNode,
253 ArrayTypeNode,
255 PairTypeNode,
257 ObjectTypeNode,
259 TypeRefNode,
261 MetadataSectionNode,
263 ParameterMetadataSectionNode,
265 MetadataObjectItemNode,
267 MetadataObjectNode,
269 MetadataArrayNode,
271 LiteralIntegerNode,
273 LiteralFloatNode,
275 LiteralBooleanNode,
277 LiteralNoneNode,
279 LiteralNullNode,
281 LiteralStringNode,
283 LiteralPairNode,
285 LiteralArrayNode,
287 LiteralMapNode,
289 LiteralMapItemNode,
291 LiteralObjectNode,
293 LiteralObjectItemNode,
295 LiteralStructNode,
297 LiteralStructItemNode,
299 LiteralHintsNode,
301 LiteralHintsItemNode,
303 LiteralInputNode,
305 LiteralInputItemNode,
307 LiteralOutputNode,
309 LiteralOutputItemNode,
311 ParenthesizedExprNode,
313 NameRefExprNode,
315 IfExprNode,
317 LogicalNotExprNode,
319 NegationExprNode,
321 LogicalOrExprNode,
323 LogicalAndExprNode,
325 EqualityExprNode,
327 InequalityExprNode,
329 LessExprNode,
331 LessEqualExprNode,
333 GreaterExprNode,
335 GreaterEqualExprNode,
337 AdditionExprNode,
339 SubtractionExprNode,
341 MultiplicationExprNode,
343 DivisionExprNode,
345 ModuloExprNode,
347 ExponentiationExprNode,
349 CallExprNode,
351 IndexExprNode,
353 AccessExprNode,
355 PlaceholderNode,
357 PlaceholderSepOptionNode,
359 PlaceholderDefaultOptionNode,
361 PlaceholderTrueFalseOptionNode,
363 ConditionalStatementNode,
365 ScatterStatementNode,
367 CallStatementNode,
369 CallTargetNode,
371 CallAliasNode,
373 CallAfterNode,
375 CallInputItemNode,
377
378 MAX,
381}
382
383impl SyntaxKind {
384 pub fn is_symbolic(&self) -> bool {
390 matches!(
391 self,
392 Self::Abandoned | Self::Unknown | Self::Unparsed | Self::MAX
393 )
394 }
395
396 pub fn describe(&self) -> &'static str {
398 match self {
399 Self::Unknown => unreachable!(),
400 Self::Unparsed => unreachable!(),
401 Self::Whitespace => "whitespace",
402 Self::Comment => "comment",
403 Self::Version => "version",
404 Self::Float => "float",
405 Self::Integer => "integer",
406 Self::Ident => "identifier",
407 Self::SingleQuote => "single quote",
408 Self::DoubleQuote => "double quote",
409 Self::OpenHeredoc => "open heredoc",
410 Self::CloseHeredoc => "close heredoc",
411 Self::ArrayTypeKeyword => "`Array` type keyword",
412 Self::BooleanTypeKeyword => "`Boolean` type keyword",
413 Self::FileTypeKeyword => "`File` type keyword",
414 Self::FloatTypeKeyword => "`Float` type keyword",
415 Self::IntTypeKeyword => "`Int` type keyword",
416 Self::MapTypeKeyword => "`Map` type keyword",
417 Self::ObjectTypeKeyword => "`Object` type keyword",
418 Self::PairTypeKeyword => "`Pair` type keyword",
419 Self::StringTypeKeyword => "`String` type keyword",
420 Self::AfterKeyword => "`after` keyword",
421 Self::AliasKeyword => "`alias` keyword",
422 Self::AsKeyword => "`as` keyword",
423 Self::CallKeyword => "`call` keyword",
424 Self::CommandKeyword => "`command` keyword",
425 Self::ElseKeyword => "`else` keyword",
426 Self::EnvKeyword => "`env` keyword",
427 Self::FalseKeyword => "`false` keyword",
428 Self::IfKeyword => "`if` keyword",
429 Self::InKeyword => "`in` keyword",
430 Self::ImportKeyword => "`import` keyword",
431 Self::InputKeyword => "`input` keyword",
432 Self::MetaKeyword => "`meta` keyword",
433 Self::NoneKeyword => "`None` keyword",
434 Self::NullKeyword => "`null` keyword",
435 Self::ObjectKeyword => "`object` keyword",
436 Self::OutputKeyword => "`output` keyword",
437 Self::ParameterMetaKeyword => "`parameter_meta` keyword",
438 Self::RuntimeKeyword => "`runtime` keyword",
439 Self::ScatterKeyword => "`scatter` keyword",
440 Self::StructKeyword => "`struct` keyword",
441 Self::TaskKeyword => "`task` keyword",
442 Self::ThenKeyword => "`then` keyword",
443 Self::TrueKeyword => "`true` keyword",
444 Self::VersionKeyword => "`version` keyword",
445 Self::WorkflowKeyword => "`workflow` keyword",
446 Self::DirectoryTypeKeyword => "`Directory` type keyword",
447 Self::HintsKeyword => "`hints` keyword",
448 Self::RequirementsKeyword => "`requirements` keyword",
449 Self::OpenBrace => "`{` symbol",
450 Self::CloseBrace => "`}` symbol",
451 Self::OpenBracket => "`[` symbol",
452 Self::CloseBracket => "`]` symbol",
453 Self::Assignment => "`=` symbol",
454 Self::Colon => "`:` symbol",
455 Self::Comma => "`,` symbol",
456 Self::OpenParen => "`(` symbol",
457 Self::CloseParen => "`)` symbol",
458 Self::QuestionMark => "`?` symbol",
459 Self::Exclamation => "`!` symbol",
460 Self::Plus => "`+` symbol",
461 Self::Minus => "`-` symbol",
462 Self::LogicalOr => "`||` symbol",
463 Self::LogicalAnd => "`&&` symbol",
464 Self::Asterisk => "`*` symbol",
465 Self::Exponentiation => "`**` symbol",
466 Self::Slash => "`/` symbol",
467 Self::Percent => "`%` symbol",
468 Self::Equal => "`==` symbol",
469 Self::NotEqual => "`!=` symbol",
470 Self::LessEqual => "`<=` symbol",
471 Self::GreaterEqual => "`>=` symbol",
472 Self::Less => "`<` symbol",
473 Self::Greater => "`>` symbol",
474 Self::Dot => "`.` symbol",
475 Self::LiteralStringText => "literal string text",
476 Self::LiteralCommandText => "literal command text",
477 Self::PlaceholderOpen => "placeholder open",
478 Self::Abandoned => unreachable!(),
479 Self::RootNode => "root node",
480 Self::VersionStatementNode => "version statement",
481 Self::ImportStatementNode => "import statement",
482 Self::ImportAliasNode => "import alias",
483 Self::StructDefinitionNode => "struct definition",
484 Self::TaskDefinitionNode => "task definition",
485 Self::WorkflowDefinitionNode => "workflow definition",
486 Self::UnboundDeclNode => "declaration without assignment",
487 Self::BoundDeclNode => "declaration with assignment",
488 Self::InputSectionNode => "input section",
489 Self::OutputSectionNode => "output section",
490 Self::CommandSectionNode => "command section",
491 Self::RequirementsSectionNode => "requirements section",
492 Self::RequirementsItemNode => "requirements item",
493 Self::TaskHintsSectionNode | Self::WorkflowHintsSectionNode => "hints section",
494 Self::TaskHintsItemNode | Self::WorkflowHintsItemNode => "hints item",
495 Self::WorkflowHintsObjectNode => "literal object",
496 Self::WorkflowHintsObjectItemNode => "literal object item",
497 Self::WorkflowHintsArrayNode => "literal array",
498 Self::RuntimeSectionNode => "runtime section",
499 Self::RuntimeItemNode => "runtime item",
500 Self::PrimitiveTypeNode => "primitive type",
501 Self::MapTypeNode => "map type",
502 Self::ArrayTypeNode => "array type",
503 Self::PairTypeNode => "pair type",
504 Self::ObjectTypeNode => "object type",
505 Self::TypeRefNode => "type reference",
506 Self::MetadataSectionNode => "metadata section",
507 Self::ParameterMetadataSectionNode => "parameter metadata section",
508 Self::MetadataObjectItemNode => "metadata object item",
509 Self::MetadataObjectNode => "metadata object",
510 Self::MetadataArrayNode => "metadata array",
511 Self::LiteralIntegerNode => "literal integer",
512 Self::LiteralFloatNode => "literal float",
513 Self::LiteralBooleanNode => "literal boolean",
514 Self::LiteralNoneNode => "literal `None`",
515 Self::LiteralNullNode => "literal null",
516 Self::LiteralStringNode => "literal string",
517 Self::LiteralPairNode => "literal pair",
518 Self::LiteralArrayNode => "literal array",
519 Self::LiteralMapNode => "literal map",
520 Self::LiteralMapItemNode => "literal map item",
521 Self::LiteralObjectNode => "literal object",
522 Self::LiteralObjectItemNode => "literal object item",
523 Self::LiteralStructNode => "literal struct",
524 Self::LiteralStructItemNode => "literal struct item",
525 Self::LiteralHintsNode => "literal hints",
526 Self::LiteralHintsItemNode => "literal hints item",
527 Self::LiteralInputNode => "literal input",
528 Self::LiteralInputItemNode => "literal input item",
529 Self::LiteralOutputNode => "literal output",
530 Self::LiteralOutputItemNode => "literal output item",
531 Self::ParenthesizedExprNode => "parenthesized expression",
532 Self::NameRefExprNode => "name reference expression",
533 Self::IfExprNode => "`if` expression",
534 Self::LogicalNotExprNode => "logical not expression",
535 Self::NegationExprNode => "negation expression",
536 Self::LogicalOrExprNode => "logical OR expression",
537 Self::LogicalAndExprNode => "logical AND expression",
538 Self::EqualityExprNode => "equality expression",
539 Self::InequalityExprNode => "inequality expression",
540 Self::LessExprNode => "less than expression",
541 Self::LessEqualExprNode => "less than or equal to expression",
542 Self::GreaterExprNode => "greater than expression",
543 Self::GreaterEqualExprNode => "greater than or equal to expression",
544 Self::AdditionExprNode => "addition expression",
545 Self::SubtractionExprNode => "subtraction expression",
546 Self::MultiplicationExprNode => "multiplication expression",
547 Self::DivisionExprNode => "division expression",
548 Self::ModuloExprNode => "modulo expression",
549 Self::ExponentiationExprNode => "exponentiation expression",
550 Self::CallExprNode => "call expression",
551 Self::IndexExprNode => "index expression",
552 Self::AccessExprNode => "access expression",
553 Self::PlaceholderNode => "placeholder",
554 Self::PlaceholderSepOptionNode => "placeholder `sep` option",
555 Self::PlaceholderDefaultOptionNode => "placeholder `default` option",
556 Self::PlaceholderTrueFalseOptionNode => "placeholder `true`/`false` option",
557 Self::ConditionalStatementNode => "conditional statement",
558 Self::ScatterStatementNode => "scatter statement",
559 Self::CallStatementNode => "call statement",
560 Self::CallTargetNode => "call target",
561 Self::CallAliasNode => "call alias",
562 Self::CallAfterNode => "call `after` clause",
563 Self::CallInputItemNode => "call input item",
564 Self::MAX => unreachable!(),
565 }
566 }
567
568 pub fn is_trivia(&self) -> bool {
570 matches!(self, Self::Whitespace | Self::Comment)
571 }
572}
573
574pub static ALL_SYNTAX_KIND: &[SyntaxKind] = SyntaxKind::VARIANTS;
576
577impl From<SyntaxKind> for rowan::SyntaxKind {
578 fn from(kind: SyntaxKind) -> Self {
579 rowan::SyntaxKind(kind as u16)
580 }
581}
582
583#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
585pub struct WorkflowDescriptionLanguage;
586
587impl rowan::Language for WorkflowDescriptionLanguage {
588 type Kind = SyntaxKind;
589
590 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
591 assert!(raw.0 <= SyntaxKind::MAX as u16);
592 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
593 }
594
595 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
596 kind.into()
597 }
598}
599
600pub type SyntaxNode = rowan::SyntaxNode<WorkflowDescriptionLanguage>;
602pub type SyntaxToken = rowan::SyntaxToken<WorkflowDescriptionLanguage>;
604pub type SyntaxElement = rowan::SyntaxElement<WorkflowDescriptionLanguage>;
606pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<WorkflowDescriptionLanguage>;
608
609pub fn construct_tree(source: &str, mut events: Vec<Event>) -> SyntaxNode {
611 let mut builder = GreenNodeBuilder::default();
612 let mut ancestors = Vec::new();
613
614 for i in 0..events.len() {
615 match std::mem::replace(&mut events[i], Event::abandoned()) {
616 Event::NodeStarted {
617 kind,
618 forward_parent,
619 } => {
620 ancestors.push(kind);
623 let mut idx = i;
624 let mut fp: Option<usize> = forward_parent;
625 while let Some(distance) = fp {
626 idx += distance;
627 fp = match std::mem::replace(&mut events[idx], Event::abandoned()) {
628 Event::NodeStarted {
629 kind,
630 forward_parent,
631 } => {
632 ancestors.push(kind);
633 forward_parent
634 }
635 _ => unreachable!(),
636 };
637 }
638
639 for kind in ancestors.drain(..).rev() {
642 if kind != SyntaxKind::Abandoned {
643 builder.start_node(kind.into());
644 }
645 }
646 }
647 Event::NodeFinished => builder.finish_node(),
648 Event::Token { kind, span } => {
649 builder.token(kind.into(), &source[span.start()..span.end()])
650 }
651 }
652 }
653
654 SyntaxNode::new_root(builder.finish())
655}
656
657#[derive(Clone, PartialEq, Eq, Hash)]
659pub struct SyntaxTree(SyntaxNode);
660
661impl SyntaxTree {
662 pub fn parse(source: &str) -> (Self, Vec<Diagnostic>) {
681 let parser = Parser::new(Lexer::new(source));
682 let (events, mut diagnostics) = grammar::document(source, parser);
683 diagnostics.sort();
684 (Self(construct_tree(source, events)), diagnostics)
685 }
686
687 pub fn root(&self) -> &SyntaxNode {
689 &self.0
690 }
691
692 pub fn green(&self) -> Cow<'_, GreenNodeData> {
694 self.0.green()
695 }
696
697 pub fn into_syntax(self) -> SyntaxNode {
699 self.0
700 }
701}
702
703impl fmt::Display for SyntaxTree {
704 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705 self.0.fmt(f)
706 }
707}
708
709impl fmt::Debug for SyntaxTree {
710 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
711 self.0.fmt(f)
712 }
713}
714
715pub trait SyntaxExt {
718 fn matches(&self, other: &SyntaxElement) -> bool;
720
721 fn parent(&self) -> Option<SyntaxNode>;
725
726 fn index(&self) -> usize;
728
729 fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElement>;
737
738 fn preceding_siblings(&self) -> impl Iterator<Item = SyntaxElement> {
742 let index = self.index();
743 self.parent()
744 .into_iter()
745 .flat_map(move |p| p.children_with_tokens().take(index))
746 }
747
748 fn succeeding_siblings(&self) -> impl Iterator<Item = SyntaxElement> {
752 self.siblings_with_tokens(Direction::Next)
753 .skip(1)
756 }
757
758 fn adjacent(&self) -> impl Iterator<Item = SyntaxElement> {
764 self.preceding_siblings().chain(self.succeeding_siblings())
765 }
766}
767
768impl SyntaxExt for SyntaxNode {
769 fn matches(&self, other: &SyntaxElement) -> bool {
770 other.as_node().map(|n| n == self).unwrap_or(false)
771 }
772
773 fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElement> {
774 self.siblings_with_tokens(direction)
775 }
776
777 fn parent(&self) -> Option<SyntaxNode> {
778 self.parent()
779 }
780
781 fn index(&self) -> usize {
782 self.index()
783 }
784}
785
786impl SyntaxExt for SyntaxToken {
787 fn matches(&self, other: &SyntaxElement) -> bool {
788 other.as_token().map(|n| n == self).unwrap_or(false)
789 }
790
791 fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElement> {
792 self.siblings_with_tokens(direction)
793 }
794
795 fn parent(&self) -> Option<SyntaxNode> {
796 self.parent()
797 }
798
799 fn index(&self) -> usize {
800 self.index()
801 }
802}
803
804impl SyntaxExt for SyntaxElement {
805 fn matches(&self, other: &SyntaxElement) -> bool {
806 self == other
807 }
808
809 fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElement> {
810 match self {
811 SyntaxElement::Node(node) => Either::Left(node.siblings_with_tokens(direction)),
812 SyntaxElement::Token(token) => Either::Right(token.siblings_with_tokens(direction)),
813 }
814 }
815
816 fn parent(&self) -> Option<SyntaxNode> {
817 self.parent()
818 }
819
820 fn index(&self) -> usize {
821 self.index()
822 }
823}
824
825pub trait SyntaxTokenExt {
827 fn preceding_trivia(&self) -> impl Iterator<Item = SyntaxToken>;
829
830 fn succeeding_trivia(&self) -> impl Iterator<Item = SyntaxToken>;
832
833 fn inline_comment(&self) -> Option<SyntaxToken>;
836}
837
838impl SyntaxTokenExt for SyntaxToken {
839 fn preceding_trivia(&self) -> impl Iterator<Item = SyntaxToken> {
840 let mut tokens = VecDeque::new();
841 let mut cur = self.prev_token();
842 while let Some(token) = cur {
843 cur = token.prev_token();
844 if !token.kind().is_trivia() {
846 break;
847 }
848 if token.kind() == SyntaxKind::Comment {
850 if let Some(prev) = token.prev_token() {
851 if prev.kind() == SyntaxKind::Whitespace {
852 let has_newlines = prev.text().chars().any(|c| c == '\n');
853 if !has_newlines && prev.prev_token().is_some() {
859 break;
860 }
861 } else {
862 break;
864 }
865 }
866 }
867 match token.kind() {
869 SyntaxKind::Whitespace
870 if token.text().chars().filter(|c| *c == '\n').count() > 1 =>
871 {
872 tokens.push_front(token);
873 }
874 SyntaxKind::Comment => {
875 tokens.push_front(token);
876 }
877 _ => {}
878 }
879 }
880 tokens.into_iter()
881 }
882
883 fn succeeding_trivia(&self) -> impl Iterator<Item = SyntaxToken> {
884 let mut next = self.next_token();
885 iter::from_fn(move || {
886 let cur = next.clone()?;
887 next = cur.next_token();
888 Some(cur)
889 })
890 .take_while(|t| {
891 t.kind().is_trivia()
893 })
894 .filter(|t| {
895 if t.kind() == SyntaxKind::Whitespace {
897 return t.text().chars().filter(|c| *c == '\n').count() > 1;
898 }
899 true
900 })
901 }
902
903 fn inline_comment(&self) -> Option<SyntaxToken> {
904 let mut next = self.next_token();
905 iter::from_fn(move || {
906 let cur = next.clone()?;
907 next = cur.next_token();
908 Some(cur)
909 })
910 .take_while(|t| {
911 if !t.kind().is_trivia() {
913 return false;
914 }
915 if t.kind() == SyntaxKind::Whitespace {
917 return !t.text().chars().any(|c| c == '\n');
918 }
919 true
920 })
921 .find(|t| t.kind() == SyntaxKind::Comment)
922 }
923}
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928 use crate::SyntaxTree;
929
930 #[test]
931 fn preceding_comments() {
932 let (tree, diagnostics) = SyntaxTree::parse(
933 "version 1.2
934
935# This comment should not be included
936task foo {} # This comment should not be included
937
938# Some
939# comments
940# are
941# long
942
943# Others are short
944
945# and, yet another
946workflow foo {} # This should not be collected.
947
948# This comment should not be included either.",
949 );
950
951 assert!(diagnostics.is_empty());
952
953 let workflow = tree.root().last_child().unwrap();
954 assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
955 let token = workflow.first_token().unwrap();
956 let mut trivia = token.preceding_trivia();
957 assert_eq!(trivia.next().unwrap().text(), "\n\n");
958 assert_eq!(trivia.next().unwrap().text(), "# Some");
959 assert_eq!(trivia.next().unwrap().text(), "# comments");
960 assert_eq!(trivia.next().unwrap().text(), "# are");
961 assert_eq!(trivia.next().unwrap().text(), "# long");
962 assert_eq!(trivia.next().unwrap().text(), "\n \n");
963 assert_eq!(trivia.next().unwrap().text(), "# Others are short");
964 assert_eq!(trivia.next().unwrap().text(), "\n\n");
965 assert_eq!(trivia.next().unwrap().text(), "# and, yet another");
966 assert!(trivia.next().is_none());
967 }
968
969 #[test]
970 fn succeeding_comments() {
971 let (tree, diagnostics) = SyntaxTree::parse(
972 "version 1.2
973
974# This comment should not be included
975task foo {}
976
977# This should not be collected.
978workflow foo {} # Here is a comment that should be collected.
979
980# This comment should be included too.",
981 );
982
983 assert!(diagnostics.is_empty());
984
985 let workflow = tree.root().last_child().unwrap();
986 assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
987 let token = workflow.last_token().unwrap();
988 let mut trivia = token.succeeding_trivia();
989 assert_eq!(
990 trivia.next().unwrap().text(),
991 "# Here is a comment that should be collected."
992 );
993 assert_eq!(trivia.next().unwrap().text(), "\n\n");
994 assert_eq!(
995 trivia.next().unwrap().text(),
996 "# This comment should be included too."
997 );
998 assert!(trivia.next().is_none());
999 }
1000
1001 #[test]
1002 fn inline_comment() {
1003 let (tree, diagnostics) = SyntaxTree::parse(
1004 "version 1.2
1005
1006# This comment should not be included
1007task foo {}
1008
1009# This should not be collected.
1010workflow foo {} # Here is a comment that should be collected.
1011
1012# This comment should not be included either.",
1013 );
1014
1015 assert!(diagnostics.is_empty());
1016
1017 let workflow = tree.root().last_child().unwrap();
1018 assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
1019 let comment = workflow.last_token().unwrap().inline_comment().unwrap();
1020 assert_eq!(
1021 comment.text(),
1022 "# Here is a comment that should be collected."
1023 );
1024 }
1025}