typstyle_core/
lib.rs

1pub mod attr;
2pub mod ext;
3pub mod partial;
4pub mod pretty;
5
6mod config;
7mod utils;
8
9pub use attr::AttrStore;
10pub use config::Config;
11use pretty::{ArenaDoc, PrettyPrinter};
12use thiserror::Error;
13use typst_syntax::Source;
14
15#[derive(Error, Debug)]
16pub enum Error {
17    #[error("The document has syntax errors")]
18    SyntaxError,
19}
20
21/// Entry point for pretty printing a typst document.
22#[derive(Debug, Clone, Default)]
23pub struct Typstyle {
24    config: Config,
25}
26
27impl Typstyle {
28    /// Create Typstyle formatter with config.
29    pub fn new(config: Config) -> Self {
30        Self { config }
31    }
32
33    /// Format typst content.
34    pub fn format_content(self, content: impl Into<String>) -> Result<String, Error> {
35        // We should ensure that the source tree is spanned.
36        self.format_source(&Source::detached(content.into()))
37    }
38
39    /// Format typst source.
40    pub fn format_source(self, source: &Source) -> Result<String, Error> {
41        self.format_source_inspect(source, |_| {})
42    }
43
44    /// Format typst source, and inspect the pretty document.
45    pub fn format_source_inspect(
46        self,
47        source: &Source,
48        inspector: impl FnOnce(&ArenaDoc<'_>),
49    ) -> Result<String, Error> {
50        let root = source.root();
51        if root.erroneous() {
52            return Err(Error::SyntaxError);
53        }
54        let attr_store = AttrStore::new(root);
55        let printer = PrettyPrinter::new(self.config.clone(), attr_store);
56        let markup = root.cast().unwrap();
57        let doc = printer.convert_markup(Default::default(), markup);
58        inspector(&doc);
59        let result = doc.pretty(self.config.max_width).to_string();
60        let result = utils::strip_trailing_whitespace(&result);
61        Ok(result)
62    }
63}
64
65/// Format typst content by Typstyle configured with given max_width.
66///
67/// It returns the original string if the source is erroneous.
68pub fn format_with_width(content: &str, width: usize) -> String {
69    let config = Config::new().with_width(width);
70    Typstyle::new(config)
71        .format_content(content)
72        .unwrap_or_else(|_| content.to_string())
73}
74
75#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
76use wasm_bindgen::prelude::*;
77
78#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
79#[wasm_bindgen]
80pub fn pretty_print_wasm(content: &str, width: usize) -> String {
81    format_with_width(content, width)
82}