@@ -13,11 +13,11 @@ mod html;
1313mod tests;
1414
1515use hir:: { InFile , Name , Semantics } ;
16- use ide_db:: { RootDatabase , SymbolKind } ;
16+ use ide_db:: RootDatabase ;
1717use rustc_hash:: FxHashMap ;
1818use syntax:: {
1919 ast:: { self , HasFormatSpecifier } ,
20- match_ast , AstNode , AstToken , Direction , NodeOrToken ,
20+ AstNode , AstToken , NodeOrToken ,
2121 SyntaxKind :: * ,
2222 SyntaxNode , TextRange , WalkEvent , T ,
2323} ;
@@ -100,7 +100,8 @@ pub struct HlRange {
100100// colon:: Emitted for the `:` token.
101101// comma:: Emitted for the `,` token.
102102// dot:: Emitted for the `.` token.
103- // Semi:: Emitted for the `;` token.
103+ // semi:: Emitted for the `;` token.
104+ // macroBang:: Emitted for the `!` token in macro calls.
104105//
105106// //-
106107//
@@ -209,107 +210,94 @@ fn traverse(
209210 // Walk all nodes, keeping track of whether we are inside a macro or not.
210211 // If in macro, expand it first and highlight the expanded code.
211212 for event in root. value . preorder_with_tokens ( ) {
212- let event_range = match & event {
213+ let range = match & event {
213214 WalkEvent :: Enter ( it) | WalkEvent :: Leave ( it) => it. text_range ( ) ,
214215 } ;
215216
216217 // Element outside of the viewport, no need to highlight
217- if range_to_highlight. intersect ( event_range ) . is_none ( ) {
218+ if range_to_highlight. intersect ( range ) . is_none ( ) {
218219 continue ;
219220 }
220221
222+ // set macro and attribute highlighting states
221223 match event. clone ( ) {
222- WalkEvent :: Enter ( NodeOrToken :: Node ( node) ) => {
223- match_ast ! {
224- match node {
225- ast:: MacroCall ( mcall) => {
226- if let Some ( range) = macro_call_range( & mcall) {
227- hl. add( HlRange {
228- range,
229- highlight: HlTag :: Symbol ( SymbolKind :: Macro ) . into( ) ,
230- binding_hash: None ,
231- } ) ;
232- }
233- current_macro_call = Some ( mcall) ;
234- continue ;
235- } ,
236- ast:: Macro ( mac) => {
237- macro_highlighter. init( ) ;
238- current_macro = Some ( mac) ;
239- continue ;
240- } ,
241- ast:: Item ( item) => {
242- if sema. is_attr_macro_call( & item) {
243- current_attr_call = Some ( item) ;
244- }
245- } ,
246- ast:: Attr ( __) => inside_attribute = true ,
247- _ => ( )
248- }
224+ WalkEvent :: Enter ( NodeOrToken :: Node ( node) ) => match ast:: Item :: cast ( node. clone ( ) ) {
225+ Some ( ast:: Item :: MacroCall ( mcall) ) => {
226+ current_macro_call = Some ( mcall) ;
227+ continue ;
249228 }
250- }
251- WalkEvent :: Leave ( NodeOrToken :: Node ( node) ) => {
252- match_ast ! {
253- match node {
254- ast:: MacroCall ( mcall) => {
255- assert_eq!( current_macro_call, Some ( mcall) ) ;
256- current_macro_call = None ;
257- } ,
258- ast:: Macro ( mac) => {
259- assert_eq!( current_macro, Some ( mac) ) ;
260- current_macro = None ;
261- macro_highlighter = MacroHighlighter :: default ( ) ;
262- } ,
263- ast:: Item ( item) => {
264- if current_attr_call == Some ( item) {
265- current_attr_call = None ;
266- }
267- } ,
268- ast:: Attr ( __) => inside_attribute = false ,
269- _ => ( )
270- }
229+ Some ( ast:: Item :: MacroRules ( mac) ) => {
230+ macro_highlighter. init ( ) ;
231+ current_macro = Some ( mac. into ( ) ) ;
232+ continue ;
271233 }
272- }
234+ Some ( ast:: Item :: MacroDef ( mac) ) => {
235+ macro_highlighter. init ( ) ;
236+ current_macro = Some ( mac. into ( ) ) ;
237+ continue ;
238+ }
239+ Some ( item) if sema. is_attr_macro_call ( & item) => current_attr_call = Some ( item) ,
240+ None if ast:: Attr :: can_cast ( node. kind ( ) ) => inside_attribute = true ,
241+ _ => ( ) ,
242+ } ,
243+ WalkEvent :: Leave ( NodeOrToken :: Node ( node) ) => match ast:: Item :: cast ( node. clone ( ) ) {
244+ Some ( ast:: Item :: MacroCall ( mcall) ) => {
245+ assert_eq ! ( current_macro_call, Some ( mcall) ) ;
246+ current_macro_call = None ;
247+ }
248+ Some ( ast:: Item :: MacroRules ( mac) ) => {
249+ assert_eq ! ( current_macro, Some ( mac. into( ) ) ) ;
250+ current_macro = None ;
251+ macro_highlighter = MacroHighlighter :: default ( ) ;
252+ }
253+ Some ( ast:: Item :: MacroDef ( mac) ) => {
254+ assert_eq ! ( current_macro, Some ( mac. into( ) ) ) ;
255+ current_macro = None ;
256+ macro_highlighter = MacroHighlighter :: default ( ) ;
257+ }
258+ Some ( item) if current_attr_call. as_ref ( ) . map_or ( false , |it| * it == item) => {
259+ current_attr_call = None
260+ }
261+ None if ast:: Attr :: can_cast ( node. kind ( ) ) => inside_attribute = false ,
262+ _ => ( ) ,
263+ } ,
273264 _ => ( ) ,
274265 }
275266
276267 let element = match event {
277268 WalkEvent :: Enter ( it) => it,
278- WalkEvent :: Leave ( it) => {
279- if let Some ( node) = it. as_node ( ) {
280- inject:: doc_comment ( hl, sema, root. with_value ( node) ) ;
281- }
269+ WalkEvent :: Leave ( NodeOrToken :: Token ( _) ) => continue ,
270+ WalkEvent :: Leave ( NodeOrToken :: Node ( node) ) => {
271+ inject:: doc_comment ( hl, sema, root. with_value ( & node) ) ;
282272 continue ;
283273 }
284274 } ;
285275
286- let range = element. text_range ( ) ;
287-
288276 if current_macro. is_some ( ) {
289277 if let Some ( tok) = element. as_token ( ) {
290278 macro_highlighter. advance ( tok) ;
291279 }
292280 }
293281
294- let descend_token = ( current_macro_call. is_some ( ) || current_attr_call. is_some ( ) )
295- && element. kind ( ) != COMMENT ;
282+ // only attempt to descend if we are inside a macro call or attribute
283+ // as calling `descend_into_macros_single` gets rather expensive if done for every single token
284+ let descend_token = current_macro_call. is_some ( ) || current_attr_call. is_some ( ) ;
296285 let element_to_highlight = if descend_token {
297- // Inside a macro -- expand it first
298- let token = match element. clone ( ) . into_token ( ) {
299- Some ( it) if current_macro_call. is_some ( ) => {
300- let not_in_tt = it. parent ( ) . map_or ( true , |it| it. kind ( ) != TOKEN_TREE ) ;
301- if not_in_tt {
302- continue ;
303- }
304- it
305- }
306- Some ( it) => it,
307- _ => continue ,
286+ let token = match & element {
287+ NodeOrToken :: Node ( _) => continue ,
288+ NodeOrToken :: Token ( tok) => tok. clone ( ) ,
289+ } ;
290+ let in_mcall_outside_tt = current_macro_call. is_some ( )
291+ && token. parent ( ) . as_ref ( ) . map ( SyntaxNode :: kind) != Some ( TOKEN_TREE ) ;
292+ let token = match in_mcall_outside_tt {
293+ // not in the macros token tree, don't attempt to descend
294+ true => token,
295+ false => sema. descend_into_macros_single ( token) ,
308296 } ;
309- let token = sema. descend_into_macros_single ( token) ;
310297 match token. parent ( ) {
311298 Some ( parent) => {
312- // We only care Name and Name_ref
299+ // Names and NameRefs have special semantics, use them instead of the tokens
300+ // as otherwise we won't ever visit them
313301 match ( token. kind ( ) , parent. kind ( ) ) {
314302 ( T ! [ ident] , NAME | NAME_REF ) => parent. into ( ) ,
315303 ( T ! [ self ] | T ! [ super ] | T ! [ crate ] , NAME_REF ) => parent. into ( ) ,
@@ -323,10 +311,14 @@ fn traverse(
323311 element. clone ( )
324312 } ;
325313
326- if macro_highlighter. highlight ( element_to_highlight. clone ( ) ) . is_some ( ) {
314+ // FIXME: do proper macro def highlighting https://github.com/rust-analyzer/rust-analyzer/issues/6232
315+ // Skip metavariables from being highlighted to prevent keyword highlighting in them
316+ if macro_highlighter. highlight ( & element_to_highlight) . is_some ( ) {
327317 continue ;
328318 }
329319
320+ // string highlight injections, note this does not use the descended element as proc-macros
321+ // can rewrite string literals which invalidates our indices
330322 if let ( Some ( token) , Some ( token_to_highlight) ) =
331323 ( element. into_token ( ) , element_to_highlight. as_token ( ) )
332324 {
@@ -354,13 +346,15 @@ fn traverse(
354346 }
355347 }
356348
357- if let Some ( ( mut highlight, binding_hash) ) = highlight:: element (
349+ // do the normal highlighting
350+ let element = highlight:: element (
358351 sema,
359352 krate,
360353 & mut bindings_shadow_count,
361354 syntactic_name_ref_highlighting,
362- element_to_highlight. clone ( ) ,
363- ) {
355+ element_to_highlight,
356+ ) ;
357+ if let Some ( ( mut highlight, binding_hash) ) = element {
364358 if inside_attribute {
365359 highlight |= HlMod :: Attribute
366360 }
@@ -369,18 +363,3 @@ fn traverse(
369363 }
370364 }
371365}
372-
373- fn macro_call_range ( macro_call : & ast:: MacroCall ) -> Option < TextRange > {
374- let path = macro_call. path ( ) ?;
375- let name_ref = path. segment ( ) ?. name_ref ( ) ?;
376-
377- let range_start = name_ref. syntax ( ) . text_range ( ) . start ( ) ;
378- let mut range_end = name_ref. syntax ( ) . text_range ( ) . end ( ) ;
379- for sibling in path. syntax ( ) . siblings_with_tokens ( Direction :: Next ) {
380- if let T ! [ !] | T ! [ ident] = sibling. kind ( ) {
381- range_end = sibling. text_range ( ) . end ( ) ;
382- }
383- }
384-
385- Some ( TextRange :: new ( range_start, range_end) )
386- }
0 commit comments