59
59
)
60
60
61
61
62
- class FindKeywordSectionVisitor (Visitor ):
62
+ class LastRealStatementFinder (Visitor ):
63
+ def __init__ (self ) -> None :
64
+ self .statement : Optional [ast .AST ] = None
65
+
66
+ @classmethod
67
+ def find_from (cls , model : ast .AST ) -> Optional [ast .AST ]:
68
+ finder = cls ()
69
+ finder .visit (model )
70
+ return finder .statement
71
+
72
+ def visit_Statement (self , statement : ast .AST ) -> None : # noqa: N802
73
+ from robot .parsing .model .statements import EmptyLine
74
+
75
+ if not isinstance (statement , EmptyLine ):
76
+ self .statement = statement
77
+
78
+
79
+ class FindSectionsVisitor (Visitor ):
63
80
def __init__ (self ) -> None :
64
81
self .keyword_sections : List [ast .AST ] = []
82
+ self .variable_sections : List [ast .AST ] = []
83
+ self .setting_sections : List [ast .AST ] = []
84
+ self .testcase_sections : List [ast .AST ] = []
85
+ self .sections : List [ast .AST ] = []
65
86
66
87
def visit_KeywordSection (self , node : ast .AST ) -> None : # noqa: N802
67
88
self .keyword_sections .append (node )
89
+ self .sections .append (node )
90
+
91
+ def visit_VariableSection (self , node : ast .AST ) -> None : # noqa: N802
92
+ self .variable_sections .append (node )
93
+ self .sections .append (node )
94
+
95
+ def visit_SettingSection (self , node : ast .AST ) -> None : # noqa: N802
96
+ self .setting_sections .append (node )
97
+ self .sections .append (node )
98
+
99
+ def visit_TestCaseSection (self , node : ast .AST ) -> None : # noqa: N802
100
+ self .testcase_sections .append (node )
101
+ self .sections .append (node )
102
+
103
+ def visit_CommentSection (self , node : ast .AST ) -> None : # noqa: N802
104
+ self .sections .append (node )
68
105
69
106
70
107
def find_keyword_sections (node : ast .AST ) -> Optional [List [ast .AST ]]:
71
- visitor = FindKeywordSectionVisitor ()
108
+ visitor = FindSectionsVisitor ()
72
109
visitor .visit (node )
73
110
return visitor .keyword_sections if visitor .keyword_sections else None
74
111
@@ -95,7 +132,7 @@ async def collect(
95
132
result .extend (code_actions )
96
133
97
134
if result :
98
- return result
135
+ return list ( sorted ( result , key = lambda ca : ca . title ))
99
136
100
137
return None
101
138
@@ -258,7 +295,7 @@ async def code_action_assign_result_to_variable(
258
295
259
296
return [
260
297
CodeAction (
261
- "Assign Result To Variable " ,
298
+ "Assign result to variable " ,
262
299
kind = "other" ,
263
300
command = Command (
264
301
self .parent .commands .get_command_name (self .assign_result_to_variable_command ),
@@ -349,7 +386,7 @@ async def code_action_create_local_variable(
349
386
return None
350
387
return [
351
388
CodeAction (
352
- "Create Local Variable " ,
389
+ "Create local variable " ,
353
390
kind = CodeActionKind .QUICK_FIX ,
354
391
command = Command (
355
392
self .parent .commands .get_command_name (self .create_local_variable_command ),
@@ -391,7 +428,7 @@ async def create_local_variable_command(self, document_uri: DocumentUri, range:
391
428
392
429
spaces = node .tokens [0 ].value if node .tokens and node .tokens [0 ].type == "SEPARATOR" else " "
393
430
394
- insert_text = f"{ spaces } ${{{ text } }} Set Variable value\n "
431
+ insert_text = f"{ spaces } ${{{ text } }} Set Variable value\n "
395
432
node_range = range_from_node (node )
396
433
insert_range = Range (start = Position (node_range .start .line , 0 ), end = Position (node_range .start .line , 0 ))
397
434
we = WorkspaceEdit (
@@ -401,7 +438,7 @@ async def create_local_variable_command(self, document_uri: DocumentUri, range:
401
438
[AnnotatedTextEdit ("create_local_variable" , insert_range , insert_text )],
402
439
)
403
440
],
404
- change_annotations = {"create_local_variable" : ChangeAnnotation ("Create Local Variable " , False )},
441
+ change_annotations = {"create_local_variable" : ChangeAnnotation ("Create Local variable " , False )},
405
442
)
406
443
407
444
if (await self .parent .workspace .apply_edit (we )).applied :
@@ -465,3 +502,118 @@ async def disable_robotcode_diagnostics_for_line_command(self, document_uri: Doc
465
502
)
466
503
467
504
await self .parent .workspace .apply_edit (we )
505
+
506
+ async def code_action_create_suite_variable (
507
+ self , sender : Any , document : TextDocument , range : Range , context : CodeActionContext
508
+ ) -> Optional [List [Union [Command , CodeAction ]]]:
509
+ if range .start == range .end and (
510
+ (context .only and CodeActionKind .QUICK_FIX in context .only )
511
+ or context .trigger_kind in [CodeActionTriggerKind .INVOKED , CodeActionTriggerKind .AUTOMATIC ]
512
+ ):
513
+ diagnostics = next (
514
+ (d for d in context .diagnostics if d .source == "robotcode.namespace" and d .code == "VariableNotFound" ),
515
+ None ,
516
+ )
517
+ if (
518
+ diagnostics is not None
519
+ and diagnostics .range .start .line == diagnostics .range .end .line
520
+ and diagnostics .range .start .character < diagnostics .range .end .character
521
+ ):
522
+ return [
523
+ CodeAction (
524
+ "Create suite variable" ,
525
+ kind = CodeActionKind .QUICK_FIX ,
526
+ command = Command (
527
+ self .parent .commands .get_command_name (self .create_suite_variable_command ),
528
+ self .parent .commands .get_command_name (self .create_suite_variable_command ),
529
+ [document .document_uri , diagnostics .range ],
530
+ ),
531
+ diagnostics = [diagnostics ],
532
+ )
533
+ ]
534
+
535
+ return None
536
+
537
+ @command ("robotcode.createSuiteVariable" )
538
+ async def create_suite_variable_command (self , document_uri : DocumentUri , range : Range ) -> None :
539
+ from robot .parsing .model .blocks import VariableSection
540
+ from robot .parsing .model .statements import Variable
541
+
542
+ if range .start .line == range .end .line and range .start .character < range .end .character :
543
+ document = await self .parent .documents .get (document_uri )
544
+ if document is None :
545
+ return
546
+
547
+ model = await self .parent .documents_cache .get_model (document , False )
548
+ nodes = await get_nodes_at_position (model , range .start )
549
+
550
+ node = nodes [- 1 ] if nodes else None
551
+
552
+ if node is None :
553
+ return
554
+
555
+ insert_range_prefix = ""
556
+ insert_range_suffix = ""
557
+
558
+ if any (n for n in nodes if isinstance (n , (VariableSection ))) and isinstance (node , Variable ):
559
+ node_range = range_from_node (node )
560
+ insert_range = Range (start = Position (node_range .start .line , 0 ), end = Position (node_range .start .line , 0 ))
561
+ else :
562
+ finder = FindSectionsVisitor ()
563
+ finder .visit (model )
564
+
565
+ if finder .variable_sections :
566
+ section = finder .variable_sections [- 1 ]
567
+
568
+ last_stmt = LastRealStatementFinder .find_from (section )
569
+ end_lineno = last_stmt .end_lineno if last_stmt else section .end_lineno
570
+ if end_lineno is None :
571
+ return
572
+
573
+ insert_range = Range (start = Position (end_lineno , 0 ), end = Position (end_lineno , 0 ))
574
+ else :
575
+ insert_range_prefix = "\n \n *** Variables ***\n "
576
+ if finder .setting_sections :
577
+ insert_range_prefix = "\n \n *** Variables ***\n "
578
+ insert_range_suffix = "\n \n "
579
+ section = finder .setting_sections [- 1 ]
580
+
581
+ last_stmt = LastRealStatementFinder .find_from (section )
582
+ end_lineno = last_stmt .end_lineno if last_stmt else section .end_lineno
583
+ if end_lineno is None :
584
+ return
585
+
586
+ insert_range = Range (start = Position (end_lineno , 0 ), end = Position (end_lineno , 0 ))
587
+ else :
588
+ insert_range_prefix = "*** Variables ***\n "
589
+ insert_range_suffix = "\n \n "
590
+ insert_range = Range (start = Position (0 , 0 ), end = Position (0 , 0 ))
591
+
592
+ lines = document .get_lines ()
593
+ text = lines [range .start .line ][range .start .character : range .end .character ]
594
+ if not text :
595
+ return
596
+ if insert_range .start .line == insert_range .end .line and insert_range .start .line >= len (lines ):
597
+ insert_range .start .line = len (lines ) - 1
598
+ insert_range .start .character = len (lines [- 1 ])
599
+ insert_range_prefix = "\n " + insert_range_prefix
600
+ insert_text = insert_range_prefix + f"${{{ text } }} value\n " + insert_range_suffix
601
+ we = WorkspaceEdit (
602
+ document_changes = [
603
+ TextDocumentEdit (
604
+ OptionalVersionedTextDocumentIdentifier (str (document .uri ), document .version ),
605
+ [AnnotatedTextEdit ("create_suite_variable" , insert_range , insert_text )],
606
+ )
607
+ ],
608
+ change_annotations = {"create_suite_variable" : ChangeAnnotation ("Create suite variable" , False )},
609
+ )
610
+
611
+ if (await self .parent .workspace .apply_edit (we )).applied :
612
+ splitted = insert_text .splitlines ()
613
+ start_line = next ((i for i , l in enumerate (splitted ) if "value" in l ), 0 )
614
+ insert_range .start .line = insert_range .start .line + start_line
615
+ insert_range .end .line = insert_range .start .line
616
+ insert_range .start .character = splitted [start_line ].index ("value" )
617
+ insert_range .end .character = insert_range .start .character + len ("value" )
618
+
619
+ await self .parent .window .show_document (str (document .uri ), take_focus = False , selection = insert_range )
0 commit comments