aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs109
1 files changed, 108 insertions, 1 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 @@
1use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
1use ra_editor::HighlightedRange; 2use ra_editor::HighlightedRange;
2use ra_db::SyntaxDatabase; 3use ra_db::SyntaxDatabase;
3 4
@@ -9,6 +10,112 @@ use crate::{
9pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 10pub(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
35fn 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
71struct MacroExpansion {
72 source_file: SourceFileNode,
73 ranges_map: Vec<(TextRange, TextRange)>,
74}
75
76impl 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)]
94mod 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}