Skip to content

Commit 63c895f

Browse files
committed
feat(debugger): better synchronization between test view and execution in vscode, part 1
1 parent a7c7bfb commit 63c895f

File tree

5 files changed

+251
-39
lines changed

5 files changed

+251
-39
lines changed

packages/debugger/src/robotcode/debugger/listeners.py

Lines changed: 93 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,42 @@
1010

1111
from .dap_types import Event, Model
1212
from .debugger import Debugger
13+
from .mixins import SyncedEventBody
1314

1415

1516
@dataclass
16-
class RobotExecutionEventBody(Model):
17+
class RobotExecutionEventBody(Model, SyncedEventBody):
1718
type: str
1819
id: str
1920
name: str
2021
parent_id: Optional[str] = None
2122
attributes: Optional[Dict[str, Any]] = None
2223
failed_keywords: Optional[List[Dict[str, Any]]] = None
24+
source: Optional[str] = None
25+
lineno: Optional[int] = None
26+
synced: bool = True
27+
28+
29+
@dataclass
30+
class RobotEnqueuedEventBody(Model, SyncedEventBody):
31+
items: List[str]
32+
synced: bool = True
33+
34+
35+
@dataclass
36+
class RobotLogMessageEventBody(Model, SyncedEventBody):
37+
item_id: Optional[str]
38+
39+
message: Optional[str]
40+
level: Optional[str]
41+
timestamp: Optional[str]
42+
html: Optional[str]
43+
44+
source: Optional[str] = None
45+
lineno: Optional[int] = None
46+
column: Optional[int] = None
47+
48+
synced: bool = True
2349

2450

2551
def source_from_attributes(attributes: Dict[str, Any]) -> str:
@@ -40,6 +66,7 @@ def __init__(self) -> None:
4066

4167
def start_suite(self, name: str, attributes: Dict[str, Any]) -> None:
4268
id = f"{source_from_attributes(attributes)};{attributes.get('longname', '')}"
69+
4370
Debugger.instance().send_event(
4471
self,
4572
Event(
@@ -50,6 +77,8 @@ def start_suite(self, name: str, attributes: Dict[str, Any]) -> None:
5077
id=id,
5178
parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
5279
attributes=dict(attributes),
80+
source=source_from_attributes(attributes) or None,
81+
lineno=attributes.get("lineno", None),
5382
),
5483
),
5584
)
@@ -76,6 +105,8 @@ def end_suite(self, name: str, attributes: Dict[str, Any]) -> None:
76105
id=id,
77106
parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
78107
failed_keywords=self.failed_keywords,
108+
source=source_from_attributes(attributes) or None,
109+
lineno=attributes.get("lineno", None),
79110
),
80111
),
81112
)
@@ -96,6 +127,8 @@ def start_test(self, name: str, attributes: Dict[str, Any]) -> None:
96127
f"{attributes.get('lineno', 0)}",
97128
parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
98129
attributes=dict(attributes),
130+
source=source_from_attributes(attributes) or None,
131+
lineno=attributes.get("lineno", None),
99132
),
100133
),
101134
)
@@ -121,6 +154,8 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None:
121154
parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
122155
attributes=dict(attributes),
123156
failed_keywords=self.failed_keywords,
157+
source=source_from_attributes(attributes) or None,
158+
lineno=attributes.get("lineno", None),
124159
),
125160
),
126161
)
@@ -135,11 +170,45 @@ def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
135170
attributes.get("type"),
136171
)
137172

173+
# if Debugger.instance().state != State.CallKeyword:
174+
# Debugger.instance().send_event(
175+
# self,
176+
# Event(
177+
# event="robotStarted",
178+
# body=RobotExecutionEventBody(
179+
# type="keyword",
180+
# name=name,
181+
# id=f"{source_from_attributes(attributes)};{name};{attributes.get('lineno', 0)}",
182+
# parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
183+
# attributes=dict(attributes),
184+
# source=source_from_attributes(attributes) or None,
185+
# lineno=attributes.get("lineno", None),
186+
# ),
187+
# ),
188+
# )
189+
138190
Debugger.instance().start_keyword(name, attributes)
139191

140192
def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
141193
Debugger.instance().end_keyword(name, attributes)
142194

195+
# if Debugger.instance().state != State.CallKeyword:
196+
# Debugger.instance().send_event(
197+
# self,
198+
# Event(
199+
# event="robotEnded",
200+
# body=RobotExecutionEventBody(
201+
# type="keyword",
202+
# name=name,
203+
# id=f"{source_from_attributes(attributes)};{name};{attributes.get('lineno', 0)}",
204+
# parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
205+
# attributes=dict(attributes),
206+
# source=source_from_attributes(attributes) or None,
207+
# lineno=attributes.get("lineno", None),
208+
# ),
209+
# ),
210+
# )
211+
143212
if attributes["type"] in ["KEYWORD", "SETUP", "TEARDOWN"]:
144213
Debugger.instance().end_output_group(name, attributes, attributes.get("type"))
145214

