@@ -128,7 +128,7 @@ async def collect(
128128 ) -> Optional [List [Union [Command , CodeAction ]]]:
129129 result = []
130130 for method in iter_methods (self , lambda m : m .__name__ .startswith ("code_action_" )):
131- code_actions = await method (self , document , range , context )
131+ code_actions = await method (document , range , context )
132132 if code_actions :
133133 result .extend (code_actions )
134134
@@ -138,11 +138,15 @@ async def collect(
138138 return None
139139
140140 async def code_action_create_keyword (
141- self , sender : Any , document : TextDocument , range : Range , context : CodeActionContext
141+ self , document : TextDocument , range : Range , context : CodeActionContext
142142 ) -> Optional [List [Union [Command , CodeAction ]]]:
143- if range .start == range .end and (
144- (context .only and CodeActionKind .QUICK_FIX in context .only )
145- or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
143+ if (
144+ range .start .line == range .end .line
145+ and range .start .character <= range .end .character
146+ and (
147+ (context .only and CodeActionKind .QUICK_FIX in context .only )
148+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
149+ )
146150 ):
147151 diagnostics = next (
148152 (
@@ -265,7 +269,7 @@ async def create_keyword_command(self, document_uri: DocumentUri, range: Range)
265269 await self .parent .window .show_document (str (document .uri ), take_focus = True , selection = insert_range )
266270
267271 async def code_action_assign_result_to_variable (
268- self , sender : Any , document : TextDocument , range : Range , context : CodeActionContext
272+ self , document : TextDocument , range : Range , context : CodeActionContext
269273 ) -> Optional [List [Union [Command , CodeAction ]]]:
270274 from robot .parsing .lexer import Token as RobotToken
271275 from robot .parsing .model .statements import (
@@ -275,9 +279,13 @@ async def code_action_assign_result_to_variable(
275279 TestTemplate ,
276280 )
277281
278- if range .start == range .end and (
279- (context .only and "other" in context .only )
280- or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
282+ if (
283+ range .start .line == range .end .line
284+ and range .start .character <= range .end .character
285+ and (
286+ (context .only and "other" in context .only )
287+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
288+ )
281289 ):
282290 model = await self .parent .documents_cache .get_model (document , False )
283291 node = await get_node_at_position (model , range .start )
@@ -318,7 +326,7 @@ async def assign_result_to_variable_command(self, document_uri: DocumentUri, ran
318326 TestTemplate ,
319327 )
320328
321- if range .start == range .end :
329+ if range .start . line == range .end . line and range . start . character <= range . end . character :
322330 document = await self .parent .documents .get (document_uri )
323331 if document is None :
324332 return
@@ -355,14 +363,18 @@ async def assign_result_to_variable_command(self, document_uri: DocumentUri, ran
355363 await self .parent .window .show_document (str (document .uri ), take_focus = True , selection = insert_range )
356364
357365 async def code_action_create_local_variable (
358- self , sender : Any , document : TextDocument , range : Range , context : CodeActionContext
366+ self , document : TextDocument , range : Range , context : CodeActionContext
359367 ) -> Optional [List [Union [Command , CodeAction ]]]:
360368 from robot .parsing .model .blocks import Keyword , TestCase
361369 from robot .parsing .model .statements import Documentation , Fixture , Statement , Template
362370
363- if range .start == range .end and (
364- (context .only and CodeActionKind .QUICK_FIX in context .only )
365- or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
371+ if (
372+ range .start .line == range .end .line
373+ and range .start .character <= range .end .character
374+ and (
375+ (context .only and CodeActionKind .QUICK_FIX in context .only )
376+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
377+ )
366378 ):
367379 diagnostics = next (
368380 (
@@ -409,7 +421,7 @@ async def create_local_variable_command(self, document_uri: DocumentUri, range:
409421 from robot .parsing .model .blocks import Keyword , TestCase
410422 from robot .parsing .model .statements import Documentation , Fixture , Statement , Template
411423
412- if range .start .line == range .end .line and range .start .character < range .end .character :
424+ if range .start .line == range .end .line and range .start .character <= range .end .character :
413425 document = await self .parent .documents .get (document_uri )
414426 if document is None :
415427 return
@@ -447,17 +459,21 @@ async def create_local_variable_command(self, document_uri: DocumentUri, range:
447459 )
448460
449461 if (await self .parent .workspace .apply_edit (we )).applied :
450- insert_range .start .character += insert_text .index ("value" )
462+ insert_range .start .character += insert_text .rindex ("value" )
451463 insert_range .end .character = insert_range .start .character + len ("value" )
452464
453465 await self .parent .window .show_document (str (document .uri ), take_focus = False , selection = insert_range )
454466
455467 async def code_action_disable_robotcode_diagnostics_for_line (
456- self , sender : Any , document : TextDocument , range : Range , context : CodeActionContext
468+ self , document : TextDocument , range : Range , context : CodeActionContext
457469 ) -> Optional [List [Union [Command , CodeAction ]]]:
458- if range .start == range .end and (
459- (context .only and CodeActionKind .QUICK_FIX in context .only )
460- or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
470+ if (
471+ range .start .line == range .end .line
472+ and range .start .character <= range .end .character
473+ and (
474+ (context .only and CodeActionKind .QUICK_FIX in context .only )
475+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
476+ )
461477 ):
462478 diagnostics = next ((d for d in context .diagnostics if d .source and d .source .startswith ("robotcode." )), None )
463479
@@ -479,7 +495,7 @@ async def code_action_disable_robotcode_diagnostics_for_line(
479495
480496 @command ("robotcode.disableRobotcodeDiagnosticsForLine" )
481497 async def disable_robotcode_diagnostics_for_line_command (self , document_uri : DocumentUri , range : Range ) -> None :
482- if range .start .line == range .end .line :
498+ if range .start .line == range .end .line and range . start . character <= range . end . character :
483499 document = await self .parent .documents .get (document_uri )
484500 if document is None :
485501 return
@@ -509,11 +525,15 @@ async def disable_robotcode_diagnostics_for_line_command(self, document_uri: Doc
509525 await self .parent .workspace .apply_edit (we )
510526
511527 async def code_action_create_suite_variable (
512- self , sender : Any , document : TextDocument , range : Range , context : CodeActionContext
528+ self , document : TextDocument , range : Range , context : CodeActionContext
513529 ) -> Optional [List [Union [Command , CodeAction ]]]:
514- if range .start == range .end and (
515- (context .only and CodeActionKind .QUICK_FIX in context .only )
516- or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
530+ if (
531+ range .start .line == range .end .line
532+ and range .start .character <= range .end .character
533+ and (
534+ (context .only and CodeActionKind .QUICK_FIX in context .only )
535+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
536+ )
517537 ):
518538 diagnostics = next (
519539 (
@@ -548,7 +568,7 @@ async def create_suite_variable_command(self, document_uri: DocumentUri, range:
548568 from robot .parsing .model .blocks import VariableSection
549569 from robot .parsing .model .statements import Variable
550570
551- if range .start .line == range .end .line and range .start .character < range .end .character :
571+ if range .start .line == range .end .line and range .start .character <= range .end .character :
552572 document = await self .parent .documents .get (document_uri )
553573 if document is None :
554574 return
@@ -622,7 +642,127 @@ async def create_suite_variable_command(self, document_uri: DocumentUri, range:
622642 start_line = next ((i for i , l in enumerate (splitted ) if "value" in l ), 0 )
623643 insert_range .start .line = insert_range .start .line + start_line
624644 insert_range .end .line = insert_range .start .line
625- insert_range .start .character = splitted [start_line ].index ("value" )
645+ insert_range .start .character = splitted [start_line ].rindex ("value" )
626646 insert_range .end .character = insert_range .start .character + len ("value" )
627647
628648 await self .parent .window .show_document (str (document .uri ), take_focus = False , selection = insert_range )
649+
650+ async def code_action_add_argument (
651+ self , document : TextDocument , range : Range , context : CodeActionContext
652+ ) -> Optional [List [Union [Command , CodeAction ]]]:
653+ from robot .parsing .model .blocks import Keyword
654+
655+ if (
656+ range .start .line == range .end .line
657+ and range .start .character <= range .end .character
658+ and (
659+ (context .only and CodeActionKind .QUICK_FIX in context .only )
660+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
661+ )
662+ ):
663+ diagnostics = next (
664+ (
665+ d
666+ for d in context .diagnostics
667+ if d .source == DIAGNOSTICS_SOURCE_NAME and d .code == Error .VARIABLE_NOT_FOUND
668+ ),
669+ None ,
670+ )
671+ if (
672+ diagnostics is not None
673+ and diagnostics .range .start .line == diagnostics .range .end .line
674+ and diagnostics .range .start .character < diagnostics .range .end .character
675+ ):
676+ text = document .get_lines ()[range .start .line ][range .start .character : range .end .character ]
677+ if not text :
678+ return None
679+
680+ model = await self .parent .documents_cache .get_model (document , False )
681+ nodes = await get_nodes_at_position (model , range .start )
682+
683+ if not any (n for n in nodes if isinstance (n , Keyword )):
684+ return None
685+
686+ return [
687+ CodeAction (
688+ "Add argument" ,
689+ kind = CodeActionKind .QUICK_FIX ,
690+ command = Command (
691+ self .parent .commands .get_command_name (self .action_add_argument_command ),
692+ self .parent .commands .get_command_name (self .action_add_argument_command ),
693+ [document .document_uri , diagnostics .range ],
694+ ),
695+ diagnostics = [diagnostics ],
696+ )
697+ ]
698+
699+ return None
700+
701+ @command ("robotcode.actionAddArgument" )
702+ async def action_add_argument_command (self , document_uri : DocumentUri , range : Range ) -> None :
703+ from robot .parsing .lexer .tokens import Token
704+ from robot .parsing .model .blocks import Keyword
705+ from robot .parsing .model .statements import Arguments , Documentation
706+
707+ if range .start .line == range .end .line and range .start .character <= range .end .character :
708+ document = await self .parent .documents .get (document_uri )
709+ if document is None :
710+ return
711+
712+ text = document .get_lines ()[range .start .line ][range .start .character : range .end .character ]
713+ if not text :
714+ return
715+
716+ model = await self .parent .documents_cache .get_model (document , False )
717+ nodes = await get_nodes_at_position (model , range .start )
718+
719+ keyword = next ((n for n in nodes if isinstance (n , Keyword )), None )
720+ if keyword is None :
721+ return
722+
723+ arguments = next ((n for n in keyword .body if isinstance (n , Arguments )), None )
724+
725+ if arguments is None :
726+ i = 0
727+ first_stmt = keyword .body [i ]
728+
729+ while isinstance (first_stmt , Documentation ) and i < len (keyword .body ):
730+ i += 1
731+ first_stmt = keyword .body [i ]
732+
733+ if i >= len (keyword .body ):
734+ return
735+
736+ spaces = (
737+ first_stmt .tokens [0 ].value
738+ if first_stmt is not None and first_stmt .tokens and first_stmt .tokens [0 ].type == "SEPARATOR"
739+ else " "
740+ )
741+
742+ insert_text = f"{ spaces } [Arguments] ${{{ text } }}=\n "
743+ node_range = range_from_node (first_stmt )
744+ insert_range = Range (start = Position (node_range .start .line , 0 ), end = Position (node_range .start .line , 0 ))
745+ else :
746+ insert_text = f" ${{{ text } }}="
747+ argument_tokens = arguments .get_tokens (Token .ARGUMENT )
748+ if argument_tokens :
749+ token_range = range_from_token (argument_tokens [- 1 ])
750+ else :
751+ token_range = range_from_token (arguments .get_token (Token .ARGUMENTS ))
752+ insert_range = Range (start = token_range .end , end = token_range .end )
753+
754+ we = WorkspaceEdit (
755+ document_changes = [
756+ TextDocumentEdit (
757+ OptionalVersionedTextDocumentIdentifier (str (document .uri ), document .version ),
758+ [AnnotatedTextEdit ("add_argument" , insert_range , insert_text )],
759+ )
760+ ],
761+ change_annotations = {"add_argument" : ChangeAnnotation ("Add Argument" , False )},
762+ )
763+
764+ if (await self .parent .workspace .apply_edit (we )).applied :
765+ insert_range .start .character += len (insert_text )
766+ insert_range .end .character = insert_range .start .character
767+
768+ await self .parent .window .show_document (str (document .uri ), take_focus = False , selection = insert_range )
0 commit comments