diff options
Diffstat (limited to 'crates/ide/src')
4 files changed, 124 insertions, 4 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index d9fc25d88..6aafd6fd5 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -68,7 +68,7 @@ pub(crate) fn highlight( | |||
68 | // When we leave a node, the we use it to flatten the highlighted ranges. | 68 | // When we leave a node, the we use it to flatten the highlighted ranges. |
69 | let mut stack = HighlightedRangeStack::new(); | 69 | let mut stack = HighlightedRangeStack::new(); |
70 | 70 | ||
71 | let mut current_macro_call: Option<ast::MacroCall> = None; | 71 | let mut current_macro_call: Option<(ast::MacroCall, Option<MacroMatcherParseState>)> = None; |
72 | let mut format_string: Option<SyntaxElement> = None; | 72 | let mut format_string: Option<SyntaxElement> = None; |
73 | 73 | ||
74 | // Walk all nodes, keeping track of whether we are inside a macro or not. | 74 | // Walk all nodes, keeping track of whether we are inside a macro or not. |
@@ -92,7 +92,6 @@ pub(crate) fn highlight( | |||
92 | // Track "inside macro" state | 92 | // Track "inside macro" state |
93 | match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { | 93 | match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { |
94 | WalkEvent::Enter(Some(mc)) => { | 94 | WalkEvent::Enter(Some(mc)) => { |
95 | current_macro_call = Some(mc.clone()); | ||
96 | if let Some(range) = macro_call_range(&mc) { | 95 | if let Some(range) = macro_call_range(&mc) { |
97 | stack.add(HighlightedRange { | 96 | stack.add(HighlightedRange { |
98 | range, | 97 | range, |
@@ -100,7 +99,9 @@ pub(crate) fn highlight( | |||
100 | binding_hash: None, | 99 | binding_hash: None, |
101 | }); | 100 | }); |
102 | } | 101 | } |
102 | let mut is_macro_rules = None; | ||
103 | if let Some(name) = mc.is_macro_rules() { | 103 | if let Some(name) = mc.is_macro_rules() { |
104 | is_macro_rules = Some(MacroMatcherParseState::new()); | ||
104 | if let Some((highlight, binding_hash)) = highlight_element( | 105 | if let Some((highlight, binding_hash)) = highlight_element( |
105 | &sema, | 106 | &sema, |
106 | &mut bindings_shadow_count, | 107 | &mut bindings_shadow_count, |
@@ -114,10 +115,11 @@ pub(crate) fn highlight( | |||
114 | }); | 115 | }); |
115 | } | 116 | } |
116 | } | 117 | } |
118 | current_macro_call = Some((mc.clone(), is_macro_rules)); | ||
117 | continue; | 119 | continue; |
118 | } | 120 | } |
119 | WalkEvent::Leave(Some(mc)) => { | 121 | WalkEvent::Leave(Some(mc)) => { |
120 | assert!(current_macro_call == Some(mc)); | 122 | assert!(current_macro_call.map(|it| it.0) == Some(mc)); |
121 | current_macro_call = None; | 123 | current_macro_call = None; |
122 | format_string = None; | 124 | format_string = None; |
123 | } | 125 | } |
@@ -146,6 +148,20 @@ pub(crate) fn highlight( | |||
146 | WalkEvent::Leave(_) => continue, | 148 | WalkEvent::Leave(_) => continue, |
147 | }; | 149 | }; |
148 | 150 | ||
151 | // check if in matcher part of a macro_rules rule | ||
152 | if let Some((_, Some(ref mut state))) = current_macro_call { | ||
153 | if let Some(tok) = element.as_token() { | ||
154 | if matches!( | ||
155 | update_macro_rules_state(tok, state), | ||
156 | RuleState::Matcher | RuleState::Expander | ||
157 | ) { | ||
158 | if skip_metavariables(element.clone()) { | ||
159 | continue; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
149 | let range = element.text_range(); | 165 | let range = element.text_range(); |
150 | 166 | ||
151 | let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { | 167 | let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { |
@@ -918,3 +934,99 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas | |||
918 | _ => default.into(), | 934 | _ => default.into(), |
919 | } | 935 | } |
920 | } | 936 | } |
937 | |||
938 | struct MacroMatcherParseState { | ||
939 | /// Opening and corresponding closing bracket of the matcher or expander of the current rule | ||
940 | paren_ty: Option<(SyntaxKind, SyntaxKind)>, | ||
941 | paren_level: usize, | ||
942 | rule_state: RuleState, | ||
943 | /// Whether we are inside the outer `{` `}` macro block that holds the rules | ||
944 | in_invoc_body: bool, | ||
945 | } | ||
946 | |||
947 | impl MacroMatcherParseState { | ||
948 | fn new() -> Self { | ||
949 | MacroMatcherParseState { | ||
950 | paren_ty: None, | ||
951 | paren_level: 0, | ||
952 | in_invoc_body: false, | ||
953 | rule_state: RuleState::None, | ||
954 | } | ||
955 | } | ||
956 | } | ||
957 | |||
958 | #[derive(Copy, Clone, PartialEq)] | ||
959 | enum RuleState { | ||
960 | Matcher, | ||
961 | Expander, | ||
962 | Between, | ||
963 | None, | ||
964 | } | ||
965 | |||
966 | impl RuleState { | ||
967 | fn transition(&mut self) { | ||
968 | *self = match self { | ||
969 | RuleState::Matcher => RuleState::Between, | ||
970 | RuleState::Expander => RuleState::None, | ||
971 | RuleState::Between => RuleState::Expander, | ||
972 | RuleState::None => RuleState::Matcher, | ||
973 | }; | ||
974 | } | ||
975 | } | ||
976 | |||
977 | fn update_macro_rules_state(tok: &SyntaxToken, state: &mut MacroMatcherParseState) -> RuleState { | ||
978 | if !state.in_invoc_body { | ||
979 | if tok.kind() == T!['{'] { | ||
980 | state.in_invoc_body = true; | ||
981 | } | ||
982 | return state.rule_state; | ||
983 | } | ||
984 | |||
985 | match state.paren_ty { | ||
986 | Some((open, close)) => { | ||
987 | if tok.kind() == open { | ||
988 | state.paren_level += 1; | ||
989 | } else if tok.kind() == close { | ||
990 | state.paren_level -= 1; | ||
991 | if state.paren_level == 0 { | ||
992 | let res = state.rule_state; | ||
993 | state.rule_state.transition(); | ||
994 | state.paren_ty = None; | ||
995 | return res; | ||
996 | } | ||
997 | } | ||
998 | } | ||
999 | None => { | ||
1000 | match tok.kind() { | ||
1001 | T!['('] => { | ||
1002 | state.paren_ty = Some((T!['('], T![')'])); | ||
1003 | } | ||
1004 | T!['{'] => { | ||
1005 | state.paren_ty = Some((T!['{'], T!['}'])); | ||
1006 | } | ||
1007 | T!['['] => { | ||
1008 | state.paren_ty = Some((T!['['], T![']'])); | ||
1009 | } | ||
1010 | _ => (), | ||
1011 | } | ||
1012 | if state.paren_ty.is_some() { | ||
1013 | state.paren_level = 1; | ||
1014 | state.rule_state.transition(); | ||
1015 | } | ||
1016 | } | ||
1017 | } | ||
1018 | state.rule_state | ||
1019 | } | ||
1020 | |||
1021 | fn skip_metavariables(element: SyntaxElement) -> bool { | ||
1022 | let tok = match element.as_token() { | ||
1023 | Some(tok) => tok, | ||
1024 | None => return false, | ||
1025 | }; | ||
1026 | let is_fragment = || tok.prev_token().map(|tok| tok.kind()) == Some(T![$]); | ||
1027 | match tok.kind() { | ||
1028 | IDENT if is_fragment() => true, | ||
1029 | kind if kind.is_keyword() && is_fragment() => true, | ||
1030 | _ => false, | ||
1031 | } | ||
1032 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 1b681b2c6..43f1b32fd 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -37,7 +37,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
37 | </style> | 37 | </style> |
38 | <pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span> | 38 | <pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span> |
39 | <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">(</span><span class="punctuation">{</span> | 39 | <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">(</span><span class="punctuation">{</span> |
40 | <span class="punctuation">$</span><span class="keyword">crate</span><span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span><span class="keyword">crate</span><span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> | 40 | <span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> |
41 | <span class="punctuation">}</span><span class="punctuation">)</span> | 41 | <span class="punctuation">}</span><span class="punctuation">)</span> |
42 | <span class="punctuation">}</span> | 42 | <span class="punctuation">}</span> |
43 | #[rustc_builtin_macro] | 43 | #[rustc_builtin_macro] |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 1d8a3c404..4f47e1be9 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -115,6 +115,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
115 | <span class="punctuation">}</span> | 115 | <span class="punctuation">}</span> |
116 | <span class="punctuation">}</span> | 116 | <span class="punctuation">}</span> |
117 | 117 | ||
118 | <span class="macro">macro_rules!</span> <span class="macro declaration">keyword_frag</span> <span class="punctuation">{</span> | ||
119 | <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">:</span>ty<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">)</span> | ||
120 | <span class="punctuation">}</span> | ||
121 | |||
118 | <span class="comment">// comment</span> | 122 | <span class="comment">// comment</span> |
119 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 123 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
120 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> | 124 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 694c4b7fa..126363b8b 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -89,6 +89,10 @@ macro_rules! noop { | |||
89 | } | 89 | } |
90 | } | 90 | } |
91 | 91 | ||
92 | macro_rules! keyword_frag { | ||
93 | ($type:ty) => ($type) | ||
94 | } | ||
95 | |||
92 | // comment | 96 | // comment |
93 | fn main() { | 97 | fn main() { |
94 | println!("Hello, {}!", 92); | 98 | println!("Hello, {}!", 92); |