#![doc = include_str!("../README.md")]
mod ast_format;
mod helpers;
mod renderer;
mod token;
#[cfg(test)]
mod test_utils;
use std::{borrow::Cow, io::Write};
use ast_format::format_node;
use helpers::{EmptyLines, INDENT_STR};
use renderer::Renderer;
use thiserror::Error;
use tree_sitter::{Node, Parser, Tree};
const LINE_WIDTH: usize = 80;
#[derive(Debug, Error)]
pub enum Error {
#[error("data store disconnected")]
IO(#[from] std::io::Error),
#[error("unknown parser error")]
Parse,
#[error("invalid module header")]
ModuleHeader,
#[error("invalid step-or-stutter sequence")]
StepOrStutter,
}
#[derive(Debug)]
pub struct ParsedFile<'a> {
t: Tree,
input: Cow<'a, str>,
}
impl<'a> ParsedFile<'a> {
pub fn new(input: &'a str) -> Result<Self, Error> {
let mut parser = Parser::new();
parser
.set_language(&tree_sitter_tlaplus::LANGUAGE.into())
.expect("error loading TLA+ grammar");
let input = match input.contains("\t") {
true => Cow::Owned(input.replace("\t", INDENT_STR)),
false => Cow::Borrowed(input),
};
Ok(Self {
t: parser.parse(input.as_bytes(), None).ok_or(Error::Parse)?,
input,
})
}
pub fn format<W>(&self, out: W) -> Result<(), Error>
where
W: Write,
{
let mut out = Renderer::new(out);
let mut empty_lines = EmptyLines::default();
format_node(self.t.root_node(), &self.input, &mut empty_lines, &mut out)?;
out.flush()?;
Ok(())
}
}
fn get_str<'a>(node: &Node<'_>, input: &'a str) -> &'a str {
&input[node.byte_range()]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mixed_tabs_spaces() {
assert_rewrite!(
"\
---- MODULE Bananas ------
X == /\\ x = 4
\t /\\ y = 2
====="
);
}
}