Skip to content

Commit e8b0273

Browse files
committed
correct completion of arguments
1 parent d730223 commit e8b0273

File tree

1 file changed

+212
-74
lines changed

1 file changed

+212
-74
lines changed

robotcode/language_server/robotframework/parts/completion.py

Lines changed: 212 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -855,98 +855,218 @@ async def complete_LibraryImport( # noqa: N802
855855
position: Position,
856856
context: Optional[CompletionContext],
857857
) -> 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
860861

861862
if self.document is None:
862863
return []
863864

864865
import_node = cast(LibraryImport, node)
865-
import_token = import_node.get_token(Token.LIBRARY)
866+
import_token = import_node.get_token(RobotToken.LIBRARY)
866867
if import_token is None:
867868
return []
868869
import_token_index = import_node.tokens.index(import_token)
869870

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:
873891
return None
874892

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()
879895

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
882898

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+
)
887904

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+
)
890913

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 ""
893915

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)
899917

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
903955
]
904-
if last_separator_index < len(text_before_position)
905-
else None
906-
)
907956

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 []
909960

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 []
911963

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:
917973
return None
918-
except (SystemExit, KeyboardInterrupt, asyncio.CancelledError):
919-
raise
920-
except BaseException:
921-
return None
922974

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]
927976

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
9501070

9511071
async def complete_ResourceImport( # noqa: N802
9521072
self,
@@ -1097,12 +1217,12 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
10971217
argument_index = -1
10981218

10991219
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)
11011221

11021222
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
11041224

1105-
if position.is_in_range(r) or r.end == position:
1225+
if position.is_in_range(completion_range) or completion_range.end == position:
11061226
argument_index += 1
11071227

11081228
if argument_index < 0:
@@ -1114,6 +1234,25 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
11141234
if keyword_token is None:
11151235
return None
11161236

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+
11171256
result = await self.get_keyworddoc_and_token_from_position(
11181257
keyword_token.value,
11191258
keyword_token,
@@ -1137,16 +1276,15 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
11371276
# analyse_run_keywords=True,
11381277
# )
11391278
pass
1140-
r = range_from_token(token_at_position)
1141-
r.start = position
1279+
11421280
return [
11431281
CompletionItem(
11441282
label=f"{e.name}=",
11451283
kind=CompletionItemKind.VARIABLE,
11461284
# detail=e.detail,
11471285
sort_text=f"02{i}_{e.name}=",
11481286
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}="),
11501288
data={
11511289
"document_uri": str(self.document.uri),
11521290
"type": "Argument",

0 commit comments

Comments
 (0)