diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references.rs | 154 | ||||
-rw-r--r-- | crates/ra_ide_api/src/syntax_tree.rs | 714 |
3 files changed, 443 insertions, 429 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 514dcaf96..e90fbd428 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -466,7 +466,7 @@ impl Analysis { | |||
466 | &self, | 466 | &self, |
467 | position: FilePosition, | 467 | position: FilePosition, |
468 | ) -> Cancelable<Option<ReferenceSearchResult>> { | 468 | ) -> Cancelable<Option<ReferenceSearchResult>> { |
469 | self.with_db(|db| references::find_all_refs(db, position)) | 469 | self.with_db(|db| references::find_all_refs(db, position).map(|it| it.info)) |
470 | } | 470 | } |
471 | 471 | ||
472 | /// Returns a short text describing element at position. | 472 | /// Returns a short text describing element at position. |
@@ -536,7 +536,7 @@ impl Analysis { | |||
536 | &self, | 536 | &self, |
537 | position: FilePosition, | 537 | position: FilePosition, |
538 | new_name: &str, | 538 | new_name: &str, |
539 | ) -> Cancelable<Option<SourceChange>> { | 539 | ) -> Cancelable<Option<RangeInfo<SourceChange>>> { |
540 | self.with_db(|db| references::rename(db, position, new_name)) | 540 | self.with_db(|db| references::rename(db, position, new_name)) |
541 | } | 541 | } |
542 | 542 | ||
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index d8a00067f..5f1f0efc3 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode} | |||
4 | use relative_path::{RelativePath, RelativePathBuf}; | 4 | use relative_path::{RelativePath, RelativePathBuf}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, | 7 | db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, |
8 | SourceChange, SourceFileEdit, TextRange, | 8 | SourceChange, SourceFileEdit, TextRange, |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -48,9 +48,9 @@ impl IntoIterator for ReferenceSearchResult { | |||
48 | pub(crate) fn find_all_refs( | 48 | pub(crate) fn find_all_refs( |
49 | db: &RootDatabase, | 49 | db: &RootDatabase, |
50 | position: FilePosition, | 50 | position: FilePosition, |
51 | ) -> Option<ReferenceSearchResult> { | 51 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
52 | let parse = db.parse(position.file_id); | 52 | let parse = db.parse(position.file_id); |
53 | let (binding, analyzer) = find_binding(db, &parse.tree(), position)?; | 53 | let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?; |
54 | let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); | 54 | let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); |
55 | 55 | ||
56 | let references = analyzer | 56 | let references = analyzer |
@@ -59,24 +59,26 @@ pub(crate) fn find_all_refs( | |||
59 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) | 59 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) |
60 | .collect::<Vec<_>>(); | 60 | .collect::<Vec<_>>(); |
61 | 61 | ||
62 | return Some(ReferenceSearchResult { declaration, references }); | 62 | return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); |
63 | 63 | ||
64 | fn find_binding<'a>( | 64 | fn find_binding<'a>( |
65 | db: &RootDatabase, | 65 | db: &RootDatabase, |
66 | source_file: &SourceFile, | 66 | source_file: &SourceFile, |
67 | position: FilePosition, | 67 | position: FilePosition, |
68 | ) -> Option<(ast::BindPat, hir::SourceAnalyzer)> { | 68 | ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> { |
69 | let syntax = source_file.syntax(); | 69 | let syntax = source_file.syntax(); |
70 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { | 70 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { |
71 | let range = binding.syntax().text_range(); | ||
71 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); | 72 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); |
72 | return Some((binding, analyzer)); | 73 | return Some(RangeInfo::new(range, (binding, analyzer))); |
73 | }; | 74 | }; |
74 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; | 75 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; |
76 | let range = name_ref.syntax().text_range(); | ||
75 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | 77 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); |
76 | let resolved = analyzer.resolve_local_name(&name_ref)?; | 78 | let resolved = analyzer.resolve_local_name(&name_ref)?; |
77 | if let Either::A(ptr) = resolved.ptr() { | 79 | if let Either::A(ptr) = resolved.ptr() { |
78 | if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { | 80 | if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { |
79 | return Some((binding, analyzer)); | 81 | return Some(RangeInfo::new(range, (binding, analyzer))); |
80 | } | 82 | } |
81 | } | 83 | } |
82 | None | 84 | None |
@@ -87,12 +89,14 @@ pub(crate) fn rename( | |||
87 | db: &RootDatabase, | 89 | db: &RootDatabase, |
88 | position: FilePosition, | 90 | position: FilePosition, |
89 | new_name: &str, | 91 | new_name: &str, |
90 | ) -> Option<SourceChange> { | 92 | ) -> Option<RangeInfo<SourceChange>> { |
91 | let parse = db.parse(position.file_id); | 93 | let parse = db.parse(position.file_id); |
92 | if let Some((ast_name, ast_module)) = | 94 | if let Some((ast_name, ast_module)) = |
93 | find_name_and_module_at_offset(parse.tree().syntax(), position) | 95 | find_name_and_module_at_offset(parse.tree().syntax(), position) |
94 | { | 96 | { |
97 | let range = ast_name.syntax().text_range(); | ||
95 | rename_mod(db, &ast_name, &ast_module, position, new_name) | 98 | rename_mod(db, &ast_name, &ast_module, position, new_name) |
99 | .map(|info| RangeInfo::new(range, info)) | ||
96 | } else { | 100 | } else { |
97 | rename_reference(db, position, new_name) | 101 | rename_reference(db, position, new_name) |
98 | } | 102 | } |
@@ -107,7 +111,7 @@ fn find_name_and_module_at_offset( | |||
107 | Some((ast_name, ast_module)) | 111 | Some((ast_name, ast_module)) |
108 | } | 112 | } |
109 | 113 | ||
110 | fn source_edit_from_fileid_range( | 114 | fn source_edit_from_file_id_range( |
111 | file_id: FileId, | 115 | file_id: FileId, |
112 | range: TextRange, | 116 | range: TextRange, |
113 | new_name: &str, | 117 | new_name: &str, |
@@ -179,19 +183,19 @@ fn rename_reference( | |||
179 | db: &RootDatabase, | 183 | db: &RootDatabase, |
180 | position: FilePosition, | 184 | position: FilePosition, |
181 | new_name: &str, | 185 | new_name: &str, |
182 | ) -> Option<SourceChange> { | 186 | ) -> Option<RangeInfo<SourceChange>> { |
183 | let refs = find_all_refs(db, position)?; | 187 | let RangeInfo { range, info: refs } = find_all_refs(db, position)?; |
184 | 188 | ||
185 | let edit = refs | 189 | let edit = refs |
186 | .into_iter() | 190 | .into_iter() |
187 | .map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name)) | 191 | .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name)) |
188 | .collect::<Vec<_>>(); | 192 | .collect::<Vec<_>>(); |
189 | 193 | ||
190 | if edit.is_empty() { | 194 | if edit.is_empty() { |
191 | return None; | 195 | return None; |
192 | } | 196 | } |
193 | 197 | ||
194 | Some(SourceChange::source_file_edits("rename", edit)) | 198 | Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) |
195 | } | 199 | } |
196 | 200 | ||
197 | #[cfg(test)] | 201 | #[cfg(test)] |
@@ -342,38 +346,43 @@ mod tests { | |||
342 | let new_name = "foo2"; | 346 | let new_name = "foo2"; |
343 | let source_change = analysis.rename(position, new_name).unwrap(); | 347 | let source_change = analysis.rename(position, new_name).unwrap(); |
344 | assert_debug_snapshot!(&source_change, | 348 | assert_debug_snapshot!(&source_change, |
345 | @r#"Some( | 349 | @r###" |
346 | SourceChange { | 350 | Some( |
347 | label: "rename", | 351 | RangeInfo { |
348 | source_file_edits: [ | 352 | range: [4; 7), |
349 | SourceFileEdit { | 353 | info: SourceChange { |
350 | file_id: FileId( | 354 | label: "rename", |
351 | 2, | 355 | source_file_edits: [ |
352 | ), | 356 | SourceFileEdit { |
353 | edit: TextEdit { | 357 | file_id: FileId( |
354 | atoms: [ | 358 | 2, |
355 | AtomTextEdit { | 359 | ), |
356 | delete: [4; 7), | 360 | edit: TextEdit { |
357 | insert: "foo2", | 361 | atoms: [ |
362 | AtomTextEdit { | ||
363 | delete: [4; 7), | ||
364 | insert: "foo2", | ||
365 | }, | ||
366 | ], | ||
367 | }, | ||
368 | }, | ||
369 | ], | ||
370 | file_system_edits: [ | ||
371 | MoveFile { | ||
372 | src: FileId( | ||
373 | 3, | ||
374 | ), | ||
375 | dst_source_root: SourceRootId( | ||
376 | 0, | ||
377 | ), | ||
378 | dst_path: "bar/foo2.rs", | ||
358 | }, | 379 | }, |
359 | ], | 380 | ], |
381 | cursor_position: None, | ||
360 | }, | 382 | }, |
361 | }, | 383 | }, |
362 | ], | 384 | ) |
363 | file_system_edits: [ | 385 | "###); |
364 | MoveFile { | ||
365 | src: FileId( | ||
366 | 3, | ||
367 | ), | ||
368 | dst_source_root: SourceRootId( | ||
369 | 0, | ||
370 | ), | ||
371 | dst_path: "bar/foo2.rs", | ||
372 | }, | ||
373 | ], | ||
374 | cursor_position: None, | ||
375 | }, | ||
376 | )"#); | ||
377 | } | 386 | } |
378 | 387 | ||
379 | #[test] | 388 | #[test] |
@@ -389,38 +398,43 @@ mod tests { | |||
389 | let new_name = "foo2"; | 398 | let new_name = "foo2"; |
390 | let source_change = analysis.rename(position, new_name).unwrap(); | 399 | let source_change = analysis.rename(position, new_name).unwrap(); |
391 | assert_debug_snapshot!(&source_change, | 400 | assert_debug_snapshot!(&source_change, |
392 | @r###"Some( | 401 | @r###" |
393 | SourceChange { | 402 | Some( |
394 | label: "rename", | 403 | RangeInfo { |
395 | source_file_edits: [ | 404 | range: [4; 7), |
396 | SourceFileEdit { | 405 | info: SourceChange { |
397 | file_id: FileId( | 406 | label: "rename", |
398 | 1, | 407 | source_file_edits: [ |
399 | ), | 408 | SourceFileEdit { |
400 | edit: TextEdit { | 409 | file_id: FileId( |
401 | atoms: [ | 410 | 1, |
402 | AtomTextEdit { | 411 | ), |
403 | delete: [4; 7), | 412 | edit: TextEdit { |
404 | insert: "foo2", | 413 | atoms: [ |
414 | AtomTextEdit { | ||
415 | delete: [4; 7), | ||
416 | insert: "foo2", | ||
417 | }, | ||
418 | ], | ||
419 | }, | ||
420 | }, | ||
421 | ], | ||
422 | file_system_edits: [ | ||
423 | MoveFile { | ||
424 | src: FileId( | ||
425 | 2, | ||
426 | ), | ||
427 | dst_source_root: SourceRootId( | ||
428 | 0, | ||
429 | ), | ||
430 | dst_path: "foo2/mod.rs", | ||
405 | }, | 431 | }, |
406 | ], | 432 | ], |
433 | cursor_position: None, | ||
407 | }, | 434 | }, |
408 | }, | 435 | }, |
409 | ], | 436 | ) |
410 | file_system_edits: [ | 437 | "### |
411 | MoveFile { | ||
412 | src: FileId( | ||
413 | 2, | ||
414 | ), | ||
415 | dst_source_root: SourceRootId( | ||
416 | 0, | ||
417 | ), | ||
418 | dst_path: "foo2/mod.rs", | ||
419 | }, | ||
420 | ], | ||
421 | cursor_position: None, | ||
422 | }, | ||
423 | )"### | ||
424 | ); | 438 | ); |
425 | } | 439 | } |
426 | 440 | ||
@@ -430,7 +444,7 @@ mod tests { | |||
430 | let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); | 444 | let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); |
431 | let mut file_id: Option<FileId> = None; | 445 | let mut file_id: Option<FileId> = None; |
432 | if let Some(change) = source_change { | 446 | if let Some(change) = source_change { |
433 | for edit in change.source_file_edits { | 447 | for edit in change.info.source_file_edits { |
434 | file_id = Some(edit.file_id); | 448 | file_id = Some(edit.file_id); |
435 | for atom in edit.edit.as_atoms() { | 449 | for atom in edit.edit.as_atoms() { |
436 | text_edit_builder.replace(atom.delete, atom.insert.clone()); | 450 | text_edit_builder.replace(atom.delete, atom.insert.clone()); |
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 @@ | |||
1 | use crate::db::RootDatabase; | 1 | use crate::db::RootDatabase; |
2 | use ra_db::SourceDatabase; | 2 | use ra_db::SourceDatabase; |
3 | use ra_syntax::{ | 3 | use 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 | ||
9 | pub use ra_db::FileId; | 9 | pub use ra_db::FileId; |
10 | 10 | ||
11 | pub(crate) fn syntax_tree( | 11 | pub(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 |
36 | fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> { | 36 | fn 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 | ||
46 | fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> { | 46 | fn 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)] |
95 | mod tests { | 95 | mod 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#" |
109 | SOURCE_FILE@[0; 11) | 109 | SOURCE_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#" |
129 | fn test() { | 129 | fn 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#" |
142 | SOURCE_FILE@[0; 60) | 142 | SOURCE_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#" |
186 | FN_DEF@[0; 11) | 186 | FN_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#" |
217 | EXPR_STMT@[16; 58) | 217 | EXPR_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 | }<|> |
244 | fn bar() { | 244 | fn 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#" |
254 | SOURCE_FILE@[0; 12) | 254 | SOURCE_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 | }<|> |
279 | fn bar() { | 279 | fn 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#" |
289 | SOURCE_FILE@[0; 12) | 289 | SOURCE_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<|>#" |
312 | fn foo() { | 312 | fn foo() { |
313 | } | 313 | } |
314 | fn bar() { | 314 | fn 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#" |
323 | SOURCE_FILE@[0; 25) | 323 | SOURCE_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 | } |