microcad_lang_format/
lib.rs1use microcad_lang_base::Diagnostics;
5use microcad_lang_parse::{Parse, ParseContext, ast};
6
7mod expression;
8mod extras;
9mod literal;
10mod node;
11mod statement;
12mod ty;
13
14pub(crate) use crate::node::{BreakMode, Node};
15
16#[derive(Debug, Clone)]
17pub struct FormatConfig {
18 pub max_width: usize,
19 pub indent_width: usize,
20}
21
22impl Default for FormatConfig {
23 fn default() -> Self {
24 Self {
25 max_width: 60,
26 indent_width: 4,
27 }
28 }
29}
30
31pub(crate) trait Format {
32 fn format(&self, f: &FormatConfig) -> Node;
33}
34
35impl<T> Format for T
36where
37 T: Into<Node> + Clone,
38{
39 fn format(&self, _f: &FormatConfig) -> Node {
40 self.clone().into()
41 }
42}
43
44impl<T: Format> Format for Option<T> {
46 fn format(&self, f: &FormatConfig) -> Node {
47 match self {
48 Some(inner) => inner.format(f),
49 None => Node::Nil, }
51 }
52}
53
54impl Format for ast::Identifier {
55 fn format(&self, _: &FormatConfig) -> Node {
56 self.name.clone().into()
57 }
58}
59
60impl Format for ast::Comment {
61 fn format(&self, _: &FormatConfig) -> Node {
62 match &self.inner {
63 ast::CommentInner::SingleLine(line) => {
64 node!(Node::Softline Node::SingleLineComment(line.into()))
65 }
66 ast::CommentInner::MultiLine(line) => {
67 node!(Node::Softline "/* " Node::from(line.clone()) " */" Node::Softline)
68 }
69 }
70 }
71}
72
73impl Format for ast::DocBlock {
74 fn format(&self, _: &FormatConfig) -> Node {
75 Node::vlist(
76 self.lines.iter().cloned().map(|line| node!(line)),
77 Node::Nil,
78 0,
79 )
80 }
81}
82
83impl Format for ast::Program {
84 fn format(&self, f: &FormatConfig) -> Node {
85 self.statements.format(f)
86 }
87}
88
89#[macro_export]
91macro_rules! node {
92 ($node:expr) => {
94 $crate::Node::from($node)
95 };
96 ($($node:expr)*) => {
98 $crate::Node::from(vec![
99 $( $crate::Node::from($node) ),*
100 ])
101 };
102 ($f:ident, $extras:expr => $($node:expr)*) => {
104 $crate::extras::with_extras(
105 &$extras,
106 $f,
107 $crate::Node::from(vec![
108 $( $node.format($f) ),*
109 ])
110
111 )
112 };
113 ($f:ident => $($node:expr)*) => {
115 $crate::Node::from(vec![
116 $( $node.format($f) ),*
117 ])
118 };
119}
120
121pub fn format(program: &ast::Program, config: &FormatConfig) -> String {
123 program.format(config).to_string()
124}
125
126pub fn format_str(source: &str, config: &FormatConfig) -> Result<String, Diagnostics> {
128 let parse_context = ParseContext::new(source);
129 let source =
130 ast::Source::parse(&parse_context).map_err(|err| err.to_diagnostics(&parse_context))?;
131 Ok(format(&source.ast, config))
132}
133
134pub fn format_source(
136 source: &ast::Source,
137 config: &FormatConfig,
138) -> Result<ast::Source, Diagnostics> {
139 let formatted = microcad_lang_base::Source::new(
140 source.url.clone(),
141 source.line_offset,
142 format(&source.ast, config),
143 );
144 let parse_context = ParseContext::from(&formatted);
145 ast::Source::parse(&parse_context).map_err(|err| err.to_diagnostics(&parse_context))
146}