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