@@ -855,98 +855,218 @@ async def complete_LibraryImport( # noqa: N802
855
855
position : Position ,
856
856
context : Optional [CompletionContext ],
857
857
) -> Union [List [CompletionItem ], CompletionList , None ]:
858
- from robot .parsing .lexer .tokens import Token
859
- from robot .parsing .model .statements import LibraryImport
858
+
859
+ from robot .parsing .lexer .tokens import Token as RobotToken
860
+ from robot .parsing .model .statements import LibraryImport , Statement
860
861
861
862
if self .document is None :
862
863
return []
863
864
864
865
import_node = cast (LibraryImport , node )
865
- import_token = import_node .get_token (Token .LIBRARY )
866
+ import_token = import_node .get_token (RobotToken .LIBRARY )
866
867
if import_token is None :
867
868
return []
868
869
import_token_index = import_node .tokens .index (import_token )
869
870
870
- if len (import_node .tokens ) > import_token_index + 2 :
871
- name_token = import_node .tokens [import_token_index + 2 ]
872
- if not position .is_in_range (r := range_from_token (name_token )) and r .end != position :
871
+ async def complete_import () -> Union [List [CompletionItem ], CompletionList , None ]:
872
+ if self .document is None :
873
+ return []
874
+
875
+ if len (import_node .tokens ) > import_token_index + 2 :
876
+ name_token = import_node .tokens [import_token_index + 2 ]
877
+ if not position .is_in_range (r := range_from_token (name_token )) and r .end != position :
878
+ return None
879
+
880
+ elif len (import_node .tokens ) > import_token_index + 1 :
881
+ name_token = import_node .tokens [import_token_index + 1 ]
882
+ if position .is_in_range (r := range_from_token (name_token )) or r .end == position :
883
+ if whitespace_at_begin_of_token (name_token ) > 1 :
884
+
885
+ ws_b = whitespace_from_begin_of_token (name_token )
886
+ r .start .character += 2 if ws_b and ws_b [0 ] != "\t " else 1
887
+
888
+ if not position .is_in_range (r ) and r .end != position :
889
+ return None
890
+ else :
873
891
return None
874
892
875
- elif len (import_node .tokens ) > import_token_index + 1 :
876
- name_token = import_node .tokens [import_token_index + 1 ]
877
- if position .is_in_range (r := range_from_token (name_token )) or r .end == position :
878
- if whitespace_at_begin_of_token (name_token ) > 1 :
893
+ pos = position .character - r .start .character
894
+ text_before_position = str (name_token .value )[:pos ].lstrip ()
879
895
880
- ws_b = whitespace_from_begin_of_token ( name_token )
881
- r . start . character += 2 if ws_b and ws_b [ 0 ] != " \t " else 1
896
+ if text_before_position != "" and all ( c == "." for c in text_before_position ):
897
+ return None
882
898
883
- if not position .is_in_range (r ) and r .end != position :
884
- return None
885
- else :
886
- return None
899
+ last_separator_index = (
900
+ len (text_before_position )
901
+ - next ((i for i , c in enumerate (reversed (text_before_position )) if c in ["." , "/" , os .sep ]), - 1 )
902
+ - 1
903
+ )
887
904
888
- pos = position .character - r .start .character
889
- text_before_position = str (name_token .value )[:pos ].lstrip ()
905
+ first_part = (
906
+ text_before_position [
907
+ : last_separator_index
908
+ + (1 if text_before_position [last_separator_index ] in ["." , "/" , os .sep ] else 0 )
909
+ ]
910
+ if last_separator_index < len (text_before_position )
911
+ else None
912
+ )
890
913
891
- if text_before_position != "" and all (c == "." for c in text_before_position ):
892
- return None
914
+ sep = text_before_position [last_separator_index ] if last_separator_index < len (text_before_position ) else ""
893
915
894
- last_separator_index = (
895
- len (text_before_position )
896
- - next ((i for i , c in enumerate (reversed (text_before_position )) if c in ["." , "/" , os .sep ]), - 1 )
897
- - 1
898
- )
916
+ imports_manger = await self .parent .documents_cache .get_imports_manager (self .document )
899
917
900
- first_part = (
901
- text_before_position [
902
- : last_separator_index + (1 if text_before_position [last_separator_index ] in ["." , "/" , os .sep ] else 0 )
918
+ try :
919
+ list = await imports_manger .complete_library_import (
920
+ first_part if first_part else None , str (self .document .uri .to_path ().parent )
921
+ )
922
+ if not list :
923
+ return None
924
+ except (SystemExit , KeyboardInterrupt , asyncio .CancelledError ):
925
+ raise
926
+ except BaseException :
927
+ return None
928
+
929
+ if text_before_position == "" :
930
+ r .start .character = position .character
931
+ else :
932
+ r .start .character += last_separator_index + 1 if last_separator_index < len (text_before_position ) else 0
933
+
934
+ return [
935
+ CompletionItem (
936
+ label = e .label ,
937
+ kind = CompletionItemKind .MODULE
938
+ if e .kind in [CompleteResultKind .MODULE , CompleteResultKind .MODULE_INTERNAL ]
939
+ else CompletionItemKind .FILE
940
+ if e .kind in [CompleteResultKind .FILE ]
941
+ else CompletionItemKind .FOLDER
942
+ if e .kind in [CompleteResultKind .FOLDER ]
943
+ else None ,
944
+ detail = e .kind .value ,
945
+ sort_text = f"030_{ e } " ,
946
+ insert_text_format = InsertTextFormat .PLAINTEXT ,
947
+ text_edit = TextEdit (range = r , new_text = e .label ) if r is not None else None ,
948
+ data = {
949
+ "document_uri" : str (self .document .uri ),
950
+ "type" : e .kind .name ,
951
+ "name" : ((first_part + sep ) if first_part is not None else "" ) + e .label ,
952
+ },
953
+ )
954
+ for e in list
903
955
]
904
- if last_separator_index < len (text_before_position )
905
- else None
906
- )
907
956
908
- sep = text_before_position [last_separator_index ] if last_separator_index < len (text_before_position ) else ""
957
+ async def complete_arguments () -> Union [List [CompletionItem ], CompletionList , None ]:
958
+ if self .document is None :
959
+ return []
909
960
910
- imports_manger = await self .parent .documents_cache .get_imports_manager (self .document )
961
+ if context is None or context .trigger_kind != CompletionTriggerKind .INVOKED :
962
+ return []
911
963
912
- try :
913
- list = await imports_manger .complete_library_import (
914
- first_part if first_part else None , str (self .document .uri .to_path ().parent )
915
- )
916
- if not list :
964
+ namespace = await self .parent .documents_cache .get_namespace (self .document )
965
+ if namespace is None :
966
+ return []
967
+
968
+ kw_node = cast (Statement , node )
969
+
970
+ tokens_at_position = get_tokens_at_position (kw_node , position )
971
+
972
+ if not tokens_at_position :
917
973
return None
918
- except (SystemExit , KeyboardInterrupt , asyncio .CancelledError ):
919
- raise
920
- except BaseException :
921
- return None
922
974
923
- if text_before_position == "" :
924
- r .start .character = position .character
925
- else :
926
- r .start .character += last_separator_index + 1 if last_separator_index < len (text_before_position ) else 0
975
+ token_at_position = tokens_at_position [- 1 ]
927
976
928
- return [
929
- CompletionItem (
930
- label = e .label ,
931
- kind = CompletionItemKind .MODULE
932
- if e .kind in [CompleteResultKind .MODULE , CompleteResultKind .MODULE_INTERNAL ]
933
- else CompletionItemKind .FILE
934
- if e .kind in [CompleteResultKind .FILE ]
935
- else CompletionItemKind .FOLDER
936
- if e .kind in [CompleteResultKind .FOLDER ]
937
- else None ,
938
- detail = e .kind .value ,
939
- sort_text = f"030_{ e } " ,
940
- insert_text_format = InsertTextFormat .PLAINTEXT ,
941
- text_edit = TextEdit (range = r , new_text = e .label ) if r is not None else None ,
942
- data = {
943
- "document_uri" : str (self .document .uri ),
944
- "type" : e .kind .name ,
945
- "name" : ((first_part + sep ) if first_part is not None else "" ) + e .label ,
946
- },
947
- )
948
- for e in list
949
- ]
977
+ if token_at_position .type not in [RobotToken .ARGUMENT , RobotToken .EOL , RobotToken .SEPARATOR ]:
978
+ return None
979
+
980
+ token_at_position_index = kw_node .tokens .index (token_at_position )
981
+
982
+ argument_token_index = token_at_position_index
983
+ while argument_token_index >= 0 and kw_node .tokens [argument_token_index ].type != RobotToken .ARGUMENT :
984
+ argument_token_index -= 1
985
+
986
+ arguments = kw_node .get_tokens (RobotToken .ARGUMENT )
987
+
988
+ if argument_token_index >= 0 :
989
+ argument_token = kw_node .tokens [argument_token_index ]
990
+ if argument_token .type == RobotToken .ARGUMENT :
991
+ argument_index = arguments .index (argument_token )
992
+ else :
993
+ argument_index = 0
994
+ else :
995
+ argument_index = - 1
996
+
997
+ if whitespace_at_begin_of_token (token_at_position ) > 1 :
998
+ completion_range = range_from_token (token_at_position )
999
+
1000
+ ws_b = whitespace_from_begin_of_token (token_at_position )
1001
+ completion_range .start .character += 2 if ws_b and ws_b [0 ] != "\t " else 1
1002
+
1003
+ if position .is_in_range (completion_range ) or completion_range .end == position :
1004
+ argument_index += 1
1005
+
1006
+ if argument_index < 0 :
1007
+ return None
1008
+
1009
+ completion_range = range_from_token (token_at_position )
1010
+ if (w := whitespace_at_begin_of_token (token_at_position )) > 0 :
1011
+ if w > 1 and completion_range .start .character + 1 < position .character :
1012
+ completion_range .start = position
1013
+ elif completion_range .start == position :
1014
+ pass
1015
+ else :
1016
+ return None
1017
+ else :
1018
+ if token_at_position .type != RobotToken .ARGUMENT :
1019
+ return None
1020
+
1021
+ if "=" in token_at_position .value :
1022
+ equal_index = token_at_position .value .index ("=" )
1023
+ if completion_range .start .character + equal_index < position .character :
1024
+ return None
1025
+ else :
1026
+ completion_range .end .character = completion_range .start .character + equal_index + 1
1027
+
1028
+ libdocs = [
1029
+ entry .library_doc
1030
+ for entry in (await namespace .get_libraries ()).values ()
1031
+ if entry .import_name == import_node .name
1032
+ and entry .args == import_node .args
1033
+ and entry .alias == import_node .alias
1034
+ ]
1035
+
1036
+ if len (libdocs ) == 1 :
1037
+ init = next ((v for v in libdocs [0 ].inits .values ()), None )
1038
+
1039
+ if init :
1040
+ return [
1041
+ CompletionItem (
1042
+ label = f"{ e .name } =" ,
1043
+ kind = CompletionItemKind .VARIABLE ,
1044
+ # detail=e.detail,
1045
+ sort_text = f"02{ i } _{ e .name } =" ,
1046
+ insert_text_format = InsertTextFormat .PLAINTEXT ,
1047
+ text_edit = TextEdit (range = completion_range , new_text = f"{ e .name } =" ),
1048
+ data = {
1049
+ "document_uri" : str (self .document .uri ),
1050
+ "type" : "Argument" ,
1051
+ "name" : e ,
1052
+ },
1053
+ )
1054
+ for i , e in enumerate (init .args )
1055
+ if e .kind
1056
+ not in [
1057
+ KeywordArgumentKind .VAR_POSITIONAL ,
1058
+ KeywordArgumentKind .VAR_NAMED ,
1059
+ KeywordArgumentKind .NAMED_ONLY_MARKER ,
1060
+ KeywordArgumentKind .POSITIONAL_ONLY_MARKER ,
1061
+ ]
1062
+ ]
1063
+
1064
+ return None
1065
+
1066
+ result = await complete_import ()
1067
+ if not result :
1068
+ result = await complete_arguments ()
1069
+ return result
950
1070
951
1071
async def complete_ResourceImport ( # noqa: N802
952
1072
self ,
@@ -1097,12 +1217,12 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
1097
1217
argument_index = - 1
1098
1218
1099
1219
if whitespace_at_begin_of_token (token_at_position ) > 1 :
1100
- r = range_from_token (token_at_position )
1220
+ completion_range = range_from_token (token_at_position )
1101
1221
1102
1222
ws_b = whitespace_from_begin_of_token (token_at_position )
1103
- r .start .character += 2 if ws_b and ws_b [0 ] != "\t " else 1
1223
+ completion_range .start .character += 2 if ws_b and ws_b [0 ] != "\t " else 1
1104
1224
1105
- if position .is_in_range (r ) or r .end == position :
1225
+ if position .is_in_range (completion_range ) or completion_range .end == position :
1106
1226
argument_index += 1
1107
1227
1108
1228
if argument_index < 0 :
@@ -1114,6 +1234,25 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
1114
1234
if keyword_token is None :
1115
1235
return None
1116
1236
1237
+ completion_range = range_from_token (token_at_position )
1238
+ if (w := whitespace_at_begin_of_token (token_at_position )) > 0 :
1239
+ if w > 1 and completion_range .start .character + 1 < position .character :
1240
+ completion_range .start = position
1241
+ elif completion_range .start == position :
1242
+ pass
1243
+ else :
1244
+ return None
1245
+ else :
1246
+ if token_at_position .type != RobotToken .ARGUMENT :
1247
+ return None
1248
+
1249
+ if "=" in token_at_position .value :
1250
+ equal_index = token_at_position .value .index ("=" )
1251
+ if completion_range .start .character + equal_index < position .character :
1252
+ return None
1253
+ else :
1254
+ completion_range .end .character = completion_range .start .character + equal_index + 1
1255
+
1117
1256
result = await self .get_keyworddoc_and_token_from_position (
1118
1257
keyword_token .value ,
1119
1258
keyword_token ,
@@ -1137,16 +1276,15 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
1137
1276
# analyse_run_keywords=True,
1138
1277
# )
1139
1278
pass
1140
- r = range_from_token (token_at_position )
1141
- r .start = position
1279
+
1142
1280
return [
1143
1281
CompletionItem (
1144
1282
label = f"{ e .name } =" ,
1145
1283
kind = CompletionItemKind .VARIABLE ,
1146
1284
# detail=e.detail,
1147
1285
sort_text = f"02{ i } _{ e .name } =" ,
1148
1286
insert_text_format = InsertTextFormat .PLAINTEXT ,
1149
- text_edit = TextEdit (range = r , new_text = f"{ e .name } =" ),
1287
+ text_edit = TextEdit (range = completion_range , new_text = f"{ e .name } =" ),
1150
1288
data = {
1151
1289
"document_uri" : str (self .document .uri ),
1152
1290
"type" : "Argument" ,
0 commit comments