Actually just three programming languages in a trenchcoat
1use crate::{Parser, Spanned};
2use source_span::Span;
3use trilogy_scanner::Token;
4use trilogy_scanner::TokenType::{self, DocInner, DocOuter};
5
6/// A documentation comment, either inner or outer.
7///
8/// ```trilogy
9/// ## Hello this is a doc comment.
10/// ## It may be multiple lines long.
11/// ```
12#[derive(Clone, Debug)]
13pub struct Documentation {
14 pub tokens: Vec<Token>,
15 pub span: Span,
16}
17
18impl Spanned for Documentation {
19 fn span(&self) -> Span {
20 self.span
21 }
22}
23
24impl Documentation {
25 fn parse(parser: &mut Parser, token_type: TokenType) -> Option<Self> {
26 let mut tokens = vec![];
27
28 while let Ok(token) = parser.expect(token_type) {
29 tokens.push(token);
30 }
31 if tokens.is_empty() {
32 return None;
33 }
34
35 Some(Self {
36 span: tokens
37 .iter()
38 .map(|token| token.span)
39 .reduce(|l, r| l.union(r))
40 .unwrap(),
41 tokens,
42 })
43 }
44
45 pub(crate) fn parse_inner(parser: &mut Parser) -> Option<Self> {
46 Self::parse(parser, DocInner)
47 }
48
49 pub(crate) fn parse_outer(parser: &mut Parser) -> Option<Self> {
50 Self::parse(parser, DocOuter)
51 }
52}
53
54#[cfg(test)]
55mod test {
56 use super::*;
57
58 test_parse!(documentation_inner: "#! hello\n" => Documentation::parse_inner => Documentation { .. });
59 test_parse!(documentation_inner_multiline: "#! hello\n#! world\n" => Documentation::parse_inner => Documentation { .. });
60 test_parse!(documentation_inner_gaps: "#! hello\n\n#! world\n" => Documentation::parse_inner => Documentation { .. });
61
62 test_parse!(documentation_outer: "## hello\n" => Documentation::parse_outer => Documentation { .. });
63 test_parse!(documentation_outer_multiline: "## hello\n## world\n" => Documentation::parse_outer => Documentation { .. });
64 test_parse!(documentation_outer_gaps: "## hello\n\n## world\n" => Documentation::parse_outer => Documentation { .. });
65}