aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide_api/src/expand_macro.rs94
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;
8use ra_syntax::{ 8use 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
14pub struct ExpandedMacro { 14pub 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.
58fn insert_whitespaces(syn: SyntaxNode) -> String { 60fn 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)]
88mod tests { 119mod 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###"
149fn 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###"
172fn some_thing() -> u32 {
173 let a = 0;
174 a+10
175}
176"###);
117 } 177 }
118} 178}