@@ -1770,11 +1770,11 @@ async def complete_VariablesImport( # noqa: N802
17701770 position : Position ,
17711771 context : Optional [CompletionContext ],
17721772 ) -> Union [List [CompletionItem ], CompletionList , None ]:
1773- from robot .parsing .lexer .tokens import Token
1774- from robot .parsing .model .statements import VariablesImport
1773+ from robot .parsing .lexer .tokens import Token as RobotToken
1774+ from robot .parsing .model .statements import Statement , VariablesImport
17751775
17761776 import_node = cast (VariablesImport , node )
1777- import_token = import_node .get_token (Token .VARIABLES )
1777+ import_token = import_node .get_token (RobotToken .VARIABLES )
17781778
17791779 if import_token is None :
17801780 return []
@@ -1784,85 +1784,191 @@ async def complete_VariablesImport( # noqa: N802
17841784
17851785 import_token_index = import_node .tokens .index (import_token )
17861786
1787- if len (import_node .tokens ) > import_token_index + 2 :
1788- name_token = import_node .tokens [import_token_index + 2 ]
1789- if not position .is_in_range (r := range_from_token (name_token )):
1790- return None
1787+ async def complete_import () -> Optional [List [CompletionItem ]]:
1788+ if len (import_node .tokens ) > import_token_index + 2 :
1789+ name_token = import_node .tokens [import_token_index + 2 ]
1790+ if not position .is_in_range (r := range_from_token (name_token )):
1791+ return None
17911792
1792- elif len (import_node .tokens ) > import_token_index + 1 :
1793- name_token = import_node .tokens [import_token_index + 1 ]
1794- if position .is_in_range (r := range_from_token (name_token )):
1795- if whitespace_at_begin_of_token (name_token ) > 1 :
1796- ws_b = whitespace_from_begin_of_token (name_token )
1797- r .start .character += 2 if ws_b and ws_b [0 ] != "\t " else 1
1793+ elif len (import_node .tokens ) > import_token_index + 1 :
1794+ name_token = import_node .tokens [import_token_index + 1 ]
1795+ if position .is_in_range (r := range_from_token (name_token )):
1796+ if whitespace_at_begin_of_token (name_token ) > 1 :
1797+ ws_b = whitespace_from_begin_of_token (name_token )
1798+ r .start .character += 2 if ws_b and ws_b [0 ] != "\t " else 1
17981799
1799- if not position .is_in_range (r ):
1800+ if not position .is_in_range (r ):
1801+ return None
1802+ else :
18001803 return None
1801- else :
1802- return None
1803- else :
1804- return None
1804+ else :
1805+ return None
18051806
1806- pos = position .character - r .start .character
1807- text_before_position = str (name_token .value )[:pos ].lstrip ()
1807+ pos = position .character - r .start .character
1808+ text_before_position = str (name_token .value )[:pos ].lstrip ()
18081809
1809- if text_before_position != "" and all (c == "." for c in text_before_position ):
1810- return None
1810+ if text_before_position != "" and all (c == "." for c in text_before_position ):
1811+ return None
18111812
1812- last_separator_index = (
1813- len (text_before_position )
1814- - next ((i for i , c in enumerate (reversed (text_before_position )) if c in ["/" , os .sep ]), - 1 )
1815- - 1
1816- )
1813+ last_separator_index = (
1814+ len (text_before_position )
1815+ - next ((i for i , c in enumerate (reversed (text_before_position )) if c in ["/" , os .sep ]), - 1 )
1816+ - 1
1817+ )
18171818
1818- first_part = (
1819- text_before_position [
1820- : last_separator_index + (1 if text_before_position [last_separator_index ] in ["/" , os .sep ] else 0 )
1819+ first_part = (
1820+ text_before_position [
1821+ : last_separator_index + (1 if text_before_position [last_separator_index ] in ["/" , os .sep ] else 0 )
1822+ ]
1823+ if last_separator_index < len (text_before_position )
1824+ else None
1825+ )
1826+
1827+ try :
1828+ complete_list = await self .namespace .imports_manager .complete_variables_import (
1829+ first_part if first_part else None ,
1830+ str (self .document .uri .to_path ().parent ),
1831+ await self .namespace .get_resolvable_variables (nodes_at_position , position ),
1832+ )
1833+ if not complete_list :
1834+ return None
1835+ except (SystemExit , KeyboardInterrupt , asyncio .CancelledError ):
1836+ raise
1837+ except BaseException :
1838+ return None
1839+
1840+ if text_before_position == "" :
1841+ r .start .character = position .character
1842+ else :
1843+ r .start .character += last_separator_index + 1 if last_separator_index < len (text_before_position ) else 0
1844+
1845+ return [
1846+ CompletionItem (
1847+ label = e .label ,
1848+ kind = CompletionItemKind .FILE
1849+ if e .kind in [CompleteResultKind .VARIABLES ]
1850+ else CompletionItemKind .FILE
1851+ if e .kind in [CompleteResultKind .FILE ]
1852+ else CompletionItemKind .FOLDER
1853+ if e .kind in [CompleteResultKind .FOLDER ]
1854+ else None ,
1855+ detail = e .kind .value ,
1856+ sort_text = f"030_{ e } " ,
1857+ insert_text_format = InsertTextFormat .PLAIN_TEXT ,
1858+ text_edit = TextEdit (range = r , new_text = e .label ) if r is not None else None ,
1859+ data = CompletionItemData (
1860+ document_uri = str (self .document .uri ),
1861+ type = e .kind .name ,
1862+ name = ((first_part ) if first_part is not None else "" ) + e .label ,
1863+ ),
1864+ )
1865+ for e in complete_list
18211866 ]
1822- if last_separator_index < len (text_before_position )
1823- else None
1824- )
18251867
1826- try :
1827- complete_list = await self .namespace .imports_manager .complete_variables_import (
1828- first_part if first_part else None ,
1829- str (self .document .uri .to_path ().parent ),
1830- await self .namespace .get_resolvable_variables (nodes_at_position , position ),
1831- )
1832- if not complete_list :
1868+ async def complete_arguments () -> Optional [List [CompletionItem ]]:
1869+ if (
1870+ import_node .name is None
1871+ or position <= range_from_token (import_node .get_token (RobotToken .NAME )).extend (end_character = 1 ).end
1872+ ):
18331873 return None
1834- except (SystemExit , KeyboardInterrupt , asyncio .CancelledError ):
1835- raise
1836- except BaseException :
1874+
1875+ with_name_token = next ((v for v in import_node .tokens if v .value == "WITH NAME" ), None )
1876+ if with_name_token is not None and position >= range_from_token (with_name_token ).start :
1877+ return None
1878+
1879+ if context is None or context .trigger_kind != CompletionTriggerKind .INVOKED :
1880+ return []
1881+
1882+ kw_node = cast (Statement , node )
1883+
1884+ tokens_at_position = get_tokens_at_position (kw_node , position )
1885+
1886+ if not tokens_at_position :
1887+ return None
1888+
1889+ token_at_position = tokens_at_position [- 1 ]
1890+
1891+ if token_at_position .type not in [RobotToken .ARGUMENT , RobotToken .EOL , RobotToken .SEPARATOR ]:
1892+ return None
1893+
1894+ if (
1895+ token_at_position .type == RobotToken .EOL
1896+ and len (tokens_at_position ) > 1
1897+ and tokens_at_position [- 2 ].type == RobotToken .KEYWORD
1898+ ):
1899+ return None
1900+
1901+ token_at_position_index = kw_node .tokens .index (token_at_position )
1902+
1903+ argument_token_index = token_at_position_index
1904+ while argument_token_index >= 0 and kw_node .tokens [argument_token_index ].type != RobotToken .ARGUMENT :
1905+ argument_token_index -= 1
1906+
1907+ argument_token : Optional [RobotToken ] = None
1908+ if argument_token_index >= 0 :
1909+ argument_token = kw_node .tokens [argument_token_index ]
1910+
1911+ completion_range = range_from_token (argument_token or token_at_position )
1912+ completion_range .end = range_from_token (token_at_position ).end
1913+ if (w := whitespace_at_begin_of_token (token_at_position )) > 0 :
1914+ if w > 1 and range_from_token (token_at_position ).start .character + 1 < position .character :
1915+ completion_range .start = position
1916+ elif completion_range .start != position :
1917+ return None
1918+ else :
1919+ if "=" in (argument_token or token_at_position ).value :
1920+ equal_index = (argument_token or token_at_position ).value .index ("=" )
1921+ if completion_range .start .character + equal_index < position .character :
1922+ return None
1923+
1924+ completion_range .end .character = completion_range .start .character + equal_index + 1
1925+ else :
1926+ completion_range .end = position
1927+
1928+ try :
1929+ libdoc = await self .namespace .get_imported_variables_libdoc (import_node .name , import_node .args )
1930+
1931+ except (SystemExit , KeyboardInterrupt , asyncio .CancelledError ):
1932+ raise
1933+ except BaseException as e :
1934+ self ._logger .exception (e )
1935+ return None
1936+
1937+ if libdoc is not None :
1938+ init = next ((v for v in libdoc .inits .values ()), None )
1939+
1940+ if init :
1941+ return [
1942+ CompletionItem (
1943+ label = f"{ e .name } =" ,
1944+ kind = CompletionItemKind .VARIABLE ,
1945+ sort_text = f"010{ i } _{ e .name } " ,
1946+ filter_text = e .name ,
1947+ insert_text_format = InsertTextFormat .PLAIN_TEXT ,
1948+ text_edit = TextEdit (range = completion_range , new_text = f"{ e .name } =" ),
1949+ data = CompletionItemData (
1950+ document_uri = str (self .document .uri ),
1951+ type = "Argument" ,
1952+ name = e .name ,
1953+ ),
1954+ )
1955+ for i , e in enumerate (init .args )
1956+ if e .kind
1957+ not in [
1958+ KeywordArgumentKind .VAR_POSITIONAL ,
1959+ KeywordArgumentKind .VAR_NAMED ,
1960+ KeywordArgumentKind .NAMED_ONLY_MARKER ,
1961+ KeywordArgumentKind .POSITIONAL_ONLY_MARKER ,
1962+ ]
1963+ ]
1964+
18371965 return None
18381966
1839- if text_before_position == "" :
1840- r .start .character = position .character
1841- else :
1842- r .start .character += last_separator_index + 1 if last_separator_index < len (text_before_position ) else 0
1967+ result = await complete_import () or []
1968+ # TODO this is not supported in robotframework, but it would be nice to have
1969+ # result.extend(await complete_arguments() or [])
18431970
1844- return [
1845- CompletionItem (
1846- label = e .label ,
1847- kind = CompletionItemKind .FILE
1848- if e .kind in [CompleteResultKind .VARIABLES ]
1849- else CompletionItemKind .FILE
1850- if e .kind in [CompleteResultKind .FILE ]
1851- else CompletionItemKind .FOLDER
1852- if e .kind in [CompleteResultKind .FOLDER ]
1853- else None ,
1854- detail = e .kind .value ,
1855- sort_text = f"030_{ e } " ,
1856- insert_text_format = InsertTextFormat .PLAIN_TEXT ,
1857- text_edit = TextEdit (range = r , new_text = e .label ) if r is not None else None ,
1858- data = CompletionItemData (
1859- document_uri = str (self .document .uri ),
1860- type = e .kind .name ,
1861- name = ((first_part ) if first_part is not None else "" ) + e .label ,
1862- ),
1863- )
1864- for e in complete_list
1865- ]
1971+ return result # noqa: RET504
18661972
18671973 async def _complete_KeywordCall_or_Fixture ( # noqa: N802
18681974 self ,
0 commit comments