aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/syntax_tree.rs714
1 files changed, 357 insertions, 357 deletions
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs
index 914759709..e2bb120b4 100644
--- a/crates/ra_ide_api/src/syntax_tree.rs
+++ b/crates/ra_ide_api/src/syntax_tree.rs
@@ -1,357 +1,357 @@
1use crate::db::RootDatabase; 1use crate::db::RootDatabase;
2use ra_db::SourceDatabase; 2use ra_db::SourceDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo, AstNode, NodeOrToken, SourceFile, 4 algo, AstNode, NodeOrToken, SourceFile,
5 SyntaxKind::{RAW_STRING, STRING}, 5 SyntaxKind::{RAW_STRING, STRING},
6 SyntaxToken, TextRange, 6 SyntaxToken, TextRange,
7}; 7};
8 8
9pub use ra_db::FileId; 9pub use ra_db::FileId;
10 10
11pub(crate) fn syntax_tree( 11pub(crate) fn syntax_tree(
12 db: &RootDatabase, 12 db: &RootDatabase,
13 file_id: FileId, 13 file_id: FileId,
14 text_range: Option<TextRange>, 14 text_range: Option<TextRange>,
15) -> String { 15) -> String {
16 let parse = db.parse(file_id); 16 let parse = db.parse(file_id);
17 if let Some(text_range) = text_range { 17 if let Some(text_range) = text_range {
18 let node = match algo::find_covering_element(parse.tree().syntax(), text_range) { 18 let node = match algo::find_covering_element(parse.tree().syntax(), text_range) {
19 NodeOrToken::Node(node) => node, 19 NodeOrToken::Node(node) => node,
20 NodeOrToken::Token(token) => { 20 NodeOrToken::Token(token) => {
21 if let Some(tree) = syntax_tree_for_string(&token, text_range) { 21 if let Some(tree) = syntax_tree_for_string(&token, text_range) {
22 return tree; 22 return tree;
23 } 23 }
24 token.parent() 24 token.parent()
25 } 25 }
26 }; 26 };
27 27
28 format!("{:#?}", node) 28 format!("{:#?}", node)
29 } else { 29 } else {
30 format!("{:#?}", parse.tree().syntax()) 30 format!("{:#?}", parse.tree().syntax())
31 } 31 }
32} 32}
33 33
34/// Attempts parsing the selected contents of a string literal 34/// Attempts parsing the selected contents of a string literal
35/// as rust syntax and returns its syntax tree 35/// as rust syntax and returns its syntax tree
36fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> { 36fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> {
37 // When the range is inside a string 37 // When the range is inside a string
38 // we'll attempt parsing it as rust syntax 38 // we'll attempt parsing it as rust syntax
39 // to provide the syntax tree of the contents of the string 39 // to provide the syntax tree of the contents of the string
40 match token.kind() { 40 match token.kind() {
41 STRING | RAW_STRING => syntax_tree_for_token(token, text_range), 41 STRING | RAW_STRING => syntax_tree_for_token(token, text_range),
42 _ => None, 42 _ => None,
43 } 43 }
44} 44}
45 45
46fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> { 46fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> {
47 // Range of the full node 47 // Range of the full node
48 let node_range = node.text_range(); 48 let node_range = node.text_range();
49 let text = node.text().to_string(); 49 let text = node.text().to_string();
50 50
51 // We start at some point inside the node 51 // We start at some point inside the node
52 // Either we have selected the whole string 52 // Either we have selected the whole string
53 // or our selection is inside it 53 // or our selection is inside it
54 let start = text_range.start() - node_range.start(); 54 let start = text_range.start() - node_range.start();
55 55
56 // how many characters we have selected 56 // how many characters we have selected
57 let len = text_range.len().to_usize(); 57 let len = text_range.len().to_usize();
58 58
59 let node_len = node_range.len().to_usize(); 59 let node_len = node_range.len().to_usize();
60 60
61 let start = start.to_usize(); 61 let start = start.to_usize();
62 62
63 // We want to cap our length 63 // We want to cap our length
64 let len = len.min(node_len); 64 let len = len.min(node_len);
65 65
66 // Ensure our slice is inside the actual string 66 // Ensure our slice is inside the actual string
67 let end = if start + len < text.len() { start + len } else { text.len() - start }; 67 let end = if start + len < text.len() { start + len } else { text.len() - start };
68 68
69 let text = &text[start..end]; 69 let text = &text[start..end];
70 70
71 // Remove possible extra string quotes from the start 71 // Remove possible extra string quotes from the start
72 // and the end of the string 72 // and the end of the string
73 let text = text 73 let text = text
74 .trim_start_matches('r') 74 .trim_start_matches('r')
75 .trim_start_matches('#') 75 .trim_start_matches('#')
76 .trim_start_matches('"') 76 .trim_start_matches('"')
77 .trim_end_matches('#') 77 .trim_end_matches('#')
78 .trim_end_matches('"') 78 .trim_end_matches('"')
79 .trim() 79 .trim()
80 // Remove custom markers 80 // Remove custom markers
81 .replace("<|>", ""); 81 .replace("<|>", "");
82 82
83 let parsed = SourceFile::parse(&text); 83 let parsed = SourceFile::parse(&text);
84 84
85 // If the "file" parsed without errors, 85 // If the "file" parsed without errors,
86 // return its syntax 86 // return its syntax
87 if parsed.errors().is_empty() { 87 if parsed.errors().is_empty() {
88 return Some(format!("{:#?}", parsed.tree().syntax())); 88 return Some(format!("{:#?}", parsed.tree().syntax()));
89 } 89 }
90 90
91 None 91 None
92} 92}
93 93
94#[cfg(test)] 94#[cfg(test)]
95mod tests { 95mod tests {
96 use test_utils::assert_eq_text; 96 use test_utils::assert_eq_text;
97 97
98 use crate::mock_analysis::{single_file, single_file_with_range}; 98 use crate::mock_analysis::{single_file, single_file_with_range};
99 99
100 #[test] 100 #[test]
101 fn test_syntax_tree_without_range() { 101 fn test_syntax_tree_without_range() {
102 // Basic syntax 102 // Basic syntax
103 let (analysis, file_id) = single_file(r#"fn foo() {}"#); 103 let (analysis, file_id) = single_file(r#"fn foo() {}"#);
104 let syn = analysis.syntax_tree(file_id, None).unwrap(); 104 let syn = analysis.syntax_tree(file_id, None).unwrap();
105 105
106 assert_eq_text!( 106 assert_eq_text!(
107 syn.trim(), 107 syn.trim(),
108 r#" 108 r#"
109SOURCE_FILE@[0; 11) 109SOURCE_FILE@[0; 11)
110 FN_DEF@[0; 11) 110 FN_DEF@[0; 11)
111 FN_KW@[0; 2) "fn" 111 FN_KW@[0; 2) "fn"
112 WHITESPACE@[2; 3) " " 112 WHITESPACE@[2; 3) " "
113 NAME@[3; 6) 113 NAME@[3; 6)
114 IDENT@[3; 6) "foo" 114 IDENT@[3; 6) "foo"
115 PARAM_LIST@[6; 8) 115 PARAM_LIST@[6; 8)
116 L_PAREN@[6; 7) "(" 116 L_PAREN@[6; 7) "("
117 R_PAREN@[7; 8) ")" 117 R_PAREN@[7; 8) ")"
118 WHITESPACE@[8; 9) " " 118 WHITESPACE@[8; 9) " "
119 BLOCK_EXPR@[9; 11) 119 BLOCK_EXPR@[9; 11)
120 BLOCK@[9; 11) 120 BLOCK@[9; 11)
121 L_CURLY@[9; 10) "{" 121 L_CURLY@[9; 10) "{"
122 R_CURLY@[10; 11) "}" 122 R_CURLY@[10; 11) "}"
123"# 123"#
124 .trim() 124 .trim()
125 ); 125 );
126 126
127 let (analysis, file_id) = single_file( 127 let (analysis, file_id) = single_file(
128 r#" 128 r#"
129fn test() { 129fn test() {
130 assert!(" 130 assert!("
131 fn foo() { 131 fn foo() {
132 } 132 }
133 ", ""); 133 ", "");
134}"# 134}"#
135 .trim(), 135 .trim(),
136 ); 136 );
137 let syn = analysis.syntax_tree(file_id, None).unwrap(); 137 let syn = analysis.syntax_tree(file_id, None).unwrap();
138 138
139 assert_eq_text!( 139 assert_eq_text!(
140 syn.trim(), 140 syn.trim(),
141 r#" 141 r#"
142SOURCE_FILE@[0; 60) 142SOURCE_FILE@[0; 60)
143 FN_DEF@[0; 60) 143 FN_DEF@[0; 60)
144 FN_KW@[0; 2) "fn" 144 FN_KW@[0; 2) "fn"
145 WHITESPACE@[2; 3) " " 145 WHITESPACE@[2; 3) " "
146 NAME@[3; 7) 146 NAME@[3; 7)
147 IDENT@[3; 7) "test" 147 IDENT@[3; 7) "test"
148 PARAM_LIST@[7; 9) 148 PARAM_LIST@[7; 9)
149 L_PAREN@[7; 8) "(" 149 L_PAREN@[7; 8) "("
150 R_PAREN@[8; 9) ")" 150 R_PAREN@[8; 9) ")"
151 WHITESPACE@[9; 10) " " 151 WHITESPACE@[9; 10) " "
152 BLOCK_EXPR@[10; 60) 152 BLOCK_EXPR@[10; 60)
153 BLOCK@[10; 60) 153 BLOCK@[10; 60)
154 L_CURLY@[10; 11) "{" 154 L_CURLY@[10; 11) "{"
155 WHITESPACE@[11; 16) "\n " 155 WHITESPACE@[11; 16) "\n "
156 EXPR_STMT@[16; 58) 156 EXPR_STMT@[16; 58)
157 MACRO_CALL@[16; 57) 157 MACRO_CALL@[16; 57)
158 PATH@[16; 22) 158 PATH@[16; 22)
159 PATH_SEGMENT@[16; 22) 159 PATH_SEGMENT@[16; 22)
160 NAME_REF@[16; 22) 160 NAME_REF@[16; 22)
161 IDENT@[16; 22) "assert" 161 IDENT@[16; 22) "assert"
162 EXCL@[22; 23) "!" 162 EXCL@[22; 23) "!"
163 TOKEN_TREE@[23; 57) 163 TOKEN_TREE@[23; 57)
164 L_PAREN@[23; 24) "(" 164 L_PAREN@[23; 24) "("
165 STRING@[24; 52) "\"\n fn foo() {\n ..." 165 STRING@[24; 52) "\"\n fn foo() {\n ..."
166 COMMA@[52; 53) "," 166 COMMA@[52; 53) ","
167 WHITESPACE@[53; 54) " " 167 WHITESPACE@[53; 54) " "
168 STRING@[54; 56) "\"\"" 168 STRING@[54; 56) "\"\""
169 R_PAREN@[56; 57) ")" 169 R_PAREN@[56; 57) ")"
170 SEMI@[57; 58) ";" 170 SEMI@[57; 58) ";"
171 WHITESPACE@[58; 59) "\n" 171 WHITESPACE@[58; 59) "\n"
172 R_CURLY@[59; 60) "}" 172 R_CURLY@[59; 60) "}"
173"# 173"#
174 .trim() 174 .trim()
175 ); 175 );
176 } 176 }
177 177
178 #[test] 178 #[test]
179 fn test_syntax_tree_with_range() { 179 fn test_syntax_tree_with_range() {
180 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); 180 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim());
181 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 181 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
182 182
183 assert_eq_text!( 183 assert_eq_text!(
184 syn.trim(), 184 syn.trim(),
185 r#" 185 r#"
186FN_DEF@[0; 11) 186FN_DEF@[0; 11)
187 FN_KW@[0; 2) "fn" 187 FN_KW@[0; 2) "fn"
188 WHITESPACE@[2; 3) " " 188 WHITESPACE@[2; 3) " "
189 NAME@[3; 6) 189 NAME@[3; 6)
190 IDENT@[3; 6) "foo" 190 IDENT@[3; 6) "foo"
191 PARAM_LIST@[6; 8) 191 PARAM_LIST@[6; 8)
192 L_PAREN@[6; 7) "(" 192 L_PAREN@[6; 7) "("
193 R_PAREN@[7; 8) ")" 193 R_PAREN@[7; 8) ")"
194 WHITESPACE@[8; 9) " " 194 WHITESPACE@[8; 9) " "
195 BLOCK_EXPR@[9; 11) 195 BLOCK_EXPR@[9; 11)
196 BLOCK@[9; 11) 196 BLOCK@[9; 11)
197 L_CURLY@[9; 10) "{" 197 L_CURLY@[9; 10) "{"
198 R_CURLY@[10; 11) "}" 198 R_CURLY@[10; 11) "}"
199"# 199"#
200 .trim() 200 .trim()
201 ); 201 );
202 202
203 let (analysis, range) = single_file_with_range( 203 let (analysis, range) = single_file_with_range(
204 r#"fn test() { 204 r#"fn test() {
205 <|>assert!(" 205 <|>assert!("
206 fn foo() { 206 fn foo() {
207 } 207 }
208 ", "");<|> 208 ", "");<|>
209}"# 209}"#
210 .trim(), 210 .trim(),
211 ); 211 );
212 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 212 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
213 213
214 assert_eq_text!( 214 assert_eq_text!(
215 syn.trim(), 215 syn.trim(),
216 r#" 216 r#"
217EXPR_STMT@[16; 58) 217EXPR_STMT@[16; 58)
218 MACRO_CALL@[16; 57) 218 MACRO_CALL@[16; 57)
219 PATH@[16; 22) 219 PATH@[16; 22)
220 PATH_SEGMENT@[16; 22) 220 PATH_SEGMENT@[16; 22)
221 NAME_REF@[16; 22) 221 NAME_REF@[16; 22)
222 IDENT@[16; 22) "assert" 222 IDENT@[16; 22) "assert"
223 EXCL@[22; 23) "!" 223 EXCL@[22; 23) "!"
224 TOKEN_TREE@[23; 57) 224 TOKEN_TREE@[23; 57)
225 L_PAREN@[23; 24) "(" 225 L_PAREN@[23; 24) "("
226 STRING@[24; 52) "\"\n fn foo() {\n ..." 226 STRING@[24; 52) "\"\n fn foo() {\n ..."
227 COMMA@[52; 53) "," 227 COMMA@[52; 53) ","
228 WHITESPACE@[53; 54) " " 228 WHITESPACE@[53; 54) " "
229 STRING@[54; 56) "\"\"" 229 STRING@[54; 56) "\"\""
230 R_PAREN@[56; 57) ")" 230 R_PAREN@[56; 57) ")"
231 SEMI@[57; 58) ";" 231 SEMI@[57; 58) ";"
232"# 232"#
233 .trim() 233 .trim()
234 ); 234 );
235 } 235 }
236 236
237 #[test] 237 #[test]
238 fn test_syntax_tree_inside_string() { 238 fn test_syntax_tree_inside_string() {
239 let (analysis, range) = single_file_with_range( 239 let (analysis, range) = single_file_with_range(
240 r#"fn test() { 240 r#"fn test() {
241 assert!(" 241 assert!("
242<|>fn foo() { 242<|>fn foo() {
243}<|> 243}<|>
244fn bar() { 244fn bar() {
245} 245}
246 ", ""); 246 ", "");
247}"# 247}"#
248 .trim(), 248 .trim(),
249 ); 249 );
250 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 250 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
251 assert_eq_text!( 251 assert_eq_text!(
252 syn.trim(), 252 syn.trim(),
253 r#" 253 r#"
254SOURCE_FILE@[0; 12) 254SOURCE_FILE@[0; 12)
255 FN_DEF@[0; 12) 255 FN_DEF@[0; 12)
256 FN_KW@[0; 2) "fn" 256 FN_KW@[0; 2) "fn"
257 WHITESPACE@[2; 3) " " 257 WHITESPACE@[2; 3) " "
258 NAME@[3; 6) 258 NAME@[3; 6)
259 IDENT@[3; 6) "foo" 259 IDENT@[3; 6) "foo"
260 PARAM_LIST@[6; 8) 260 PARAM_LIST@[6; 8)
261 L_PAREN@[6; 7) "(" 261 L_PAREN@[6; 7) "("
262 R_PAREN@[7; 8) ")" 262 R_PAREN@[7; 8) ")"
263 WHITESPACE@[8; 9) " " 263 WHITESPACE@[8; 9) " "
264 BLOCK_EXPR@[9; 12) 264 BLOCK_EXPR@[9; 12)
265 BLOCK@[9; 12) 265 BLOCK@[9; 12)
266 L_CURLY@[9; 10) "{" 266 L_CURLY@[9; 10) "{"
267 WHITESPACE@[10; 11) "\n" 267 WHITESPACE@[10; 11) "\n"
268 R_CURLY@[11; 12) "}" 268 R_CURLY@[11; 12) "}"
269"# 269"#
270 .trim() 270 .trim()
271 ); 271 );
272 272
273 // With a raw string 273 // With a raw string
274 let (analysis, range) = single_file_with_range( 274 let (analysis, range) = single_file_with_range(
275 r###"fn test() { 275 r###"fn test() {
276 assert!(r#" 276 assert!(r#"
277<|>fn foo() { 277<|>fn foo() {
278}<|> 278}<|>
279fn bar() { 279fn bar() {
280} 280}
281 "#, ""); 281 "#, "");
282}"### 282}"###
283 .trim(), 283 .trim(),
284 ); 284 );
285 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 285 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
286 assert_eq_text!( 286 assert_eq_text!(
287 syn.trim(), 287 syn.trim(),
288 r#" 288 r#"
289SOURCE_FILE@[0; 12) 289SOURCE_FILE@[0; 12)
290 FN_DEF@[0; 12) 290 FN_DEF@[0; 12)
291 FN_KW@[0; 2) "fn" 291 FN_KW@[0; 2) "fn"
292 WHITESPACE@[2; 3) " " 292 WHITESPACE@[2; 3) " "
293 NAME@[3; 6) 293 NAME@[3; 6)
294 IDENT@[3; 6) "foo" 294 IDENT@[3; 6) "foo"
295 PARAM_LIST@[6; 8) 295 PARAM_LIST@[6; 8)
296 L_PAREN@[6; 7) "(" 296 L_PAREN@[6; 7) "("
297 R_PAREN@[7; 8) ")" 297 R_PAREN@[7; 8) ")"
298 WHITESPACE@[8; 9) " " 298 WHITESPACE@[8; 9) " "
299 BLOCK_EXPR@[9; 12) 299 BLOCK_EXPR@[9; 12)
300 BLOCK@[9; 12) 300 BLOCK@[9; 12)
301 L_CURLY@[9; 10) "{" 301 L_CURLY@[9; 10) "{"
302 WHITESPACE@[10; 11) "\n" 302 WHITESPACE@[10; 11) "\n"
303 R_CURLY@[11; 12) "}" 303 R_CURLY@[11; 12) "}"
304"# 304"#
305 .trim() 305 .trim()
306 ); 306 );
307 307
308 // With a raw string 308 // With a raw string
309 let (analysis, range) = single_file_with_range( 309 let (analysis, range) = single_file_with_range(
310 r###"fn test() { 310 r###"fn test() {
311 assert!(r<|>#" 311 assert!(r<|>#"
312fn foo() { 312fn foo() {
313} 313}
314fn bar() { 314fn bar() {
315}"<|>#, ""); 315}"<|>#, "");
316}"### 316}"###
317 .trim(), 317 .trim(),
318 ); 318 );
319 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 319 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
320 assert_eq_text!( 320 assert_eq_text!(
321 syn.trim(), 321 syn.trim(),
322 r#" 322 r#"
323SOURCE_FILE@[0; 25) 323SOURCE_FILE@[0; 25)
324 FN_DEF@[0; 12) 324 FN_DEF@[0; 12)
325 FN_KW@[0; 2) "fn" 325 FN_KW@[0; 2) "fn"
326 WHITESPACE@[2; 3) " " 326 WHITESPACE@[2; 3) " "
327 NAME@[3; 6) 327 NAME@[3; 6)
328 IDENT@[3; 6) "foo" 328 IDENT@[3; 6) "foo"
329 PARAM_LIST@[6; 8) 329 PARAM_LIST@[6; 8)
330 L_PAREN@[6; 7) "(" 330 L_PAREN@[6; 7) "("
331 R_PAREN@[7; 8) ")" 331 R_PAREN@[7; 8) ")"
332 WHITESPACE@[8; 9) " " 332 WHITESPACE@[8; 9) " "
333 BLOCK_EXPR@[9; 12) 333 BLOCK_EXPR@[9; 12)
334 BLOCK@[9; 12) 334 BLOCK@[9; 12)
335 L_CURLY@[9; 10) "{" 335 L_CURLY@[9; 10) "{"
336 WHITESPACE@[10; 11) "\n" 336 WHITESPACE@[10; 11) "\n"
337 R_CURLY@[11; 12) "}" 337 R_CURLY@[11; 12) "}"
338 WHITESPACE@[12; 13) "\n" 338 WHITESPACE@[12; 13) "\n"
339 FN_DEF@[13; 25) 339 FN_DEF@[13; 25)
340 FN_KW@[13; 15) "fn" 340 FN_KW@[13; 15) "fn"
341 WHITESPACE@[15; 16) " " 341 WHITESPACE@[15; 16) " "
342 NAME@[16; 19) 342 NAME@[16; 19)
343 IDENT@[16; 19) "bar" 343 IDENT@[16; 19) "bar"
344 PARAM_LIST@[19; 21) 344 PARAM_LIST@[19; 21)
345 L_PAREN@[19; 20) "(" 345 L_PAREN@[19; 20) "("
346 R_PAREN@[20; 21) ")" 346 R_PAREN@[20; 21) ")"
347 WHITESPACE@[21; 22) " " 347 WHITESPACE@[21; 22) " "
348 BLOCK_EXPR@[22; 25) 348 BLOCK_EXPR@[22; 25)
349 BLOCK@[22; 25) 349 BLOCK@[22; 25)
350 L_CURLY@[22; 23) "{" 350 L_CURLY@[22; 23) "{"
351 WHITESPACE@[23; 24) "\n" 351 WHITESPACE@[23; 24) "\n"
352 R_CURLY@[24; 25) "}" 352 R_CURLY@[24; 25) "}"
353"# 353"#
354 .trim() 354 .trim()
355 ); 355 );
356 } 356 }
357} 357}