@@ -13,11 +13,11 @@ mod html;
13
13
mod tests;
14
14
15
15
use hir:: { InFile , Name , Semantics } ;
16
- use ide_db:: { RootDatabase , SymbolKind } ;
16
+ use ide_db:: RootDatabase ;
17
17
use rustc_hash:: FxHashMap ;
18
18
use syntax:: {
19
19
ast:: { self , HasFormatSpecifier } ,
20
- match_ast , AstNode , AstToken , Direction , NodeOrToken ,
20
+ AstNode , AstToken , NodeOrToken ,
21
21
SyntaxKind :: * ,
22
22
SyntaxNode , TextRange , WalkEvent , T ,
23
23
} ;
@@ -100,7 +100,8 @@ pub struct HlRange {
100
100
// colon:: Emitted for the `:` token.
101
101
// comma:: Emitted for the `,` token.
102
102
// 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.
104
105
//
105
106
// //-
106
107
//
@@ -209,107 +210,94 @@ fn traverse(
209
210
// Walk all nodes, keeping track of whether we are inside a macro or not.
210
211
// If in macro, expand it first and highlight the expanded code.
211
212
for event in root. value . preorder_with_tokens ( ) {
212
- let event_range = match & event {
213
+ let range = match & event {
213
214
WalkEvent :: Enter ( it) | WalkEvent :: Leave ( it) => it. text_range ( ) ,
214
215
} ;
215
216
216
217
// 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 ( ) {
218
219
continue ;
219
220
}
220
221
222
+ // set macro and attribute highlighting states
221
223
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 ;
249
228
}
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 ;
271
233
}
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
+ } ,
273
264
_ => ( ) ,
274
265
}
275
266
276
267
let element = match event {
277
268
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) ) ;
282
272
continue ;
283
273
}
284
274
} ;
285
275
286
- let range = element. text_range ( ) ;
287
-
288
276
if current_macro. is_some ( ) {
289
277
if let Some ( tok) = element. as_token ( ) {
290
278
macro_highlighter. advance ( tok) ;
291
279
}
292
280
}
293
281
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 ( ) ;
296
285
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) ,
308
296
} ;
309
- let token = sema. descend_into_macros_single ( token) ;
310
297
match token. parent ( ) {
311
298
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
313
301
match ( token. kind ( ) , parent. kind ( ) ) {
314
302
( T ! [ ident] , NAME | NAME_REF ) => parent. into ( ) ,
315
303
( T ! [ self ] | T ! [ super ] | T ! [ crate ] , NAME_REF ) => parent. into ( ) ,
@@ -323,10 +311,14 @@ fn traverse(
323
311
element. clone ( )
324
312
} ;
325
313
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 ( ) {
327
317
continue ;
328
318
}
329
319
320
+ // string highlight injections, note this does not use the descended element as proc-macros
321
+ // can rewrite string literals which invalidates our indices
330
322
if let ( Some ( token) , Some ( token_to_highlight) ) =
331
323
( element. into_token ( ) , element_to_highlight. as_token ( ) )
332
324
{
@@ -354,13 +346,15 @@ fn traverse(
354
346
}
355
347
}
356
348
357
- if let Some ( ( mut highlight, binding_hash) ) = highlight:: element (
349
+ // do the normal highlighting
350
+ let element = highlight:: element (
358
351
sema,
359
352
krate,
360
353
& mut bindings_shadow_count,
361
354
syntactic_name_ref_highlighting,
362
- element_to_highlight. clone ( ) ,
363
- ) {
355
+ element_to_highlight,
356
+ ) ;
357
+ if let Some ( ( mut highlight, binding_hash) ) = element {
364
358
if inside_attribute {
365
359
highlight |= HlMod :: Attribute
366
360
}
@@ -369,18 +363,3 @@ fn traverse(
369
363
}
370
364
}
371
365
}
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