diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/expand_macro.rs | 94 |
1 files changed, 77 insertions, 17 deletions
diff --git a/crates/ra_ide_api/src/expand_macro.rs b/crates/ra_ide_api/src/expand_macro.rs index 44e77ba50..07a7c738a 100644 --- a/crates/ra_ide_api/src/expand_macro.rs +++ b/crates/ra_ide_api/src/expand_macro.rs | |||
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap; | |||
8 | use ra_syntax::{ | 8 | use ra_syntax::{ |
9 | algo::{find_node_at_offset, replace_descendants}, | 9 | algo::{find_node_at_offset, replace_descendants}, |
10 | ast::{self}, | 10 | ast::{self}, |
11 | AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, | 11 | AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | pub struct ExpandedMacro { | 14 | pub struct ExpandedMacro { |
@@ -55,9 +55,12 @@ fn expand_macro_recur( | |||
55 | Some(replace_descendants(&expanded, &replaces)) | 55 | Some(replace_descendants(&expanded, &replaces)) |
56 | } | 56 | } |
57 | 57 | ||
58 | // FIXME: It would also be cool to share logic here and in the mbe tests, | ||
59 | // which are pretty unreadable at the moment. | ||
58 | fn insert_whitespaces(syn: SyntaxNode) -> String { | 60 | fn insert_whitespaces(syn: SyntaxNode) -> String { |
59 | let mut res = String::new(); | 61 | use SyntaxKind::*; |
60 | 62 | ||
63 | let mut res = String::new(); | ||
61 | let mut token_iter = syn | 64 | let mut token_iter = syn |
62 | .preorder_with_tokens() | 65 | .preorder_with_tokens() |
63 | .filter_map(|event| { | 66 | .filter_map(|event| { |
@@ -69,16 +72,44 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
69 | }) | 72 | }) |
70 | .peekable(); | 73 | .peekable(); |
71 | 74 | ||
75 | let mut indent = 0; | ||
76 | let mut last: Option<SyntaxKind> = None; | ||
77 | |||
72 | while let Some(token) = token_iter.next() { | 78 | while let Some(token) = token_iter.next() { |
73 | res += &token.text().to_string(); | 79 | let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { |
74 | if token.kind().is_keyword() | 80 | token_iter.peek().map(|it| f(it.kind())).unwrap_or(default) |
75 | || token.kind().is_literal() | 81 | }; |
76 | || token.kind() == SyntaxKind::IDENT | 82 | let is_last = |f: fn(SyntaxKind) -> bool, default| -> bool { |
77 | { | 83 | last.map(|it| f(it)).unwrap_or(default) |
78 | if !token_iter.peek().map(|it| it.kind().is_punct()).unwrap_or(false) { | 84 | }; |
79 | res += " "; | 85 | |
86 | res += &match token.kind() { | ||
87 | k @ _ | ||
88 | if (k.is_keyword() || k.is_literal() || k == IDENT) | ||
89 | && is_next(|it| !it.is_punct(), true) => | ||
90 | { | ||
91 | token.text().to_string() + " " | ||
80 | } | 92 | } |
81 | } | 93 | L_CURLY if is_next(|it| it != R_CURLY, true) => { |
94 | indent += 1; | ||
95 | format!(" {{\n{}", " ".repeat(indent)) | ||
96 | } | ||
97 | R_CURLY if is_last(|it| it != L_CURLY, true) => { | ||
98 | indent = indent.checked_sub(1).unwrap_or(0); | ||
99 | format!("\n}}{}", " ".repeat(indent)) | ||
100 | } | ||
101 | R_CURLY => { | ||
102 | indent = indent.checked_sub(1).unwrap_or(0); | ||
103 | format!("}}\n{}", " ".repeat(indent)) | ||
104 | } | ||
105 | T![;] => format!(";\n{}", " ".repeat(indent)), | ||
106 | T![->] => " -> ".to_string(), | ||
107 | T![=] => " = ".to_string(), | ||
108 | T![=>] => " => ".to_string(), | ||
109 | _ => token.text().to_string(), | ||
110 | }; | ||
111 | |||
112 | last = Some(token.kind()); | ||
82 | } | 113 | } |
83 | 114 | ||
84 | res | 115 | res |
@@ -86,19 +117,18 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
86 | 117 | ||
87 | #[cfg(test)] | 118 | #[cfg(test)] |
88 | mod tests { | 119 | mod tests { |
120 | use super::*; | ||
89 | use crate::mock_analysis::analysis_and_position; | 121 | use crate::mock_analysis::analysis_and_position; |
122 | use insta::assert_snapshot; | ||
90 | 123 | ||
91 | fn check_expand_macro(fixture: &str, expected: (&str, &str)) { | 124 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { |
92 | let (analysis, pos) = analysis_and_position(fixture); | 125 | let (analysis, pos) = analysis_and_position(fixture); |
93 | 126 | analysis.expand_macro(pos).unwrap().unwrap() | |
94 | let result = analysis.expand_macro(pos).unwrap().unwrap(); | ||
95 | assert_eq!(result.name, expected.0.to_string()); | ||
96 | assert_eq!(result.expansion, expected.1.to_string()); | ||
97 | } | 127 | } |
98 | 128 | ||
99 | #[test] | 129 | #[test] |
100 | fn macro_expand_recursive_expansion() { | 130 | fn macro_expand_recursive_expansion() { |
101 | check_expand_macro( | 131 | let res = check_expand_macro( |
102 | r#" | 132 | r#" |
103 | //- /lib.rs | 133 | //- /lib.rs |
104 | macro_rules! bar { | 134 | macro_rules! bar { |
@@ -112,7 +142,37 @@ mod tests { | |||
112 | } | 142 | } |
113 | f<|>oo!(); | 143 | f<|>oo!(); |
114 | "#, | 144 | "#, |
115 | ("foo", "fn b(){}"), | ||
116 | ); | 145 | ); |
146 | |||
147 | assert_eq!(res.name, "foo"); | ||
148 | assert_snapshot!(res.expansion, @r###" | ||
149 | fn b(){} | ||
150 | "###); | ||
151 | } | ||
152 | |||
153 | #[test] | ||
154 | fn macro_expand_multiple_lines() { | ||
155 | let res = check_expand_macro( | ||
156 | r#" | ||
157 | //- /lib.rs | ||
158 | macro_rules! foo { | ||
159 | () => { | ||
160 | fn some_thing() -> u32 { | ||
161 | let a = 0; | ||
162 | a + 10 | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | f<|>oo!(); | ||
167 | "#, | ||
168 | ); | ||
169 | |||
170 | assert_eq!(res.name, "foo"); | ||
171 | assert_snapshot!(res.expansion, @r###" | ||
172 | fn some_thing() -> u32 { | ||
173 | let a = 0; | ||
174 | a+10 | ||
175 | } | ||
176 | "###); | ||
117 | } | 177 | } |
118 | } | 178 | } |