diff options
-rw-r--r-- | crates/ra_analysis/src/syntax_highlighting.rs | 109 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 2 |
3 files changed, 113 insertions, 2 deletions
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs index 0bf19eea0..80f51a09c 100644 --- a/crates/ra_analysis/src/syntax_highlighting.rs +++ b/crates/ra_analysis/src/syntax_highlighting.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use ra_syntax::{ast, AstNode, SourceFileNode, TextRange}; | ||
1 | use ra_editor::HighlightedRange; | 2 | use ra_editor::HighlightedRange; |
2 | use ra_db::SyntaxDatabase; | 3 | use ra_db::SyntaxDatabase; |
3 | 4 | ||
@@ -9,6 +10,112 @@ use crate::{ | |||
9 | pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { | 10 | pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { |
10 | let source_file = db.source_file(file_id); | 11 | let source_file = db.source_file(file_id); |
11 | let mut res = ra_editor::highlight(&source_file); | 12 | let mut res = ra_editor::highlight(&source_file); |
12 | for node in source_file.syntax().descendants() {} | 13 | for macro_call in source_file |
14 | .syntax() | ||
15 | .descendants() | ||
16 | .filter_map(ast::MacroCall::cast) | ||
17 | { | ||
18 | if let Some(exp) = expand(db, file_id, macro_call) { | ||
19 | let mapped_ranges = ra_editor::highlight(exp.source_file()) | ||
20 | .into_iter() | ||
21 | .filter_map(|r| { | ||
22 | let mapped_range = exp.map_range_back(r.range)?; | ||
23 | let res = HighlightedRange { | ||
24 | range: mapped_range, | ||
25 | tag: r.tag, | ||
26 | }; | ||
27 | Some(res) | ||
28 | }); | ||
29 | res.extend(mapped_ranges); | ||
30 | } | ||
31 | } | ||
13 | Ok(res) | 32 | Ok(res) |
14 | } | 33 | } |
34 | |||
35 | fn expand( | ||
36 | _db: &RootDatabase, | ||
37 | _file_id: FileId, | ||
38 | macro_call: ast::MacroCall, | ||
39 | ) -> Option<MacroExpansion> { | ||
40 | let path = macro_call.path()?; | ||
41 | if path.qualifier().is_some() { | ||
42 | return None; | ||
43 | } | ||
44 | let name_ref = path.segment()?.name_ref()?; | ||
45 | if name_ref.text() != "ctry" { | ||
46 | return None; | ||
47 | } | ||
48 | |||
49 | let arg = macro_call.token_tree()?; | ||
50 | let text = format!( | ||
51 | r" | ||
52 | fn dummy() {{ | ||
53 | match {} {{ | ||
54 | None => return Ok(None), | ||
55 | Some(it) => it, | ||
56 | }} | ||
57 | }}", | ||
58 | arg.syntax().text() | ||
59 | ); | ||
60 | let file = SourceFileNode::parse(&text); | ||
61 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
62 | let match_arg = match_expr.expr()?; | ||
63 | let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())]; | ||
64 | let res = MacroExpansion { | ||
65 | source_file: file, | ||
66 | ranges_map, | ||
67 | }; | ||
68 | Some(res) | ||
69 | } | ||
70 | |||
71 | struct MacroExpansion { | ||
72 | source_file: SourceFileNode, | ||
73 | ranges_map: Vec<(TextRange, TextRange)>, | ||
74 | } | ||
75 | |||
76 | impl MacroExpansion { | ||
77 | fn source_file(&self) -> &SourceFileNode { | ||
78 | &self.source_file | ||
79 | } | ||
80 | fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
81 | for (s_range, t_range) in self.ranges_map.iter() { | ||
82 | if tgt_range.is_subrange(&t_range) { | ||
83 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
84 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
85 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
86 | return Some(src_range); | ||
87 | } | ||
88 | } | ||
89 | None | ||
90 | } | ||
91 | } | ||
92 | |||
93 | #[cfg(test)] | ||
94 | mod tests { | ||
95 | use crate::mock_analysis::single_file; | ||
96 | use test_utils::assert_eq_dbg; | ||
97 | |||
98 | #[test] | ||
99 | fn highlights_code_inside_macros() { | ||
100 | let (analysis, file_id) = single_file( | ||
101 | " | ||
102 | fn main() { | ||
103 | ctry!({ let x = 92; x}); | ||
104 | } | ||
105 | ", | ||
106 | ); | ||
107 | let highlights = analysis.highlight(file_id).unwrap(); | ||
108 | assert_eq_dbg( | ||
109 | r#"[HighlightedRange { range: [13; 15), tag: "keyword" }, | ||
110 | HighlightedRange { range: [16; 20), tag: "function" }, | ||
111 | HighlightedRange { range: [41; 45), tag: "text" }, | ||
112 | HighlightedRange { range: [49; 52), tag: "keyword" }, | ||
113 | HighlightedRange { range: [57; 59), tag: "literal" }, | ||
114 | HighlightedRange { range: [49; 52), tag: "keyword" }, | ||
115 | HighlightedRange { range: [53; 54), tag: "function" }, | ||
116 | HighlightedRange { range: [57; 59), tag: "literal" }, | ||
117 | HighlightedRange { range: [61; 62), tag: "text" }]"#, | ||
118 | &highlights, | ||
119 | ) | ||
120 | } | ||
121 | } | ||
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index b0d2c3e20..c5ac90a62 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1877,6 +1877,10 @@ impl<'a> MacroCall<'a> { | |||
1877 | pub fn token_tree(self) -> Option<TokenTree<'a>> { | 1877 | pub fn token_tree(self) -> Option<TokenTree<'a>> { |
1878 | super::child_opt(self) | 1878 | super::child_opt(self) |
1879 | } | 1879 | } |
1880 | |||
1881 | pub fn path(self) -> Option<Path<'a>> { | ||
1882 | super::child_opt(self) | ||
1883 | } | ||
1880 | } | 1884 | } |
1881 | 1885 | ||
1882 | // MatchArm | 1886 | // MatchArm |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 07b8433b2..aab4839a9 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -484,7 +484,7 @@ Grammar( | |||
484 | 484 | ||
485 | "Name": (), | 485 | "Name": (), |
486 | "NameRef": (), | 486 | "NameRef": (), |
487 | "MacroCall": ( options: [ "TokenTree" ] ), | 487 | "MacroCall": ( options: [ "TokenTree", "Path" ] ), |
488 | "Attr": ( options: [ ["value", "TokenTree"] ] ), | 488 | "Attr": ( options: [ ["value", "TokenTree"] ] ), |
489 | "TokenTree": (), | 489 | "TokenTree": (), |
490 | "TypeParamList": ( | 490 | "TypeParamList": ( |