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