diff options
Diffstat (limited to 'support/texlab/src/syntax/bibtex')
-rw-r--r-- | support/texlab/src/syntax/bibtex/ast.rs | 686 | ||||
-rw-r--r-- | support/texlab/src/syntax/bibtex/finder.rs | 110 | ||||
-rw-r--r-- | support/texlab/src/syntax/bibtex/formatter.rs | 329 | ||||
-rw-r--r-- | support/texlab/src/syntax/bibtex/lexer.rs | 135 | ||||
-rw-r--r-- | support/texlab/src/syntax/bibtex/mod.rs | 431 | ||||
-rw-r--r-- | support/texlab/src/syntax/bibtex/parser.rs | 372 |
6 files changed, 1318 insertions, 745 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); } } } diff --git a/support/texlab/src/syntax/bibtex/finder.rs b/support/texlab/src/syntax/bibtex/finder.rs deleted file mode 100644 index acc46c6f45..0000000000 --- a/support/texlab/src/syntax/bibtex/finder.rs +++ /dev/null @@ -1,110 +0,0 @@ -use super::ast::*; -use crate::range::RangeExt; -use crate::syntax::text::SyntaxNode; -use lsp_types::Position; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum BibtexNode<'a> { - Root(&'a BibtexRoot), - Preamble(&'a BibtexPreamble), - String(&'a BibtexString), - Entry(&'a BibtexEntry), - Comment(&'a BibtexComment), - Field(&'a BibtexField), - Word(&'a BibtexWord), - Command(&'a BibtexCommand), - QuotedContent(&'a BibtexQuotedContent), - BracedContent(&'a BibtexBracedContent), - Concat(&'a BibtexConcat), -} - -#[derive(Debug)] -pub struct BibtexFinder<'a> { - pub position: Position, - pub results: Vec<BibtexNode<'a>>, -} - -impl<'a> BibtexFinder<'a> { - pub fn new(position: Position) -> Self { - BibtexFinder { - position, - results: Vec::new(), - } - } -} - -impl<'a> BibtexVisitor<'a> for BibtexFinder<'a> { - fn visit_root(&mut self, root: &'a BibtexRoot) { - if root.range().contains(self.position) { - self.results.push(BibtexNode::Root(root)); - BibtexWalker::walk_root(self, root); - } - } - - fn visit_comment(&mut self, comment: &'a BibtexComment) { - if comment.range.contains(self.position) { - self.results.push(BibtexNode::Comment(comment)); - } - } - - fn visit_preamble(&mut self, preamble: &'a BibtexPreamble) { - if preamble.range.contains(self.position) { - self.results.push(BibtexNode::Preamble(preamble)); - BibtexWalker::walk_preamble(self, preamble); - } - } - - fn visit_string(&mut self, string: &'a BibtexString) { - if string.range.contains(self.position) { - self.results.push(BibtexNode::String(string)); - BibtexWalker::walk_string(self, string); - } - } - - fn visit_entry(&mut self, entry: &'a BibtexEntry) { - if entry.range.contains(self.position) { - self.results.push(BibtexNode::Entry(entry)); - BibtexWalker::walk_entry(self, entry); - } - } - - fn visit_field(&mut self, field: &'a BibtexField) { - if field.range.contains(self.position) { - self.results.push(BibtexNode::Field(field)); - BibtexWalker::walk_field(self, field); - } - } - - fn visit_word(&mut self, word: &'a BibtexWord) { - if word.range.contains(self.position) { - self.results.push(BibtexNode::Word(word)); - } - } - - fn visit_command(&mut self, command: &'a BibtexCommand) { - if command.range.contains(self.position) { - self.results.push(BibtexNode::Command(command)); - } - } - - fn visit_quoted_content(&mut self, content: &'a BibtexQuotedContent) { - if content.range.contains(self.position) { - self.results.push(BibtexNode::QuotedContent(content)); - BibtexWalker::walk_quoted_content(self, content); - } - } - - fn visit_braced_content(&mut self, content: &'a BibtexBracedContent) { - if content.range.contains(self.position) { - self.results.push(BibtexNode::BracedContent(content)); - BibtexWalker::walk_braced_content(self, content); - } - } - - fn visit_concat(&mut self, concat: &'a BibtexConcat) { - if concat.range.contains(self.position) { - self.results.push(BibtexNode::Concat(concat)); - BibtexWalker::walk_concat(self, concat); - } - } -} diff --git a/support/texlab/src/syntax/bibtex/formatter.rs b/support/texlab/src/syntax/bibtex/formatter.rs new file mode 100644 index 0000000000..d4965e7cd4 --- /dev/null +++ b/support/texlab/src/syntax/bibtex/formatter.rs @@ -0,0 +1,329 @@ +use super::ast::*; +use crate::{protocol::BibtexFormattingOptions, syntax::text::SyntaxNode}; +use petgraph::graph::NodeIndex; +use std::{i32, string::String as StdString}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct FormattingParams<'a> { + pub tab_size: usize, + pub insert_spaces: bool, + pub options: &'a BibtexFormattingOptions, +} + +impl<'a> FormattingParams<'a> { + fn line_length(self) -> i32 { + let line_length = self.options.line_length.unwrap_or(120); + if line_length <= 0 { + i32::MAX + } else { + line_length + } + } + + fn indent(self) -> StdString { + if self.insert_spaces { + let mut buffer = StdString::new(); + for _ in 0..self.tab_size { + buffer.push(' '); + } + buffer + } else { + "\t".into() + } + } +} + +#[derive(Debug, Clone)] +struct Formatter<'a> { + params: FormattingParams<'a>, + indent: StdString, + output: StdString, + align: Vec<usize>, +} + +impl<'a> Formatter<'a> { + fn new(params: FormattingParams<'a>) -> Self { + Self { + params, + indent: params.indent(), + output: StdString::new(), + align: Vec::new(), + } + } + + fn visit_token_lowercase(&mut self, token: &Token) { + self.output.push_str(token.text().to_lowercase().as_ref()); + } + + fn should_insert_space(previous: &Token, current: &Token) -> bool { + previous.start().line != current.start().line + || previous.end().character < current.start().character + } +} + +impl<'a, 'b> Visitor<'b> for Formatter<'a> { + fn visit(&mut self, tree: &'b Tree, node: NodeIndex) { + match &tree.graph[node] { + Node::Root(_) => tree.walk(self, node), + Node::Comment(comment) => self.output.push_str(comment.token.text()), + Node::Preamble(preamble) => { + self.visit_token_lowercase(&preamble.ty); + self.output.push('{'); + if tree.has_children(node) { + self.align.push(self.output.chars().count()); + tree.walk(self, node); + self.output.push('}'); + } + } + Node::String(string) => { + self.visit_token_lowercase(&string.ty); + self.output.push('{'); + if let Some(name) = &string.name { + self.output.push_str(name.text()); + self.output.push_str(" = "); + if tree.has_children(node) { + self.align.push(self.output.chars().count()); + tree.walk(self, node); + self.output.push('}'); + } + } + } + Node::Entry(entry) => { + self.visit_token_lowercase(&entry.ty); + self.output.push('{'); + if let Some(key) = &entry.key { + self.output.push_str(key.text()); + self.output.push(','); + self.output.push('\n'); + tree.walk(self, node); + self.output.push('}'); + } + } + Node::Field(field) => { + self.output.push_str(&self.indent); + self.visit_token_lowercase(&field.name); + self.output.push_str(" = "); + if tree.has_children(node) { + let count = field.name.text().chars().count(); + self.align.push(self.params.tab_size as usize + count + 3); + tree.walk(self, node); + self.output.push(','); + self.output.push('\n'); + } + } + Node::Word(_) + | Node::Command(_) + | Node::BracedContent(_) + | Node::QuotedContent(_) + | Node::Concat(_) => { + let mut analyzer = ContentAnalyzer::default(); + analyzer.visit(tree, node); + let tokens = analyzer.tokens; + self.output.push_str(tokens[0].text()); + + let align = self.align.pop().unwrap_or_default(); + let mut length = align + tokens[0].text().chars().count(); + for i in 1..tokens.len() { + let previous = tokens[i - 1]; + let current = tokens[i]; + let current_length = current.text().chars().count(); + + let insert_space = Self::should_insert_space(previous, current); + let space_length = if insert_space { 1 } else { 0 }; + + if length + current_length + space_length > self.params.line_length() as usize { + self.output.push('\n'); + self.output.push_str(self.indent.as_ref()); + for _ in 0..=align - self.params.tab_size { + self.output.push(' '); + } + length = align; + } else if insert_space { + self.output.push(' '); + length += 1; + } + self.output.push_str(current.text()); + length += current_length; + } + } + } + } +} + +#[derive(Debug, Default)] +struct ContentAnalyzer<'a> { + tokens: Vec<&'a Token>, +} + +impl<'a> Visitor<'a> for ContentAnalyzer<'a> { + fn visit(&mut self, tree: &'a Tree, node: NodeIndex) { + match &tree.graph[node] { + Node::Root(_) + | Node::Comment(_) + | Node::Preamble(_) + | Node::String(_) + | Node::Entry(_) + | Node::Field(_) => tree.walk(self, node), + Node::Word(word) => self.tokens.push(&word.token), + Node::Command(cmd) => self.tokens.push(&cmd.token), + Node::QuotedContent(content) => { + self.tokens.push(&content.left); + tree.walk(self, node); + if let Some(right) = &content.right { + self.tokens.push(right); + } + } + Node::BracedContent(content) => { + self.tokens.push(&content.left); + tree.walk(self, node); + if let Some(right) = &content.right { + self.tokens.push(right); + } + } + Node::Concat(concat) => { + let mut children = tree.children(node); + let left = children.next().unwrap(); + self.visit(tree, left); + self.tokens.push(&concat.operator); + children.for_each(|right| self.visit(tree, right)); + } + } + } +} + +pub fn format(tree: &Tree, node: NodeIndex, params: FormattingParams) -> StdString { + let mut formatter = Formatter::new(params); + formatter.visit(tree, node); + formatter.output +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::syntax::bibtex; + use indoc::indoc; + + fn verify(source: &str, expected: &str, line_length: i32) { + let tree = bibtex::open(source); + let options = BibtexFormattingOptions { + line_length: Some(line_length), + formatter: None, + }; + + let mut children = tree.children(tree.root); + let declaration = children.next().unwrap(); + assert_eq!(children.next(), None); + + let actual = format( + &tree, + declaration, + FormattingParams { + tab_size: 4, + insert_spaces: true, + options: &options, + }, + ); + assert_eq!(actual, expected); + } + + #[test] + fn wrap_long_lines() { + let source = + "@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}"; + let expected = indoc!( + " + @article{foo, + bar = {Lorem ipsum dolor + sit amet, + consectetur + adipiscing elit.}, + }" + ); + verify(source, expected, 30); + } + + #[test] + fn line_length_zero() { + let source = + "@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}"; + let expected = indoc!( + " + @article{foo, + bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit.}, + }" + ); + verify(source, expected, 0); + } + + #[test] + fn trailing_commas() { + let source = "@article{foo, bar = baz}"; + let expected = indoc!( + " + @article{foo, + bar = baz, + }" + ); + verify(source, expected, 30); + } + + #[test] + fn insert_braces() { + let source = "@article{foo, bar = baz,"; + let expected = indoc!( + " + @article{foo, + bar = baz, + }" + ); + verify(source, expected, 30); + } + + #[test] + fn commands() { + let source = "@article{foo, bar = \"\\baz\",}"; + let expected = indoc!( + "@article{foo, + bar = \"\\baz\", + }" + ); + verify(source, expected, 30); + } + + #[test] + fn concatenation() { + let source = "@article{foo, bar = \"baz\" # \"qux\"}"; + let expected = indoc!( + " + @article{foo, + bar = \"baz\" # \"qux\", + }" + ); + verify(source, expected, 30); + } + + #[test] + fn parentheses() { + let source = "@article(foo,)"; + let expected = indoc!( + " + @article{foo, + }" + ); + verify(source, expected, 30); + } + + #[test] + fn string() { + let source = "@string{foo=\"bar\"}"; + let expected = "@string{foo = \"bar\"}"; + verify(source, expected, 30); + } + + #[test] + fn preamble() { + let source = "@preamble{\n\"foo bar baz\"}"; + let expected = "@preamble{\"foo bar baz\"}"; + verify(source, expected, 30); + } +} diff --git a/support/texlab/src/syntax/bibtex/lexer.rs b/support/texlab/src/syntax/bibtex/lexer.rs index 8278ef2631..e089b15864 100644 --- a/support/texlab/src/syntax/bibtex/lexer.rs +++ b/support/texlab/src/syntax/bibtex/lexer.rs @@ -1,18 +1,19 @@ -use super::ast::{BibtexToken, BibtexTokenKind}; +use super::ast::{Token, TokenKind}; use crate::syntax::text::CharStream; -pub struct BibtexLexer<'a> { +#[derive(Debug)] +pub struct Lexer<'a> { stream: CharStream<'a>, } -impl<'a> BibtexLexer<'a> { +impl<'a> Lexer<'a> { pub fn new(text: &'a str) -> Self { - BibtexLexer { + Self { stream: CharStream::new(text), } } - fn kind(&mut self) -> BibtexToken { + fn kind(&mut self) -> Token { fn is_type_char(c: char) -> bool { c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' } @@ -24,26 +25,26 @@ impl<'a> BibtexLexer<'a> { } let span = self.stream.end_span(); let kind = match span.text.to_lowercase().as_ref() { - "@preamble" => BibtexTokenKind::PreambleKind, - "@string" => BibtexTokenKind::StringKind, - _ => BibtexTokenKind::EntryKind, + "@preamble" => TokenKind::PreambleKind, + "@string" => TokenKind::StringKind, + _ => TokenKind::EntryKind, }; - BibtexToken::new(span, kind) + Token::new(span, kind) } - fn single_character(&mut self, kind: BibtexTokenKind) -> BibtexToken { + fn single_character(&mut self, kind: TokenKind) -> Token { self.stream.start_span(); self.stream.next(); let span = self.stream.end_span(); - BibtexToken::new(span, kind) + Token::new(span, kind) } - fn command(&mut self) -> BibtexToken { + fn command(&mut self) -> Token { let span = self.stream.command(); - BibtexToken::new(span, BibtexTokenKind::Command) + Token::new(span, TokenKind::Command) } - fn word(&mut self) -> BibtexToken { + fn word(&mut self) -> Token { fn is_word_char(c: char) -> bool { !c.is_whitespace() && c != '@' @@ -62,25 +63,25 @@ impl<'a> BibtexLexer<'a> { self.stream.next(); } let span = self.stream.end_span(); - BibtexToken::new(span, BibtexTokenKind::Word) + Token::new(span, TokenKind::Word) } } -impl<'a> Iterator for BibtexLexer<'a> { - type Item = BibtexToken; +impl<'a> Iterator for Lexer<'a> { + type Item = Token; - fn next(&mut self) -> Option<BibtexToken> { + fn next(&mut self) -> Option<Token> { loop { match self.stream.peek() { Some('@') => return Some(self.kind()), - Some('=') => return Some(self.single_character(BibtexTokenKind::Assign)), - Some(',') => return Some(self.single_character(BibtexTokenKind::Comma)), - Some('#') => return Some(self.single_character(BibtexTokenKind::Concat)), - Some('"') => return Some(self.single_character(BibtexTokenKind::Quote)), - Some('{') => return Some(self.single_character(BibtexTokenKind::BeginBrace)), - Some('}') => return Some(self.single_character(BibtexTokenKind::EndBrace)), - Some('(') => return Some(self.single_character(BibtexTokenKind::BeginParen)), - Some(')') => return Some(self.single_character(BibtexTokenKind::EndParen)), + Some('=') => return Some(self.single_character(TokenKind::Assign)), + Some(',') => return Some(self.single_character(TokenKind::Comma)), + Some('#') => return Some(self.single_character(TokenKind::Concat)), + Some('"') => return Some(self.single_character(TokenKind::Quote)), + Some('{') => return Some(self.single_character(TokenKind::BeginBrace)), + Some('}') => return Some(self.single_character(TokenKind::EndBrace)), + Some('(') => return Some(self.single_character(TokenKind::BeginParen)), + Some(')') => return Some(self.single_character(TokenKind::EndParen)), Some('\\') => return Some(self.command()), Some(c) => { if c.is_whitespace() { @@ -100,77 +101,73 @@ impl<'a> Iterator for BibtexLexer<'a> { #[cfg(test)] mod tests { use super::*; - use crate::syntax::text::Span; - use lsp_types::{Position, Range}; - - fn verify<'a>( - lexer: &mut BibtexLexer<'a>, - line: u64, - character: u64, - text: &str, - kind: BibtexTokenKind, - ) { + use crate::{ + protocol::{Position, Range}, + syntax::text::Span, + }; + + fn verify<'a>(lexer: &mut Lexer<'a>, line: u64, character: u64, text: &str, kind: TokenKind) { let start = Position::new(line, character); let end = Position::new(line, character + text.chars().count() as u64); let range = Range::new(start, end); let span = Span::new(range, text.to_owned()); - let token = BibtexToken::new(span, kind); + let token = Token::new(span, kind); assert_eq!(Some(token), lexer.next()); } #[test] - fn test_word() { - let mut lexer = BibtexLexer::new("foo bar baz"); - verify(&mut lexer, 0, 0, "foo", BibtexTokenKind::Word); - verify(&mut lexer, 0, 4, "bar", BibtexTokenKind::Word); - verify(&mut lexer, 0, 8, "baz", BibtexTokenKind::Word); + fn word() { + let mut lexer = Lexer::new("foo bar baz"); + verify(&mut lexer, 0, 0, "foo", TokenKind::Word); + verify(&mut lexer, 0, 4, "bar", TokenKind::Word); + verify(&mut lexer, 0, 8, "baz", TokenKind::Word); assert_eq!(None, lexer.next()); } #[test] - fn test_command() { - let mut lexer = BibtexLexer::new("\\foo\\bar@baz"); - verify(&mut lexer, 0, 0, "\\foo", BibtexTokenKind::Command); - verify(&mut lexer, 0, 4, "\\bar@baz", BibtexTokenKind::Command); + fn command() { + let mut lexer = Lexer::new("\\foo\\bar@baz"); + verify(&mut lexer, 0, 0, "\\foo", TokenKind::Command); + verify(&mut lexer, 0, 4, "\\bar@baz", TokenKind::Command); assert_eq!(None, lexer.next()); } #[test] - fn test_escape_sequence() { - let mut lexer = BibtexLexer::new("\\foo*\n\\%\\**"); - verify(&mut lexer, 0, 0, "\\foo*", BibtexTokenKind::Command); - verify(&mut lexer, 1, 0, "\\%", BibtexTokenKind::Command); - verify(&mut lexer, 1, 2, "\\*", BibtexTokenKind::Command); - verify(&mut lexer, 1, 4, "*", BibtexTokenKind::Word); + fn escape_sequence() { + let mut lexer = Lexer::new("\\foo*\n\\%\\**"); + verify(&mut lexer, 0, 0, "\\foo*", TokenKind::Command); + verify(&mut lexer, 1, 0, "\\%", TokenKind::Command); + verify(&mut lexer, 1, 2, "\\*", TokenKind::Command); + verify(&mut lexer, 1, 4, "*", TokenKind::Word); assert_eq!(None, lexer.next()); } #[test] - fn test_delimiter() { - let mut lexer = BibtexLexer::new("{}()\""); - verify(&mut lexer, 0, 0, "{", BibtexTokenKind::BeginBrace); - verify(&mut lexer, 0, 1, "}", BibtexTokenKind::EndBrace); - verify(&mut lexer, 0, 2, "(", BibtexTokenKind::BeginParen); - verify(&mut lexer, 0, 3, ")", BibtexTokenKind::EndParen); - verify(&mut lexer, 0, 4, "\"", BibtexTokenKind::Quote); + fn delimiter() { + let mut lexer = Lexer::new("{}()\""); + verify(&mut lexer, 0, 0, "{", TokenKind::BeginBrace); + verify(&mut lexer, 0, 1, "}", TokenKind::EndBrace); + verify(&mut lexer, 0, 2, "(", TokenKind::BeginParen); + verify(&mut lexer, 0, 3, ")", TokenKind::EndParen); + verify(&mut lexer, 0, 4, "\"", TokenKind::Quote); assert_eq!(None, lexer.next()); } #[test] - fn test_kind() { - let mut lexer = BibtexLexer::new("@pReAmBlE\n@article\n@string"); - verify(&mut lexer, 0, 0, "@pReAmBlE", BibtexTokenKind::PreambleKind); - verify(&mut lexer, 1, 0, "@article", BibtexTokenKind::EntryKind); - verify(&mut lexer, 2, 0, "@string", BibtexTokenKind::StringKind); + fn kind() { + let mut lexer = Lexer::new("@pReAmBlE\n@article\n@string"); + verify(&mut lexer, 0, 0, "@pReAmBlE", TokenKind::PreambleKind); + verify(&mut lexer, 1, 0, "@article", TokenKind::EntryKind); + verify(&mut lexer, 2, 0, "@string", TokenKind::StringKind); assert_eq!(None, lexer.next()); } #[test] - fn test_operator() { - let mut lexer = BibtexLexer::new("=,#"); - verify(&mut lexer, 0, 0, "=", BibtexTokenKind::Assign); - verify(&mut lexer, 0, 1, ",", BibtexTokenKind::Comma); - verify(&mut lexer, 0, 2, "#", BibtexTokenKind::Concat); + fn operator() { + let mut lexer = Lexer::new("=,#"); + verify(&mut lexer, 0, 0, "=", TokenKind::Assign); + verify(&mut lexer, 0, 1, ",", TokenKind::Comma); + verify(&mut lexer, 0, 2, "#", TokenKind::Concat); assert_eq!(None, lexer.next()); } } diff --git a/support/texlab/src/syntax/bibtex/mod.rs b/support/texlab/src/syntax/bibtex/mod.rs index 9910390fe7..aa7242c1f7 100644 --- a/support/texlab/src/syntax/bibtex/mod.rs +++ b/support/texlab/src/syntax/bibtex/mod.rs @@ -1,75 +1,396 @@ mod ast; -mod finder; +mod formatter; mod lexer; mod parser; -use self::lexer::BibtexLexer; -use self::parser::BibtexParser; +pub use self::{ast::*, formatter::*}; -pub use self::ast::*; -pub use self::finder::*; -use lsp_types::Position; +use self::{lexer::Lexer, parser::Parser}; -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct BibtexSyntaxTree { - pub root: BibtexRoot, +pub fn open(text: &str) -> Tree { + let lexer = Lexer::new(text); + let parser = Parser::new(lexer); + parser.parse() } -impl BibtexSyntaxTree { - pub fn entries(&self) -> Vec<&BibtexEntry> { - let mut entries: Vec<&BibtexEntry> = Vec::new(); - for declaration in &self.root.children { - if let BibtexDeclaration::Entry(entry) = declaration { - entries.push(&entry); - } - } - entries +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + protocol::{Range, RangeExt}, + syntax::text::SyntaxNode, + }; + use petgraph::graph::NodeIndex; + + #[derive(Debug, Default)] + struct TreeTraversal { + nodes: Vec<NodeIndex>, } - pub fn strings(&self) -> Vec<&BibtexString> { - let mut strings: Vec<&BibtexString> = Vec::new(); - for declaration in &self.root.children { - if let BibtexDeclaration::String(string) = declaration { - strings.push(&string); - } + impl<'a> Visitor<'a> for TreeTraversal { + fn visit(&mut self, tree: &Tree, node: NodeIndex) { + self.nodes.push(node); + tree.walk(self, node); } - strings } - pub fn find(&self, position: Position) -> Vec<BibtexNode> { - let mut finder = BibtexFinder::new(position); - finder.visit_root(&self.root); - finder.results - } + mod range { + use super::*; + use indoc::indoc; - pub fn find_entry(&self, key: &str) -> Option<&BibtexEntry> { - self.entries() - .into_iter() - .find(|entry| entry.key.as_ref().map(BibtexToken::text) == Some(key)) - } + fn verify(expected_ranges: Vec<Range>, text: &str) { + let tree = open(text.trim()); - pub fn resolve_crossref(&self, entry: &BibtexEntry) -> Option<&BibtexEntry> { - if let Some(field) = entry.find_field("crossref") { - if let Some(BibtexContent::BracedContent(content)) = &field.content { - if let Some(BibtexContent::Word(name)) = content.children.get(0) { - return self.find_entry(name.token.text()); - } - } + let mut traversal = TreeTraversal::default(); + traversal.visit(&tree, tree.root); + let actual_ranges: Vec<_> = traversal + .nodes + .into_iter() + .map(|node| tree.graph[node].range()) + .collect(); + + assert_eq!(actual_ranges, expected_ranges); } - None - } -} -impl From<BibtexRoot> for BibtexSyntaxTree { - fn from(root: BibtexRoot) -> Self { - BibtexSyntaxTree { root } - } -} + #[test] + fn empty_document() { + verify(vec![Range::new_simple(0, 0, 0, 0)], ""); + } + + #[test] + fn comment() { + verify( + vec![Range::new_simple(0, 0, 0, 3), Range::new_simple(0, 0, 0, 3)], + "foo", + ); + } + + #[test] + fn preamble_no_left() { + verify( + vec![Range::new_simple(0, 0, 0, 9), Range::new_simple(0, 0, 0, 9)], + "@preamble", + ); + } + + #[test] + fn preamble_no_content() { + verify( + vec![ + Range::new_simple(0, 0, 0, 10), + Range::new_simple(0, 0, 0, 10), + ], + "@preamble{", + ); + } + + #[test] + fn preamble_no_right() { + verify( + vec![ + Range::new_simple(0, 0, 0, 15), + Range::new_simple(0, 0, 0, 15), + Range::new_simple(0, 10, 0, 15), + Range::new_simple(0, 11, 0, 14), + ], + r#"@preamble{"foo""#, + ); + } + + #[test] + fn preamble_complete() { + verify( + vec![ + Range::new_simple(0, 0, 0, 16), + Range::new_simple(0, 0, 0, 16), + Range::new_simple(0, 10, 0, 15), + Range::new_simple(0, 11, 0, 14), + ], + r#"@preamble{"foo"}"#, + ); + } + + #[test] + fn string_no_left() { + verify( + vec![Range::new_simple(0, 0, 0, 7), Range::new_simple(0, 0, 0, 7)], + r#"@string"#, + ); + } + + #[test] + fn string_no_name() { + verify( + vec![Range::new_simple(0, 0, 0, 8), Range::new_simple(0, 0, 0, 8)], + r#"@string{"#, + ); + } + + #[test] + fn string_no_assign() { + verify( + vec![ + Range::new_simple(0, 0, 0, 11), + Range::new_simple(0, 0, 0, 11), + ], + r#"@string{foo"#, + ); + } + + #[test] + fn string_no_value() { + verify( + vec![ + Range::new_simple(0, 0, 0, 13), + Range::new_simple(0, 0, 0, 13), + ], + r#"@string{foo ="#, + ); + } + + #[test] + fn string_no_right() { + verify( + vec![ + Range::new_simple(0, 0, 0, 19), + Range::new_simple(0, 0, 0, 19), + Range::new_simple(0, 14, 0, 19), + Range::new_simple(0, 15, 0, 18), + ], + r#"@string{foo = "bar""#, + ); + } + + #[test] + fn string_complete() { + verify( + vec![ + Range::new_simple(0, 0, 0, 20), + Range::new_simple(0, 0, 0, 20), + Range::new_simple(0, 14, 0, 19), + Range::new_simple(0, 15, 0, 18), + ], + r#"@string{foo = "bar"}"#, + ); + } -impl From<&str> for BibtexSyntaxTree { - fn from(text: &str) -> Self { - let lexer = BibtexLexer::new(text); - let mut parser = BibtexParser::new(lexer); - parser.root().into() + #[test] + fn entry_no_left() { + verify( + vec![Range::new_simple(0, 0, 0, 8), Range::new_simple(0, 0, 0, 8)], + r#"@article"#, + ); + } + + #[test] + fn entry_no_key() { + verify( + vec![Range::new_simple(0, 0, 0, 9), Range::new_simple(0, 0, 0, 9)], + r#"@article{"#, + ); + } + + #[test] + fn entry_no_comma() { + verify( + vec![ + Range::new_simple(0, 0, 0, 12), + Range::new_simple(0, 0, 0, 12), + ], + r#"@article{foo"#, + ); + } + + #[test] + fn entry_no_right() { + verify( + vec![ + Range::new_simple(0, 0, 0, 13), + Range::new_simple(0, 0, 0, 13), + ], + r#"@article{foo,"#, + ); + } + + #[test] + fn entry_parentheses() { + verify( + vec![ + Range::new_simple(0, 0, 0, 14), + Range::new_simple(0, 0, 0, 14), + ], + r#"@article(foo,)"#, + ); + } + + #[test] + fn field_no_assign() { + verify( + vec![ + Range::new_simple(0, 0, 1, 10), + Range::new_simple(0, 0, 1, 10), + Range::new_simple(1, 4, 1, 10), + ], + indoc!( + r#" + @article{foo, + author + "# + ), + ); + } + + #[test] + fn field_no_value() { + verify( + vec![ + Range::new_simple(0, 0, 1, 12), + Range::new_simple(0, 0, 1, 12), + Range::new_simple(1, 4, 1, 12), + ], + indoc!( + r#" + @article{foo, + author = + "# + ), + ); + } + + #[test] + fn field_no_comma() { + verify( + vec![ + Range::new_simple(0, 0, 1, 16), + Range::new_simple(0, 0, 1, 16), + Range::new_simple(1, 4, 1, 16), + Range::new_simple(1, 13, 1, 16), + ], + indoc!( + r#" + @article{foo, + author = bar + "# + ), + ); + } + + #[test] + fn field_complete() { + verify( + vec![ + Range::new_simple(0, 0, 2, 1), + Range::new_simple(0, 0, 2, 1), + Range::new_simple(1, 4, 1, 17), + Range::new_simple(1, 13, 1, 16), + ], + indoc!( + r#" + @article{foo, + author = bar, + } + "# + ), + ); + } + + #[test] + fn entry_two_fields() { + verify( + vec![ + Range::new_simple(0, 0, 3, 1), + Range::new_simple(0, 0, 3, 1), + Range::new_simple(1, 4, 1, 17), + Range::new_simple(1, 13, 1, 16), + Range::new_simple(2, 4, 2, 16), + Range::new_simple(2, 12, 2, 15), + ], + indoc!( + r#" + @article{foo, + author = bar, + title = baz, + } + "# + ), + ); + } + + #[test] + fn quoted_content_no_children() { + verify( + vec![ + Range::new_simple(0, 0, 0, 12), + Range::new_simple(0, 0, 0, 12), + Range::new_simple(0, 10, 0, 11), + ], + r#"@preamble{"}"#, + ); + } + + #[test] + fn quoted_content_no_right() { + verify( + vec![ + Range::new_simple(0, 0, 0, 16), + Range::new_simple(0, 0, 0, 16), + Range::new_simple(0, 10, 0, 15), + Range::new_simple(0, 11, 0, 15), + ], + r#"@preamble{"word}"#, + ); + } + + #[test] + fn braced_content_no_children() { + verify( + vec![ + Range::new_simple(0, 0, 0, 11), + Range::new_simple(0, 0, 0, 11), + Range::new_simple(0, 10, 0, 11), + ], + r#"@preamble{{"#, + ); + } + + #[test] + fn braced_content_no_right() { + verify( + vec![ + Range::new_simple(0, 0, 0, 15), + Range::new_simple(0, 0, 0, 15), + Range::new_simple(0, 10, 0, 15), + Range::new_simple(0, 11, 0, 15), + ], + r#"@preamble{{word"#, + ); + } + + #[test] + fn concat_no_right() { + verify( + vec![ + Range::new_simple(0, 0, 0, 16), + Range::new_simple(0, 0, 0, 16), + Range::new_simple(0, 10, 0, 15), + Range::new_simple(0, 10, 0, 13), + ], + r#"@preamble{foo #}"#, + ); + } + + #[test] + fn concat_complete() { + verify( + vec![ + Range::new_simple(0, 0, 0, 20), + Range::new_simple(0, 0, 0, 20), + Range::new_simple(0, 10, 0, 19), + Range::new_simple(0, 10, 0, 13), + Range::new_simple(0, 16, 0, 19), + ], + r#"@preamble{foo # bar}"#, + ); + } } } diff --git a/support/texlab/src/syntax/bibtex/parser.rs b/support/texlab/src/syntax/bibtex/parser.rs index ffc33da545..b23903aeb3 100644 --- a/support/texlab/src/syntax/bibtex/parser.rs +++ b/support/texlab/src/syntax/bibtex/parser.rs @@ -1,197 +1,379 @@ use super::ast::*; +use crate::{ + protocol::{Range, RangeExt}, + syntax::text::SyntaxNode, +}; +use petgraph::graph::{Graph, NodeIndex}; use std::iter::Peekable; -pub struct BibtexParser<I: Iterator<Item = BibtexToken>> { +pub struct Parser<I: Iterator<Item = Token>> { + graph: Graph<Node, ()>, tokens: Peekable<I>, } -impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> { +impl<I: Iterator<Item = Token>> Parser<I> { pub fn new(tokens: I) -> Self { - BibtexParser { + Self { tokens: tokens.peekable(), + graph: Graph::new(), } } - pub fn root(&mut self) -> BibtexRoot { + pub fn parse(mut self) -> Tree { let mut children = Vec::new(); + while let Some(ref token) = self.tokens.peek() { match token.kind { - BibtexTokenKind::PreambleKind => { - let preamble = Box::new(self.preamble()); - children.push(BibtexDeclaration::Preamble(preamble)); - } - BibtexTokenKind::StringKind => { - let string = Box::new(self.string()); - children.push(BibtexDeclaration::String(string)); - } - BibtexTokenKind::EntryKind => { - let entry = Box::new(self.entry()); - children.push(BibtexDeclaration::Entry(entry)); - } - _ => { - let comment = BibtexComment::new(self.tokens.next().unwrap()); - children.push(BibtexDeclaration::Comment(Box::new(comment))); - } + TokenKind::PreambleKind => children.push(self.preamble()), + TokenKind::StringKind => children.push(self.string()), + TokenKind::EntryKind => children.push(self.entry()), + _ => children.push(self.comment()), } } - BibtexRoot::new(children) + + let range = if children.is_empty() { + Range::new_simple(0, 0, 0, 0) + } else { + let start = self.graph[children[0]].start(); + let end = self.graph[children[children.len() - 1]].end(); + Range::new(start, end) + }; + + let root = self.graph.add_node(Node::Root(Root { range })); + self.connect(root, &children); + Tree { + graph: self.graph, + root, + } } - fn preamble(&mut self) -> BibtexPreamble { + fn preamble(&mut self) -> NodeIndex { let ty = self.tokens.next().unwrap(); - let left = self.expect2(BibtexTokenKind::BeginBrace, BibtexTokenKind::BeginParen); + let left = self.expect2(TokenKind::BeginBrace, TokenKind::BeginParen); if left.is_none() { - return BibtexPreamble::new(ty, None, None, None); + return self.graph.add_node(Node::Preamble(Box::new(Preamble { + range: ty.range(), + ty, + left: None, + right: None, + }))); } if !self.can_match_content() { - return BibtexPreamble::new(ty, left, None, None); + return self.graph.add_node(Node::Preamble(Box::new(Preamble { + range: Range::new(ty.start(), left.as_ref().unwrap().end()), + ty, + left, + right: None, + }))); } + let content = self.content(); - let right = self.expect2(BibtexTokenKind::EndBrace, BibtexTokenKind::EndParen); - BibtexPreamble::new(ty, left, Some(content), right) + let right = self.expect2(TokenKind::EndBrace, TokenKind::EndParen); + let end = right + .as_ref() + .map(Token::end) + .unwrap_or_else(|| self.graph[content].end()); + + let parent = self.graph.add_node(Node::Preamble(Box::new(Preamble { + range: Range::new(ty.start(), end), + ty, + left, + right, + }))); + self.graph.add_edge(parent, content, ()); + parent } - fn string(&mut self) -> BibtexString { + fn string(&mut self) -> NodeIndex { let ty = self.tokens.next().unwrap(); - let left = self.expect2(BibtexTokenKind::BeginBrace, BibtexTokenKind::BeginParen); + let left = self.expect2(TokenKind::BeginBrace, TokenKind::BeginParen); if left.is_none() { - return BibtexString::new(ty, None, None, None, None, None); + return self.graph.add_node(Node::String(Box::new(String { + range: ty.range(), + ty, + left: None, + name: None, + assign: None, + right: None, + }))); } - let name = self.expect1(BibtexTokenKind::Word); + let name = self.expect1(TokenKind::Word); if name.is_none() { - return BibtexString::new(ty, left, None, None, None, None); + return self.graph.add_node(Node::String(Box::new(String { + range: Range::new(ty.start(), left.as_ref().unwrap().end()), + ty, + left, + name: None, + assign: None, + right: None, + }))); } - let assign = self.expect1(BibtexTokenKind::Assign); + let assign = self.expect1(TokenKind::Assign); if assign.is_none() { - return BibtexString::new(ty, left, name, None, None, None); + return self.graph.add_node(Node::String(Box::new(String { + range: Range::new(ty.start(), name.as_ref().unwrap().end()), + ty, + left, + name, + assign: None, + right: None, + }))); } if !self.can_match_content() { - return BibtexString::new(ty, left, name, assign, None, None); + return self.graph.add_node(Node::String(Box::new(String { + range: Range::new(ty.start(), assign.as_ref().unwrap().end()), + ty, + left, + name, + assign, + right: None, + }))); } let value = self.content(); - let right = self.expect2(BibtexTokenKind::EndBrace, BibtexTokenKind::EndParen); - BibtexString::new(ty, left, name, assign, Some(value), right) + let right = self.expect2(TokenKind::EndBrace, TokenKind::EndParen); + let end = right + .as_ref() + .map(Token::end) + .unwrap_or_else(|| self.graph[value].end()); + + let parent = self.graph.add_node(Node::String(Box::new(String { + range: Range::new(ty.start(), end), + ty, + left, + name, + assign, + right, + }))); + self.graph.add_edge(parent, value, ()); + parent } - fn entry(&mut self) -> BibtexEntry { + fn entry(&mut self) -> NodeIndex { let ty = self.tokens.next().unwrap(); - let left = self.expect2(BibtexTokenKind::BeginBrace, BibtexTokenKind::BeginParen); + let left = self.expect2(TokenKind::BeginBrace, TokenKind::BeginParen); if left.is_none() { - return BibtexEntry::new(ty, None, None, None, Vec::new(), None); + return self.graph.add_node(Node::Entry(Box::new(Entry { + range: ty.range(), + ty, + left: None, + key: None, + comma: None, + right: None, + }))); } - let name = self.expect1(BibtexTokenKind::Word); - if name.is_none() { - return BibtexEntry::new(ty, left, None, None, Vec::new(), None); + let key = self.expect1(TokenKind::Word); + if key.is_none() { + return self.graph.add_node(Node::Entry(Box::new(Entry { + range: Range::new(ty.start(), left.as_ref().unwrap().end()), + ty, + left, + key: None, + comma: None, + right: None, + }))); } - let comma = self.expect1(BibtexTokenKind::Comma); + let comma = self.expect1(TokenKind::Comma); if comma.is_none() { - return BibtexEntry::new(ty, left, name, None, Vec::new(), None); + return self.graph.add_node(Node::Entry(Box::new(Entry { + range: Range::new(ty.start(), key.as_ref().unwrap().end()), + ty, + left, + key, + comma: None, + right: None, + }))); } let mut fields = Vec::new(); - while self.next_of_kind(BibtexTokenKind::Word) { + while self.next_of_kind(TokenKind::Word) { fields.push(self.field()); } - let right = self.expect2(BibtexTokenKind::EndBrace, BibtexTokenKind::EndParen); - BibtexEntry::new(ty, left, name, comma, fields, right) + let right = self.expect2(TokenKind::EndBrace, TokenKind::EndParen); + + let end = right + .as_ref() + .map(Token::end) + .or_else(|| fields.last().map(|field| self.graph[*field].end())) + .unwrap_or_else(|| comma.as_ref().unwrap().end()); + let parent = self.graph.add_node(Node::Entry(Box::new(Entry { + range: Range::new(ty.start(), end), + ty, + left, + key, + comma, + right, + }))); + self.connect(parent, &fields); + parent } - fn field(&mut self) -> BibtexField { + fn comment(&mut self) -> NodeIndex { + let token = self.tokens.next().unwrap(); + self.graph.add_node(Node::Comment(Comment { token })) + } + + fn field(&mut self) -> NodeIndex { let name = self.tokens.next().unwrap(); - let assign = self.expect1(BibtexTokenKind::Assign); + let assign = self.expect1(TokenKind::Assign); if assign.is_none() { - return BibtexField::new(name, None, None, None); + return self.graph.add_node(Node::Field(Box::new(Field { + range: name.range(), + name, + assign: None, + comma: None, + }))); } if !self.can_match_content() { - return BibtexField::new(name, assign, None, None); + return self.graph.add_node(Node::Field(Box::new(Field { + range: Range::new(name.start(), assign.as_ref().unwrap().end()), + name, + assign, + comma: None, + }))); } + let content = self.content(); - let comma = self.expect1(BibtexTokenKind::Comma); - BibtexField::new(name, assign, Some(content), comma) + let comma = self.expect1(TokenKind::Comma); + + let end = comma + .as_ref() + .map(Token::end) + .unwrap_or_else(|| self.graph[content].end()); + let parent = self.graph.add_node(Node::Field(Box::new(Field { + range: Range::new(name.start(), end), + name, + assign, + comma, + }))); + self.graph.add_edge(parent, content, ()); + parent } - fn content(&mut self) -> BibtexContent { + fn content(&mut self) -> NodeIndex { let token = self.tokens.next().unwrap(); let left = match token.kind { - BibtexTokenKind::PreambleKind - | BibtexTokenKind::StringKind - | BibtexTokenKind::EntryKind - | BibtexTokenKind::Word - | BibtexTokenKind::Assign - | BibtexTokenKind::Comma - | BibtexTokenKind::BeginParen - | BibtexTokenKind::EndParen => BibtexContent::Word(BibtexWord::new(token)), - BibtexTokenKind::Command => BibtexContent::Command(BibtexCommand::new(token)), - BibtexTokenKind::Quote => { + TokenKind::PreambleKind + | TokenKind::StringKind + | TokenKind::EntryKind + | TokenKind::Word + | TokenKind::Assign + | TokenKind::Comma + | TokenKind::BeginParen + | TokenKind::EndParen => self.graph.add_node(Node::Word(Word { token })), + TokenKind::Command => self.graph.add_node(Node::Command(Command { token })), + TokenKind::Quote => { let mut children = Vec::new(); while self.can_match_content() { - if self.next_of_kind(BibtexTokenKind::Quote) { + if self.next_of_kind(TokenKind::Quote) { break; } children.push(self.content()); } - let right = self.expect1(BibtexTokenKind::Quote); - BibtexContent::QuotedContent(BibtexQuotedContent::new(token, children, right)) + let right = self.expect1(TokenKind::Quote); + + let end = right + .as_ref() + .map(Token::end) + .or_else(|| children.last().map(|child| self.graph[*child].end())) + .unwrap_or_else(|| token.end()); + let parent = self.graph.add_node(Node::QuotedContent(QuotedContent { + range: Range::new(token.start(), end), + left: token, + right, + })); + self.connect(parent, &children); + parent } - BibtexTokenKind::BeginBrace => { + TokenKind::BeginBrace => { let mut children = Vec::new(); while self.can_match_content() { children.push(self.content()); } - let right = self.expect1(BibtexTokenKind::EndBrace); - BibtexContent::BracedContent(BibtexBracedContent::new(token, children, right)) + let right = self.expect1(TokenKind::EndBrace); + + let end = right + .as_ref() + .map(Token::end) + .or_else(|| children.last().map(|child| self.graph[*child].end())) + .unwrap_or_else(|| token.end()); + let parent = self.graph.add_node(Node::BracedContent(BracedContent { + range: Range::new(token.start(), end), + left: token, + right, + })); + self.connect(parent, &children); + parent } _ => unreachable!(), }; - if let Some(operator) = self.expect1(BibtexTokenKind::Concat) { - let right = if self.can_match_content() { - Some(self.content()) - } else { - None - }; - BibtexContent::Concat(Box::new(BibtexConcat::new(left, operator, right))) - } else { - left + + match self.expect1(TokenKind::Concat) { + Some(operator) => { + if self.can_match_content() { + let right = self.content(); + let parent = self.graph.add_node(Node::Concat(Concat { + range: Range::new(self.graph[left].start(), self.graph[right].end()), + operator, + })); + self.graph.add_edge(parent, left, ()); + self.graph.add_edge(parent, right, ()); + parent + } else { + let parent = self.graph.add_node(Node::Concat(Concat { + range: Range::new(self.graph[left].start(), operator.end()), + operator, + })); + self.graph.add_edge(parent, left, ()); + parent + } + } + None => left, + } + } + + fn connect(&mut self, parent: NodeIndex, children: &[NodeIndex]) { + for child in children { + self.graph.add_edge(parent, *child, ()); } } fn can_match_content(&mut self) -> bool { if let Some(ref token) = self.tokens.peek() { match token.kind { - BibtexTokenKind::PreambleKind - | BibtexTokenKind::StringKind - | BibtexTokenKind::EntryKind - | BibtexTokenKind::Word - | BibtexTokenKind::Command - | BibtexTokenKind::Assign - | BibtexTokenKind::Comma - | BibtexTokenKind::Quote - | BibtexTokenKind::BeginBrace - | BibtexTokenKind::BeginParen - | BibtexTokenKind::EndParen => true, - BibtexTokenKind::Concat | BibtexTokenKind::EndBrace => false, + TokenKind::PreambleKind + | TokenKind::StringKind + | TokenKind::EntryKind + | TokenKind::Word + | TokenKind::Command + | TokenKind::Assign + | TokenKind::Comma + | TokenKind::Quote + | TokenKind::BeginBrace + | TokenKind::BeginParen + | TokenKind::EndParen => true, + TokenKind::Concat | TokenKind::EndBrace => false, } } else { false } } - fn expect1(&mut self, kind: BibtexTokenKind) -> Option<BibtexToken> { + fn expect1(&mut self, kind: TokenKind) -> Option<Token> { if let Some(ref token) = self.tokens.peek() { if token.kind == kind { return self.tokens.next(); @@ -200,7 +382,7 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> { None } - fn expect2(&mut self, kind1: BibtexTokenKind, kind2: BibtexTokenKind) -> Option<BibtexToken> { + fn expect2(&mut self, kind1: TokenKind, kind2: TokenKind) -> Option<Token> { if let Some(ref token) = self.tokens.peek() { if token.kind == kind1 || token.kind == kind2 { return self.tokens.next(); @@ -209,7 +391,7 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> { None } - fn next_of_kind(&mut self, kind: BibtexTokenKind) -> bool { + fn next_of_kind(&mut self, kind: TokenKind) -> bool { if let Some(token) = self.tokens.peek() { token.kind == kind } else { |