diff options
Diffstat (limited to 'support/texlab/src/syntax/bibtex/ast.rs')
-rw-r--r-- | support/texlab/src/syntax/bibtex/ast.rs | 686 |
1 files changed, 270 insertions, 416 deletions
diff --git a/support/texlab/src/syntax/bibtex/ast.rs b/support/texlab/src/syntax/bibtex/ast.rs index 09f2ee5b88..77ee98e26d 100644 --- a/support/texlab/src/syntax/bibtex/ast.rs +++ b/support/texlab/src/syntax/bibtex/ast.rs @@ -1,9 +1,14 @@ -use crate::range::RangeExt; -use crate::syntax::text::{Span, SyntaxNode}; -use lsp_types::Range; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum BibtexTokenKind { +use crate::{ + protocol::{Position, Range, RangeExt}, + syntax::{Span, SyntaxNode}, +}; +use itertools::Itertools; +use petgraph::graph::{Graph, NodeIndex}; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum TokenKind { PreambleKind, StringKind, EntryKind, @@ -19,559 +24,408 @@ pub enum BibtexTokenKind { EndParen, } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexToken { +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Token { pub span: Span, - pub kind: BibtexTokenKind, + pub kind: TokenKind, } -impl BibtexToken { - pub fn new(span: Span, kind: BibtexTokenKind) -> Self { - BibtexToken { span, kind } +impl SyntaxNode for Token { + fn range(&self) -> Range { + self.span.range() } +} - pub fn text(&self) -> &str { - &self.span.text +impl Token { + pub fn new(span: Span, kind: TokenKind) -> Self { + Self { span, kind } } -} -impl SyntaxNode for BibtexToken { - fn range(&self) -> Range { - self.span.range + pub fn text(&self) -> &str { + &self.span.text } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexRoot { - pub children: Vec<BibtexDeclaration>, +#[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub struct Root { + pub range: Range, } -impl BibtexRoot { - pub fn new(children: Vec<BibtexDeclaration>) -> Self { - BibtexRoot { children } +impl SyntaxNode for Root { + fn range(&self) -> Range { + self.range } } -impl SyntaxNode for BibtexRoot { - fn range(&self) -> Range { - if self.children.is_empty() { - Range::new_simple(0, 0, 0, 0) - } else { - Range::new( - self.children[0].start(), - self.children[self.children.len() - 1].end(), - ) - } +impl fmt::Debug for Root { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Root") } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum BibtexDeclaration { - Comment(Box<BibtexComment>), - Preamble(Box<BibtexPreamble>), - String(Box<BibtexString>), - Entry(Box<BibtexEntry>), +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Comment { + pub token: Token, } -impl BibtexDeclaration { - pub fn accept<'a, T: BibtexVisitor<'a>>(&'a self, visitor: &mut T) { - match self { - BibtexDeclaration::Comment(comment) => visitor.visit_comment(comment), - BibtexDeclaration::Preamble(preamble) => visitor.visit_preamble(preamble), - BibtexDeclaration::String(string) => visitor.visit_string(string), - BibtexDeclaration::Entry(entry) => visitor.visit_entry(entry), - } +impl SyntaxNode for Comment { + fn range(&self) -> Range { + self.token.range() } } -impl SyntaxNode for BibtexDeclaration { - fn range(&self) -> Range { - match self { - BibtexDeclaration::Comment(comment) => comment.range, - BibtexDeclaration::Preamble(preamble) => preamble.range, - BibtexDeclaration::String(string) => string.range, - BibtexDeclaration::Entry(entry) => entry.range, - } +impl fmt::Debug for Comment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Comment({})", self.token.text()) } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexComment { +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Preamble { pub range: Range, - pub token: BibtexToken, + pub ty: Token, + pub left: Option<Token>, + pub right: Option<Token>, } -impl BibtexComment { - pub fn new(token: BibtexToken) -> Self { - BibtexComment { - range: token.range(), - token, - } +impl SyntaxNode for Preamble { + fn range(&self) -> Range { + self.range } } -impl SyntaxNode for BibtexComment { - fn range(&self) -> Range { - self.range +impl fmt::Debug for Preamble { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Preamble") } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexPreamble { +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct String { pub range: Range, - pub ty: BibtexToken, - pub left: Option<BibtexToken>, - pub content: Option<BibtexContent>, - pub right: Option<BibtexToken>, -} - -impl BibtexPreamble { - pub fn new( - ty: BibtexToken, - left: Option<BibtexToken>, - content: Option<BibtexContent>, - right: Option<BibtexToken>, - ) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else if let Some(ref content) = content { - content.end() - } else if let Some(ref left) = left { - left.end() - } else { - ty.end() - }; - BibtexPreamble { - range: Range::new(ty.start(), end), - ty, - left, - content, - right, - } - } + pub ty: Token, + pub left: Option<Token>, + pub name: Option<Token>, + pub assign: Option<Token>, + pub right: Option<Token>, } -impl SyntaxNode for BibtexPreamble { +impl SyntaxNode for String { fn range(&self) -> Range { self.range } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexString { - pub range: Range, - pub ty: BibtexToken, - pub left: Option<BibtexToken>, - pub name: Option<BibtexToken>, - pub assign: Option<BibtexToken>, - pub value: Option<BibtexContent>, - pub right: Option<BibtexToken>, -} - -impl BibtexString { - pub fn new( - ty: BibtexToken, - left: Option<BibtexToken>, - name: Option<BibtexToken>, - assign: Option<BibtexToken>, - value: Option<BibtexContent>, - right: Option<BibtexToken>, - ) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else if let Some(ref value) = value { - value.end() - } else if let Some(ref assign) = assign { - assign.end() - } else if let Some(ref name) = name { - name.end() - } else if let Some(ref left) = left { - left.end() - } else { - ty.end() - }; - - BibtexString { - range: Range::new(ty.start(), end), - ty, - left, - name, - assign, - value, - right, - } +impl fmt::Debug for String { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "String({:?})", self.name.as_ref().map(Token::text)) } } -impl SyntaxNode for BibtexString { +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Entry { + pub range: Range, + pub ty: Token, + pub left: Option<Token>, + pub key: Option<Token>, + pub comma: Option<Token>, + pub right: Option<Token>, +} + +impl SyntaxNode for Entry { fn range(&self) -> Range { self.range } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexEntry { - pub range: Range, - pub ty: BibtexToken, - pub left: Option<BibtexToken>, - pub key: Option<BibtexToken>, - pub comma: Option<BibtexToken>, - pub fields: Vec<BibtexField>, - pub right: Option<BibtexToken>, -} - -impl BibtexEntry { - pub fn new( - ty: BibtexToken, - left: Option<BibtexToken>, - key: Option<BibtexToken>, - comma: Option<BibtexToken>, - fields: Vec<BibtexField>, - right: Option<BibtexToken>, - ) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else if !fields.is_empty() { - fields[fields.len() - 1].range.end - } else if let Some(ref comma) = comma { - comma.end() - } else if let Some(ref key) = key { - key.end() - } else if let Some(ref left) = left { - left.end() - } else { - ty.end() - }; - - BibtexEntry { - range: Range::new(ty.start(), end), - ty, - left, - key, - comma, - fields, - right, - } +impl fmt::Debug for Entry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Entry({:?})", self.key.as_ref().map(Token::text)) } +} +impl Entry { pub fn is_comment(&self) -> bool { self.ty.text().to_lowercase() == "@comment" } +} - pub fn find_field(&self, name: &str) -> Option<&BibtexField> { - self.fields - .iter() - .find(|field| field.name.text().to_lowercase() == name) - } +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Field { + pub range: Range, + pub name: Token, + pub assign: Option<Token>, + pub comma: Option<Token>, } -impl SyntaxNode for BibtexEntry { +impl SyntaxNode for Field { fn range(&self) -> Range { self.range } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexField { - pub range: Range, - pub name: BibtexToken, - pub assign: Option<BibtexToken>, - pub content: Option<BibtexContent>, - pub comma: Option<BibtexToken>, -} - -impl BibtexField { - pub fn new( - name: BibtexToken, - assign: Option<BibtexToken>, - content: Option<BibtexContent>, - comma: Option<BibtexToken>, - ) -> Self { - let end = if let Some(ref comma) = comma { - comma.end() - } else if let Some(ref content) = content { - content.end() - } else if let Some(ref assign) = assign { - assign.end() - } else { - name.end() - }; - - BibtexField { - range: Range::new(name.start(), end), - name, - assign, - content, - comma, - } - } -} - -impl SyntaxNode for BibtexField { - fn range(&self) -> Range { - self.range +impl fmt::Debug for Field { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Field({})", self.name.text()) } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum BibtexContent { - Word(BibtexWord), - Command(BibtexCommand), - QuotedContent(BibtexQuotedContent), - BracedContent(BibtexBracedContent), - Concat(Box<BibtexConcat>), +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Word { + pub token: Token, } -impl BibtexContent { - pub fn accept<'a, T: BibtexVisitor<'a>>(&'a self, visitor: &mut T) { - match self { - BibtexContent::Word(word) => visitor.visit_word(word), - BibtexContent::Command(command) => visitor.visit_command(command), - BibtexContent::QuotedContent(content) => visitor.visit_quoted_content(content), - BibtexContent::BracedContent(content) => visitor.visit_braced_content(content), - BibtexContent::Concat(concat) => visitor.visit_concat(concat), - } +impl SyntaxNode for Word { + fn range(&self) -> Range { + self.token.range() } } -impl SyntaxNode for BibtexContent { - fn range(&self) -> Range { - match self { - BibtexContent::Word(word) => word.range(), - BibtexContent::Command(command) => command.range(), - BibtexContent::QuotedContent(content) => content.range(), - BibtexContent::BracedContent(content) => content.range(), - BibtexContent::Concat(concat) => concat.range(), - } +impl fmt::Debug for Word { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Word({})", self.token.text()) } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexWord { - pub range: Range, - pub token: BibtexToken, +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Command { + pub token: Token, } -impl BibtexWord { - pub fn new(token: BibtexToken) -> Self { - BibtexWord { - range: token.range(), - token, - } +impl SyntaxNode for Command { + fn range(&self) -> Range { + self.token.range() } } -impl SyntaxNode for BibtexWord { - fn range(&self) -> Range { - self.range +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Command({})", self.token.text()) } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexCommand { +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct QuotedContent { pub range: Range, - pub token: BibtexToken, + pub left: Token, + pub right: Option<Token>, } -impl BibtexCommand { - pub fn new(token: BibtexToken) -> Self { - BibtexCommand { - range: token.range(), - token, - } +impl SyntaxNode for QuotedContent { + fn range(&self) -> Range { + self.range } } -impl SyntaxNode for BibtexCommand { - fn range(&self) -> Range { - self.range +impl fmt::Debug for QuotedContent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "QuotedContent") } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexQuotedContent { +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct BracedContent { pub range: Range, - pub left: BibtexToken, - pub children: Vec<BibtexContent>, - pub right: Option<BibtexToken>, -} - -impl BibtexQuotedContent { - pub fn new( - left: BibtexToken, - children: Vec<BibtexContent>, - right: Option<BibtexToken>, - ) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else if !children.is_empty() { - children[children.len() - 1].end() - } else { - left.end() - }; - - BibtexQuotedContent { - range: Range::new(left.start(), end), - left, - children, - right, - } - } + pub left: Token, + pub right: Option<Token>, } -impl SyntaxNode for BibtexQuotedContent { +impl SyntaxNode for BracedContent { fn range(&self) -> Range { self.range } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexBracedContent { - pub range: Range, - pub left: BibtexToken, - pub children: Vec<BibtexContent>, - pub right: Option<BibtexToken>, -} - -impl BibtexBracedContent { - pub fn new( - left: BibtexToken, - children: Vec<BibtexContent>, - right: Option<BibtexToken>, - ) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else if !children.is_empty() { - children[children.len() - 1].end() - } else { - left.end() - }; - - BibtexBracedContent { - range: Range::new(left.start(), end), - left, - children, - right, - } +impl fmt::Debug for BracedContent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "BracedContent") } } -impl SyntaxNode for BibtexBracedContent { +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Concat { + pub range: Range, + pub operator: Token, +} + +impl SyntaxNode for Concat { fn range(&self) -> Range { self.range } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexConcat { - pub range: Range, - pub left: BibtexContent, - pub operator: BibtexToken, - pub right: Option<BibtexContent>, +impl fmt::Debug for Concat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Concat") + } } -impl BibtexConcat { - pub fn new(left: BibtexContent, operator: BibtexToken, right: Option<BibtexContent>) -> Self { - let end = if let Some(ref right) = right { - right.end() - } else { - operator.end() - }; - - BibtexConcat { - range: Range::new(left.start(), end), - left, - operator, - right, - } - } +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub enum Node { + Root(Root), + Comment(Comment), + Preamble(Box<Preamble>), + String(Box<String>), + Entry(Box<Entry>), + Field(Box<Field>), + Word(Word), + Command(Command), + QuotedContent(QuotedContent), + BracedContent(BracedContent), + Concat(Concat), } -impl SyntaxNode for BibtexConcat { +impl SyntaxNode for Node { fn range(&self) -> Range { - self.range + match self { + Self::Root(root) => root.range(), + Self::Comment(comment) => comment.range(), + Self::Preamble(preamble) => preamble.range(), + Self::String(string) => string.range(), + Self::Entry(entry) => entry.range(), + Self::Field(field) => field.range(), + Self::Word(word) => word.range(), + Self::Command(cmd) => cmd.range(), + Self::QuotedContent(content) => content.range(), + Self::BracedContent(content) => content.range(), + Self::Concat(concat) => concat.range(), + } } } -pub trait BibtexVisitor<'a> { - fn visit_root(&mut self, root: &'a BibtexRoot); - - fn visit_comment(&mut self, comment: &'a BibtexComment); - - fn visit_preamble(&mut self, preamble: &'a BibtexPreamble); - - fn visit_string(&mut self, string: &'a BibtexString); - - fn visit_entry(&mut self, entry: &'a BibtexEntry); - - fn visit_field(&mut self, field: &'a BibtexField); - - fn visit_word(&mut self, word: &'a BibtexWord); +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Tree { + pub graph: Graph<Node, ()>, + pub root: NodeIndex, +} - fn visit_command(&mut self, command: &'a BibtexCommand); +impl Tree { + pub fn children(&self, parent: NodeIndex) -> impl Iterator<Item = NodeIndex> { + self.graph + .neighbors(parent) + .sorted_by_key(|child| self.graph[*child].start()) + } - fn visit_quoted_content(&mut self, content: &'a BibtexQuotedContent); + pub fn has_children(&self, parent: NodeIndex) -> bool { + self.children(parent).next().is_some() + } - fn visit_braced_content(&mut self, content: &'a BibtexBracedContent); + pub fn walk<'a, V: Visitor<'a>>(&'a self, visitor: &mut V, parent: NodeIndex) { + for child in self.children(parent) { + visitor.visit(self, child); + } + } - fn visit_concat(&mut self, concat: &'a BibtexConcat); -} + pub fn find(&self, pos: Position) -> Vec<NodeIndex> { + let mut finder = Finder::new(pos); + finder.visit(self, self.root); + finder.results + } -pub struct BibtexWalker; + pub fn as_preamble(&self, node: NodeIndex) -> Option<&Preamble> { + if let Node::Preamble(preamble) = &self.graph[node] { + Some(preamble) + } else { + None + } + } -impl BibtexWalker { - pub fn walk_root<'a, T: BibtexVisitor<'a>>(visitor: &mut T, root: &'a BibtexRoot) { - for declaration in &root.children { - declaration.accept(visitor); + pub fn as_string(&self, node: NodeIndex) -> Option<&String> { + if let Node::String(string) = &self.graph[node] { + Some(string) + } else { + None } } - pub fn walk_preamble<'a, T: BibtexVisitor<'a>>(visitor: &mut T, preamble: &'a BibtexPreamble) { - if let Some(ref content) = preamble.content { - content.accept(visitor); + pub fn as_entry(&self, node: NodeIndex) -> Option<&Entry> { + if let Node::Entry(entry) = &self.graph[node] { + Some(entry) + } else { + None } } - pub fn walk_string<'a, T: BibtexVisitor<'a>>(visitor: &mut T, string: &'a BibtexString) { - if let Some(ref value) = string.value { - value.accept(visitor); + pub fn as_field(&self, node: NodeIndex) -> Option<&Field> { + if let Node::Field(field) = &self.graph[node] { + Some(field) + } else { + None } } - pub fn walk_entry<'a, T: BibtexVisitor<'a>>(visitor: &mut T, entry: &'a BibtexEntry) { - for field in &entry.fields { - visitor.visit_field(field); + pub fn as_command(&self, node: NodeIndex) -> Option<&Command> { + if let Node::Command(cmd) = &self.graph[node] { + Some(cmd) + } else { + None } } - pub fn walk_field<'a, T: BibtexVisitor<'a>>(visitor: &mut T, field: &'a BibtexField) { - if let Some(ref content) = field.content { - content.accept(visitor); + pub fn as_word(&self, node: NodeIndex) -> Option<&Word> { + if let Node::Word(word) = &self.graph[node] { + Some(word) + } else { + None } } - pub fn walk_quoted_content<'a, T: BibtexVisitor<'a>>( - visitor: &mut T, - content: &'a BibtexQuotedContent, - ) { - for child in &content.children { - child.accept(visitor); + pub fn entry_by_key(&self, key: &str) -> Option<NodeIndex> { + for node in self.children(self.root) { + if let Some(entry) = self.as_entry(node) { + if entry.key.as_ref().map(Token::text) == Some(key) { + return Some(node); + } + } + } + None + } + + pub fn field_by_name(&self, parent: NodeIndex, name: &str) -> Option<NodeIndex> { + let name = name.to_lowercase(); + self.as_entry(parent)?; + for node in self.children(parent) { + if let Some(field) = self.as_field(node) { + if field.name.text() == name { + return Some(node); + } + } } + None + } + + pub fn crossref(&self, entry: NodeIndex) -> Option<NodeIndex> { + let field = self.field_by_name(entry, "crossref")?; + let content = self.children(field).next()?; + let key = self.as_word(self.children(content).next()?)?; + self.entry_by_key(key.token.text()) } +} + +pub trait Visitor<'a> { + fn visit(&mut self, tree: &'a Tree, node: NodeIndex); +} - pub fn walk_braced_content<'a, T: BibtexVisitor<'a>>( - visitor: &mut T, - content: &'a BibtexBracedContent, - ) { - for child in &content.children { - child.accept(visitor); +#[derive(Debug)] +struct Finder { + position: Position, + results: Vec<NodeIndex>, +} + +impl Finder { + fn new(position: Position) -> Self { + Self { + position, + results: Vec::new(), } } +} - pub fn walk_concat<'a, T: BibtexVisitor<'a>>(visitor: &mut T, concat: &'a BibtexConcat) { - concat.left.accept(visitor); - if let Some(ref right) = concat.right { - right.accept(visitor); +impl<'a> Visitor<'a> for Finder { + fn visit(&mut self, tree: &'a Tree, node: NodeIndex) { + if tree.graph[node].range().contains(self.position) { + self.results.push(node); + tree.walk(self, node); } } } |