typstyle_core/
lib.rs

1pub mod attr;
2pub mod ext;
3pub mod liteval;
4pub mod partial;
5pub mod pretty;
6
7mod config;
8mod utils;
9
10pub use attr::AttrStore;
11pub use config::Config;
12use pretty::{prelude::*, PrettyPrinter};
13use thiserror::Error;
14use typst_syntax::Source;
15
16#[derive(Error, Debug)]
17pub enum Error {
18    #[error("The document has syntax errors")]
19    SyntaxError,
20    #[error("An error occurred while rendering the document")]
21    RenderError,
22}
23
24/// Main struct for Typst formatting.
25#[derive(Debug, Clone, Default)]
26pub struct Typstyle {
27    config: Config,
28}
29
30impl Typstyle {
31    /// Creates a new `Typstyle` with the given style configuration.
32    pub fn new(config: Config) -> Self {
33        Self { config }
34    }
35
36    /// Prepares a text string for formatting.
37    pub fn format_text(&self, text: impl Into<String>) -> Formatter {
38        // We should ensure that the source tree is spanned.
39        self.format_source(Source::detached(text.into()))
40    }
41
42    /// Prepares a source for formatting.
43    pub fn format_source(&self, source: Source) -> Formatter {
44        Formatter::new(self.config.clone(), source)
45    }
46}
47
48/// Handles the formatting of a specific Typst source.
49pub struct Formatter<'a> {
50    source: Source,
51    printer: PrettyPrinter<'a>,
52}
53
54impl<'a> Formatter<'a> {
55    fn new(config: Config, source: Source) -> Self {
56        let attr_store = AttrStore::new(source.root());
57        let printer = PrettyPrinter::new(config, attr_store);
58        Self { source, printer }
59    }
60
61    /// Renders the document's pretty IR.
62    pub fn render_ir(&'a self) -> Result<String, Error> {
63        let doc = self.build_doc()?;
64        Ok(format!("{doc:#?}"))
65    }
66
67    /// Renders the formatted document to a string.
68    pub fn render(&'a self) -> Result<String, Error> {
69        let doc = self.build_doc()?;
70        let mut buf = String::new();
71        doc.render_fmt(self.printer.config().max_width, &mut buf)
72            .map_err(|_| Error::RenderError)?;
73        let result = utils::strip_trailing_whitespace(&buf);
74        Ok(result)
75    }
76
77    fn build_doc(&'a self) -> Result<ArenaDoc<'a>, Error> {
78        let root = self.source.root();
79        if root.erroneous() {
80            return Err(Error::SyntaxError);
81        }
82        let markup = root.cast().unwrap();
83        let doc = self.printer.convert_markup(Default::default(), markup);
84        Ok(doc)
85    }
86}