summaryrefslogtreecommitdiff
path: root/support/texlab/src/syntax/bibtex
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/syntax/bibtex')
-rw-r--r--support/texlab/src/syntax/bibtex/ast.rs686
-rw-r--r--support/texlab/src/syntax/bibtex/finder.rs110
-rw-r--r--support/texlab/src/syntax/bibtex/formatter.rs329
-rw-r--r--support/texlab/src/syntax/bibtex/lexer.rs135
-rw-r--r--support/texlab/src/syntax/bibtex/mod.rs431
-rw-r--r--support/texlab/src/syntax/bibtex/parser.rs372
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 {