Skip to content

Commit beed056

Browse files
ghackettclaude
andcommitted
Address the review of PR 609: a change that waited for a failed reload is still judged
_on_loaded's failure branch returned before _settle_pending_change, so a file-changed queued behind a reload whose read then failed (the file gone or binary between the monitor and the read) was dropped: the buffer never learnt the file was gone. The reload branch settles it now; check_editor_save.py drives the race. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_011sbC3wuGjnM1AysiE7RjJ8
1 parent 4abe67d commit beed056

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

‎collins/editor.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,10 @@ def _on_loaded(self, opened: _OpenFile, load_id: int, kind: str, value) -> None:
903903
self._notify(
904904
_("Couldn't reload {name}: {message}").format(name=opened.path.name, message=failure)
905905
)
906+
# A `file-changed` that waited for this reload is judged all
907+
# the same: the file may be gone or binary now, and that
908+
# event is what tells the buffer so (review of PR 609).
909+
self._settle_pending_change(opened)
906910
else:
907911
self._notify(
908912
_("Couldn't open {name}: {message}").format(name=opened.path.name, message=failure)

‎scripts/check_editor_save.py‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
file carries the edit; a clean buffer reloads silently, cursor kept; a
1717
save over a file that moved underneath is refused `stale` and asks before
1818
overwriting (the "changed on disk" dialog, stubbed), the file untouched
19-
until Overwrite; a deleted file is told and marked dirty; a closed tab's
19+
until Overwrite; a deleted file is told and marked dirty; a change that
20+
waited for a reload is judged even when the reload fails; a closed tab's
2021
watch is gone. The pane never opens a file itself.
2122
2223
This is a script, not a pytest test, on purpose: tests/conftest.py blocks
@@ -64,6 +65,8 @@
6465
Adw.init()
6566

6667
from collins import dialogs, remotefiles # noqa: E402
68+
from collins.api import protocol # noqa: E402
69+
from collins.api.protocol import RequestRefused # noqa: E402
6770
from collins.editor import EditorPane # noqa: E402
6871

6972
PASSED = 0
@@ -269,6 +272,36 @@ def fake_confirm(parent, heading, body, confirm_label, on_confirm, on_dismiss=No
269272
check("and its buffer is dirty, so nothing saves over nothing silently", other.buffer.get_modified())
270273
check("its mtime is forgotten", other.mtime is None)
271274

275+
# -- a change that waited for a reload is judged even when the reload fails ------
276+
# Two external changes racing one reload: the second is queued while the
277+
# read is in flight, and the read then fails (the file went away between
278+
# the monitor and the read). The queued `gone` is still judged, so the
279+
# buffer is told and marked dirty (the review of PR 609).
280+
third = os.path.join(root, "third.txt")
281+
with open(third, "w") as fh:
282+
fh.write("third\n")
283+
pane.open_file(third)
284+
racing = pane._open[third]
285+
if not wait_for(lambda: not racing.loading):
286+
print("FAIL the third read never landed")
287+
return 1
288+
pane._banner.set_revealed(False)
289+
racing.loading = True # a reload in flight...
290+
racing.reloading = True
291+
pane._check_external(
292+
racing, {"handle": racing.watch_handle, "path": third, "mtime": None, "size": None, "gone": True}
293+
)
294+
check("a change during a reload waits for it", racing.pending_change is not None and not racing.buffer.get_modified())
295+
pane._on_loaded(
296+
racing, racing.load_id, "refused", RequestRefused(protocol.ERROR_GONE, "{name} is not there", {"name": "third.txt"})
297+
)
298+
check("the failed reload is told", pane._banner.get_revealed())
299+
check(
300+
"and the waiting change is judged after it",
301+
racing.pending_change is None and racing.mtime is None and racing.buffer.get_modified(),
302+
(racing.pending_change, racing.mtime, racing.buffer.get_modified()),
303+
)
304+
272305
# -- closing a tab drops its watch -----------------------------------------------
273306
handle = other.watch_handle
274307
pane._close_confirmed.add(pane._pages[second])

0 commit comments

Comments
 (0)