1
- mod tags ;
1
+ mod format ;
2
2
mod html;
3
3
mod injection;
4
+ mod macro_rules;
5
+ mod tags;
4
6
#[ cfg( test) ]
5
7
mod tests;
6
8
@@ -17,9 +19,11 @@ use syntax::{
17
19
SyntaxNode , SyntaxToken , TextRange , WalkEvent , T ,
18
20
} ;
19
21
20
- use crate :: FileId ;
22
+ use crate :: {
23
+ syntax_highlighting:: { format:: FormatStringHighlighter , macro_rules:: MacroRulesHighlighter } ,
24
+ FileId ,
25
+ } ;
21
26
22
- use ast:: FormatSpecifier ;
23
27
pub ( crate ) use html:: highlight_as_html;
24
28
pub use tags:: { Highlight , HighlightModifier , HighlightModifiers , HighlightTag } ;
25
29
@@ -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 ;
72
- let mut format_string: Option < SyntaxElement > = None ;
75
+ let mut current_macro_call: Option < ast:: MacroCall > = None ;
76
+ let mut format_string_highlighter = FormatStringHighlighter :: default ( ) ;
77
+ let mut macro_rules_highlighter = MacroRulesHighlighter :: default ( ) ;
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
- format_string = None ;
128
+ format_string_highlighter = FormatStringHighlighter :: default ( ) ;
129
+ macro_rules_highlighter = MacroRulesHighlighter :: default ( ) ;
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 {
@@ -173,29 +164,9 @@ pub(crate) fn highlight(
173
164
let token = sema. descend_into_macros ( token. clone ( ) ) ;
174
165
let parent = token. parent ( ) ;
175
166
176
- // Check if macro takes a format string and remember it for highlighting later.
177
- // The macros that accept a format string expand to a compiler builtin macros
178
- // `format_args` and `format_args_nl`.
179
- if let Some ( name) = parent
180
- . parent ( )
181
- . and_then ( ast:: MacroCall :: cast)
182
- . and_then ( |mc| mc. path ( ) )
183
- . and_then ( |p| p. segment ( ) )
184
- . and_then ( |s| s. name_ref ( ) )
185
- {
186
- match name. text ( ) . as_str ( ) {
187
- "format_args" | "format_args_nl" => {
188
- format_string = parent
189
- . children_with_tokens ( )
190
- . filter ( |t| t. kind ( ) != WHITESPACE )
191
- . nth ( 1 )
192
- . filter ( |e| {
193
- ast:: String :: can_cast ( e. kind ( ) )
194
- || ast:: RawString :: can_cast ( e. kind ( ) )
195
- } )
196
- }
197
- _ => { }
198
- }
167
+ format_string_highlighter. check_for_format_string ( & parent) ;
168
+ if let Some ( tok) = element. as_token ( ) {
169
+ macro_rules_highlighter. advance ( tok) ;
199
170
}
200
171
201
172
// We only care Name and Name_ref
@@ -214,31 +185,20 @@ pub(crate) fn highlight(
214
185
}
215
186
}
216
187
217
- let is_format_string = format_string. as_ref ( ) == Some ( & element_to_highlight) ;
218
-
219
188
if let Some ( ( highlight, binding_hash) ) = highlight_element (
220
189
& sema,
221
190
& mut bindings_shadow_count,
222
191
syntactic_name_ref_highlighting,
223
192
element_to_highlight. clone ( ) ,
224
193
) {
225
- 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
+
226
198
if let Some ( string) =
227
199
element_to_highlight. as_token ( ) . cloned ( ) . and_then ( ast:: String :: cast)
228
200
{
229
- if is_format_string {
230
- stack. push ( ) ;
231
- string. lex_format_specifier ( |piece_range, kind| {
232
- if let Some ( highlight) = highlight_format_specifier ( kind) {
233
- stack. add ( HighlightedRange {
234
- range : piece_range + range. start ( ) ,
235
- highlight : highlight. into ( ) ,
236
- binding_hash : None ,
237
- } ) ;
238
- }
239
- } ) ;
240
- stack. pop ( ) ;
241
- }
201
+ format_string_highlighter. highlight_format_string ( & mut stack, & string, range) ;
242
202
// Highlight escape sequences
243
203
if let Some ( char_ranges) = string. char_ranges ( ) {
244
204
stack. push ( ) ;
@@ -256,19 +216,7 @@ pub(crate) fn highlight(
256
216
} else if let Some ( string) =
257
217
element_to_highlight. as_token ( ) . cloned ( ) . and_then ( ast:: RawString :: cast)
258
218
{
259
- if is_format_string {
260
- stack. push ( ) ;
261
- string. lex_format_specifier ( |piece_range, kind| {
262
- if let Some ( highlight) = highlight_format_specifier ( kind) {
263
- stack. add ( HighlightedRange {
264
- range : piece_range + range. start ( ) ,
265
- highlight : highlight. into ( ) ,
266
- binding_hash : None ,
267
- } ) ;
268
- }
269
- } ) ;
270
- stack. pop ( ) ;
271
- }
219
+ format_string_highlighter. highlight_format_string ( & mut stack, & string, range) ;
272
220
}
273
221
}
274
222
}
@@ -436,24 +384,6 @@ impl HighlightedRangeStack {
436
384
}
437
385
}
438
386
439
- fn highlight_format_specifier ( kind : FormatSpecifier ) -> Option < HighlightTag > {
440
- Some ( match kind {
441
- FormatSpecifier :: Open
442
- | FormatSpecifier :: Close
443
- | FormatSpecifier :: Colon
444
- | FormatSpecifier :: Fill
445
- | FormatSpecifier :: Align
446
- | FormatSpecifier :: Sign
447
- | FormatSpecifier :: NumberSign
448
- | FormatSpecifier :: DollarSign
449
- | FormatSpecifier :: Dot
450
- | FormatSpecifier :: Asterisk
451
- | FormatSpecifier :: QuestionMark => HighlightTag :: FormatSpecifier ,
452
- FormatSpecifier :: Integer | FormatSpecifier :: Zero => HighlightTag :: NumericLiteral ,
453
- FormatSpecifier :: Identifier => HighlightTag :: Local ,
454
- } )
455
- }
456
-
457
387
fn macro_call_range ( macro_call : & ast:: MacroCall ) -> Option < TextRange > {
458
388
let path = macro_call. path ( ) ?;
459
389
let name_ref = path. segment ( ) ?. name_ref ( ) ?;
@@ -934,99 +864,3 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
934
864
_ => default. into ( ) ,
935
865
}
936
866
}
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
- }
0 commit comments