1
1
from __future__ import annotations
2
2
3
3
import ast
4
- from typing import TYPE_CHECKING , Any , List , Optional , cast
4
+ import time
5
+ from typing import TYPE_CHECKING , Any , List , Optional
5
6
7
+ from robot .parsing .model .blocks import If , Keyword , TestCase
6
8
from robotcode .core .logging import LoggingDescriptor
7
9
from robotcode .core .lsp .types import FoldingRange
8
10
9
11
from ...common .decorators import language_id
10
12
from ...common .text_document import TextDocument
11
- from ..utils .async_ast import AsyncVisitor
13
+ from ..utils .async_ast import Visitor
12
14
from .protocol_part import RobotLanguageServerProtocolPart
13
15
14
16
if TYPE_CHECKING :
17
19
)
18
20
19
21
20
- class _Visitor (AsyncVisitor ):
22
+ class _Visitor (Visitor ):
21
23
def __init__ (self , parent : RobotFoldingRangeProtocolPart ) -> None :
22
24
super ().__init__ ()
23
25
self .parent = parent
@@ -33,72 +35,83 @@ def __init__(self, parent: RobotFoldingRangeProtocolPart) -> None:
33
35
)
34
36
35
37
self .result : List [FoldingRange ] = []
38
+ self .current_if : List [ast .AST ] = []
36
39
37
- async def visit (self , node : ast .AST ) -> None :
38
- await super ().visit (node )
40
+ def visit (self , node : ast .AST ) -> None :
41
+ super ().visit (node )
39
42
40
43
@classmethod
41
- async def find_from (cls , model : ast .AST , parent : RobotFoldingRangeProtocolPart ) -> Optional [List [FoldingRange ]]:
44
+ def find_from (cls , model : ast .AST , parent : RobotFoldingRangeProtocolPart ) -> Optional [List [FoldingRange ]]:
42
45
finder = cls (parent )
43
46
44
- await finder .visit (model )
47
+ finder .visit (model )
45
48
46
49
return finder .result if finder .result else None
47
50
48
- def __append (self , node : ast .AST , kind : str ) -> None :
51
+ def __append (self , start_node : ast .AST , kind : str , end_node : Optional [ast .AST ] = None ) -> None :
52
+ if end_node is None :
53
+ end_node = start_node
49
54
if not self .line_folding_only :
50
55
self .result .append (
51
56
FoldingRange (
52
- start_line = node .lineno - 1 ,
53
- end_line = node .end_lineno - 1 if node .end_lineno is not None else node .lineno - 1 ,
54
- start_character = node .col_offset if not self .line_folding_only else None ,
55
- end_character = node .end_col_offset if not self .line_folding_only else None ,
57
+ start_line = start_node .lineno - 1 ,
58
+ end_line = end_node .end_lineno - 1 if end_node .end_lineno is not None else end_node .lineno - 1 ,
59
+ start_character = start_node .col_offset if not self .line_folding_only else None ,
60
+ end_character = end_node .end_col_offset if not self .line_folding_only else None ,
56
61
kind = kind ,
57
62
)
58
63
)
59
64
else :
60
65
self .result .append (
61
66
FoldingRange (
62
- start_line = node .lineno - 1 ,
63
- end_line = node .end_lineno - 1 if node .end_lineno is not None else node .lineno - 1 ,
67
+ start_line = start_node .lineno - 1 ,
68
+ end_line = end_node .end_lineno - 1 if end_node .end_lineno is not None else end_node .lineno - 1 ,
64
69
kind = kind ,
65
70
)
66
71
)
67
72
68
- async def visit_Section (self , node : ast .AST ) -> None : # noqa: N802
73
+ def visit_Section (self , node : ast .AST ) -> None : # noqa: N802
69
74
self .__append (node , kind = "section" )
70
75
71
- await self .generic_visit (node )
76
+ self .generic_visit (node )
72
77
73
- async def visit_CommentSection (self , node : ast .AST ) -> None : # noqa: N802
78
+ def visit_CommentSection (self , node : ast .AST ) -> None : # noqa: N802
74
79
self .__append (node , kind = "comment" )
75
- await self .generic_visit (node )
80
+ self .generic_visit (node )
76
81
77
- async def visit_TestCase (self , node : ast .AST ) -> None : # noqa: N802
78
- from robot .parsing .model .blocks import TestCase
79
-
80
- if cast (TestCase , node ).name :
82
+ def visit_TestCase (self , node : TestCase ) -> None : # noqa: N802
83
+ if node .name :
81
84
self .__append (node , kind = "testcase" )
82
- await self .generic_visit (node )
83
-
84
- async def visit_Keyword (self , node : ast .AST ) -> None : # noqa: N802
85
- from robot .parsing .model .blocks import Keyword
85
+ self .generic_visit (node )
86
86
87
- if cast (Keyword , node ).name :
87
+ def visit_Keyword (self , node : Keyword ) -> None : # noqa: N802
88
+ if node .name :
88
89
self .__append (node , kind = "keyword" )
89
- await self .generic_visit (node )
90
+ self .generic_visit (node )
90
91
91
- async def visit_ForLoop (self , node : ast .AST ) -> None : # noqa: N802, pragma: no cover
92
+ def visit_ForLoop (self , node : ast .AST ) -> None : # noqa: N802
92
93
self .__append (node , kind = "for_loop" )
93
- await self .generic_visit (node )
94
+ self .generic_visit (node )
94
95
95
- async def visit_For (self , node : ast .AST ) -> None : # noqa: N802
96
+ def visit_For (self , node : ast .AST ) -> None : # noqa: N802
96
97
self .__append (node , kind = "for" )
97
- await self .generic_visit (node )
98
+ self .generic_visit (node )
99
+
100
+ def visit_If (self , node : If ) -> None : # noqa: N802
101
+ if node .orelse is not None and node .body [- 1 ]:
102
+ self .__append (node , kind = "if" , end_node = node .body [- 1 ])
103
+ elif node .orelse is None and node .type == "ELSE" :
104
+ self .__append (node , kind = "if" , end_node = self .current_if [- 1 ] if self .current_if else None )
105
+ else :
106
+ self .__append (node , kind = "if" )
98
107
99
- async def visit_If (self , node : ast .AST ) -> None : # noqa: N802
100
- self .__append (node , kind = "if" )
101
- await self .generic_visit (node )
108
+ if node .type == "IF" :
109
+ self .current_if .append (node )
110
+
111
+ self .generic_visit (node )
112
+
113
+ if node .type == "IF" :
114
+ self .current_if .remove (node )
102
115
103
116
104
117
class RobotFoldingRangeProtocolPart (RobotLanguageServerProtocolPart ):
@@ -112,4 +125,10 @@ def __init__(self, parent: RobotLanguageServerProtocol) -> None:
112
125
@language_id ("robotframework" )
113
126
@_logger .call
114
127
async def collect (self , sender : Any , document : TextDocument ) -> Optional [List [FoldingRange ]]:
115
- return await _Visitor .find_from (await self .parent .documents_cache .get_model (document , False ), self )
128
+ start = time .monotonic ()
129
+ try :
130
+ return _Visitor .find_from (await self .parent .documents_cache .get_model (document , False ), self )
131
+ finally :
132
+ self ._logger .critical (
133
+ lambda : f"Folding ranges collected in { (time .monotonic () - start ) * 1000 } ms" ,
134
+ )
0 commit comments