@@ -189,14 +258,16 @@ def log_message(self, message: Dict[str, Any]) -> None:
189258
self,
190259
Event(
191260
event="robotLog",
192-
body={
193-
"itemId": item_id,
194-
"source": source,
195-
"lineno": line,
196-
"column": column,
197-
**dict(message),
198-
**({"message": msg} if msg else {}),
199-
},
261+
body=RobotLogMessageEventBody(
262+
item_id=item_id,
263+
message=msg if msg else message.get("message", None),
264+
level=message.get("level", None),
265+
timestamp=message.get("timestamp", None),
266+
html=message.get("html", None),
267+
source=source,
268+
lineno=line,
269+
column=column,
270+
),
200271
),
201272
)
202273

@@ -236,14 +307,16 @@ def message(self, message: Dict[str, Any]) -> None:
236307
self,
237308
Event(
238309
event="robotMessage",
239-
body={
240-
"itemId": item_id,
241-
"source": source,
242-
"lineno": line,
243-
"column": column,
244-
**dict(message),
245-
**({"message": msg} if msg else {}),
246-
},
310+
body=RobotLogMessageEventBody(
311+
item_id=item_id,
312+
message=msg if msg else message.get("message", None),
313+
level=message.get("level", None),
314+
timestamp=message.get("timestamp", None),
315+
html=message.get("html", None),
316+
source=source,
317+
lineno=line,
318+
column=column,
319+
),
247320
),
248321
)
249322

@@ -305,7 +378,7 @@ def enqueue(
305378

306379
items = list(reversed(list(enqueue(cast(running.model.TestSuite, data)))))
307380

308-
Debugger.instance().send_event(self, Event(event="robotEnqueued", body={"items": items}))
381+
Debugger.instance().send_event(self, Event(event="robotEnqueued", body=RobotEnqueuedEventBody(items)))
309382

310383
self._event_sended = True
311384

@@ -337,6 +410,8 @@ def report_status(
337410
if isinstance(result_item, result.TestCase)
338411
else ""
339412
),
413+
source=str(normalized_path(Path(result_item.source))) if result_item.source else None,
414+
lineno=data_item.lineno if data_item else None,
340415
),
341416
),
342417
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from typing import Optional
2+
3+
4+
class SyncedEventBody:
5+
synced: Optional[bool]

packages/debugger/src/robotcode/debugger/server.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
)
5151
from .debugger import Debugger, PathMapping
5252
from .default_capabilities import DFEAULT_CAPABILITIES
53+
from .mixins import SyncedEventBody
5354
from .protocol import DebugAdapterProtocol
5455

5556
TCP_DEFAULT_PORT = 6612
@@ -78,12 +79,22 @@ def __init__(self) -> None:
7879
self._received_configuration_done_event = threading.Event()
7980
self._received_configuration_done = False
8081
self.received_configuration_done_callback: Optional[Callable[[], None]] = None
82+
self.sync_event: threading.Event = threading.Event()
8183

8284
Debugger.instance().send_event.add(self.on_debugger_send_event)
8385

84-
def on_debugger_send_event(self, sender: Any, event: Event) -> None:
86+
def on_debugger_send_event(self, sender: Any, event: Event, synced: bool = False) -> None:
8587
if self._loop is not None:
86-
asyncio.run_coroutine_threadsafe(self.send_event_async(event), loop=self._loop)
88+
synced = True if isinstance(event.body, SyncedEventBody) and event.body.synced else False
89+
90+
if synced:
91+
self.sync_event.clear()
92+
93+
# asyncio.run_coroutine_threadsafe(self.send_event_async(event), loop=self._loop).result(15)
94+
self._loop.call_soon_threadsafe(self.send_event, event)
95+
96+
if synced:
97+
self.sync_event.wait(15)
8798

8899
@property
89100
def connected(self) -> bool:
@@ -383,6 +394,14 @@ async def _completions(
383394
result = Debugger.instance().completions(text, column, line, frame_id)
384395
return CompletionsResponseBody(targets=result)
385396

397+
@rpc_method(name="robot/sync", threaded=True)
398+
def _robot_sync(
399+
self,
400+
*args: Any,
401+
**kwargs: Any,
402+
) -> None:
403+
self.sync_event.set()
404+
386405

387406
class DebugAdapterServer(JsonRPCServer[DebugAdapterServerProtocol]):
388407
def __init__(

robot.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ python-path = [
33
"tests/robotcode/language_server/robotframework/parts/data/resources",
44
]
55
paths = "tests/robotcode/language_server/robotframework/parts/data"
6+
rpa = false
67

78
[tool.robotcode-analyze.code]
89
exit-code-mask = ["info", "hint"]

0 commit comments

Comments
 (0)