Skip to content

Commit 536db5e

Browse files
committed
fix(langserver): correted folding of IF blocks with multiple ELSE/ELSE IF's
1 parent 5987743 commit 536db5e

File tree

359 files changed

+3547
-11863
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

359 files changed

+3547
-11863
lines changed
Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
from __future__ import annotations
22

33
import ast
4-
from typing import TYPE_CHECKING, Any, List, Optional, cast
4+
import time
5+
from typing import TYPE_CHECKING, Any, List, Optional
56

7+
from robot.parsing.model.blocks import If, Keyword, TestCase
68
from robotcode.core.logging import LoggingDescriptor
79
from robotcode.core.lsp.types import FoldingRange
810

911
from ...common.decorators import language_id
1012
from ...common.text_document import TextDocument
11-
from ..utils.async_ast import AsyncVisitor
13+
from ..utils.async_ast import Visitor
1214
from .protocol_part import RobotLanguageServerProtocolPart
1315

1416
if TYPE_CHECKING:
@@ -17,7 +19,7 @@
1719
)
1820

1921

20-
class _Visitor(AsyncVisitor):
22+
class _Visitor(Visitor):
2123
def __init__(self, parent: RobotFoldingRangeProtocolPart) -> None:
2224
super().__init__()
2325
self.parent = parent
@@ -33,72 +35,83 @@ def __init__(self, parent: RobotFoldingRangeProtocolPart) -> None:
3335
)
3436

3537
self.result: List[FoldingRange] = []
38+
self.current_if: List[ast.AST] = []
3639

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)
3942

4043
@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]]:
4245
finder = cls(parent)
4346

44-
await finder.visit(model)
47+
finder.visit(model)
4548

4649
return finder.result if finder.result else None
4750

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
4954
if not self.line_folding_only:
5055
self.result.append(
5156
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,
5661
kind=kind,
5762
)
5863
)
5964
else:
6065
self.result.append(
6166
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,
6469
kind=kind,
6570
)
6671
)
6772

68-
async def visit_Section(self, node: ast.AST) -> None: # noqa: N802
73+
def visit_Section(self, node: ast.AST) -> None: # noqa: N802
6974
self.__append(node, kind="section")
7075

71-
await self.generic_visit(node)
76+
self.generic_visit(node)
7277

73-
async def visit_CommentSection(self, node: ast.AST) -> None: # noqa: N802
78+
def visit_CommentSection(self, node: ast.AST) -> None: # noqa: N802
7479
self.__append(node, kind="comment")
75-
await self.generic_visit(node)
80+
self.generic_visit(node)
7681

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:
8184
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)
8686

87-
if cast(Keyword, node).name:
87+
def visit_Keyword(self, node: Keyword) -> None: # noqa: N802
88+
if node.name:
8889
self.__append(node, kind="keyword")
89-
await self.generic_visit(node)
90+
self.generic_visit(node)
9091

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
9293
self.__append(node, kind="for_loop")
93-
await self.generic_visit(node)
94+
self.generic_visit(node)
9495

95-
async def visit_For(self, node: ast.AST) -> None: # noqa: N802
96+
def visit_For(self, node: ast.AST) -> None: # noqa: N802
9697
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")
98107

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)
102115

103116

104117
class RobotFoldingRangeProtocolPart(RobotLanguageServerProtocolPart):
@@ -112,4 +125,10 @@ def __init__(self, parent: RobotLanguageServerProtocol) -> None:
112125
@language_id("robotframework")
113126
@_logger.call
114127
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+
)

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf41/test_foldingrange.test[False-foldingrange.robot-000-001-Settings_Start].out

Lines changed: 0 additions & 82 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
data: !GeneratedTestData
2+
character: 1
3+
line: 0
4+
name: Settings start
5+
result:
6+
- !FoldingRange
7+
collapsed_text: null
8+
end_character: 1
9+
end_line: 4
10+
kind: section
11+
start_character: 0
12+
start_line: 0

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf41/test_foldingrange.test[False-foldingrange.robot-005-001-Settings_End].out

Lines changed: 0 additions & 82 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
data: !GeneratedTestData
2+
character: 1
3+
line: 5
4+
name: Settings end
5+
result:
6+
- !FoldingRange
7+
collapsed_text: null
8+
end_character: 1
9+
end_line: 4
10+
kind: section
11+
start_character: 0
12+
start_line: 0

0 commit comments

Comments
 (0)