summaryrefslogtreecommitdiff
path: root/support/texlab/src/syntax/bibtex/formatter.rs
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-05-23 03:00:39 +0000
committerNorbert Preining <norbert@preining.info>2021-05-23 03:00:39 +0000
commitf1261b349e875b842745b63258c3e338cb1fe3bf (patch)
treeb5d402b3e80818cde2c079a42249f3dcb9732247 /support/texlab/src/syntax/bibtex/formatter.rs
parent58aa1ac09b1d9e4769d0a0661cf12e2b2db41b14 (diff)
CTAN sync 202105230300
Diffstat (limited to 'support/texlab/src/syntax/bibtex/formatter.rs')
-rw-r--r--support/texlab/src/syntax/bibtex/formatter.rs329
1 files changed, 0 insertions, 329 deletions
diff --git a/support/texlab/src/syntax/bibtex/formatter.rs b/support/texlab/src/syntax/bibtex/formatter.rs
deleted file mode 100644
index d4965e7cd4..0000000000
--- a/support/texlab/src/syntax/bibtex/formatter.rs
+++ /dev/null
@@ -1,329 +0,0 @@
-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);
- }
-}