2727from robotcode .core .async_itertools import async_chain , async_chain_iterator
2828from robotcode .core .logging import LoggingDescriptor
2929from robotcode .core .lsp .types import (
30+ Command ,
3031 CompletionContext ,
3132 CompletionItem ,
3233 CompletionItemKind ,
@@ -104,6 +105,7 @@ async def get_header_style(self, config: CompletionConfig) -> str:
104105 "." ,
105106 "/" ,
106107 "{" ,
108+ "=" ,
107109 os .sep ,
108110 ],
109111 )
@@ -2021,8 +2023,8 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
20212023 from robot .parsing .lexer .tokens import Token as RobotToken
20222024 from robot .parsing .model .statements import Statement
20232025
2024- if context is None or context .trigger_kind != CompletionTriggerKind .INVOKED :
2025- return []
2026+ # if context is None or context.trigger_kind != CompletionTriggerKind.INVOKED:
2027+ # return []
20262028
20272029 kw_node = cast (Statement , node )
20282030
@@ -2040,24 +2042,35 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
20402042 if token_at_position .type not in [RobotToken .ARGUMENT , RobotToken .EOL , RobotToken .SEPARATOR ]:
20412043 return None
20422044
2043- if (
2044- token_at_position .type in [RobotToken .EOL , RobotToken .SEPARATOR ]
2045- and len (tokens_at_position ) > 1
2046- and tokens_at_position [- 2 ].type == RobotToken .KEYWORD
2047- ):
2045+ keyword_doc_and_token : Optional [Tuple [Optional [KeywordDoc ], Token ]] = None
2046+
2047+ keyword_token = kw_node .get_token (keyword_name_token_type )
2048+ if keyword_token is None :
2049+ return None
2050+
2051+ keyword_doc_and_token = await self .get_keyworddoc_and_token_from_position (
2052+ keyword_token .value ,
2053+ keyword_token ,
2054+ [t for t in kw_node .get_tokens (RobotToken .ARGUMENT )],
2055+ self .namespace ,
2056+ range_from_token (keyword_token ).start ,
2057+ analyse_run_keywords = False ,
2058+ )
2059+
2060+ if keyword_doc_and_token is None or keyword_doc_and_token [0 ] is None :
20482061 return None
20492062
2050- token_at_position_index = kw_node . tokens . index ( token_at_position )
2063+ keyword_doc = keyword_doc_and_token [ 0 ]
20512064
2052- argument_token_index = token_at_position_index
2053- while argument_token_index >= 0 and kw_node . tokens [ argument_token_index ]. type != RobotToken . ARGUMENT :
2054- argument_token_index -= 1
2065+ if keyword_doc . is_any_run_keyword ():
2066+ # TODO
2067+ pass
20552068
2056- argument_token : Optional [ RobotToken ] = None
2057- if argument_token_index >= 0 :
2058- argument_token = kw_node . tokens [ argument_token_index ]
2069+ argument_index , kw_arguments , argument_token = self . get_argument_info_at_position (
2070+ keyword_doc , kw_node . tokens , token_at_position , position
2071+ )
20592072
2060- result : Optional [ Tuple [ Optional [ KeywordDoc ], Token ]]
2073+ complete_argument_names = True
20612074
20622075 completion_range = range_from_token (argument_token or token_at_position )
20632076 completion_range .end = range_from_token (token_at_position ).end
@@ -2069,56 +2082,107 @@ async def _complete_KeywordCall_or_Fixture( # noqa: N802
20692082 else :
20702083 if "=" in (argument_token or token_at_position ).value :
20712084 equal_index = (argument_token or token_at_position ).value .index ("=" )
2072- if completion_range .start .character + equal_index < position . character :
2073- return None
2074-
2075- completion_range . end . character = completion_range . start . character + equal_index + 1
2076- else :
2077- completion_range .end = position
2085+ if position . character < completion_range .start .character + equal_index :
2086+ completion_range . end . character = completion_range . start . character + equal_index + 1
2087+ else :
2088+ complete_argument_names = False
2089+ completion_range . start . character = completion_range . start . character + equal_index + 1
2090+ completion_range .end = position
20782091
2079- result = await self .get_keyworddoc_and_token_from_position (
2080- keyword_token .value ,
2081- keyword_token ,
2082- [cast (Token , t ) for t in kw_node .get_tokens (RobotToken .ARGUMENT )],
2083- self .namespace ,
2084- range_from_token (keyword_token ).start ,
2085- analyse_run_keywords = False ,
2086- )
2092+ result = []
20872093
2088- if result is None or result [0 ] is None :
2089- return None
2094+ if argument_index >= 0 and keyword_doc .parent is not None and argument_index < len (kw_arguments ):
2095+ type_infos = keyword_doc .parent .get_types (kw_arguments [argument_index ].types )
2096+ for i , type_info in enumerate (type_infos ):
2097+ if type_info .name == "boolean" :
2098+ snippets = [
2099+ "True" ,
2100+ "False" ,
2101+ ]
2102+ for i , snippet in enumerate (snippets ):
2103+ result .append (
2104+ CompletionItem (
2105+ label = snippet ,
2106+ kind = CompletionItemKind .VALUE ,
2107+ detail = type_info .name ,
2108+ documentation = MarkupContent (MarkupKind .MARKDOWN , type_info .to_markdown ()),
2109+ sort_text = f"02{ i :03} _{ snippet } " ,
2110+ insert_text_format = InsertTextFormat .PLAIN_TEXT ,
2111+ text_edit = TextEdit (
2112+ range = completion_range ,
2113+ new_text = snippet ,
2114+ ),
2115+ )
2116+ )
20902117
2091- if result [0 ].is_any_run_keyword ():
2092- # TODO: complete run keyword
2093- # ks = await self.get_keyworddoc_and_token_from_position(
2094- # keyword_token.value,
2095- # keyword_token,
2096- # [cast(Token, t) for t in kw_node.get_tokens(RobotToken.ARGUMENT)],
2097- # namespace,
2098- # position,
2099- # analyse_run_keywords=True,
2100- # )
2101- pass
2118+ if type_info .members :
2119+ for member in type_info .members :
2120+ result .append (
2121+ CompletionItem (
2122+ label = member .name ,
2123+ kind = CompletionItemKind .ENUM_MEMBER ,
2124+ detail = type_info .name ,
2125+ documentation = MarkupContent (
2126+ MarkupKind .MARKDOWN , f"```python\n { member .name } = { member .value } \n ```"
2127+ ),
2128+ sort_text = f"01{ i } _{ member .name } " ,
2129+ insert_text_format = InsertTextFormat .PLAIN_TEXT ,
2130+ text_edit = TextEdit (
2131+ range = completion_range ,
2132+ new_text = member .name ,
2133+ ),
2134+ )
2135+ )
2136+ if type_info .items :
2137+ snippets = [
2138+ "{"
2139+ + ", " .join (
2140+ (str (m .key ) + ": ${" + str (i + 1 ) + "}" )
2141+ for i , m in enumerate (type_info .items )
2142+ if m .required
2143+ )
2144+ + "}" ,
2145+ f"{{{ ', ' .join ((str (m .key )+ ': ${' + str (i + 1 )+ '}' ) for i , m in enumerate (type_info .items ))} }}" ,
2146+ ]
2147+ for i , snippet in enumerate (snippets ):
2148+ result .append (
2149+ CompletionItem (
2150+ label = snippet ,
2151+ kind = CompletionItemKind .STRUCT ,
2152+ detail = type_info .name ,
2153+ documentation = MarkupContent (MarkupKind .MARKDOWN , type_info .to_markdown ()),
2154+ sort_text = f"02{ i :03} _{ snippet } " ,
2155+ insert_text_format = InsertTextFormat .SNIPPET ,
2156+ text_edit = TextEdit (
2157+ range = completion_range ,
2158+ new_text = snippet ,
2159+ ),
2160+ )
2161+ )
21022162
2103- return [
2104- CompletionItem (
2105- label = f"{ e .name } =" ,
2106- kind = CompletionItemKind .VARIABLE ,
2107- detail = "Argument" ,
2108- filter_text = e .name ,
2109- sort_text = f"02{ i } _{ e .name } =" ,
2110- insert_text_format = InsertTextFormat .PLAIN_TEXT ,
2111- text_edit = TextEdit (range = completion_range , new_text = f"{ e .name } =" ),
2112- )
2113- for i , e in enumerate (result [0 ].arguments )
2114- if e .kind
2115- not in [
2116- KeywordArgumentKind .VAR_POSITIONAL ,
2117- KeywordArgumentKind .VAR_NAMED ,
2118- KeywordArgumentKind .NAMED_ONLY_MARKER ,
2119- KeywordArgumentKind .POSITIONAL_ONLY_MARKER ,
2163+ if complete_argument_names :
2164+ result += [
2165+ CompletionItem (
2166+ label = f"{ e .name } =" ,
2167+ kind = CompletionItemKind .VARIABLE ,
2168+ detail = "Argument" ,
2169+ filter_text = e .name ,
2170+ sort_text = f"03{ i :03} _{ e .name } =" ,
2171+ insert_text_format = InsertTextFormat .PLAIN_TEXT ,
2172+ text_edit = TextEdit (range = completion_range , new_text = f"{ e .name } =" ),
2173+ command = Command ("" , "editor.action.triggerSuggest" , []),
2174+ )
2175+ for i , e in enumerate (kw_arguments )
2176+ if e .kind
2177+ not in [
2178+ KeywordArgumentKind .VAR_POSITIONAL ,
2179+ KeywordArgumentKind .VAR_NAMED ,
2180+ KeywordArgumentKind .NAMED_ONLY_MARKER ,
2181+ KeywordArgumentKind .POSITIONAL_ONLY_MARKER ,
2182+ ]
21202183 ]
2121- ]
2184+
2185+ return result
21222186
21232187 async def complete_KeywordCall ( # noqa: N802
21242188 self ,
0 commit comments