1
1
mod format;
2
2
mod html;
3
3
mod injection;
4
+ mod macro_rules;
4
5
mod tags;
5
6
#[ cfg( test) ]
6
7
mod tests;
@@ -18,7 +19,10 @@ use syntax::{
18
19
SyntaxNode , SyntaxToken , TextRange , WalkEvent , T ,
19
20
} ;
20
21
21
- use crate :: { syntax_highlighting:: format:: FormatStringHighlighter , FileId } ;
22
+ use crate :: {
23
+ syntax_highlighting:: { format:: FormatStringHighlighter , macro_rules:: MacroRulesHighlighter } ,
24
+ FileId ,
25
+ } ;
22
26
23
27
pub ( crate ) use html:: highlight_as_html;
24
28
pub use tags:: { Highlight , HighlightModifier , HighlightModifiers , HighlightTag } ;
@@ -68,8 +72,9 @@ pub(crate) fn highlight(
68
72
// When we leave a node, the we use it to flatten the highlighted ranges.
69
73
let mut stack = HighlightedRangeStack :: new ( ) ;
70
74
71
- let mut current_macro_call: Option < ( ast:: MacroCall , Option < MacroMatcherParseState > ) > = None ;
75
+ let mut current_macro_call: Option < ast:: MacroCall > = None ;
72
76
let mut format_string_highlighter = FormatStringHighlighter :: default ( ) ;
77
+ let mut macro_rules_highlighter = MacroRulesHighlighter :: new ( ) ;
73
78
74
79
// Walk all nodes, keeping track of whether we are inside a macro or not.
75
80
// If in macro, expand it first and highlight the expanded code.
@@ -99,9 +104,8 @@ pub(crate) fn highlight(
99
104
binding_hash : None ,
100
105
} ) ;
101
106
}
102
- let mut is_macro_rules = None ;
103
107
if let Some ( name) = mc. is_macro_rules ( ) {
104
- is_macro_rules = Some ( MacroMatcherParseState :: new ( ) ) ;
108
+ macro_rules_highlighter . init ( ) ;
105
109
if let Some ( ( highlight, binding_hash) ) = highlight_element (
106
110
& sema,
107
111
& mut bindings_shadow_count,
@@ -115,13 +119,14 @@ pub(crate) fn highlight(
115
119
} ) ;
116
120
}
117
121
}
118
- current_macro_call = Some ( ( mc. clone ( ) , is_macro_rules ) ) ;
122
+ current_macro_call = Some ( mc. clone ( ) ) ;
119
123
continue ;
120
124
}
121
125
WalkEvent :: Leave ( Some ( mc) ) => {
122
- assert ! ( current_macro_call. map ( |it| it . 0 ) == Some ( mc) ) ;
126
+ assert ! ( current_macro_call == Some ( mc) ) ;
123
127
current_macro_call = None ;
124
128
format_string_highlighter. reset ( ) ;
129
+ macro_rules_highlighter. reset ( ) ;
125
130
}
126
131
_ => ( ) ,
127
132
}
@@ -148,20 +153,6 @@ pub(crate) fn highlight(
148
153
WalkEvent :: Leave ( _) => continue ,
149
154
} ;
150
155
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
-
165
156
let range = element. text_range ( ) ;
166
157
167
158
let element_to_highlight = if current_macro_call. is_some ( ) && element. kind ( ) != COMMENT {
@@ -174,6 +165,9 @@ pub(crate) fn highlight(
174
165
let parent = token. parent ( ) ;
175
166
176
167
format_string_highlighter. check_for_format_string ( & parent) ;
168
+ if let Some ( tok) = element. as_token ( ) {
169
+ macro_rules_highlighter. advance ( tok) ;
170
+ }
177
171
178
172
// We only care Name and Name_ref
179
173
match ( token. kind ( ) , parent. kind ( ) ) {
@@ -197,7 +191,10 @@ pub(crate) fn highlight(
197
191
syntactic_name_ref_highlighting,
198
192
element_to_highlight. clone ( ) ,
199
193
) {
200
- stack. add ( HighlightedRange { range, highlight, binding_hash } ) ;
194
+ if macro_rules_highlighter. highlight ( element_to_highlight. clone ( ) ) . is_none ( ) {
195
+ stack. add ( HighlightedRange { range, highlight, binding_hash } ) ;
196
+ }
197
+
201
198
if let Some ( string) =
202
199
element_to_highlight. as_token ( ) . cloned ( ) . and_then ( ast:: String :: cast)
203
200
{
@@ -867,99 +864,3 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
867
864
_ => default. into ( ) ,
868
865
}
869
866
}
870
-
871
- struct MacroMatcherParseState {
872
- /// Opening and corresponding closing bracket of the matcher or expander of the current rule
873
- paren_ty : Option < ( SyntaxKind , SyntaxKind ) > ,
874
- paren_level : usize ,
875
- rule_state : RuleState ,
876
- /// Whether we are inside the outer `{` `}` macro block that holds the rules
877
- in_invoc_body : bool ,
878
- }
879
-
880
- impl MacroMatcherParseState {
881
- fn new ( ) -> Self {
882
- MacroMatcherParseState {
883
- paren_ty : None ,
884
- paren_level : 0 ,
885
- in_invoc_body : false ,
886
- rule_state : RuleState :: None ,
887
- }
888
- }
889
- }
890
-
891
- #[ derive( Copy , Clone , PartialEq ) ]
892
- enum RuleState {
893
- Matcher ,
894
- Expander ,
895
- Between ,
896
- None ,
897
- }
898
-
899
- impl RuleState {
900
- fn transition ( & mut self ) {
901
- * self = match self {
902
- RuleState :: Matcher => RuleState :: Between ,
903
- RuleState :: Expander => RuleState :: None ,
904
- RuleState :: Between => RuleState :: Expander ,
905
- RuleState :: None => RuleState :: Matcher ,
906
- } ;
907
- }
908
- }
909
-
910
- fn update_macro_rules_state ( tok : & SyntaxToken , state : & mut MacroMatcherParseState ) -> RuleState {
911
- if !state. in_invoc_body {
912
- if tok. kind ( ) == T ! [ '{' ] {
913
- state. in_invoc_body = true ;
914
- }
915
- return state. rule_state ;
916
- }
917
-
918
- match state. paren_ty {
919
- Some ( ( open, close) ) => {
920
- if tok. kind ( ) == open {
921
- state. paren_level += 1 ;
922
- } else if tok. kind ( ) == close {
923
- state. paren_level -= 1 ;
924
- if state. paren_level == 0 {
925
- let res = state. rule_state ;
926
- state. rule_state . transition ( ) ;
927
- state. paren_ty = None ;
928
- return res;
929
- }
930
- }
931
- }
932
- None => {
933
- match tok. kind ( ) {
934
- T ! [ '(' ] => {
935
- state. paren_ty = Some ( ( T ! [ '(' ] , T ! [ ')' ] ) ) ;
936
- }
937
- T ! [ '{' ] => {
938
- state. paren_ty = Some ( ( T ! [ '{' ] , T ! [ '}' ] ) ) ;
939
- }
940
- T ! [ '[' ] => {
941
- state. paren_ty = Some ( ( T ! [ '[' ] , T ! [ ']' ] ) ) ;
942
- }
943
- _ => ( ) ,
944
- }
945
- if state. paren_ty . is_some ( ) {
946
- state. paren_level = 1 ;
947
- state. rule_state . transition ( ) ;
948
- }
949
- }
950
- }
951
- state. rule_state
952
- }
953
-
954
- fn skip_metavariables ( element : SyntaxElement ) -> bool {
955
- let tok = match element. as_token ( ) {
956
- Some ( tok) => tok,
957
- None => return false ,
958
- } ;
959
- let is_fragment = || tok. prev_token ( ) . map ( |tok| tok. kind ( ) ) == Some ( T ! [ $] ) ;
960
- match tok. kind ( ) {
961
- IDENT if is_fragment ( ) => true ,
962
- kind if kind. is_keyword ( ) && is_fragment ( ) => true ,
963
- _ => false ,
964
- }
965
- }
0 commit comments