2323from robot .parsing .lexer .tokens import Token
2424from robot .parsing .model .statements import (
2525 Arguments ,
26+ Documentation ,
2627 Fixture ,
2728 KeywordCall ,
2829 LibraryImport ,
6263from robotcode .robot .diagnostics .namespace import DEFAULT_BDD_PREFIXES , Namespace
6364from robotcode .robot .utils import get_robot_version
6465from robotcode .robot .utils .ast import (
66+ cached_isinstance ,
6567 iter_nodes ,
6668 iter_over_keyword_names_and_owners ,
6769 token_in_range ,
@@ -120,6 +122,7 @@ class RobotSemTokenTypes(Enum):
120122
121123class RobotSemTokenModifiers (Enum ):
122124 BUILTIN = "builtin"
125+ EMBEDDED = "embedded"
123126
124127
125128@dataclass
@@ -340,6 +343,7 @@ def generate_sem_sub_tokens(
340343 node : ast .AST ,
341344 col_offset : Optional [int ] = None ,
342345 length : Optional [int ] = None ,
346+ yield_arguments : bool = False ,
343347 ) -> Iterator [SemTokenInfo ]:
344348 sem_info = cls .mapping ().get (token .type , None ) if token .type is not None else None
345349 if sem_info is not None :
@@ -391,7 +395,7 @@ def generate_sem_sub_tokens(
391395 yield SemTokenInfo .from_token (token , sem_type , sem_mod )
392396
393397 elif token .type in [Token .KEYWORD , ROBOT_KEYWORD_INNER ] or (
394- token .type == Token .NAME and isinstance (node , ( Fixture , Template , TestTemplate ) )
398+ token .type == Token .NAME and cached_isinstance (node , Fixture , Template , TestTemplate )
395399 ):
396400 if (
397401 namespace .find_keyword (
@@ -461,6 +465,9 @@ def generate_sem_sub_tokens(
461465
462466 kw_index = len (kw_namespace ) + 1 if kw_namespace else 0
463467
468+ if token .type == Token .NAME and kw_doc is not None :
469+ sem_type = RobotSemTokenTypes .KEYWORD
470+
464471 if kw_namespace :
465472 kw = token .value [kw_index :]
466473
@@ -501,13 +508,25 @@ def generate_sem_sub_tokens(
501508 col_offset + kw_index + start ,
502509 arg_start - start ,
503510 )
504- yield SemTokenInfo . from_token (
505- token ,
506- RobotSemTokenTypes . EMBEDDED_ARGUMENT ,
507- sem_mod ,
508- col_offset + kw_index + arg_start ,
509- arg_end - arg_start ,
511+
512+ embedded_token = Token (
513+ Token . ARGUMENT ,
514+ token . value [ arg_start : arg_end ] ,
515+ token . lineno ,
516+ token . col_offset + arg_start ,
510517 )
518+
519+ for sub_token in ModelHelper .tokenize_variables (
520+ embedded_token ,
521+ ignore_errors = True ,
522+ identifiers = "$@&%" ,
523+ ):
524+ for e in cls .generate_sem_sub_tokens (
525+ namespace , builtin_library_doc , sub_token , node , yield_arguments = True
526+ ):
527+ e .sem_modifiers = {RobotSemTokenModifiers .EMBEDDED }
528+ yield e
529+
511530 start = arg_end + 1
512531
513532 if start < end :
@@ -521,7 +540,7 @@ def generate_sem_sub_tokens(
521540
522541 else :
523542 yield SemTokenInfo .from_token (token , sem_type , sem_mod , col_offset + kw_index , len (kw ))
524- elif token .type == Token .NAME and isinstance (node , ( LibraryImport , ResourceImport , VariablesImport ) ):
543+ elif token .type == Token .NAME and cached_isinstance (node , LibraryImport , ResourceImport , VariablesImport ):
525544 if "\\ " in token .value :
526545 if col_offset is None :
527546 col_offset = token .col_offset
@@ -543,7 +562,9 @@ def generate_sem_sub_tokens(
543562 length ,
544563 )
545564 elif get_robot_version () >= (5 , 0 ) and token .type == Token .OPTION :
546- if (isinstance (node , ExceptHeader ) or isinstance (node , WhileHeader )) and "=" in token .value :
565+ if (
566+ cached_isinstance (node , ExceptHeader ) or cached_isinstance (node , WhileHeader )
567+ ) and "=" in token .value :
547568 if col_offset is None :
548569 col_offset = token .col_offset
549570
@@ -589,7 +610,12 @@ def generate_sem_sub_tokens(
589610 1 ,
590611 )
591612 else :
592- if token .type != Token .ARGUMENT or token .type != Token .NAME and isinstance (node , Metadata ):
613+ if (
614+ yield_arguments
615+ or token .type != Token .ARGUMENT
616+ or token .type != Token .NAME
617+ and cached_isinstance (node , Metadata )
618+ ):
593619 yield SemTokenInfo .from_token (token , sem_type , sem_mod , col_offset , length )
594620
595621 def generate_sem_tokens (
@@ -602,25 +628,25 @@ def generate_sem_tokens(
602628 if (
603629 token .type in {Token .ARGUMENT , Token .TESTCASE_NAME , Token .KEYWORD_NAME }
604630 or token .type == Token .NAME
605- and isinstance (node , ( VariablesImport , LibraryImport , ResourceImport ) )
631+ and cached_isinstance (node , VariablesImport , LibraryImport , ResourceImport )
606632 ):
607- if (isinstance ( node , Variable ) and token . type == Token . ARGUMENT and node . name and node . name [ 0 ] == "&" ) or (
608- isinstance (node , Arguments )
609- ):
633+ if (
634+ cached_isinstance (node , Variable ) and token . type == Token . ARGUMENT and node . name and node . name [ 0 ] == "&"
635+ ) or ( cached_isinstance ( node , Arguments )) :
610636 name , value = split_from_equals (token .value )
611637 if value is not None :
612638 length = len (name )
613639
614640 yield SemTokenInfo .from_token (
615641 Token (
616- ROBOT_NAMED_ARGUMENT if isinstance (node , Variable ) else SemanticTokenTypes .PARAMETER ,
642+ ROBOT_NAMED_ARGUMENT if cached_isinstance (node , Variable ) else SemanticTokenTypes .PARAMETER ,
617643 name ,
618644 token .lineno ,
619645 token .col_offset ,
620646 ),
621647 (
622648 RobotSemTokenTypes .NAMED_ARGUMENT
623- if isinstance (node , Variable )
649+ if cached_isinstance (node , Variable )
624650 else SemanticTokenTypes .PARAMETER
625651 ),
626652 )
@@ -640,7 +666,7 @@ def generate_sem_tokens(
640666 token .col_offset + length + 1 ,
641667 token .error ,
642668 )
643- elif isinstance (node , Arguments ) and name :
669+ elif cached_isinstance (node , Arguments ) and name :
644670 yield SemTokenInfo .from_token (
645671 Token (
646672 ROBOT_NAMED_ARGUMENT ,
@@ -663,11 +689,13 @@ def generate_sem_tokens(
663689 ignore_errors = True ,
664690 identifiers = "$" if token .type == Token .KEYWORD_NAME else "$@&%" ,
665691 ):
666- for e in self .generate_sem_sub_tokens (namespace , builtin_library_doc , sub_token , node ):
692+ for e in self .generate_sem_sub_tokens (
693+ namespace , builtin_library_doc , sub_token , node , yield_arguments = True
694+ ):
667695 yield e
668696
669697 else :
670- for e in self .generate_sem_sub_tokens (namespace , builtin_library_doc , token , node ):
698+ for e in self .generate_sem_sub_tokens (namespace , builtin_library_doc , token , node , yield_arguments = True ):
671699 yield e
672700
673701 def generate_run_kw_tokens (
@@ -956,8 +984,8 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]:
956984 for node in iter_nodes (model ):
957985 check_current_task_canceled ()
958986
959- if isinstance (node , Statement ):
960- if isinstance (node , LibraryImport ) and node .name :
987+ if cached_isinstance (node , Statement ):
988+ if cached_isinstance (node , LibraryImport ) and node .name :
961989 lib_doc = namespace .get_imported_library_libdoc (node .name , node .args , node .alias )
962990 kw_doc = lib_doc .inits .keywords [0 ] if lib_doc and lib_doc .inits else None
963991 if lib_doc is not None :
@@ -1009,7 +1037,7 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]:
10091037
10101038 yield token , node
10111039 continue
1012- if isinstance (node , VariablesImport ) and node .name :
1040+ if cached_isinstance (node , VariablesImport ) and node .name :
10131041 lib_doc = namespace .get_imported_variables_libdoc (node .name , node .args )
10141042 kw_doc = lib_doc .inits .keywords [0 ] if lib_doc and lib_doc .inits else None
10151043 if lib_doc is not None :
@@ -1061,12 +1089,12 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]:
10611089
10621090 yield token , node
10631091 continue
1064- if isinstance (node , ( KeywordCall , Fixture ) ):
1092+ if cached_isinstance (node , KeywordCall , Fixture ):
10651093 kw_token = cast (
10661094 Token ,
10671095 (
10681096 node .get_token (Token .KEYWORD )
1069- if isinstance (node , KeywordCall )
1097+ if cached_isinstance (node , KeywordCall )
10701098 else node .get_token (Token .NAME )
10711099 ),
10721100 )
@@ -1109,8 +1137,16 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]:
11091137 yield kw_res
11101138
11111139 continue
1140+ if cached_isinstance (node , Documentation ):
1141+ for token in node .tokens :
1142+ if token .type == Token .ARGUMENT :
1143+ continue
1144+ yield token , node
1145+ continue
11121146
11131147 for token in node .tokens :
1148+ if token .type == Token .COMMENT :
1149+ continue
11141150 yield token , node
11151151
11161152 lines = document .get_lines ()
@@ -1136,6 +1172,7 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]:
11361172 ),
11371173 ),
11381174 )
1175+
11391176 token_col_offset = token_range .start .character
11401177 token_length = token_range .end .character - token_range .start .character
11411178
0 commit comments