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#[derive(Debug, Clone, Default)]
23pub struct Typstyle {
24 config: Config,
25}
26
27impl Typstyle {
28 pub fn new(config: Config) -> Self {
30 Self { config }
31 }
32
33 pub fn format_content(self, content: impl Into<String>) -> Result<String, Error> {
35 self.format_source(&Source::detached(content.into()))
37 }
38
39 pub fn format_source(self, source: &Source) -> Result<String, Error> {
41 self.format_source_inspect(source, |_| {})
42 }
43
44 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
65pub 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}