1use std::fmt::{self, Write};
4
5use span::{Edition, ErasedFileAstId};
6
7use crate::{
8 item_tree::{
9 Const, Enum, ExternBlock, ExternCrate, Function, Impl, ItemTree, Macro2, MacroCall,
10 MacroRules, Mod, ModItemId, ModItemKind, ModKind, RawVisibilityId, SourceDatabase, Static,
11 Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, attrs::AttrsOrCfg,
12 },
13 visibility::RawVisibility,
14};
15
16pub(super) fn print_item_tree(
17 db: &dyn SourceDatabase,
18 tree: &ItemTree,
19 edition: Edition,
20) -> String {
21 let mut p =
22 Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true, edition };
23
24 if let Some(top_attrs) = tree.top_level_attrs() {
25 p.print_attrs(top_attrs, true, "\n");
26 p.blank();
27 }
28
29 for item in tree.top_level_items() {
30 p.print_mod_item(*item);
31 }
32
33 let mut s = p.buf.trim_end_matches('\n').to_owned();
34 s.push('\n');
35 s
36}
37
38macro_rules! w {
39 ($dst:expr, $($arg:tt)*) => {
40 { let _ = write!($dst, $($arg)*); }
41 };
42}
43
44macro_rules! wln {
45 ($dst:expr) => {
46 { let _ = writeln!($dst); }
47 };
48 ($dst:expr, $($arg:tt)*) => {
49 { let _ = writeln!($dst, $($arg)*); }
50 };
51}
52
53struct Printer<'a> {
54 db: &'a dyn SourceDatabase,
55 tree: &'a ItemTree,
56 buf: String,
57 indent_level: usize,
58 needs_indent: bool,
59 edition: Edition,
60}
61
62impl Printer<'_> {
63 fn indented(&mut self, f: impl FnOnce(&mut Self)) {
64 self.indent_level += 1;
65 wln!(self);
66 f(self);
67 self.indent_level -= 1;
68 self.buf = self.buf.trim_end_matches('\n').to_owned();
69 }
70
71 fn blank(&mut self) {
73 let mut iter = self.buf.chars().rev().fuse();
74 match (iter.next(), iter.next()) {
75 (Some('\n'), Some('\n') | None) | (None, None) => {}
76 (Some('\n'), Some(_)) => {
77 self.buf.push('\n');
78 }
79 (Some(_), _) => {
80 self.buf.push('\n');
81 self.buf.push('\n');
82 }
83 (None, Some(_)) => unreachable!(),
84 }
85 }
86
87 fn print_attrs(&mut self, attrs: &AttrsOrCfg, inner: bool, separated_by: &str) {
88 let (cfg_disabled_expr, attrs) = match attrs {
89 AttrsOrCfg::Enabled { attrs } => (None, attrs),
90 AttrsOrCfg::CfgDisabled(inner_box) => (Some(&inner_box.0), &inner_box.1),
91 };
92 let inner = if inner { "!" } else { "" };
93 for attr in &*attrs.as_ref() {
94 w!(
95 self,
96 "#{}[{}{}]{}",
97 inner,
98 attr.path.display(self.db, self.edition),
99 attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
100 separated_by,
101 );
102 }
103 if let Some(expr) = cfg_disabled_expr {
104 w!(self, "#{inner}[cfg({expr})]{separated_by}");
105 }
106 }
107
108 fn print_attrs_of(&mut self, of: ModItemId, separated_by: &str) {
109 if let Some(attrs) = self.tree.attrs(of) {
110 self.print_attrs(attrs, false, separated_by);
111 }
112 }
113
114 fn print_visibility(&mut self, vis: RawVisibilityId) {
115 match &self.tree[vis] {
116 RawVisibility::Module(path, _expl) => {
117 w!(self, "pub(in {}) ", path.display(self.db, self.edition))
118 }
119 RawVisibility::Public => w!(self, "pub "),
120 RawVisibility::PubCrate => w!(self, "pub(crate) "),
121 RawVisibility::PubSelf(_) => w!(self, "pub(self) "),
122 };
123 }
124
125 fn print_use_tree(&mut self, use_tree: &UseTree) {
126 match &use_tree.kind {
127 UseTreeKind::Single { path, alias } => {
128 w!(self, "{}", path.display(self.db, self.edition));
129 if let Some(alias) = alias {
130 w!(self, " as {}", alias.display(self.edition));
131 }
132 }
133 UseTreeKind::Glob { path } => {
134 if let Some(path) = path {
135 w!(self, "{}::", path.display(self.db, self.edition));
136 }
137 w!(self, "*");
138 }
139 UseTreeKind::Prefixed { prefix, list } => {
140 if let Some(prefix) = prefix {
141 w!(self, "{}::", prefix.display(self.db, self.edition));
142 }
143 w!(self, "{{");
144 for (i, tree) in list.iter().enumerate() {
145 if i != 0 {
146 w!(self, ", ");
147 }
148 self.print_use_tree(tree);
149 }
150 w!(self, "}}");
151 }
152 }
153 }
154
155 fn print_mod_item(&mut self, item: ModItemId) {
156 self.print_attrs_of(item, "\n");
157
158 match self.tree.index(item) {
159 ModItemKind::Use(ast_id, item) => {
160 let Use { visibility, use_tree } = item;
161 self.print_ast_id(ast_id.erase());
162 self.print_visibility(*visibility);
163 w!(self, "use ");
164 self.print_use_tree(use_tree);
165 wln!(self, ";");
166 }
167 ModItemKind::ExternCrate(ast_id, item) => {
168 let ExternCrate { name, alias, visibility } = item;
169 self.print_ast_id(ast_id.erase());
170 self.print_visibility(*visibility);
171 w!(self, "extern crate {}", name.display(self.db, self.edition));
172 if let Some(alias) = alias {
173 w!(self, " as {}", alias.display(self.edition));
174 }
175 wln!(self, ";");
176 }
177 ModItemKind::ExternBlock(ast_id, item) => {
178 let ExternBlock { children } = item;
179 self.print_ast_id(ast_id.erase());
180 w!(self, "extern {{");
181 self.indented(|this| {
182 for child in &**children {
183 this.print_mod_item(*child);
184 }
185 });
186 wln!(self, "}}");
187 }
188 ModItemKind::Function(ast_id, item) => {
189 let Function { name, visibility } = item;
190 self.print_ast_id(ast_id.erase());
191 self.print_visibility(*visibility);
192 wln!(self, "fn {};", name.display(self.db, self.edition));
193 }
194 ModItemKind::Struct(ast_id, item) => {
195 let Struct { visibility, name, value_ns_ctor: _ } = item;
196 self.print_ast_id(ast_id.erase());
197 self.print_visibility(*visibility);
198 wln!(self, "struct {} {{ ... }}", name.display(self.db, self.edition));
199 }
200 ModItemKind::Union(ast_id, item) => {
201 let Union { name, visibility } = item;
202 self.print_ast_id(ast_id.erase());
203 self.print_visibility(*visibility);
204 wln!(self, "union {} {{ ... }}", name.display(self.db, self.edition));
205 }
206 ModItemKind::Enum(ast_id, item) => {
207 let Enum { name, visibility } = item;
208 self.print_ast_id(ast_id.erase());
209 self.print_visibility(*visibility);
210 wln!(self, "enum {} {{ ... }}", name.display(self.db, self.edition));
211 }
212 ModItemKind::Const(ast_id, item) => {
213 let Const { name, visibility } = item;
214 self.print_ast_id(ast_id.erase());
215 self.print_visibility(*visibility);
216 w!(self, "const ");
217 match name {
218 Some(name) => w!(self, "{}", name.display(self.db, self.edition)),
219 None => w!(self, "_"),
220 }
221 wln!(self, " = _;");
222 }
223 ModItemKind::Static(ast_id, item) => {
224 let Static { name, visibility } = item;
225 self.print_ast_id(ast_id.erase());
226 self.print_visibility(*visibility);
227 w!(self, "static ");
228 w!(self, "{}", name.display(self.db, self.edition));
229 w!(self, " = _;");
230 wln!(self);
231 }
232 ModItemKind::Trait(ast_id, item) => {
233 let Trait { name, visibility } = item;
234 self.print_ast_id(ast_id.erase());
235 self.print_visibility(*visibility);
236 w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition));
237 }
238 ModItemKind::Impl(ast_id, item) => {
239 let Impl { is_trait_impl: _ } = item;
240 self.print_ast_id(ast_id.erase());
241 w!(self, "impl {{ ... }}");
242 }
243 ModItemKind::TypeAlias(ast_id, item) => {
244 let TypeAlias { name, visibility } = item;
245 self.print_ast_id(ast_id.erase());
246 self.print_visibility(*visibility);
247 w!(self, "type {}", name.display(self.db, self.edition));
248 w!(self, ";");
249 wln!(self);
250 }
251 ModItemKind::Mod(ast_id, item) => {
252 let Mod { name, visibility, kind } = item;
253 self.print_ast_id(ast_id.erase());
254 self.print_visibility(*visibility);
255 w!(self, "mod {}", name.display(self.db, self.edition));
256 match kind {
257 ModKind::Inline { items } => {
258 w!(self, " {{");
259 self.indented(|this| {
260 for item in &**items {
261 this.print_mod_item(*item);
262 }
263 });
264 wln!(self, "}}");
265 }
266 ModKind::Outline => {
267 wln!(self, ";");
268 }
269 }
270 }
271 ModItemKind::MacroCall(ast_id, item) => {
272 let MacroCall { path, expand_to, ctxt } = item;
273 let _ = writeln!(
274 self,
275 "// AstId: {:#?}, SyntaxContextId: {}, ExpandTo: {:?}",
276 ast_id.erase(),
277 ctxt,
278 expand_to
279 );
280 wln!(self, "{}!(...);", path.display(self.db, self.edition));
281 }
282 ModItemKind::MacroRules(ast_id, item) => {
283 let MacroRules { name } = item;
284 self.print_ast_id(ast_id.erase());
285 wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db, self.edition));
286 }
287 ModItemKind::Macro2(ast_id, item) => {
288 let Macro2 { name, visibility } = item;
289 self.print_ast_id(ast_id.erase());
290 self.print_visibility(*visibility);
291 wln!(self, "macro {} {{ ... }}", name.display(self.db, self.edition));
292 }
293 }
294
295 self.blank();
296 }
297
298 fn print_ast_id(&mut self, ast_id: ErasedFileAstId) {
299 wln!(self, "// AstId: {ast_id:#?}");
300 }
301}
302
303impl Write for Printer<'_> {
304 fn write_str(&mut self, s: &str) -> fmt::Result {
305 for line in s.split_inclusive('\n') {
306 if self.needs_indent {
307 match self.buf.chars().last() {
308 Some('\n') | None => {}
309 _ => self.buf.push('\n'),
310 }
311 self.buf.push_str(&" ".repeat(self.indent_level));
312 self.needs_indent = false;
313 }
314
315 self.buf.push_str(line);
316 self.needs_indent = line.ends_with('\n');
317 }
318
319 Ok(())
320 }
321}