1- mod tags ;
1+ mod format ;
22mod html;
33mod injection;
4+ mod macro_rules;
5+ mod tags;
46#[ cfg( test) ]
57mod tests;
68
@@ -17,9 +19,11 @@ use syntax::{
1719 SyntaxNode , SyntaxToken , TextRange , WalkEvent , T ,
1820} ;
1921
20- use crate :: FileId ;
22+ use crate :: {
23+ syntax_highlighting:: { format:: FormatStringHighlighter , macro_rules:: MacroRulesHighlighter } ,
24+ FileId ,
25+ } ;
2126
22- use ast:: FormatSpecifier ;
2327pub ( crate ) use html:: highlight_as_html;
2428pub use tags:: { Highlight , HighlightModifier , HighlightModifiers , HighlightTag } ;
2529
@@ -68,8 +72,9 @@ pub(crate) fn highlight(
6872 // When we leave a node, the we use it to flatten the highlighted ranges.
6973 let mut stack = HighlightedRangeStack :: new ( ) ;
7074
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 ( ) ;
7378
7479 // Walk all nodes, keeping track of whether we are inside a macro or not.
7580 // If in macro, expand it first and highlight the expanded code.
@@ -99,9 +104,8 @@ pub(crate) fn highlight(
99104 binding_hash : None ,
100105 } ) ;
101106 }
102- let mut is_macro_rules = None ;
103107 if let Some ( name) = mc. is_macro_rules ( ) {
104- is_macro_rules = Some ( MacroMatcherParseState :: new ( ) ) ;
108+ macro_rules_highlighter . init ( ) ;
105109 if let Some ( ( highlight, binding_hash) ) = highlight_element (
106110 & sema,
107111 & mut bindings_shadow_count,
@@ -115,13 +119,14 @@ pub(crate) fn highlight(
115119 } ) ;
116120 }
117121 }
118- current_macro_call = Some ( ( mc. clone ( ) , is_macro_rules ) ) ;
122+ current_macro_call = Some ( mc. clone ( ) ) ;
119123 continue ;
120124 }
121125 WalkEvent :: Leave ( Some ( mc) ) => {
122- assert ! ( current_macro_call. map ( |it| it . 0 ) == Some ( mc) ) ;
126+ assert ! ( current_macro_call == Some ( mc) ) ;
123127 current_macro_call = None ;
124- format_string = None ;
128+ format_string_highlighter = FormatStringHighlighter :: default ( ) ;
129+ macro_rules_highlighter = MacroRulesHighlighter :: default ( ) ;
125130 }
126131 _ => ( ) ,
127132 }
@@ -148,20 +153,6 @@ pub(crate) fn highlight(
148153 WalkEvent :: Leave ( _) => continue ,
149154 } ;
150155
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-
165156 let range = element. text_range ( ) ;
166157
167158 let element_to_highlight = if current_macro_call. is_some ( ) && element. kind ( ) != COMMENT {
@@ -173,29 +164,9 @@ pub(crate) fn highlight(
173164 let token = sema. descend_into_macros ( token. clone ( ) ) ;
174165 let parent = token. parent ( ) ;
175166
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) ;
199170 }
200171
201172 // We only care Name and Name_ref
@@ -214,31 +185,20 @@ pub(crate) fn highlight(
214185 }
215186 }
216187
217- let is_format_string = format_string. as_ref ( ) == Some ( & element_to_highlight) ;
218-
219188 if let Some ( ( highlight, binding_hash) ) = highlight_element (
220189 & sema,
221190 & mut bindings_shadow_count,
222191 syntactic_name_ref_highlighting,
223192 element_to_highlight. clone ( ) ,
224193 ) {
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+
226198 if let Some ( string) =
227199 element_to_highlight. as_token ( ) . cloned ( ) . and_then ( ast:: String :: cast)
228200 {
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) ;
242202 // Highlight escape sequences
243203 if let Some ( char_ranges) = string. char_ranges ( ) {
244204 stack. push ( ) ;
@@ -256,19 +216,7 @@ pub(crate) fn highlight(
256216 } else if let Some ( string) =
257217 element_to_highlight. as_token ( ) . cloned ( ) . and_then ( ast:: RawString :: cast)
258218 {
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) ;
272220 }
273221 }
274222 }
@@ -436,24 +384,6 @@ impl HighlightedRangeStack {
436384 }
437385}
438386
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-
457387fn macro_call_range ( macro_call : & ast:: MacroCall ) -> Option < TextRange > {
458388 let path = macro_call. path ( ) ?;
459389 let name_ref = path. segment ( ) ?. name_ref ( ) ?;
@@ -934,99 +864,3 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
934864 _ => default. into ( ) ,
935865 }
936866}
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