Skip to content

Commit 1e070cf

Browse files
committed
Add selection version wait endpoint
1 parent 5e2e12a commit 1e070cf

2 files changed

Lines changed: 101 additions & 25 deletions

File tree

src/robotframework_unity_editor/bridge_script.py

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@
2222
private static Thread _listenerThread;
2323
private static readonly object MainThreadQueueLock = new object();
2424
private static readonly Queue<Action> MainThreadQueue = new Queue<Action>();
25+
private static readonly object SelectionStateLock = new object();
26+
private static long _selectionVersion = 0;
27+
private static string _selectionHierarchyPath = "";
2528
2629
[Serializable]
2730
private class SelectionPayload
2831
{
2932
public bool ok;
3033
public string hierarchy_path;
34+
public long selection_version;
3135
public string error;
3236
}
3337
@@ -43,6 +47,7 @@
4347
EditorApplication.update += PumpMainThreadQueue;
4448
AssemblyReloadEvents.beforeAssemblyReload += StopBridge;
4549
EditorApplication.quitting += StopBridge;
50+
Selection.selectionChanged += OnSelectionChanged;
4651
}
4752
4853
private static void StartBridge()
@@ -52,6 +57,8 @@
5257
return;
5358
}
5459
60+
UpdateSelectionState();
61+
5562
try
5663
{
5764
_listener = new HttpListener();
@@ -146,6 +153,22 @@
146153
}
147154
}
148155
156+
private static void OnSelectionChanged()
157+
{
158+
UpdateSelectionState();
159+
}
160+
161+
private static void UpdateSelectionState()
162+
{
163+
var hierarchyPath = GetSelectedHierarchyPath();
164+
lock (SelectionStateLock)
165+
{
166+
_selectionVersion += 1;
167+
_selectionHierarchyPath = hierarchyPath ?? "";
168+
Monitor.PulseAll(SelectionStateLock);
169+
}
170+
}
171+
149172
private static bool ExecuteOnMainThread<T>(
150173
Func<T> function,
151174
int timeoutMs,
@@ -216,35 +239,62 @@
216239
var method = context.Request.HttpMethod.ToUpperInvariant();
217240
var path = context.Request.Url?.AbsolutePath ?? "/";
218241
219-
if (method == "GET" && path == "/v1/selection")
242+
if (method == "GET" && path == "/v1/selection/wait")
220243
{
221-
if (
222-
!ExecuteOnMainThread(
223-
GetSelectedHierarchyPath,
224-
RequestDispatchTimeoutMs,
225-
out string hierarchyPath,
226-
out string dispatchError
227-
)
228-
)
244+
long afterVersion = 0;
245+
var afterRaw = context.Request.QueryString["after_version"] ?? "";
246+
long.TryParse(afterRaw, out afterVersion);
247+
248+
var timeoutMs = 350;
249+
var timeoutRaw = context.Request.QueryString["timeout_ms"] ?? "";
250+
if (int.TryParse(timeoutRaw, out int parsedTimeoutMs))
229251
{
230-
WriteJson(
231-
context.Response,
232-
503,
233-
new SelectionPayload
252+
timeoutMs = parsedTimeoutMs;
253+
}
254+
timeoutMs = Math.Max(0, Math.Min(15000, timeoutMs));
255+
256+
var deadline = DateTime.UtcNow.AddMilliseconds(timeoutMs);
257+
SelectionPayload payload;
258+
lock (SelectionStateLock)
259+
{
260+
while (_selectionVersion <= afterVersion)
261+
{
262+
var remainingMs = (int)Math.Ceiling(
263+
(deadline - DateTime.UtcNow).TotalMilliseconds
264+
);
265+
if (remainingMs <= 0)
234266
{
235-
ok = false,
236-
hierarchy_path = "",
237-
error = dispatchError
267+
break;
238268
}
239-
);
240-
return;
269+
Monitor.Wait(SelectionStateLock, remainingMs);
270+
}
271+
payload = new SelectionPayload
272+
{
273+
ok = true,
274+
hierarchy_path = _selectionHierarchyPath ?? "",
275+
selection_version = _selectionVersion,
276+
error = ""
277+
};
241278
}
242279
243-
WriteJson(
244-
context.Response,
245-
200,
246-
new SelectionPayload { ok = true, hierarchy_path = hierarchyPath, error = "" }
247-
);
280+
WriteJson(context.Response, 200, payload);
281+
return;
282+
}
283+
284+
if (method == "GET" && path == "/v1/selection")
285+
{
286+
SelectionPayload payload;
287+
lock (SelectionStateLock)
288+
{
289+
payload = new SelectionPayload
290+
{
291+
ok = true,
292+
hierarchy_path = _selectionHierarchyPath ?? "",
293+
selection_version = _selectionVersion,
294+
error = ""
295+
};
296+
}
297+
WriteJson(context.Response, 200, payload);
248298
return;
249299
}
250300
@@ -322,7 +372,13 @@
322372
WriteJson(
323373
context.Response,
324374
200,
325-
new SelectionPayload { ok = true, hierarchy_path = normalized, error = "" }
375+
new SelectionPayload
376+
{
377+
ok = true,
378+
hierarchy_path = normalized,
379+
selection_version = _selectionVersion,
380+
error = ""
381+
}
326382
);
327383
return;
328384
}
@@ -334,6 +390,7 @@
334390
{
335391
ok = false,
336392
hierarchy_path = "",
393+
selection_version = _selectionVersion,
337394
error = "Endpoint not found."
338395
}
339396
);
@@ -343,7 +400,13 @@
343400
WriteJson(
344401
context.Response,
345402
500,
346-
new SelectionPayload { ok = false, hierarchy_path = "", error = ex.Message }
403+
new SelectionPayload
404+
{
405+
ok = false,
406+
hierarchy_path = "",
407+
selection_version = _selectionVersion,
408+
error = ex.Message
409+
}
347410
);
348411
}
349412
}

tests/test_helpers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ def test_bridge_script_does_not_dispatch_requests_via_delay_call_from_listener_t
3535
)
3636

3737

38+
def test_bridge_script_includes_selection_version_in_payload() -> None:
39+
assert "public long selection_version;" in UNITY_EDITOR_BRIDGE_SCRIPT
40+
41+
42+
def test_bridge_script_tracks_selection_changes_with_versioning() -> None:
43+
assert "Selection.selectionChanged +=" in UNITY_EDITOR_BRIDGE_SCRIPT
44+
45+
46+
def test_bridge_script_supports_selection_wait_endpoint() -> None:
47+
assert 'path == "/v1/selection/wait"' in UNITY_EDITOR_BRIDGE_SCRIPT
48+
assert "Monitor.Wait" in UNITY_EDITOR_BRIDGE_SCRIPT
49+
50+
3851
def test_bridge_script_supports_wildcard_root_hierarchy_paths() -> None:
3952
assert (
4053
'var allowAnyRoot = string.Equals(segments[0], "*", StringComparison.Ordinal);'

0 commit comments

Comments
 (0)