diff options
Diffstat (limited to 'support/texlab/crates/texlab/src/features/formatting/bibtex_internal.rs')
-rw-r--r-- | support/texlab/crates/texlab/src/features/formatting/bibtex_internal.rs | 199 |
1 files changed, 12 insertions, 187 deletions
diff --git a/support/texlab/crates/texlab/src/features/formatting/bibtex_internal.rs b/support/texlab/crates/texlab/src/features/formatting/bibtex_internal.rs index 2b16939f43..0070765b4b 100644 --- a/support/texlab/crates/texlab/src/features/formatting/bibtex_internal.rs +++ b/support/texlab/crates/texlab/src/features/formatting/bibtex_internal.rs @@ -1,7 +1,6 @@ -use base_db::{util::LineIndex, Document, Workspace}; +use base_db::{Document, Workspace}; use lsp_types::{FormattingOptions, TextEdit}; -use rowan::{ast::AstNode, NodeOrToken}; -use syntax::bibtex::{self, HasName, HasType, HasValue}; +use rowan::{TextLen, TextRange}; use crate::util::line_index_ext::LineIndexExt; @@ -10,189 +9,15 @@ pub fn format_bibtex_internal( document: &Document, options: &FormattingOptions, ) -> Option<Vec<TextEdit>> { - let mut indent = String::new(); - - if options.insert_spaces { - for _ in 0..options.tab_size { - indent.push(' '); - } - } else { - indent.push('\t'); - } - - let line_length = workspace.config().formatting.line_length; - - let line_index = &document.line_index; - let data = document.data.as_bib()?; - let mut edits = Vec::new(); - - for node in data.root_node().children().filter(|node| { - matches!( - node.kind(), - bibtex::PREAMBLE | bibtex::STRING | bibtex::ENTRY - ) - }) { - let range = node.text_range(); - - let mut formatter = - Formatter::new(indent.clone(), options.tab_size, line_length, line_index); - - formatter.visit_node(node); - edits.push(TextEdit { - range: line_index.line_col_lsp_range(range), - new_text: formatter.output, - }); - } - - Some(edits) -} - -struct Formatter<'a> { - indent: String, - tab_size: u32, - line_length: usize, - output: String, - align: Vec<usize>, - line_index: &'a LineIndex, -} - -impl<'a> Formatter<'a> { - fn new(indent: String, tab_size: u32, line_length: usize, line_index: &'a LineIndex) -> Self { - Self { - indent, - tab_size, - line_length, - output: String::new(), - align: Vec::new(), - line_index, - } - } - - fn visit_token_lowercase(&mut self, token: &bibtex::SyntaxToken) { - self.output.push_str(&token.text().to_lowercase()); - } - - fn should_insert_space( - &self, - previous: &bibtex::SyntaxToken, - current: &bibtex::SyntaxToken, - ) -> bool { - let previous_range = self.line_index.line_col_lsp_range(previous.text_range()); - let current_range = self.line_index.line_col_lsp_range(current.text_range()); - previous_range.start.line != current_range.start.line - || previous_range.end.character < current_range.start.character - } - - fn base_align(&self) -> usize { - self.output[self.output.rfind('\n').unwrap_or(0)..] - .chars() - .count() - } - - fn visit_node(&mut self, parent: bibtex::SyntaxNode) { - match parent.kind() { - bibtex::PREAMBLE => { - let preamble = bibtex::Preamble::cast(parent).unwrap(); - self.visit_token_lowercase(&preamble.type_token().unwrap()); - self.output.push('{'); - if preamble.syntax().children().next().is_some() { - self.align.push(self.base_align()); - for node in preamble.syntax().children() { - self.visit_node(node); - } - self.output.push('}'); - } - } - bibtex::STRING => { - let string = bibtex::StringDef::cast(parent).unwrap(); - self.visit_token_lowercase(&string.type_token().unwrap()); - self.output.push('{'); - if let Some(name) = string.name_token() { - self.output.push_str(name.text()); - self.output.push_str(" = "); - if let Some(value) = string.value() { - self.align.push(self.base_align()); - self.visit_node(value.syntax().clone()); - self.output.push('}'); - } - } - } - bibtex::ENTRY => { - let entry = bibtex::Entry::cast(parent).unwrap(); - self.visit_token_lowercase(&entry.type_token().unwrap()); - self.output.push('{'); - if let Some(key) = entry.name_token() { - self.output.push_str(&key.to_string()); - self.output.push(','); - self.output.push('\n'); - for field in entry.fields() { - self.visit_node(field.syntax().clone()); - } - self.output.push('}'); - } - } - bibtex::FIELD => { - let field = bibtex::Field::cast(parent).unwrap(); - self.output.push_str(&self.indent); - let name = field.name_token().unwrap(); - self.output.push_str(name.text()); - self.output.push_str(" = "); - if let Some(value) = field.value() { - let count = name.text().chars().count(); - self.align.push(self.tab_size as usize + count + 3); - self.visit_node(value.syntax().clone()); - self.output.push(','); - self.output.push('\n'); - } - } - kind if bibtex::Value::can_cast(kind) => { - let tokens: Vec<_> = parent - .descendants_with_tokens() - .filter_map(|element| element.into_token()) - .filter(|token| token.kind() != bibtex::WHITESPACE) - .collect(); - - 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.line_length { - self.output.push('\n'); - self.output.push_str(self.indent.as_ref()); - for _ in 0..=align - self.tab_size as usize { - self.output.push(' '); - } - length = align; - } else if insert_space { - self.output.push(' '); - length += 1; - } - self.output.push_str(current.text()); - length += current_length; - } - } - bibtex::ROOT | bibtex::JUNK => { - for element in parent.children_with_tokens() { - match element { - NodeOrToken::Token(token) => { - self.output.push_str(token.text()); - } - NodeOrToken::Node(node) => { - self.visit_node(node); - } - } - } - } - _ => unreachable!(), - } - } + let options = bibfmt::Options { + insert_spaces: options.insert_spaces, + line_length: workspace.config().formatting.line_length, + tab_size: options.tab_size as usize, + }; + + let output = bibfmt::format(&data.root_node(), &document.line_index, &options); + let range = TextRange::new(0.into(), document.text.text_len()); + let range = document.line_index.line_col_lsp_range(range)?; + Some(vec![lsp_types::TextEdit::new(range, output)]) } |