summaryrefslogtreecommitdiff
path: root/support/texlab/src/syntax/latex/parser.rs
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-05-23 03:00:39 +0000
committerNorbert Preining <norbert@preining.info>2021-05-23 03:00:39 +0000
commitf1261b349e875b842745b63258c3e338cb1fe3bf (patch)
treeb5d402b3e80818cde2c079a42249f3dcb9732247 /support/texlab/src/syntax/latex/parser.rs
parent58aa1ac09b1d9e4769d0a0661cf12e2b2db41b14 (diff)
CTAN sync 202105230300
Diffstat (limited to 'support/texlab/src/syntax/latex/parser.rs')
-rw-r--r--support/texlab/src/syntax/latex/parser.rs1809
1 files changed, 1666 insertions, 143 deletions
diff --git a/support/texlab/src/syntax/latex/parser.rs b/support/texlab/src/syntax/latex/parser.rs
index 4ace4fbcd6..4333235ba5 100644
--- a/support/texlab/src/syntax/latex/parser.rs
+++ b/support/texlab/src/syntax/latex/parser.rs
@@ -1,186 +1,1709 @@
-use super::ast::*;
-use crate::{
- protocol::{Range, RangeExt},
- syntax::{
- generic_ast::{Ast, AstNodeIndex},
- text::SyntaxNode,
- },
+use cstree::GreenNodeBuilder;
+
+use super::{
+ lexer::Lexer,
+ SyntaxKind::{self, *},
+ SyntaxNode,
};
-use std::iter::Peekable;
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum Scope {
- Root,
- Group,
- Options,
+#[derive(Clone)]
+pub struct Parse {
+ pub root: SyntaxNode,
+}
+
+#[derive(Debug, Clone, Copy)]
+struct ParserContext {
+ allow_environment: bool,
+ allow_comma: bool,
+}
+
+impl Default for ParserContext {
+ fn default() -> Self {
+ Self {
+ allow_environment: true,
+ allow_comma: true,
+ }
+ }
}
#[derive(Debug)]
-pub struct Parser<I: Iterator<Item = Token>> {
- tree: Ast<Node>,
- tokens: Peekable<I>,
+struct Parser<'a> {
+ lexer: Lexer<'a>,
+ builder: GreenNodeBuilder<'static, 'static>,
}
-impl<I: Iterator<Item = Token>> Parser<I> {
- pub fn new(tokens: I) -> Self {
+impl<'a> Parser<'a> {
+ pub fn new(text: &'a str) -> Self {
Self {
- tree: Ast::new(),
- tokens: tokens.peekable(),
+ lexer: Lexer::new(text),
+ builder: GreenNodeBuilder::new(),
}
}
- pub fn parse(mut self) -> Tree {
- let children = self.content(Scope::Root);
+ fn eat(&mut self) {
+ let (kind, text) = self.lexer.eat().unwrap();
+ self.builder.token(kind.into(), text);
+ }
- let range = if children.is_empty() {
- Range::new_simple(0, 0, 0, 0)
+ fn peek(&self) -> Option<SyntaxKind> {
+ self.lexer.peek()
+ }
+
+ fn expect(&mut self, kind: SyntaxKind) {
+ if self.peek() == Some(kind) {
+ self.eat();
+ self.trivia();
} else {
- let start = self.tree[children[0]].start();
- let end = self.tree[children[children.len() - 1]].end();
- Range::new(start, end)
- };
+ self.builder.token(MISSING.into(), "");
+ }
+ }
- let root = self.tree.add_node(Node::Root(Root { range }));
- self.connect(root, &children);
- Tree {
- inner: self.tree,
- root,
+ fn expect2(&mut self, kind1: SyntaxKind, kind2: SyntaxKind) {
+ if self
+ .peek()
+ .filter(|&kind| kind == kind1 || kind == kind2)
+ .is_some()
+ {
+ self.eat();
+ self.trivia();
+ } else {
+ self.builder.token(MISSING.into(), "");
}
}
- fn content(&mut self, scope: Scope) -> Vec<AstNodeIndex> {
- let mut children = Vec::new();
- while let Some(ref token) = self.tokens.peek() {
- match token.kind {
- TokenKind::Word | TokenKind::BeginOptions => {
- children.push(self.text(scope));
- }
- TokenKind::Command => {
- children.push(self.command());
- }
- TokenKind::Comma => {
- children.push(self.comma());
- }
- TokenKind::Math => {
- children.push(self.math());
- }
- TokenKind::BeginGroup => {
- children.push(self.group(GroupKind::Group));
- }
- TokenKind::EndGroup => {
- if scope == Scope::Root {
- self.tokens.next();
- } else {
- return children;
- }
- }
- TokenKind::EndOptions => {
- if scope == Scope::Options {
- return children;
+ fn trivia(&mut self) {
+ while self
+ .peek()
+ .filter(|&kind| matches!(kind, WHITESPACE | COMMENT))
+ .is_some()
+ {
+ self.eat();
+ }
+ }
+
+ pub fn parse(mut self) -> Parse {
+ self.builder.start_node(ROOT.into());
+ self.preamble();
+ while self.peek().is_some() {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ let (green_node, interner) = self.builder.finish();
+ Parse {
+ root: SyntaxNode::new_root_with_resolver(green_node, interner.unwrap()),
+ }
+ }
+
+ fn content(&mut self, context: ParserContext) {
+ match self.peek().unwrap() {
+ WHITESPACE | COMMENT => self.eat(),
+ L_CURLY if context.allow_environment => self.curly_group(),
+ L_CURLY => self.curly_group_without_environments(),
+ L_BRACK | L_PAREN => self.mixed_group(),
+ R_CURLY | R_BRACK | R_PAREN => {
+ self.builder.start_node(ERROR.into());
+ self.eat();
+ self.builder.finish_node();
+ }
+ PARAMETER => self.eat(),
+ WORD | COMMA => self.text(context),
+ EQUALITY_SIGN => self.eat(),
+ DOLLAR => self.formula(),
+ GENERIC_COMMAND_NAME => self.generic_command(),
+ BEGIN_ENVIRONMENT_NAME if context.allow_environment => self.environment(),
+ BEGIN_ENVIRONMENT_NAME => self.generic_command(),
+ END_ENVIRONMENT_NAME => self.generic_command(),
+ BEGIN_EQUATION_NAME => self.equation(),
+ END_EQUATION_NAME => self.generic_command(),
+ MISSING | ERROR => self.eat(),
+ PART_NAME => self.part(),
+ CHAPTER_NAME => self.chapter(),
+ SECTION_NAME => self.section(),
+ SUBSECTION_NAME => self.subsection(),
+ SUBSUBSECTION_NAME => self.subsubsection(),
+ PARAGRAPH_NAME => self.paragraph(),
+ SUBPARAGRAPH_NAME => self.subparagraph(),
+ ENUM_ITEM_NAME => self.enum_item(),
+ CAPTION_NAME => self.caption(),
+ CITATION_NAME => self.citation(),
+ PACKAGE_INCLUDE_NAME => self.package_include(),
+ CLASS_INCLUDE_NAME => self.class_include(),
+ LATEX_INCLUDE_NAME => self.latex_include(),
+ BIBLATEX_INCLUDE_NAME => self.biblatex_include(),
+ BIBTEX_INCLUDE_NAME => self.bibtex_include(),
+ GRAPHICS_INCLUDE_NAME => self.graphics_include(),
+ SVG_INCLUDE_NAME => self.svg_include(),
+ INKSCAPE_INCLUDE_NAME => self.inkscape_include(),
+ VERBATIM_INCLUDE_NAME => self.verbatim_include(),
+ IMPORT_NAME => self.import(),
+ LABEL_DEFINITION_NAME => self.label_definition(),
+ LABEL_REFERENCE_NAME => self.label_reference(),
+ LABEL_REFERENCE_RANGE_NAME => self.label_reference_range(),
+ LABEL_NUMBER_NAME => self.label_number(),
+ COMMAND_DEFINITION_NAME => self.command_definition(),
+ MATH_OPERATOR_NAME => self.math_operator(),
+ GLOSSARY_ENTRY_DEFINITION_NAME => self.glossary_entry_definition(),
+ GLOSSARY_ENTRY_REFERENCE_NAME => self.glossary_entry_reference(),
+ ACRONYM_DEFINITION_NAME => self.acronym_definition(),
+ ACRONYM_DECLARATION_NAME => self.acronym_declaration(),
+ ACRONYM_REFERENCE_NAME => self.acronym_reference(),
+ THEOREM_DEFINITION_NAME => self.theorem_definition(),
+ COLOR_REFERENCE_NAME => self.color_reference(),
+ COLOR_DEFINITION_NAME => self.color_definition(),
+ COLOR_SET_DEFINITION_NAME => self.color_set_definition(),
+ TIKZ_LIBRARY_IMPORT_NAME => self.tikz_library_import(),
+ ENVIRONMENT_DEFINIITION_NAME => self.environment_definition(),
+ _ => unreachable!(),
+ }
+ }
+
+ fn text(&mut self, context: ParserContext) {
+ self.builder.start_node(TEXT.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| {
+ matches!(kind, WHITESPACE | COMMENT | WORD | COMMA)
+ && (context.allow_comma || kind != COMMA)
+ })
+ .is_some()
+ {
+ self.eat();
+ }
+ self.builder.finish_node();
+ }
+
+ fn group_with_token(
+ &mut self,
+ node_kind: SyntaxKind,
+ content_kind: SyntaxKind,
+ right_kind: SyntaxKind,
+ ) {
+ self.builder.start_node(node_kind.into());
+ self.eat();
+ self.trivia();
+ self.expect(content_kind);
+ self.expect(right_kind);
+ self.builder.finish_node();
+ }
+
+ fn curly_group(&mut self) {
+ self.builder.start_node(CURLY_GROUP.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| !matches!(kind, R_CURLY | END_ENVIRONMENT_NAME))
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
+
+ fn curly_group_without_environments(&mut self) {
+ self.builder.start_node(CURLY_GROUP.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| !matches!(kind, R_CURLY))
+ .is_some()
+ {
+ self.content(ParserContext {
+ allow_environment: false,
+ allow_comma: true,
+ });
+ }
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
+
+ fn curly_group_word(&mut self) {
+ self.group_with_token(CURLY_GROUP_WORD, WORD, R_CURLY);
+ }
+
+ fn curly_group_word_list(&mut self) {
+ self.builder.start_node(CURLY_GROUP_WORD_LIST.into());
+ self.eat();
+
+ while self
+ .peek()
+ .filter(|&kind| matches!(kind, WHITESPACE | COMMENT | WORD | COMMA))
+ .is_some()
+ {
+ self.eat();
+ }
+
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
+
+ fn curly_group_command(&mut self) {
+ self.group_with_token(CURLY_GROUP_COMMAND, GENERIC_COMMAND_NAME, R_CURLY);
+ }
+
+ fn brack_group(&mut self) {
+ self.builder.start_node(BRACK_GROUP.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ R_CURLY
+ | R_BRACK
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ | PARAGRAPH_NAME
+ | SUBPARAGRAPH_NAME
+ | ENUM_ITEM_NAME
+ | END_ENVIRONMENT_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.expect(R_BRACK);
+ self.builder.finish_node();
+ }
+
+ fn brack_group_word(&mut self) {
+ self.group_with_token(BRACK_GROUP_WORD, WORD, R_BRACK);
+ }
+
+ // fn paren_group(&mut self) {
+ // self.builder.start_node(PAREN_GROUP.into());
+ // self.eat();
+ // while self
+ // .peek()
+ // .filter(|&kind| {
+ // !matches!(
+ // kind,
+ // R_CURLY
+ // | R_BRACK
+ // | R_PAREN
+ // | PART_NAME
+ // | CHAPTER_NAME
+ // | SECTION_NAME
+ // | SUBSECTION_NAME
+ // | PARAGRAPH_NAME
+ // | SUBPARAGRAPH_NAME
+ // | ENUM_ITEM_NAME
+ // | END_ENVIRONMENT_NAME
+ // )
+ // })
+ // .is_some()
+ // {
+ // self.content();
+ // }
+ // self.expect(R_PAREN);
+ // self.builder.finish_node();
+ // }
+
+ fn mixed_group(&mut self) {
+ self.builder.start_node(MIXED_GROUP.into());
+ self.eat();
+ self.trivia();
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ R_CURLY
+ | R_BRACK
+ | R_PAREN
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ | PARAGRAPH_NAME
+ | SUBPARAGRAPH_NAME
+ | ENUM_ITEM_NAME
+ | END_ENVIRONMENT_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.expect2(R_BRACK, R_PAREN);
+ self.builder.finish_node();
+ }
+
+ fn key(&mut self) {
+ self.builder.start_node(KEY.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| matches!(kind, WHITESPACE | COMMENT | WORD))
+ .is_some()
+ {
+ self.eat();
+ }
+ self.builder.finish_node();
+ }
+
+ fn value(&mut self) {
+ self.builder.start_node(VALUE.into());
+ self.content(ParserContext {
+ allow_environment: true,
+ allow_comma: false,
+ });
+ self.builder.finish_node();
+ }
+
+ fn key_value_pair(&mut self) {
+ self.builder.start_node(KEY_VALUE_PAIR.into());
+ self.key();
+ if self.peek() == Some(EQUALITY_SIGN) {
+ self.eat();
+ self.trivia();
+ if self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME | R_CURLY | R_BRACK | R_PAREN | COMMA
+ )
+ })
+ .is_some()
+ {
+ self.value();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn key_value_body(&mut self) {
+ self.builder.start_node(KEY_VALUE_BODY.into());
+ while let Some(kind) = self.peek() {
+ match kind {
+ WHITESPACE | COMMENT => self.eat(),
+ WORD => {
+ self.key_value_pair();
+ if self.peek() == Some(COMMA) {
+ self.eat();
} else {
- children.push(self.text(scope));
+ break;
}
}
+ _ => break,
}
}
- children
- }
-
- fn group(&mut self, kind: GroupKind) -> AstNodeIndex {
- let left = self.tokens.next().unwrap();
- let scope = match kind {
- GroupKind::Group => Scope::Group,
- GroupKind::Options => Scope::Options,
- };
-
- let children = self.content(scope);
- let right_kind = match kind {
- GroupKind::Group => TokenKind::EndGroup,
- GroupKind::Options => TokenKind::EndOptions,
- };
-
- let right = if self.next_of_kind(right_kind) {
- self.tokens.next()
- } else {
- None
- };
-
- let end = right
- .as_ref()
- .map(SyntaxNode::end)
- .or_else(|| children.last().map(|child| self.tree[*child].end()))
- .unwrap_or_else(|| left.end());
- let range = Range::new(left.start(), end);
-
- let node = self.tree.add_node(Node::Group(Group {
- range,
- left,
- kind,
- right,
- }));
- self.connect(node, &children);
- node
- }
-
- fn command(&mut self) -> AstNodeIndex {
- let name = self.tokens.next().unwrap();
- let mut children = Vec::new();
- while let Some(token) = self.tokens.peek() {
- match token.kind {
- TokenKind::BeginGroup => children.push(self.group(GroupKind::Group)),
- TokenKind::BeginOptions => children.push(self.group(GroupKind::Options)),
+ self.builder.finish_node();
+ }
+
+ fn group_key_value(&mut self, node_kind: SyntaxKind, right_kind: SyntaxKind) {
+ self.builder.start_node(node_kind.into());
+ self.eat();
+ self.trivia();
+ self.key_value_body();
+ self.expect(right_kind);
+ self.builder.finish_node();
+ }
+
+ fn curly_group_key_value(&mut self) {
+ self.group_key_value(CURLY_GROUP_KEY_VALUE, R_CURLY);
+ }
+
+ fn brack_group_key_value(&mut self) {
+ self.group_key_value(BRACK_GROUP_KEY_VALUE, R_BRACK);
+ }
+
+ fn formula(&mut self) {
+ self.builder.start_node(FORMULA.into());
+ self.eat();
+ self.trivia();
+ while self
+ .peek()
+ .filter(|&kind| !matches!(kind, R_CURLY | END_ENVIRONMENT_NAME | DOLLAR))
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.expect(DOLLAR);
+ self.builder.finish_node();
+ }
+
+ fn generic_command(&mut self) {
+ self.builder.start_node(GENERIC_COMMAND.into());
+ self.eat();
+ while let Some(kind) = self.peek() {
+ match kind {
+ WHITESPACE | COMMENT => self.eat(),
+ L_CURLY => self.curly_group(),
+ L_BRACK | L_PAREN => self.mixed_group(),
_ => break,
}
}
+ self.builder.finish_node();
+ }
+
+ fn equation(&mut self) {
+ self.builder.start_node(EQUATION.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| !matches!(kind, END_ENVIRONMENT_NAME | R_CURLY | END_EQUATION_NAME))
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.expect(END_EQUATION_NAME);
+ self.builder.finish_node();
+ }
- let end = children
- .last()
- .map(|child| self.tree[*child].end())
- .unwrap_or_else(|| name.end());
- let range = Range::new(name.start(), end);
+ fn begin(&mut self) {
+ self.builder.start_node(BEGIN.into());
+ self.eat();
+ self.trivia();
- let node = self.tree.add_node(Node::Command(Command { range, name }));
- self.connect(node, &children);
- node
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.peek() == Some(L_BRACK) {
+ self.brack_group();
+ }
+ self.builder.finish_node();
}
- fn text(&mut self, scope: Scope) -> AstNodeIndex {
- let mut words = Vec::new();
- while let Some(ref token) = self.tokens.peek() {
- let kind = token.kind;
- let opts = kind == TokenKind::EndOptions && scope != Scope::Options;
- if kind == TokenKind::Word || kind == TokenKind::BeginOptions || opts {
- words.push(self.tokens.next().unwrap());
- } else {
- break;
- }
+ fn end(&mut self) {
+ self.builder.start_node(END.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
}
- let range = Range::new(words[0].start(), words[words.len() - 1].end());
- self.tree.add_node(Node::Text(Text { range, words }))
+
+ self.builder.finish_node();
}
- fn comma(&mut self) -> AstNodeIndex {
- let token = self.tokens.next().unwrap();
- let range = token.range();
- self.tree.add_node(Node::Comma(Comma { range, token }))
+ fn environment(&mut self) {
+ self.builder.start_node(ENVIRONMENT.into());
+ self.begin();
+
+ while self
+ .peek()
+ .filter(|&kind| kind != END_ENVIRONMENT_NAME)
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+
+ if self.peek() == Some(END_ENVIRONMENT_NAME) {
+ self.end();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ self.builder.finish_node();
}
- fn math(&mut self) -> AstNodeIndex {
- let token = self.tokens.next().unwrap();
- let range = token.range();
- self.tree.add_node(Node::Math(Math { range, token }))
+ fn preamble(&mut self) {
+ self.builder.start_node(PREAMBLE.into());
+ while self
+ .peek()
+ .filter(|&kind| kind != END_ENVIRONMENT_NAME)
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
}
- fn connect(&mut self, parent: AstNodeIndex, children: &[AstNodeIndex]) {
- for child in children {
- self.tree.add_edge(parent, *child);
+ fn part(&mut self) {
+ self.builder.start_node(PART.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
}
+
+ while self
+ .peek()
+ .filter(|&kind| !matches!(kind, END_ENVIRONMENT_NAME | R_CURLY | PART_NAME))
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
}
- fn next_of_kind(&mut self, kind: TokenKind) -> bool {
- self.tokens
+ fn chapter(&mut self) {
+ self.builder.start_node(CHAPTER.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ while self
.peek()
- .filter(|token| token.kind == kind)
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME | R_CURLY | PART_NAME | CHAPTER_NAME
+ )
+ })
.is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn section(&mut self) {
+ self.builder.start_node(SECTION.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME | R_CURLY | PART_NAME | CHAPTER_NAME | SECTION_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn subsection(&mut self) {
+ self.builder.start_node(SUBSECTION.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME
+ | R_CURLY
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn subsubsection(&mut self) {
+ self.builder.start_node(SUBSUBSECTION.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME
+ | R_CURLY
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ | SUBSUBSECTION_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn paragraph(&mut self) {
+ self.builder.start_node(PARAGRAPH.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME
+ | R_CURLY
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ | SUBSUBSECTION_NAME
+ | PARAGRAPH_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn subparagraph(&mut self) {
+ self.builder.start_node(SUBPARAGRAPH.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME
+ | R_CURLY
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ | SUBSUBSECTION_NAME
+ | PARAGRAPH_NAME
+ | SUBPARAGRAPH_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn enum_item(&mut self) {
+ self.builder.start_node(ENUM_ITEM.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_BRACK) {
+ self.brack_group();
+ }
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ !matches!(
+ kind,
+ END_ENVIRONMENT_NAME
+ | R_CURLY
+ | PART_NAME
+ | CHAPTER_NAME
+ | SECTION_NAME
+ | SUBSECTION_NAME
+ | SUBSUBSECTION_NAME
+ | PARAGRAPH_NAME
+ | SUBPARAGRAPH_NAME
+ | ENUM_ITEM_NAME
+ )
+ })
+ .is_some()
+ {
+ self.content(ParserContext::default());
+ }
+ self.builder.finish_node();
+ }
+
+ fn caption(&mut self) {
+ self.builder.start_node(CAPTION.into());
+ self.eat();
+ self.trivia();
+
+ if self.peek() == Some(L_BRACK) {
+ self.brack_group();
+ }
+
+ if self.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn citation(&mut self) {
+ self.builder.start_node(CITATION.into());
+ self.eat();
+ self.trivia();
+ for _ in 0..2 {
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group();
+ }
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word_list();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn generic_include(&mut self, kind: SyntaxKind, options: bool) {
+ self.builder.start_node(kind.into());
+ self.eat();
+ self.trivia();
+ if options && self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_key_value();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word_list();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn package_include(&mut self) {
+ self.generic_include(PACKAGE_INCLUDE, true);
+ }
+
+ fn class_include(&mut self) {
+ self.generic_include(CLASS_INCLUDE, true);
+ }
+
+ fn latex_include(&mut self) {
+ self.generic_include(LATEX_INCLUDE, false);
+ }
+
+ fn biblatex_include(&mut self) {
+ self.generic_include(BIBLATEX_INCLUDE, true);
+ }
+
+ fn bibtex_include(&mut self) {
+ self.generic_include(BIBTEX_INCLUDE, false);
+ }
+
+ fn graphics_include(&mut self) {
+ self.generic_include(GRAPHICS_INCLUDE, true);
+ }
+
+ fn svg_include(&mut self) {
+ self.generic_include(SVG_INCLUDE, true);
+ }
+
+ fn inkscape_include(&mut self) {
+ self.generic_include(INKSCAPE_INCLUDE, true);
+ }
+
+ fn verbatim_include(&mut self) {
+ self.generic_include(VERBATIM_INCLUDE, false);
+ }
+
+ fn import(&mut self) {
+ self.builder.start_node(IMPORT.into());
+ self.eat();
+ self.trivia();
+
+ for _ in 0..2 {
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn label_definition(&mut self) {
+ self.builder.start_node(LABEL_DEFINITION.into());
+ self.eat();
+ self.trivia();
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ self.builder.finish_node();
+ }
+
+ fn label_reference(&mut self) {
+ self.builder.start_node(LABEL_REFERENCE.into());
+ self.eat();
+ self.trivia();
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word_list();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ self.builder.finish_node();
+ }
+
+ fn label_reference_range(&mut self) {
+ self.builder.start_node(LABEL_REFERENCE_RANGE.into());
+ self.eat();
+ self.trivia();
+
+ for _ in 0..2 {
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn label_number(&mut self) {
+ self.builder.start_node(LABEL_NUMBER.into());
+ self.eat();
+ self.trivia();
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group();
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn command_definition(&mut self) {
+ self.builder.start_node(COMMAND_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_command();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_word();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn math_operator(&mut self) {
+ self.builder.start_node(MATH_OPERATOR.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_command();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn glossary_entry_definition(&mut self) {
+ self.builder.start_node(GLOSSARY_ENTRY_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_key_value();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn glossary_entry_reference(&mut self) {
+ self.builder.start_node(GLOSSARY_ENTRY_REFERENCE.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_key_value();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn acronym_definition(&mut self) {
+ self.builder.start_node(ACRONYM_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_key_value();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ for _ in 0..2 {
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn acronym_declaration(&mut self) {
+ self.builder.start_node(ACRONYM_DECLARATION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_key_value();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn acronym_reference(&mut self) {
+ self.builder.start_node(ACRONYM_REFERENCE.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_key_value();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn theorem_definition(&mut self) {
+ self.builder.start_node(THEOREM_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_word();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_word();
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn color_reference(&mut self) {
+ self.builder.start_node(COLOR_REFERENCE.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn color_definition(&mut self) {
+ self.builder.start_node(COLOR_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn color_set_definition(&mut self) {
+ self.builder.start_node(COLOR_SET_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_word();
+ }
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word_list();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ for _ in 0..3 {
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn tikz_library_import(&mut self) {
+ self.builder.start_node(TIKZ_LIBRARY_IMPORT.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word_list();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
+ fn environment_definition(&mut self) {
+ self.builder.start_node(ENVIRONMENT_DEFINITION.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_word();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group_word();
+ }
+
+ for _ in 0..2 {
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.curly_group_without_environments();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+ }
+
+ self.builder.finish_node();
+ }
+}
+
+pub fn parse(text: &str) -> Parse {
+ Parser::new(text).parse()
+}
+
+#[cfg(test)]
+mod tests {
+ use insta::assert_debug_snapshot;
+
+ use super::*;
+
+ fn setup(text: &str) -> SyntaxNode {
+ parse(&text.trim().replace("\r", "")).root
+ }
+
+ #[test]
+ fn test_empty() {
+ assert_debug_snapshot!(setup(r#""#));
+ }
+
+ #[test]
+ fn test_hello_world() {
+ assert_debug_snapshot!(setup(r#"Hello World!"#));
+ }
+
+ #[test]
+ fn test_generic_command_empty() {
+ assert_debug_snapshot!(setup(r#"\foo"#));
+ }
+
+ #[test]
+ fn test_generic_command_escape() {
+ assert_debug_snapshot!(setup(r#"\#"#));
+ }
+
+ #[test]
+ fn test_generic_command_args() {
+ assert_debug_snapshot!(setup(r#"\foo{bar}[qux]"#));
+ }
+
+ #[test]
+ fn test_inline() {
+ assert_debug_snapshot!(setup(r#"$x \in [0, \infty)$"#));
+ }
+
+ #[test]
+ fn test_inline_double_dollar() {
+ assert_debug_snapshot!(setup(r#"$$x \in [0, \infty)$$"#));
+ }
+
+ #[test]
+ fn test_brace_group_simple() {
+ assert_debug_snapshot!(setup(r#"{hello world}"#));
+ }
+
+ #[test]
+ fn test_brace_group_missing_end() {
+ assert_debug_snapshot!(setup(r#"{hello world"#));
+ }
+
+ #[test]
+ fn test_unmatched_braces() {
+ assert_debug_snapshot!(setup(r#"}{"#));
+ }
+
+ #[test]
+ fn test_unmatched_brackets() {
+ assert_debug_snapshot!(setup(r#"]["#));
+ }
+
+ #[test]
+ fn test_unmatched_brackets_with_group() {
+ assert_debug_snapshot!(setup(r#"{][}"#));
+ }
+
+ #[test]
+ fn test_escaped_brackets() {
+ assert_debug_snapshot!(setup(r#"{[}{]}"#));
+ }
+
+ #[test]
+ fn test_parameter() {
+ assert_debug_snapshot!(setup(r#"#1"#));
+ }
+
+ #[test]
+ fn test_parameter_error() {
+ assert_debug_snapshot!(setup(r#"#"#));
+ }
+
+ #[test]
+ fn test_environment_simple() {
+ assert_debug_snapshot!(setup(r#"\begin{foo} Hello World \end{bar}"#));
+ }
+
+ #[test]
+ fn test_environment_nested() {
+ assert_debug_snapshot!(setup(r#"\begin{foo} \begin{qux} \end{baz} \end{bar}"#));
+ }
+
+ #[test]
+ fn test_environment_nested_missing_braces() {
+ assert_debug_snapshot!(setup(
+ r#"\begin{foo \begin{qux Hello World \end{baz} \end{bar"#
+ ));
+ }
+
+ #[test]
+ fn test_structure_siblings() {
+ assert_debug_snapshot!(setup(r#"\section{Foo} Foo \section{Bar} Bar"#));
+ }
+
+ #[test]
+ fn test_structure_nested() {
+ assert_debug_snapshot!(setup(
+ r#"\part{1}\chapter{2}\section{3}\subsection{4}\subsubsection{5}\paragraph{6}\subparagraph{7}"#
+ ));
+ }
+
+ #[test]
+ fn test_structure_enum_item() {
+ assert_debug_snapshot!(setup(
+ r#"\begin{enumerate} \item 1 \item[2] 2 \item 3 \end{enumerate}"#
+ ));
+ }
+
+ #[test]
+ fn test_structure_invalid_nesting() {
+ assert_debug_snapshot!(setup(r#"\section{Foo} \chapter{Bar}"#));
+ }
+
+ #[test]
+ fn test_equation() {
+ assert_debug_snapshot!(setup(r#"\[ foo bar \]"#));
+ }
+
+ #[test]
+ fn test_equation_missing_end() {
+ assert_debug_snapshot!(setup(r#"\begin{a} \[ foo bar \end{b}"#));
+ }
+
+ #[test]
+ fn test_equation_missing_begin() {
+ assert_debug_snapshot!(setup(r#"\begin{a} foo bar \] \end{b}"#));
+ }
+
+ #[test]
+ fn test_caption_minimal() {
+ assert_debug_snapshot!(setup(r#"\caption{Foo \Bar Baz}"#));
+ }
+
+ #[test]
+ fn test_caption_minimal_error() {
+ assert_debug_snapshot!(setup(r#"\caption{Foo \Bar Baz"#));
+ }
+
+ #[test]
+ fn test_caption() {
+ assert_debug_snapshot!(setup(r#"\caption[qux]{Foo \Bar Baz}"#));
+ }
+
+ #[test]
+ fn test_caption_error() {
+ assert_debug_snapshot!(setup(r#"\caption[qux]{Foo \Bar Baz"#));
+ }
+
+ #[test]
+ fn test_caption_figure() {
+ assert_debug_snapshot!(setup(r#"\begin{figure}\caption{Foo}\end{figure}"#));
+ }
+
+ #[test]
+ fn test_citation_empty() {
+ assert_debug_snapshot!(setup(r#"\cite{}"#));
+ }
+
+ #[test]
+ fn test_citation_simple() {
+ assert_debug_snapshot!(setup(r#"\cite{foo}"#));
+ }
+
+ #[test]
+ fn test_citation_multiple_keys() {
+ assert_debug_snapshot!(setup(r#"\cite{foo, bar}"#));
+ }
+
+ #[test]
+ fn test_citation_star() {
+ assert_debug_snapshot!(setup(r#"\nocite{*}"#));
+ }
+
+ #[test]
+ fn test_citation_prenote() {
+ assert_debug_snapshot!(setup(r#"\cite[foo]{bar}"#));
+ }
+
+ #[test]
+ fn test_citation_prenote_postnote() {
+ assert_debug_snapshot!(setup(r#"\cite[foo][bar]{baz}"#));
+ }
+
+ #[test]
+ fn test_citation_missing_brace() {
+ assert_debug_snapshot!(setup(r#"\cite{foo"#));
+ }
+
+ #[test]
+ fn test_citation_redundant_comma() {
+ assert_debug_snapshot!(setup(r#"\cite{,foo,}"#));
+ }
+
+ #[test]
+ fn test_package_include_empty() {
+ assert_debug_snapshot!(setup(r#"\usepackage{}"#));
+ }
+
+ #[test]
+ fn test_package_include_simple() {
+ assert_debug_snapshot!(setup(r#"\usepackage{amsmath}"#));
+ }
+
+ #[test]
+ fn test_package_include_multiple() {
+ assert_debug_snapshot!(setup(r#"\usepackage{amsmath, lipsum}"#));
+ }
+
+ #[test]
+ fn test_package_include_options() {
+ assert_debug_snapshot!(setup(r#"\usepackage[foo = bar, baz, qux]{amsmath}"#));
+ }
+
+ #[test]
+ fn test_class_include_empty() {
+ assert_debug_snapshot!(setup(r#"\documentclass{}"#));
+ }
+
+ #[test]
+ fn test_class_include_simple() {
+ assert_debug_snapshot!(setup(r#"\documentclass{article}"#));
+ }
+
+ #[test]
+ fn test_class_include_options() {
+ assert_debug_snapshot!(setup(r#"\documentclass[foo = bar, baz, qux]{article}"#));
+ }
+
+ #[test]
+ fn test_latex_include_simple() {
+ assert_debug_snapshot!(setup(r#"\include{foo/bar}"#));
+ }
+
+ #[test]
+ fn test_latex_input_simple() {
+ assert_debug_snapshot!(setup(r#"\input{foo/bar.tex}"#));
+ }
+
+ #[test]
+ fn test_biblatex_include_simple() {
+ assert_debug_snapshot!(setup(r#"\addbibresource{foo/bar.bib}"#));
+ }
+
+ #[test]
+ fn test_biblatex_include_options() {
+ assert_debug_snapshot!(setup(r#"\addbibresource[foo=bar, baz]{foo/bar.bib}"#));
+ }
+
+ #[test]
+ fn test_bibtex_include_simple() {
+ assert_debug_snapshot!(setup(r#"\bibliography{foo/bar}"#));
+ }
+
+ #[test]
+ fn test_graphics_include_simple() {
+ assert_debug_snapshot!(setup(r#"\includegraphics{foo/bar.pdf}"#));
+ }
+
+ #[test]
+ fn test_graphics_include_options() {
+ assert_debug_snapshot!(setup(r#"\includegraphics[scale=.5]{foo/bar.pdf}"#));
+ }
+
+ #[test]
+ fn test_svg_include_simple() {
+ assert_debug_snapshot!(setup(r#"\includesvg{foo/bar.svg}"#));
+ }
+
+ #[test]
+ fn test_svg_include_options() {
+ assert_debug_snapshot!(setup(r#"\includesvg[scale=.5]{foo/bar.svg}"#));
+ }
+
+ #[test]
+ fn test_inkscape_include_simple() {
+ assert_debug_snapshot!(setup(r#"\includesvg{foo/bar}"#));
+ }
+
+ #[test]
+ fn test_inkscape_include_options() {
+ assert_debug_snapshot!(setup(r#"\includesvg[scale=.5]{foo/bar}"#));
+ }
+
+ #[test]
+ fn test_verbatim_include_simple() {
+ assert_debug_snapshot!(setup(r#"\verbatiminput{foo/bar.txt}"#));
+ }
+
+ #[test]
+ fn test_import_simple() {
+ assert_debug_snapshot!(setup(r#"\import{foo}{bar}"#));
+ }
+
+ #[test]
+ fn test_import_incomplete() {
+ assert_debug_snapshot!(setup(r#"\import{foo"#));
+ }
+
+ #[test]
+ fn test_label_definition_simple() {
+ assert_debug_snapshot!(setup(r#"\label{foo}"#));
+ }
+
+ #[test]
+ fn test_label_reference_simple() {
+ assert_debug_snapshot!(setup(r#"\ref{foo}"#));
+ }
+
+ #[test]
+ fn test_label_reference_multiple() {
+ assert_debug_snapshot!(setup(r#"\ref{foo, bar}"#));
+ }
+
+ #[test]
+ fn test_equation_label_reference_simple() {
+ assert_debug_snapshot!(setup(r#"\eqref{foo}"#));
+ }
+
+ #[test]
+ fn test_label_reference_range_simple() {
+ assert_debug_snapshot!(setup(r#"\crefrange{foo}{bar}"#));
+ }
+
+ #[test]
+ fn test_label_reference_range_incomplete() {
+ assert_debug_snapshot!(setup(r#"\crefrange{foo}"#));
+ }
+
+ #[test]
+ fn test_label_reference_range_error() {
+ assert_debug_snapshot!(setup(r#"\crefrange{foo{bar}"#));
+ }
+
+ #[test]
+ fn test_label_number() {
+ assert_debug_snapshot!(setup(r#"\newlabel{foo}{{1.1}}"#));
+ }
+
+ #[test]
+ fn test_command_definition_simple() {
+ assert_debug_snapshot!(setup(r#"\newcommand[1]{\id}{#1}"#));
+ }
+
+ #[test]
+ fn test_command_definition_no_argc() {
+ assert_debug_snapshot!(setup(r#"\newcommand{\foo}{foo}"#));
+ }
+
+ #[test]
+ fn test_command_definition_no_impl() {
+ assert_debug_snapshot!(setup(r#"\newcommand{\foo}"#));
+ }
+
+ #[test]
+ fn test_command_definition_no_impl_error() {
+ assert_debug_snapshot!(setup(r#"\newcommand{\foo"#));
+ }
+
+ #[test]
+ fn test_math_operator_simple() {
+ assert_debug_snapshot!(setup(r#"\DeclareMathOperator{\foo}{foo}"#));
+ }
+
+ #[test]
+ fn test_math_operator_no_impl() {
+ assert_debug_snapshot!(setup(r#"\DeclareMathOperator{\foo}"#));
+ }
+
+ #[test]
+ fn test_glossary_entry_definition_simple() {
+ assert_debug_snapshot!(setup(r#"\newglossaryentry{foo}{bar = baz, qux,}"#));
+ }
+
+ #[test]
+ fn test_glossary_entry_reference_simple() {
+ assert_debug_snapshot!(setup(r#"\gls{foo}"#));
+ }
+
+ #[test]
+ fn test_glossary_entry_reference_options() {
+ assert_debug_snapshot!(setup(r#"\gls[foo = bar, qux]{baz}"#));
+ }
+
+ #[test]
+ fn test_acroynm_definition_simple() {
+ assert_debug_snapshot!(setup(r#"\newacronym{fpsLabel}{FPS}{Frame per Second}"#));
+ }
+
+ #[test]
+ fn test_acroynm_definition_options() {
+ assert_debug_snapshot!(setup(
+ r#"\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}"#
+ ));
+ }
+
+ #[test]
+ fn test_acroynm_reference_simple() {
+ assert_debug_snapshot!(setup(r#"\acrshort{fpsLabel}"#));
+ }
+
+ #[test]
+ fn test_acroynm_reference_options() {
+ assert_debug_snapshot!(setup(r#"\acrshort[foo=bar,baz]{fpsLabel}"#));
+ }
+
+ #[test]
+ fn test_theorem_definition_only_name() {
+ assert_debug_snapshot!(setup(r#"\newtheorem{foo}"#));
+ }
+
+ #[test]
+ fn test_theorem_definition_name_with_description() {
+ assert_debug_snapshot!(setup(r#"\newtheorem{foo}{Foo}"#));
+ }
+
+ #[test]
+ fn test_theorem_definition_name_with_description_and_counter() {
+ assert_debug_snapshot!(setup(r#"\newtheorem{foo}[bar]{Foo}"#));
+ }
+
+ #[test]
+ fn test_theorem_definition_name_with_counter() {
+ assert_debug_snapshot!(setup(r#"\newtheorem{foo}[bar]"#));
+ }
+
+ #[test]
+ fn test_theorem_definition_full() {
+ assert_debug_snapshot!(setup(r#"\newtheorem{foo}[bar]{Foo}[baz]"#));
+ }
+
+ #[test]
+ fn test_color_reference_simple() {
+ assert_debug_snapshot!(setup(r#"\color{black}"#));
+ }
+
+ #[test]
+ fn test_color_definition_simple() {
+ assert_debug_snapshot!(setup(r#"\definecolor{foo}{rgb}{255,168,0}"#));
+ }
+
+ #[test]
+ fn test_color_set_definition_simple() {
+ assert_debug_snapshot!(setup(r#"\definecolorset[ty]{rgb,HTML}{foo}{bar}{baz}"#));
+ }
+
+ #[test]
+ fn test_color_set_definition_error1() {
+ assert_debug_snapshot!(setup(r#"\definecolorset[ty]{rgb,HTML}{foo}{bar}"#));
+ }
+
+ #[test]
+ fn test_color_set_definition_error2() {
+ assert_debug_snapshot!(setup(r#"\definecolorset{rgb,HTML}{foo}"#));
+ }
+
+ #[test]
+ fn test_color_set_definition_error3() {
+ assert_debug_snapshot!(setup(r#"\definecolorset{rgb,HTML}"#));
+ }
+
+ #[test]
+ fn test_color_set_definition_error4() {
+ assert_debug_snapshot!(setup(r#"\definecolorset"#));
+ }
+
+ #[test]
+ fn test_pgf_library_import_simple() {
+ assert_debug_snapshot!(setup(r#"\usepgflibrary{foo}"#));
+ }
+
+ #[test]
+ fn test_tikz_library_import_simple() {
+ assert_debug_snapshot!(setup(r#"\usetikzlibrary{foo}"#));
+ }
+
+ #[test]
+ fn test_environment_definition() {
+ assert_debug_snapshot!(setup(r#"\newenvironment{bar}[1]{\begin{foo}}{\end{foo}}"#));
+ }
+
+ #[test]
+ fn test_acronym_declaration() {
+ assert_debug_snapshot!(setup(
+ r#"\DeclareAcronym{eg}{short = e.g,long = for example,tag = abbrev}"#
+ ));
}
}