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 EnumKeyword,
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 EnumDefinitionNode,
215 EnumTypeParameterNode,
217 EnumVariantNode,
219 TaskDefinitionNode,
221 WorkflowDefinitionNode,
223 UnboundDeclNode,
225 BoundDeclNode,
227 InputSectionNode,
229 OutputSectionNode,
231 CommandSectionNode,
233 RequirementsSectionNode,
235 RequirementsItemNode,
237 TaskHintsSectionNode,
239 WorkflowHintsSectionNode,
241 TaskHintsItemNode,
243 WorkflowHintsItemNode,
245 WorkflowHintsObjectNode,
247 WorkflowHintsObjectItemNode,
249 WorkflowHintsArrayNode,
251 RuntimeSectionNode,
253 RuntimeItemNode,
255 PrimitiveTypeNode,
257 MapTypeNode,
259 ArrayTypeNode,
261 PairTypeNode,
263 ObjectTypeNode,
265 TypeRefNode,
267 MetadataSectionNode,
269 ParameterMetadataSectionNode,
271 MetadataObjectItemNode,
273 MetadataObjectNode,
275 MetadataArrayNode,
277 LiteralIntegerNode,
279 LiteralFloatNode,
281 LiteralBooleanNode,
283 LiteralNoneNode,
285 LiteralNullNode,
287 LiteralStringNode,
289 LiteralPairNode,
291 LiteralArrayNode,
293 LiteralMapNode,
295 LiteralMapItemNode,
297 LiteralObjectNode,
299 LiteralObjectItemNode,
301 LiteralStructNode,
303 LiteralStructItemNode,
305 LiteralHintsNode,
307 LiteralHintsItemNode,
309 LiteralInputNode,
311 LiteralInputItemNode,
313 LiteralOutputNode,
315 LiteralOutputItemNode,
317 ParenthesizedExprNode,
319 NameRefExprNode,
321 IfExprNode,
323 LogicalNotExprNode,
325 NegationExprNode,
327 LogicalOrExprNode,
329 LogicalAndExprNode,
331 EqualityExprNode,
333 InequalityExprNode,
335 LessExprNode,
337 LessEqualExprNode,
339 GreaterExprNode,
341 GreaterEqualExprNode,
343 AdditionExprNode,
345 SubtractionExprNode,
347 MultiplicationExprNode,
349 DivisionExprNode,
351 ModuloExprNode,
353 ExponentiationExprNode,
355 CallExprNode,
357 IndexExprNode,
359 AccessExprNode,
361 PlaceholderNode,
363 PlaceholderSepOptionNode,
365 PlaceholderDefaultOptionNode,
367 PlaceholderTrueFalseOptionNode,
369 ConditionalStatementNode,
371 ConditionalStatementClauseNode,
373 ScatterStatementNode,
375 CallStatementNode,
377 CallTargetNode,
379 CallAliasNode,
381 CallAfterNode,
383 CallInputItemNode,
385
386 MAX,
389}
390
391impl SyntaxKind {
392 pub fn is_symbolic(&self) -> bool {
398 matches!(
399 self,
400 Self::Abandoned | Self::Unknown | Self::Unparsed | Self::MAX
401 )
402 }
403
404 pub fn describe(&self) -> &'static str {
406 match self {
407 Self::Unknown => unreachable!(),
408 Self::Unparsed => unreachable!(),
409 Self::Whitespace => "whitespace",
410 Self::Comment => "comment",
411 Self::Version => "version",
412 Self::Float => "float",
413 Self::Integer => "integer",
414 Self::Ident => "identifier",
415 Self::SingleQuote => "single quote",
416 Self::DoubleQuote => "double quote",
417 Self::OpenHeredoc => "open heredoc",
418 Self::CloseHeredoc => "close heredoc",
419 Self::ArrayTypeKeyword => "`Array` type keyword",
420 Self::BooleanTypeKeyword => "`Boolean` type keyword",
421 Self::FileTypeKeyword => "`File` type keyword",
422 Self::FloatTypeKeyword => "`Float` type keyword",
423 Self::IntTypeKeyword => "`Int` type keyword",
424 Self::MapTypeKeyword => "`Map` type keyword",
425 Self::ObjectTypeKeyword => "`Object` type keyword",
426 Self::PairTypeKeyword => "`Pair` type keyword",
427 Self::StringTypeKeyword => "`String` type keyword",
428 Self::AfterKeyword => "`after` keyword",
429 Self::AliasKeyword => "`alias` keyword",
430 Self::AsKeyword => "`as` keyword",
431 Self::CallKeyword => "`call` keyword",
432 Self::CommandKeyword => "`command` keyword",
433 Self::ElseKeyword => "`else` keyword",
434 Self::EnvKeyword => "`env` keyword",
435 Self::FalseKeyword => "`false` keyword",
436 Self::IfKeyword => "`if` keyword",
437 Self::InKeyword => "`in` keyword",
438 Self::ImportKeyword => "`import` keyword",
439 Self::InputKeyword => "`input` keyword",
440 Self::MetaKeyword => "`meta` keyword",
441 Self::NoneKeyword => "`None` keyword",
442 Self::NullKeyword => "`null` keyword",
443 Self::ObjectKeyword => "`object` keyword",
444 Self::OutputKeyword => "`output` keyword",
445 Self::ParameterMetaKeyword => "`parameter_meta` keyword",
446 Self::RuntimeKeyword => "`runtime` keyword",
447 Self::ScatterKeyword => "`scatter` keyword",
448 Self::StructKeyword => "`struct` keyword",
449 Self::EnumKeyword => "`enum` keyword",
450 Self::TaskKeyword => "`task` keyword",
451 Self::ThenKeyword => "`then` keyword",
452 Self::TrueKeyword => "`true` keyword",
453 Self::VersionKeyword => "`version` keyword",
454 Self::WorkflowKeyword => "`workflow` keyword",
455 Self::DirectoryTypeKeyword => "`Directory` type keyword",
456 Self::HintsKeyword => "`hints` keyword",
457 Self::RequirementsKeyword => "`requirements` keyword",
458 Self::OpenBrace => "`{` symbol",
459 Self::CloseBrace => "`}` symbol",
460 Self::OpenBracket => "`[` symbol",
461 Self::CloseBracket => "`]` symbol",
462 Self::Assignment => "`=` symbol",
463 Self::Colon => "`:` symbol",
464 Self::Comma => "`,` symbol",
465 Self::OpenParen => "`(` symbol",
466 Self::CloseParen => "`)` symbol",
467 Self::QuestionMark => "`?` symbol",
468 Self::Exclamation => "`!` symbol",
469 Self::Plus => "`+` symbol",
470 Self::Minus => "`-` symbol",
471 Self::LogicalOr => "`||` symbol",
472 Self::LogicalAnd => "`&&` symbol",
473 Self::Asterisk => "`*` symbol",
474 Self::Exponentiation => "`**` symbol",
475 Self::Slash => "`/` symbol",
476 Self::Percent => "`%` symbol",
477 Self::Equal => "`==` symbol",
478 Self::NotEqual => "`!=` symbol",
479 Self::LessEqual => "`<=` symbol",
480 Self::GreaterEqual => "`>=` symbol",
481 Self::Less => "`<` symbol",
482 Self::Greater => "`>` symbol",
483 Self::Dot => "`.` symbol",
484 Self::LiteralStringText => "literal string text",
485 Self::LiteralCommandText => "literal command text",
486 Self::PlaceholderOpen => "placeholder open",
487 Self::Abandoned => unreachable!(),
488 Self::RootNode => "root node",
489 Self::VersionStatementNode => "version statement",
490 Self::ImportStatementNode => "import statement",
491 Self::ImportAliasNode => "import alias",
492 Self::StructDefinitionNode => "struct definition",
493 Self::EnumDefinitionNode => "enum definition",
494 Self::EnumTypeParameterNode => "enum type parameter",
495 Self::EnumVariantNode => "enum variant",
496 Self::TaskDefinitionNode => "task definition",
497 Self::WorkflowDefinitionNode => "workflow definition",
498 Self::UnboundDeclNode => "declaration without assignment",
499 Self::BoundDeclNode => "declaration with assignment",
500 Self::InputSectionNode => "input section",
501 Self::OutputSectionNode => "output section",
502 Self::CommandSectionNode => "command section",
503 Self::RequirementsSectionNode => "requirements section",
504 Self::RequirementsItemNode => "requirements item",
505 Self::TaskHintsSectionNode | Self::WorkflowHintsSectionNode => "hints section",
506 Self::TaskHintsItemNode | Self::WorkflowHintsItemNode => "hints item",
507 Self::WorkflowHintsObjectNode => "literal object",
508 Self::WorkflowHintsObjectItemNode => "literal object item",
509 Self::WorkflowHintsArrayNode => "literal array",
510 Self::RuntimeSectionNode => "runtime section",
511 Self::RuntimeItemNode => "runtime item",
512 Self::PrimitiveTypeNode => "primitive type",
513 Self::MapTypeNode => "map type",
514 Self::ArrayTypeNode => "array type",
515 Self::PairTypeNode => "pair type",
516 Self::ObjectTypeNode => "object type",
517 Self::TypeRefNode => "type reference",
518 Self::MetadataSectionNode => "metadata section",
519 Self::ParameterMetadataSectionNode => "parameter metadata section",
520 Self::MetadataObjectItemNode => "metadata object item",
521 Self::MetadataObjectNode => "metadata object",
522 Self::MetadataArrayNode => "metadata array",
523 Self::LiteralIntegerNode => "literal integer",
524 Self::LiteralFloatNode => "literal float",
525 Self::LiteralBooleanNode => "literal boolean",
526 Self::LiteralNoneNode => "literal `None`",
527 Self::LiteralNullNode => "literal null",
528 Self::LiteralStringNode => "literal string",
529 Self::LiteralPairNode => "literal pair",
530 Self::LiteralArrayNode => "literal array",
531 Self::LiteralMapNode => "literal map",
532 Self::LiteralMapItemNode => "literal map item",
533 Self::LiteralObjectNode => "literal object",
534 Self::LiteralObjectItemNode => "literal object item",
535 Self::LiteralStructNode => "literal struct",
536 Self::LiteralStructItemNode => "literal struct item",
537 Self::LiteralHintsNode => "literal hints",
538 Self::LiteralHintsItemNode => "literal hints item",
539 Self::LiteralInputNode => "literal input",
540 Self::LiteralInputItemNode => "literal input item",
541 Self::LiteralOutputNode => "literal output",
542 Self::LiteralOutputItemNode => "literal output item",
543 Self::ParenthesizedExprNode => "parenthesized expression",
544 Self::NameRefExprNode => "name reference expression",
545 Self::IfExprNode => "`if` expression",
546 Self::LogicalNotExprNode => "logical not expression",
547 Self::NegationExprNode => "negation expression",
548 Self::LogicalOrExprNode => "logical OR expression",
549 Self::LogicalAndExprNode => "logical AND expression",
550 Self::EqualityExprNode => "equality expression",
551 Self::InequalityExprNode => "inequality expression",
552 Self::LessExprNode => "less than expression",
553 Self::LessEqualExprNode => "less than or equal to expression",
554 Self::GreaterExprNode => "greater than expression",
555 Self::GreaterEqualExprNode => "greater than or equal to expression",
556 Self::AdditionExprNode => "addition expression",
557 Self::SubtractionExprNode => "subtraction expression",
558 Self::MultiplicationExprNode => "multiplication expression",
559 Self::DivisionExprNode => "division expression",
560 Self::ModuloExprNode => "modulo expression",
561 Self::ExponentiationExprNode => "exponentiation expression",
562 Self::CallExprNode => "call expression",
563 Self::IndexExprNode => "index expression",
564 Self::AccessExprNode => "access expression",
565 Self::PlaceholderNode => "placeholder",
566 Self::PlaceholderSepOptionNode => "placeholder `sep` option",
567 Self::PlaceholderDefaultOptionNode => "placeholder `default` option",
568 Self::PlaceholderTrueFalseOptionNode => "placeholder `true`/`false` option",
569 Self::ConditionalStatementNode => "conditional statement",
570 Self::ConditionalStatementClauseNode => "conditional statement clause",
571 Self::ScatterStatementNode => "scatter statement",
572 Self::CallStatementNode => "call statement",
573 Self::CallTargetNode => "call target",
574 Self::CallAliasNode => "call alias",
575 Self::CallAfterNode => "call `after` clause",
576 Self::CallInputItemNode => "call input item",
577 Self::MAX => unreachable!(),
578 }
579 }
580
581 pub fn is_trivia(&self) -> bool {
583 matches!(self, Self::Whitespace | Self::Comment)
584 }
585
586 pub fn is_keyword(&self) -> bool {
588 matches!(
589 self,
590 SyntaxKind::AfterKeyword
591 | SyntaxKind::AliasKeyword
592 | SyntaxKind::ArrayTypeKeyword
593 | SyntaxKind::AsKeyword
594 | SyntaxKind::BooleanTypeKeyword
595 | SyntaxKind::CallKeyword
596 | SyntaxKind::CommandKeyword
597 | SyntaxKind::DirectoryTypeKeyword
598 | SyntaxKind::ElseKeyword
599 | SyntaxKind::EnvKeyword
600 | SyntaxKind::FalseKeyword
601 | SyntaxKind::FileTypeKeyword
602 | SyntaxKind::FloatTypeKeyword
603 | SyntaxKind::HintsKeyword
604 | SyntaxKind::IfKeyword
605 | SyntaxKind::ImportKeyword
606 | SyntaxKind::InKeyword
607 | SyntaxKind::InputKeyword
608 | SyntaxKind::IntTypeKeyword
609 | SyntaxKind::MapTypeKeyword
610 | SyntaxKind::MetaKeyword
611 | SyntaxKind::NoneKeyword
612 | SyntaxKind::NullKeyword
613 | SyntaxKind::ObjectKeyword
614 | SyntaxKind::ObjectTypeKeyword
615 | SyntaxKind::OutputKeyword
616 | SyntaxKind::PairTypeKeyword
617 | SyntaxKind::ParameterMetaKeyword
618 | SyntaxKind::RequirementsKeyword
619 | SyntaxKind::RuntimeKeyword
620 | SyntaxKind::ScatterKeyword
621 | SyntaxKind::StringTypeKeyword
622 | SyntaxKind::StructKeyword
623 | SyntaxKind::EnumKeyword
624 | SyntaxKind::TaskKeyword
625 | SyntaxKind::ThenKeyword
626 | SyntaxKind::TrueKeyword
627 | SyntaxKind::VersionKeyword
628 | SyntaxKind::WorkflowKeyword
629 )
630 }
631
632 pub fn is_operator(&self) -> bool {
634 matches!(
635 self,
636 SyntaxKind::Plus
637 | SyntaxKind::Minus
638 | SyntaxKind::Slash
639 | SyntaxKind::Percent
640 | SyntaxKind::Asterisk
641 | SyntaxKind::Exponentiation
642 | SyntaxKind::Equal
643 | SyntaxKind::NotEqual
644 | SyntaxKind::Less
645 | SyntaxKind::LessEqual
646 | SyntaxKind::Greater
647 | SyntaxKind::GreaterEqual
648 | SyntaxKind::LogicalAnd
649 | SyntaxKind::LogicalOr
650 | SyntaxKind::Exclamation
651 | SyntaxKind::Assignment
652 | SyntaxKind::QuestionMark
653 | SyntaxKind::Dot
654 | SyntaxKind::Colon
655 )
656 }
657}
658
659pub static ALL_SYNTAX_KIND: &[SyntaxKind] = SyntaxKind::VARIANTS;
661
662impl From<SyntaxKind> for rowan::SyntaxKind {
663 fn from(kind: SyntaxKind) -> Self {
664 rowan::SyntaxKind(kind as u16)
665 }
666}
667
668#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
670pub struct WorkflowDescriptionLanguage;
671
672impl rowan::Language for WorkflowDescriptionLanguage {
673 type Kind = SyntaxKind;
674
675 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
676 assert!(raw.0 <= SyntaxKind::MAX as u16);
677 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
678 }
679
680 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
681 kind.into()
682 }
683}
684
685pub type SyntaxNode = rowan::SyntaxNode<WorkflowDescriptionLanguage>;
687pub type SyntaxToken = rowan::SyntaxToken<WorkflowDescriptionLanguage>;
689pub type SyntaxElement = rowan::SyntaxElement<WorkflowDescriptionLanguage>;
691pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<WorkflowDescriptionLanguage>;
693
694pub fn construct_tree(source: &str, mut events: Vec<Event>) -> SyntaxNode {
696 let mut builder = GreenNodeBuilder::default();
697 let mut ancestors = Vec::new();
698
699 for i in 0..events.len() {
700 match std::mem::replace(&mut events[i], Event::abandoned()) {
701 Event::NodeStarted {
702 kind,
703 forward_parent,
704 } => {
705 ancestors.push(kind);
708 let mut idx = i;
709 let mut fp: Option<usize> = forward_parent;
710 while let Some(distance) = fp {
711 idx += distance;
712 fp = match std::mem::replace(&mut events[idx], Event::abandoned()) {
713 Event::NodeStarted {
714 kind,
715 forward_parent,
716 } => {
717 ancestors.push(kind);
718 forward_parent
719 }
720 _ => unreachable!(),
721 };
722 }
723
724 for kind in ancestors.drain(..).rev() {
727 if kind != SyntaxKind::Abandoned {
728 builder.start_node(kind.into());
729 }
730 }
731 }
732 Event::NodeFinished => builder.finish_node(),
733 Event::Token { kind, span } => {
734 builder.token(kind.into(), &source[span.start()..span.end()])
735 }
736 }
737 }
738
739 SyntaxNode::new_root(builder.finish())
740}
741
742#[derive(Clone, PartialEq, Eq, Hash)]
744pub struct SyntaxTree(SyntaxNode);
745
746impl SyntaxTree {
747 pub fn parse(source: &str) -> (Self, Vec<Diagnostic>) {
766 let parser = Parser::new(Lexer::new(source));
767 let (events, mut diagnostics) = grammar::document(parser);
768 diagnostics.sort();
769 (Self(construct_tree(source, events)), diagnostics)
770 }
771
772 pub fn root(&self) -> &SyntaxNode {
774 &self.0
775 }
776
777 pub fn green(&self) -> Cow<'_, GreenNodeData> {
779 self.0.green()
780 }
781
782 pub fn into_syntax(self) -> SyntaxNode {
784 self.0
785 }
786}
787
788impl fmt::Display for SyntaxTree {
789 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790 self.0.fmt(f)
791 }
792}
793
794impl fmt::Debug for SyntaxTree {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 self.0.fmt(f)
797 }
798}
799
800pub trait SyntaxTokenExt {
802 fn preceding_trivia(&self) -> impl Iterator<Item = SyntaxToken>;
804
805 fn inline_comment(&self) -> Option<SyntaxToken>;
808}
809
810impl SyntaxTokenExt for SyntaxToken {
811 fn preceding_trivia(&self) -> impl Iterator<Item = SyntaxToken> {
812 let mut tokens = VecDeque::new();
813 let mut cur = self.prev_token();
814 while let Some(token) = cur {
815 cur = token.prev_token();
816 if !token.kind().is_trivia() {
818 break;
819 }
820 if token.kind() == SyntaxKind::Comment
822 && let Some(prev) = token.prev_token()
823 {
824 if prev.kind() == SyntaxKind::Whitespace {
825 let has_newlines = prev.text().chars().any(|c| c == '\n');
826 if !has_newlines && prev.prev_token().is_some() {
832 break;
833 }
834 } else {
835 break;
837 }
838 }
839 match token.kind() {
841 SyntaxKind::Whitespace
842 if token.text().chars().filter(|c| *c == '\n').count() > 1 =>
843 {
844 tokens.push_front(token);
845 }
846 SyntaxKind::Comment => {
847 tokens.push_front(token);
848 }
849 _ => {}
850 }
851 }
852 tokens.into_iter()
853 }
854
855 fn inline_comment(&self) -> Option<SyntaxToken> {
856 let mut next = self.next_token();
857 iter::from_fn(move || {
858 let cur = next.clone()?;
859 next = cur.next_token();
860 Some(cur)
861 })
862 .take_while(|t| {
863 if !t.kind().is_trivia() {
865 return false;
866 }
867 if t.kind() == SyntaxKind::Whitespace {
869 return !t.text().chars().any(|c| c == '\n');
870 }
871 true
872 })
873 .find(|t| t.kind() == SyntaxKind::Comment)
874 }
875}
876
877#[cfg(test)]
878mod tests {
879 use super::*;
880 use crate::SyntaxTree;
881
882 #[test]
883 fn preceding_comments() {
884 let (tree, diagnostics) = SyntaxTree::parse(
885 "version 1.2
886
887# This comment should not be included
888task foo {} # This comment should not be included
889
890# Some
891# comments
892# are
893# long
894
895# Others are short
896
897# and, yet another
898workflow foo {} # This should not be collected.
899
900# This comment should not be included either.",
901 );
902
903 assert!(diagnostics.is_empty());
904
905 let workflow = tree.root().last_child().unwrap();
906 assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
907 let token = workflow.first_token().unwrap();
908 let mut trivia = token.preceding_trivia();
909 assert_eq!(trivia.next().unwrap().text(), "\n\n");
910 assert_eq!(trivia.next().unwrap().text(), "# Some");
911 assert_eq!(trivia.next().unwrap().text(), "# comments");
912 assert_eq!(trivia.next().unwrap().text(), "# are");
913 assert_eq!(trivia.next().unwrap().text(), "# long");
914 assert_eq!(trivia.next().unwrap().text(), "\n \n");
915 assert_eq!(trivia.next().unwrap().text(), "# Others are short");
916 assert_eq!(trivia.next().unwrap().text(), "\n\n");
917 assert_eq!(trivia.next().unwrap().text(), "# and, yet another");
918 assert!(trivia.next().is_none());
919 }
920
921 #[test]
922 fn inline_comment() {
923 let (tree, diagnostics) = SyntaxTree::parse(
924 "version 1.2
925
926# This comment should not be included
927task foo {}
928
929# This should not be collected.
930workflow foo {} # Here is a comment that should be collected.
931
932# This comment should not be included either.",
933 );
934
935 assert!(diagnostics.is_empty());
936
937 let workflow = tree.root().last_child().unwrap();
938 assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
939 let comment = workflow.last_token().unwrap().inline_comment().unwrap();
940 assert_eq!(
941 comment.text(),
942 "# Here is a comment that should be collected."
943 );
944 }
945}