typst_fmt/lib.rs
1// use ast::Expr::*;
2// use regex::Regex;
3// use typst::syntax::parse;
4// use typst::syntax::{ast, SyntaxNode};
5
6// // Optimize: could return Text edit that should be applied one after the other
7// // instead of String
8// pub fn typst_format(s: &str) -> String {
9// format_with_rules(s, &[SpaceRule.as_dyn()])
10// }
11
12// fn format_with_rules(s: &str, rules: &[Box<dyn Rule>]) -> String {
13// let syntax_node = parse(s);
14// format_recursive(&syntax_node, 0, (), rules)
15// }
16
17// // Optimize: consider returning &str instead and other optimisations
18// //
19// fn format_recursive(
20// syntax_node: &SyntaxNode,
21// recurse: usize,
22// // feel free to include what your rule needs to know here
23// // and change the definition of the function if you need to
24// // for instance this could contain the parent if any
25// context: (),
26// rules: &[Box<dyn Rule>],
27// ) -> String {
28// // rules either leave the result unchanged or format it
29// // apply rules, append to result, do some for children
30// let mut result;
31// // currently only the first rule that matches is selected (this behavior could be changed)
32// // the most specific rules should come first.
33// if let Some(rule) = rules.iter().find(|&rule| rule.accept(syntax_node, context)) {
34// result = rule.eat(syntax_node);
35// } else {
36// // test this returns what I think
37// result = String::from(syntax_node.text())
38// }
39
40// for child in syntax_node.children() {
41// result.push_str(&format_recursive(child, recurse + 1, (), rules))
42// }
43// result
44// }
45
46// trait Rule {
47// fn accept(&self, syntax_node: &SyntaxNode, context: ()) -> bool;
48
49// fn eat(&self, syntax_node: &SyntaxNode) -> String;
50
51// fn as_dyn(self: Self) -> Box<dyn Rule>
52// where
53// Self: Sized + 'static,
54// {
55// Box::new(self)
56// }
57// }
58
59// struct SpaceRule;
60// impl Rule for SpaceRule {
61// fn accept(&self, syntax_node: &SyntaxNode, context: ()) -> bool {
62// syntax_node.is::<ast::Space>()
63// }
64
65// fn eat(&self, syntax_node: &SyntaxNode) -> String {
66// //let x = syntax_node.cast::<ast::Space>().unwrap();
67// assert!(syntax_node.text() != "");
68// let t = syntax_node.text().as_str();
69// let rg = Regex::new("\\s+").unwrap();
70// rg.replace_all(t, " ").to_string()
71// }
72// }
73
74// #[cfg(test)]
75// mod tests {
76// use super::*;
77
78// #[test]
79// fn test_name() {
80// assert_eq!(format_with_rules("#{ }", &[SpaceRule.as_dyn()]), "#{ }");
81// }
82
83// #[test]
84// fn two_spaces_become_one() {
85// assert_eq!(format_with_rules("#{ }", &[SpaceRule.as_dyn()]), "#{ }");
86// }
87// }
88
89// // const SOURCE1: &str = r##"
90// // #import "template.typ":*"##;
91
92// // const SOURCE: &str = r##"
93// // #import "template.typ": *
94// // #show: letter.with(
95// // sender: [
96// // Jane Smith, Universal Exports, 1 Heavy Plaza, Morristown, NJ 07964
97// // ],
98// // recipient: [
99// // Mr. John Doe \
100// // Acme Corp. \
101// // 123 Glennwood Ave \
102// // Quarto Creek, VA 22438
103// // ],
104// // date: [Morristown, June 9th, 2023],
105// // subject: [test],
106// // name: [Jane Smith \ Regional Director],
107// // )
108
109// // Dear Joe,
110
111// // #lorem(99)
112
113// // Best,
114// // "##;
115
116// /// rules :
117// /// ModuleImport, space after colon
118// /// ImportItems : no trailing comma
119
120// #[test]
121// fn feature() {
122// for a in [
123// "",
124// " ",
125// r##"#import "template.typ":*"##,
126// r##"#import "template.typ": *"##,
127// r##"#import "template.typ": func1, func2,"##,
128// ] {
129// println! {"parsing: {:?}",a};
130
131// let syntax_node = parse(a);
132// dbg!(syntax_node.erroneous());
133// println!("parse:\n{:?}", &syntax_node);
134// println!("--------------");
135// }
136// }