ra_ap_syntax_bridge/
prettify_macro_expansion.rs1use syntax::{
3 ast::make,
4 ted::{self, Position},
5 NodeOrToken,
6 SyntaxKind::{self, *},
7 SyntaxNode, SyntaxToken, WalkEvent, T,
8};
9
10#[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"]
16pub fn prettify_macro_expansion(
17 syn: SyntaxNode,
18 dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> SyntaxToken,
19) -> SyntaxNode {
20 let mut indent = 0;
21 let mut last: Option<SyntaxKind> = None;
22 let mut mods = Vec::new();
23 let mut dollar_crate_replacements = Vec::new();
24 let syn = syn.clone_subtree().clone_for_update();
25
26 let before = Position::before;
27 let after = Position::after;
28
29 let do_indent = |pos: fn(_) -> Position, token: &SyntaxToken, indent| {
30 (pos(token.clone()), make::tokens::whitespace(&" ".repeat(4 * indent)))
31 };
32 let do_ws = |pos: fn(_) -> Position, token: &SyntaxToken| {
33 (pos(token.clone()), make::tokens::single_space())
34 };
35 let do_nl = |pos: fn(_) -> Position, token: &SyntaxToken| {
36 (pos(token.clone()), make::tokens::single_newline())
37 };
38
39 for event in syn.preorder_with_tokens() {
40 let token = match event {
41 WalkEvent::Enter(NodeOrToken::Token(token)) => token,
42 WalkEvent::Leave(NodeOrToken::Node(node))
43 if matches!(
44 node.kind(),
45 ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES
46 ) =>
47 {
48 if indent > 0 {
49 mods.push((
50 Position::after(node.clone()),
51 make::tokens::whitespace(&" ".repeat(4 * indent)),
52 ));
53 }
54 if node.parent().is_some() {
55 mods.push((Position::after(node), make::tokens::single_newline()));
56 }
57 continue;
58 }
59 _ => continue,
60 };
61 if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" {
62 dollar_crate_replacements.push((token.clone(), dollar_crate_replacement(&token)));
63 }
64 let tok = &token;
65
66 let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
67 tok.next_token().map(|it| f(it.kind())).unwrap_or(default)
68 };
69 let is_last =
70 |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
71
72 match tok.kind() {
73 k if is_text(k)
74 && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) =>
75 {
76 mods.push(do_ws(after, tok));
77 }
78 L_CURLY if is_next(|it| it != R_CURLY, true) => {
79 indent += 1;
80 if is_last(is_text, false) {
81 mods.push(do_ws(before, tok));
82 }
83
84 mods.push(do_indent(after, tok, indent));
85 mods.push(do_nl(after, tok));
86 }
87 R_CURLY if is_last(|it| it != L_CURLY, true) => {
88 indent = indent.saturating_sub(1);
89
90 if indent > 0 {
91 mods.push(do_indent(before, tok, indent));
92 }
93 mods.push(do_nl(before, tok));
94 }
95 R_CURLY => {
96 if indent > 0 {
97 mods.push(do_indent(after, tok, indent));
98 }
99 mods.push(do_nl(after, tok));
100 }
101 LIFETIME_IDENT if is_next(is_text, true) => {
102 mods.push(do_ws(after, tok));
103 }
104 AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW => {
105 mods.push(do_ws(after, tok));
106 }
107 T![;] if is_next(|it| it != R_CURLY, true) => {
108 if indent > 0 {
109 mods.push(do_indent(after, tok, indent));
110 }
111 mods.push(do_nl(after, tok));
112 }
113 T![=] if is_next(|it| it == T![>], false) => {
114 mods.push(do_ws(before, tok));
117 mods.push(do_ws(after, &tok.next_token().unwrap()));
118 }
119 T![->] | T![=] | T![=>] => {
120 mods.push(do_ws(before, tok));
121 mods.push(do_ws(after, tok));
122 }
123 T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => {
124 mods.push(do_ws(after, tok));
125 }
126 _ => (),
127 }
128
129 last = Some(tok.kind());
130 }
131
132 for (pos, insert) in mods {
133 ted::insert(pos, insert);
134 }
135 for (old, new) in dollar_crate_replacements {
136 ted::replace(old, new);
137 }
138
139 if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) {
140 ted::remove(it);
141 }
142
143 syn
144}
145
146fn is_text(k: SyntaxKind) -> bool {
147 k.is_any_identifier() || k.is_literal() || k == UNDERSCORE
149}