@@ -136,6 +136,13 @@ public class SemanticTokensProvider {
136136 BSLLexer .ANNOTATION_CUSTOM_SYMBOL
137137 );
138138
139+ private static final Set <Integer > SPEC_LITERALS = Set .of (
140+ BSLLexer .UNDEFINED ,
141+ BSLLexer .TRUE ,
142+ BSLLexer .FALSE ,
143+ BSLLexer .NULL
144+ );
145+
139146 private static final String [] NO_MODIFIERS = new String [0 ];
140147 private static final String [] DOC_ONLY = new String []{SemanticTokenModifiers .Documentation };
141148
@@ -333,26 +340,33 @@ private void addAnnotationsFromAst(List<TokenEntry> entries, ParseTree parseTree
333340 }
334341
335342 private void addPreprocessorFromAst (List <TokenEntry > entries , ParseTree parseTree ) {
336- // 1) Regions as Namespace: handle all regionStart and regionEnd nodes explicitly
337- for (var regionStart : Trees .<RegionStartContext >findAllRuleNodes (parseTree , BSLParser .RULE_regionStart )) {
338- // Namespace only for '#'+keyword part to avoid overlap with region name token
339- var preprocessor = Trees .<PreprocessorContext >getAncestorByRuleIndex (regionStart , BSLParser .RULE_preprocessor );
340- if (preprocessor != null && regionStart .PREPROC_REGION () != null ) {
341- addRange (entries ,
342- Ranges .create (preprocessor .getStart (), regionStart .PREPROC_REGION ().getSymbol ()),
343- SemanticTokenTypes .Namespace );
344- } else {
345- addNamespaceForPreprocessorNode (entries , regionStart );
343+ addRegionsNamespaces (entries , parseTree );
344+ addDirectives (entries , parseTree );
345+ addOtherPreprocs (entries , parseTree );
346+ }
347+
348+ // 2) Other preprocessor directives: Macro for each HASH and PREPROC_* token,
349+ // excluding region start/end (handled as Namespace)
350+ private void addOtherPreprocs (List <TokenEntry > entries , ParseTree parseTree ) {
351+ for (var preprocessor : Trees .<PreprocessorContext >findAllRuleNodes (parseTree , BSLParser .RULE_preprocessor )) {
352+ boolean containsRegion = (preprocessor .regionStart () != null ) || (preprocessor .regionEnd () != null );
353+ if (containsRegion ) {
354+ continue ; // region handled as Namespace above
346355 }
347- // region name highlighted as Variable (consistent with #Использовать <libName>)
348- if (regionStart .regionName () != null ) {
349- addRange (entries , Ranges .create (regionStart .regionName ()), SemanticTokenTypes .Variable );
356+
357+ for (Token token : Trees .getTokens (preprocessor )) {
358+ if (token .getChannel () != Token .DEFAULT_CHANNEL ) {
359+ continue ;
360+ }
361+ String symbolicName = BSLLexer .VOCABULARY .getSymbolicName (token .getType ());
362+ if (token .getType () == BSLLexer .HASH || (symbolicName != null && symbolicName .startsWith ("PREPROC_" ))) {
363+ addRange (entries , Ranges .create (token ), SemanticTokenTypes .Macro );
364+ }
350365 }
351366 }
352- for (var regionEnd : Trees .<RegionEndContext >findAllRuleNodes (parseTree , BSLParser .RULE_regionEnd )) {
353- addNamespaceForPreprocessorNode (entries , regionEnd );
354- }
367+ }
355368
369+ private void addDirectives (List <TokenEntry > entries , ParseTree parseTree ) {
356370 // 1.1) Use directives as Namespace: #Использовать ... (moduleAnnotations scope)
357371 for (var use : Trees .<UseContext >findAllRuleNodes (parseTree , BSLParser .RULE_use )) {
358372 addNamespaceForUse (entries , use );
@@ -369,25 +383,28 @@ private void addPreprocessorFromAst(List<TokenEntry> entries, ParseTree parseTre
369383 addRange (entries , Ranges .create (nativeKw ), SemanticTokenTypes .Macro );
370384 }
371385 }
386+ }
372387
373- // 2) Other preprocessor directives: Macro for each HASH and PREPROC_* token,
374- // excluding region start/end (handled as Namespace)
375- for (var preprocessor : Trees .<PreprocessorContext >findAllRuleNodes (parseTree , BSLParser .RULE_preprocessor )) {
376- boolean containsRegion = (preprocessor .regionStart () != null ) || (preprocessor .regionEnd () != null );
377- if (containsRegion ) {
378- continue ; // region handled as Namespace above
388+ // 1) Regions as Namespace: handle all regionStart and regionEnd nodes explicitly
389+ private void addRegionsNamespaces (List <TokenEntry > entries , ParseTree parseTree ) {
390+ for (var regionStart : Trees .<RegionStartContext >findAllRuleNodes (parseTree , BSLParser .RULE_regionStart )) {
391+ // Namespace only for '#'+keyword part to avoid overlap with region name token
392+ var preprocessor = Trees .<PreprocessorContext >getAncestorByRuleIndex (regionStart , BSLParser .RULE_preprocessor );
393+ if (preprocessor != null && regionStart .PREPROC_REGION () != null ) {
394+ addRange (entries ,
395+ Ranges .create (preprocessor .getStart (), regionStart .PREPROC_REGION ().getSymbol ()),
396+ SemanticTokenTypes .Namespace );
397+ } else {
398+ addNamespaceForPreprocessorNode (entries , regionStart );
379399 }
380-
381- for (Token token : Trees .getTokens (preprocessor )) {
382- if (token .getChannel () != Token .DEFAULT_CHANNEL ) {
383- continue ;
384- }
385- String symbolicName = BSLLexer .VOCABULARY .getSymbolicName (token .getType ());
386- if (token .getType () == BSLLexer .HASH || (symbolicName != null && symbolicName .startsWith ("PREPROC_" ))) {
387- addRange (entries , Ranges .create (token ), SemanticTokenTypes .Macro );
388- }
400+ // region name highlighted as Variable (consistent with #Использовать <libName>)
401+ if (regionStart .regionName () != null ) {
402+ addRange (entries , Ranges .create (regionStart .regionName ()), SemanticTokenTypes .Variable );
389403 }
390404 }
405+ for (var regionEnd : Trees .<RegionEndContext >findAllRuleNodes (parseTree , BSLParser .RULE_regionEnd )) {
406+ addNamespaceForPreprocessorNode (entries , regionEnd );
407+ }
391408 }
392409
393410 private void addNamespaceForPreprocessorNode (List <TokenEntry > entries , ParserRuleContext preprocessorChildNode ) {
@@ -404,8 +421,8 @@ private void addNamespaceForPreprocessorNode(List<TokenEntry> entries, ParserRul
404421 }
405422
406423 private void addNamespaceForUse (List <TokenEntry > entries , UseContext useCtx ) {
407- TerminalNode hashNode = useCtx .HASH ();
408- TerminalNode useNode = useCtx .PREPROC_USE_KEYWORD ();
424+ var hashNode = useCtx .HASH ();
425+ var useNode = useCtx .PREPROC_USE_KEYWORD ();
409426
410427 if (hashNode != null && useNode != null ) {
411428 addRange (entries , Ranges .create (hashNode , useNode ), SemanticTokenTypes .Namespace );
@@ -510,49 +527,26 @@ private void addLexicalTokens(List<Token> tokens, List<TokenEntry> entries) {
510527 for (Token token : tokens ) {
511528 var tokenType = token .getType ();
512529 var tokenText = Objects .toString (token .getText (), "" );
513- if (tokenText .isEmpty ()) {
514- continue ;
515- }
516-
517- // strings
518- if (STRING_TYPES .contains (tokenType )) {
519- addRange (entries , Ranges .create (token ), SemanticTokenTypes .String );
520- continue ;
521- }
522-
523- // date literals in single quotes
524- if (tokenType == BSLLexer .DATETIME ) {
525- addRange (entries , Ranges .create (token ), SemanticTokenTypes .String );
526- continue ;
527- }
528-
529- // numbers
530- if (NUMBER_TYPES .contains (tokenType )) {
531- addRange (entries , Ranges .create (token ), SemanticTokenTypes .Number );
532- continue ;
533- }
534-
535- // operators and punctuators
536- if (OPERATOR_TYPES .contains (tokenType )) {
537- addRange (entries , Ranges .create (token ), SemanticTokenTypes .Operator );
538- continue ;
530+ if (!tokenText .isEmpty ()) {
531+ selectAndAddSemanticToken (entries , token , tokenType );
539532 }
533+ }
534+ }
540535
536+ private void selectAndAddSemanticToken (List <TokenEntry > entries , Token token , int tokenType ) {
537+ if (STRING_TYPES .contains (tokenType )) { // strings
538+ addRange (entries , Ranges .create (token ), SemanticTokenTypes .String );
539+ } else if (tokenType == BSLLexer .DATETIME ) { // date literals in single quotes
540+ addRange (entries , Ranges .create (token ), SemanticTokenTypes .String );
541+ } else if (NUMBER_TYPES .contains (tokenType )) { // numbers
542+ addRange (entries , Ranges .create (token ), SemanticTokenTypes .Number );
543+ } else if (OPERATOR_TYPES .contains (tokenType )) { // operators and punctuators
544+ addRange (entries , Ranges .create (token ), SemanticTokenTypes .Operator );
545+ } else if (tokenType == BSLLexer .AMPERSAND || ANNOTATION_TOKENS .contains (tokenType )) {
541546 // Skip '&' and all ANNOTATION_* symbol tokens here to avoid duplicate Decorator emission (handled via AST)
542- if (tokenType == BSLLexer .AMPERSAND || ANNOTATION_TOKENS .contains (tokenType )) {
543- continue ;
544- }
545-
546- // specific literals as keywords: undefined/boolean/null
547- if (tokenType == BSLLexer .UNDEFINED
548- || tokenType == BSLLexer .TRUE
549- || tokenType == BSLLexer .FALSE
550- || tokenType == BSLLexer .NULL ) {
551- addRange (entries , Ranges .create (token ), SemanticTokenTypes .Keyword );
552- continue ;
553- }
554-
555- // keywords (by symbolic name suffix), skip PREPROC_* (handled via AST)
547+ } else if (SPEC_LITERALS .contains (tokenType )) { // specific literals as keywords: undefined/boolean/null
548+ addRange (entries , Ranges .create (token ), SemanticTokenTypes .Keyword );
549+ } else { // keywords (by symbolic name suffix), skip PREPROC_* (handled via AST)
556550 String symbolicName = BSLLexer .VOCABULARY .getSymbolicName (tokenType );
557551 if (symbolicName != null && symbolicName .endsWith ("_KEYWORD" ) && !symbolicName .startsWith ("PREPROC_" )) {
558552 addRange (entries , Ranges .create (token ), SemanticTokenTypes .Keyword );
0 commit comments