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