From 45c7bac9080d91b53c686e776fc6217d7f139b86 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Wed, 12 Apr 2023 03:01:14 +0000 Subject: CTAN sync 202304120301 --- support/texlab/crates/syntax/src/bibtex.rs | 304 +++++++++++ support/texlab/crates/syntax/src/latex.rs | 29 ++ support/texlab/crates/syntax/src/latex/cst.rs | 688 +++++++++++++++++++++++++ support/texlab/crates/syntax/src/latex/kind.rs | 91 ++++ support/texlab/crates/syntax/src/lib.rs | 35 ++ 5 files changed, 1147 insertions(+) create mode 100644 support/texlab/crates/syntax/src/bibtex.rs create mode 100644 support/texlab/crates/syntax/src/latex.rs create mode 100644 support/texlab/crates/syntax/src/latex/cst.rs create mode 100644 support/texlab/crates/syntax/src/latex/kind.rs create mode 100644 support/texlab/crates/syntax/src/lib.rs (limited to 'support/texlab/crates/syntax/src') diff --git a/support/texlab/crates/syntax/src/bibtex.rs b/support/texlab/crates/syntax/src/bibtex.rs new file mode 100644 index 0000000000..9b4b1ab77e --- /dev/null +++ b/support/texlab/crates/syntax/src/bibtex.rs @@ -0,0 +1,304 @@ +use rowan::{ast::AstNode, NodeOrToken}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +#[allow(non_camel_case_types)] +#[repr(u16)] +pub enum SyntaxKind { + WHITESPACE, + JUNK, + L_DELIM, + R_DELIM, + L_CURLY, + R_CURLY, + COMMA, + POUND, + QUOTE, + EQ, + TYPE, + WORD, + NAME, + INTEGER, + NBSP, + ACCENT_NAME, + COMMAND_NAME, + + PREAMBLE, + STRING, + ENTRY, + FIELD, + VALUE, + LITERAL, + JOIN, + ACCENT, + COMMAND, + CURLY_GROUP, + QUOTE_GROUP, + ROOT, +} + +pub use SyntaxKind::*; + +impl From for rowan::SyntaxKind { + fn from(kind: SyntaxKind) -> Self { + Self(kind as u16) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum Lang {} + +impl rowan::Language for Lang { + type Kind = SyntaxKind; + + fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= SyntaxKind::ROOT as u16); + unsafe { std::mem::transmute::(raw.0) } + } + + fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { + kind.into() + } +} + +pub type SyntaxNode = rowan::SyntaxNode; + +pub type SyntaxToken = rowan::SyntaxToken; + +pub type SyntaxElement = rowan::SyntaxElement; + +macro_rules! ast_node { + (name: $name:ident, kinds: [$($kind:pat),+], traits: [$($trait: ident),*]) => { + #[derive(Clone)] + pub struct $name { + node: SyntaxNode, + } + + impl AstNode for $name { + type Language = Lang; + + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + $($kind => true,)+ + _ => false, + } + } + + fn cast(node: SyntaxNode) -> Option + where + Self: Sized, + { + match node.kind() { + $($kind => Some(Self { node}),)+ + _ => None, + } + } + + fn syntax(&self) -> &SyntaxNode { + &self.node + } + } + + $( + impl $trait for $name { } + )* + }; +} + +macro_rules! ast_node_enum { + (name: $name:ident, variants: [$($variant:ident),+]) => { + #[derive(Clone)] + pub enum $name { + $($variant($variant),)* + } + + impl AstNode for $name { + type Language = Lang; + + fn can_cast(kind: SyntaxKind) -> bool { + false $(|| $variant::can_cast(kind))+ + } + + fn cast(node: SyntaxNode) -> Option + where + Self: Sized, + { + None $(.or_else(|| $variant::cast(node.clone()).map(Self::$variant)))* + } + + fn syntax(&self) -> &SyntaxNode { + match self { + $(Self::$variant(node) => node.syntax(),)* + } + } + } + + $( + impl From<$variant> for $name { + fn from(node: $variant) -> Self { + Self::$variant(node) + } + } + )* + }; +} + +pub trait HasType: AstNode { + fn type_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == TYPE) + } +} + +pub trait HasDelims: AstNode { + fn left_delim_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == L_DELIM) + } + + fn right_delim_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == R_DELIM) + } +} + +pub trait HasName: AstNode { + fn name_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == NAME) + } +} + +pub trait HasEq: AstNode { + fn eq_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == NAME) + } +} + +pub trait HasComma: AstNode { + fn comma_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == COMMA) + } +} + +pub trait HasPound: AstNode { + fn pound_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == POUND) + } +} + +pub trait HasInteger: AstNode { + fn integer_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == INTEGER) + } +} + +pub trait HasCommandName: AstNode { + fn command_name_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == COMMAND_NAME) + } +} + +pub trait HasAccentName: AstNode { + fn accent_name_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == ACCENT_NAME) + } +} + +pub trait HasWord: AstNode { + fn word_token(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|token| token.kind() == WORD) + } +} + +pub trait HasValue: AstNode { + fn value(&self) -> Option { + self.syntax().children().find_map(Value::cast) + } +} + +ast_node!(name: Root, kinds: [ROOT], traits: []); + +impl Root { + pub fn strings(&self) -> impl Iterator { + self.syntax().children().filter_map(StringDef::cast) + } + + pub fn entries(&self) -> impl Iterator { + self.syntax().children().filter_map(Entry::cast) + } + + pub fn find_entry(&self, name: &str) -> Option { + self.entries().find(|entry| { + entry + .name_token() + .map_or(false, |token| token.text() == name) + }) + } +} + +ast_node!(name: Preamble, kinds: [PREAMBLE], traits: [HasType, HasDelims, HasValue]); + +ast_node!(name: StringDef, kinds: [STRING], traits: [HasType, HasDelims, HasName, HasEq, HasValue]); + +ast_node!(name: Entry, kinds: [ENTRY], traits: [HasType, HasDelims, HasName, HasComma]); + +impl Entry { + pub fn fields(&self) -> impl Iterator { + self.syntax().children().filter_map(Field::cast) + } +} + +ast_node!(name: Field, kinds: [FIELD], traits: [HasName, HasEq, HasValue, HasComma]); + +ast_node_enum!(name: Value, variants: [Literal, CurlyGroup, QuoteGroup, Join, Accent, Command]); + +ast_node!(name: Literal, kinds: [LITERAL], traits: [HasName, HasInteger]); + +ast_node!(name: CurlyGroup, kinds: [CURLY_GROUP], traits: []); + +ast_node!(name: QuoteGroup, kinds: [QUOTE_GROUP], traits: []); + +ast_node!(name: Join, kinds: [JOIN], traits: [HasPound]); + +impl Join { + pub fn left_value(&self) -> Option { + self.syntax().children().find_map(Value::cast) + } + + pub fn right_value(&self) -> Option { + self.syntax().children().filter_map(Value::cast).nth(1) + } +} + +ast_node!(name: Accent, kinds: [ACCENT], traits: [HasAccentName, HasWord]); + +ast_node!(name: Command, kinds: [COMMAND], traits: [HasCommandName]); diff --git a/support/texlab/crates/syntax/src/latex.rs b/support/texlab/crates/syntax/src/latex.rs new file mode 100644 index 0000000000..ce1ca125fd --- /dev/null +++ b/support/texlab/crates/syntax/src/latex.rs @@ -0,0 +1,29 @@ +mod cst; +mod kind; + +pub use self::{ + cst::*, + kind::SyntaxKind::{self, *}, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum LatexLanguage {} + +impl rowan::Language for LatexLanguage { + type Kind = SyntaxKind; + + fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= ROOT as u16); + unsafe { std::mem::transmute::(raw.0) } + } + + fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { + kind.into() + } +} + +pub type SyntaxNode = rowan::SyntaxNode; + +pub type SyntaxToken = rowan::SyntaxToken; + +pub type SyntaxElement = rowan::SyntaxElement; diff --git a/support/texlab/crates/syntax/src/latex/cst.rs b/support/texlab/crates/syntax/src/latex/cst.rs new file mode 100644 index 0000000000..767aa50ecc --- /dev/null +++ b/support/texlab/crates/syntax/src/latex/cst.rs @@ -0,0 +1,688 @@ +use itertools::{EitherOrBoth, Itertools}; +use rowan::{ast::AstNode, TextRange}; + +use super::{ + LatexLanguage, + SyntaxKind::{self, *}, + SyntaxNode, SyntaxToken, +}; + +pub fn small_range(node: &dyn AstNode) -> TextRange { + let full_range = node.syntax().text_range(); + let start = full_range.start(); + let mut token = node.syntax().last_token(); + while let Some(current) = token { + if !matches!(current.kind(), LINE_BREAK | WHITESPACE | COMMENT) { + return TextRange::new(start, current.text_range().end()); + } + token = current.prev_token(); + } + + TextRange::new(start, start) +} + +macro_rules! cst_node { + ($name:ident, $($kind:pat),+) => { + #[derive(Clone)] + #[repr(transparent)] + pub struct $name(SyntaxNode); + + impl AstNode for $name { + type Language = LatexLanguage; + + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + $($kind => true,)+ + _ => false, + } + } + + fn cast(node: SyntaxNode) -> Option + where + Self: Sized, + { + match node.kind() { + $($kind => Some(Self(node)),)+ + _ => None, + } + } + + fn syntax(&self) -> &SyntaxNode { + &self.0 + } + } + }; +} + +cst_node!(Text, TEXT); + +impl Text { + pub fn words(&self) -> impl Iterator { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .filter(|node| node.kind() == WORD) + } +} + +pub trait HasCurly: AstNode { + fn left_curly(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == L_CURLY) + } + + fn right_curly(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == R_CURLY) + } + + fn content_text(&self) -> Option { + self.left_curly()?; + self.right_curly()?; + let mut text = String::new(); + for child in self + .syntax() + .descendants_with_tokens() + .filter_map(|child| child.into_token()) + .filter(|token| !matches!(token.kind(), COMMENT)) + { + text.push_str(child.text()); + } + let text = text.trim_end(); + let text = text[1..text.len() - 1].trim().to_string(); + + Some(text) + } +} + +cst_node!(CurlyGroup, CURLY_GROUP); + +impl HasCurly for CurlyGroup {} + +impl CurlyGroup {} + +pub trait HasBrack: AstNode { + fn left_brack(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == L_BRACK) + } + + fn right_brack(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == R_BRACK) + } + + fn content_text(&self) -> Option { + self.left_brack()?; + self.right_brack()?; + let mut text = String::new(); + for child in self + .syntax() + .descendants_with_tokens() + .filter_map(|child| child.into_token()) + .filter(|token| !matches!(token.kind(), COMMENT)) + { + text.push_str(child.text()); + } + let text = text.trim_end(); + let text = text[1..text.len() - 1].trim().to_string(); + + Some(text) + } +} + +cst_node!(BrackGroup, BRACK_GROUP); + +impl BrackGroup {} + +impl HasBrack for BrackGroup {} + +pub trait HasParen: AstNode { + fn left_paren(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == L_PAREN) + } + + fn right_paren(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == R_PAREN) + } +} + +cst_node!(ParenGroup, PAREN_GROUP); + +impl HasParen for ParenGroup {} + +cst_node!(MixedGroup, MIXED_GROUP); + +impl MixedGroup { + pub fn left_delim(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| matches!(node.kind(), L_BRACK | L_PAREN)) + } + + pub fn right_delim(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| matches!(node.kind(), R_BRACK | R_PAREN)) + } +} + +cst_node!(CurlyGroupWord, CURLY_GROUP_WORD); + +impl HasCurly for CurlyGroupWord {} + +impl CurlyGroupWord { + pub fn key(&self) -> Option { + self.syntax().children().find_map(Key::cast) + } +} + +cst_node!(BrackGroupWord, BRACK_GROUP_WORD); + +impl HasBrack for BrackGroupWord {} + +impl BrackGroupWord { + pub fn key(&self) -> Option { + self.syntax().children().find_map(Key::cast) + } +} + +cst_node!(CurlyGroupWordList, CURLY_GROUP_WORD_LIST); + +impl HasCurly for CurlyGroupWordList {} + +impl CurlyGroupWordList { + pub fn keys(&self) -> impl Iterator { + self.syntax().children().filter_map(Key::cast) + } +} + +cst_node!(CurlyGroupCommand, CURLY_GROUP_COMMAND); + +impl HasCurly for CurlyGroupCommand {} + +impl CurlyGroupCommand { + pub fn command(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == COMMAND_NAME) + } +} + +cst_node!(Key, KEY); + +impl Key { + pub fn words(&self) -> impl Iterator { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .filter(|node| !matches!(node.kind(), WHITESPACE | LINE_BREAK | COMMENT)) + } +} + +impl PartialEq for Key { + fn eq(&self, other: &Self) -> bool { + self.words() + .zip_longest(other.words()) + .all(|result| match result { + EitherOrBoth::Both(left, right) => left.text() == right.text(), + EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => false, + }) + } +} + +impl Eq for Key {} + +impl ToString for Key { + fn to_string(&self) -> String { + let mut buf = String::new(); + for word in self.words() { + buf.push_str(word.text()); + buf.push(' '); + } + + buf.pop().unwrap(); + buf + } +} + +cst_node!(Value, VALUE); + +cst_node!(KeyValuePair, KEY_VALUE_PAIR); + +impl KeyValuePair { + pub fn key(&self) -> Option { + self.syntax().children().find_map(Key::cast) + } + + pub fn value(&self) -> Option { + self.syntax().children().find_map(Value::cast) + } +} + +cst_node!(KeyValueBody, KEY_VALUE_BODY); + +impl KeyValueBody { + pub fn pairs(&self) -> impl Iterator { + self.syntax().children().filter_map(KeyValuePair::cast) + } +} + +pub trait HasKeyValueBody: AstNode { + fn body(&self) -> Option { + self.syntax().children().find_map(KeyValueBody::cast) + } +} + +cst_node!(CurlyGroupKeyValue, CURLY_GROUP_KEY_VALUE); + +impl HasCurly for CurlyGroupKeyValue {} + +impl HasKeyValueBody for CurlyGroupKeyValue {} + +cst_node!(BrackGroupKeyValue, BRACK_GROUP_KEY_VALUE); + +impl HasBrack for BrackGroupKeyValue {} + +impl HasKeyValueBody for BrackGroupKeyValue {} + +cst_node!(Formula, FORMULA); + +cst_node!(GenericCommand, GENERIC_COMMAND); + +impl GenericCommand { + pub fn name(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|node| node.into_token()) + .find(|node| node.kind() == COMMAND_NAME) + } +} + +cst_node!(Equation, EQUATION); + +cst_node!(Begin, BEGIN); + +impl Begin { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } + + pub fn options(&self) -> Option { + self.syntax().children().find_map(BrackGroup::cast) + } +} + +cst_node!(End, END); + +impl End { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(Environment, ENVIRONMENT); + +impl Environment { + pub fn begin(&self) -> Option { + self.syntax().children().find_map(Begin::cast) + } + + pub fn end(&self) -> Option { + self.syntax().children().find_map(End::cast) + } +} + +cst_node!( + Section, + PART, + CHAPTER, + SECTION, + SUBSECTION, + SUBSUBSECTION, + PARAGRAPH, + SUBPARAGRAPH +); + +impl Section { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroup::cast) + } +} + +cst_node!(EnumItem, ENUM_ITEM); + +impl EnumItem { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn label(&self) -> Option { + self.syntax().children().find_map(BrackGroup::cast) + } +} + +cst_node!(Caption, CAPTION); + +impl Caption { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn short(&self) -> Option { + self.syntax().children().find_map(BrackGroup::cast) + } + + pub fn long(&self) -> Option { + self.syntax().children().find_map(CurlyGroup::cast) + } +} + +cst_node!(Citation, CITATION); + +impl Citation { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn prenote(&self) -> Option { + self.syntax().children().find_map(BrackGroup::cast) + } + + pub fn postnote(&self) -> Option { + self.syntax().children().filter_map(BrackGroup::cast).nth(1) + } + + pub fn key_list(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWordList::cast) + } +} + +cst_node!( + Include, + PACKAGE_INCLUDE, + CLASS_INCLUDE, + LATEX_INCLUDE, + BIBLATEX_INCLUDE, + BIBTEX_INCLUDE, + GRAPHICS_INCLUDE, + SVG_INCLUDE, + INKSCAPE_INCLUDE, + VERBATIM_INCLUDE +); + +impl Include { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn path_list(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWordList::cast) + } +} + +cst_node!(Import, IMPORT); + +impl Import { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn directory(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } + + pub fn file(&self) -> Option { + self.syntax() + .children() + .filter_map(CurlyGroupWord::cast) + .nth(1) + } +} + +cst_node!(LabelDefinition, LABEL_DEFINITION); + +impl LabelDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(LabelReference, LABEL_REFERENCE); + +impl LabelReference { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name_list(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWordList::cast) + } +} + +cst_node!(LabelReferenceRange, LABEL_REFERENCE_RANGE); + +impl LabelReferenceRange { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn from(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } + + pub fn to(&self) -> Option { + self.syntax() + .children() + .filter_map(CurlyGroupWord::cast) + .nth(1) + } +} + +cst_node!(LabelNumber, LABEL_NUMBER); + +impl LabelNumber { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } + + pub fn text(&self) -> Option { + self.syntax().children().find_map(CurlyGroup::cast) + } +} + +cst_node!(TheoremDefinition, THEOREM_DEFINITION); + +impl TheoremDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } + + pub fn description(&self) -> Option { + self.syntax().children().find_map(CurlyGroup::cast) + } +} + +cst_node!(CommandDefinition, COMMAND_DEFINITION, MATH_OPERATOR); + +impl CommandDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupCommand::cast) + } + + pub fn implementation(&self) -> Option { + self.syntax().children().find_map(CurlyGroup::cast) + } +} + +cst_node!(AcronymReference, ACRONYM_REFERENCE); + +impl AcronymReference { + pub fn command(&self) -> Option { + self.syntax().first_token() + } +} + +cst_node!(AcronymDefinition, ACRONYM_DEFINITION); + +impl AcronymDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(AcronymDeclaration, ACRONYM_DECLARATION); + +impl AcronymDeclaration { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(ColorDefinition, COLOR_DEFINITION); + +impl ColorDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } + + pub fn model(&self) -> Option { + self.syntax() + .children() + .filter_map(CurlyGroupWord::cast) + .nth(1) + } + + pub fn spec(&self) -> Option { + self.syntax().children().find_map(CurlyGroup::cast) + } +} + +cst_node!(ColorSetDefinition, COLOR_SET_DEFINITION); + +impl ColorSetDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn model_list(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWordList::cast) + } +} + +cst_node!(ColorReference, COLOR_REFERENCE); + +impl ColorReference { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(GlossaryEntryReference, GLOSSARY_ENTRY_REFERENCE); + +impl GlossaryEntryReference { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(GlossaryEntryDefinition, GLOSSARY_ENTRY_DEFINITION); + +impl GlossaryEntryDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(TikzLibraryImport, TIKZ_LIBRARY_IMPORT); + +impl TikzLibraryImport { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name_list(&self) -> Option { + self.syntax().children().find_map(CurlyGroupWordList::cast) + } +} + +cst_node!(GraphicsPath, GRAPHICS_PATH); + +impl GraphicsPath { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn path_list(&self) -> impl Iterator { + self.syntax().descendants().filter_map(CurlyGroupWord::cast) + } +} diff --git a/support/texlab/crates/syntax/src/latex/kind.rs b/support/texlab/crates/syntax/src/latex/kind.rs new file mode 100644 index 0000000000..3eadc90b48 --- /dev/null +++ b/support/texlab/crates/syntax/src/latex/kind.rs @@ -0,0 +1,91 @@ +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] +#[allow(non_camel_case_types)] +#[repr(u16)] +pub enum SyntaxKind { + ERROR = 0, + + LINE_BREAK, + WHITESPACE, + COMMENT, + VERBATIM, + L_CURLY, + R_CURLY, + L_BRACK, + R_BRACK, + L_PAREN, + R_PAREN, + COMMA, + EQUALITY_SIGN, + WORD, + DOLLAR, + COMMAND_NAME, + + PREAMBLE, + TEXT, + KEY, + VALUE, + KEY_VALUE_PAIR, + KEY_VALUE_BODY, + CURLY_GROUP, + CURLY_GROUP_WORD, + CURLY_GROUP_WORD_LIST, + CURLY_GROUP_COMMAND, + CURLY_GROUP_KEY_VALUE, + BRACK_GROUP, + BRACK_GROUP_WORD, + BRACK_GROUP_KEY_VALUE, + PAREN_GROUP, + MIXED_GROUP, + GENERIC_COMMAND, + ENVIRONMENT, + BEGIN, + END, + EQUATION, + PART, + CHAPTER, + SECTION, + SUBSECTION, + SUBSUBSECTION, + PARAGRAPH, + SUBPARAGRAPH, + ENUM_ITEM, + FORMULA, + CAPTION, + CITATION, + PACKAGE_INCLUDE, + CLASS_INCLUDE, + LATEX_INCLUDE, + BIBLATEX_INCLUDE, + BIBTEX_INCLUDE, + GRAPHICS_INCLUDE, + SVG_INCLUDE, + INKSCAPE_INCLUDE, + VERBATIM_INCLUDE, + IMPORT, + LABEL_DEFINITION, + LABEL_REFERENCE, + LABEL_REFERENCE_RANGE, + LABEL_NUMBER, + COMMAND_DEFINITION, + MATH_OPERATOR, + GLOSSARY_ENTRY_DEFINITION, + GLOSSARY_ENTRY_REFERENCE, + ACRONYM_DEFINITION, + ACRONYM_DECLARATION, + ACRONYM_REFERENCE, + THEOREM_DEFINITION, + COLOR_REFERENCE, + COLOR_DEFINITION, + COLOR_SET_DEFINITION, + TIKZ_LIBRARY_IMPORT, + ENVIRONMENT_DEFINITION, + GRAPHICS_PATH, + BLOCK_COMMENT, + ROOT, +} + +impl From for rowan::SyntaxKind { + fn from(kind: SyntaxKind) -> Self { + Self(kind as u16) + } +} diff --git a/support/texlab/crates/syntax/src/lib.rs b/support/texlab/crates/syntax/src/lib.rs new file mode 100644 index 0000000000..c2c0552652 --- /dev/null +++ b/support/texlab/crates/syntax/src/lib.rs @@ -0,0 +1,35 @@ +pub mod bibtex; +pub mod latex; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +pub enum BuildErrorLevel { + Error, + Warning, +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub struct BuildError { + pub relative_path: std::path::PathBuf, + pub level: BuildErrorLevel, + pub message: String, + pub hint: Option, + pub line: Option, +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub struct BuildLog { + pub errors: Vec, +} + +#[macro_export] +macro_rules! match_ast { + (match $node:ident { $($tt:tt)* }) => { $crate::match_ast!(match ($node) { $($tt)* }) }; + + (match ($node:expr) { + $( $( $path:ident )::+ ($it:pat) => $res:expr, )* + _ => $catch_all:expr $(,)? + }) => {{ + $( if let Some($it) = $($path::)+cast($node.clone()) { $res } else )* + { $catch_all } + }}; +} -- cgit v1.2.3