fix(cutter): read the active document from core configuration

Read file.path through CutterCore's scalar configuration API instead of invoking the debug-only currentlyOpenFile backup field. Preserve exact string paths and unknowns so existing discovery, generation admission and Evidence consumers receive the owning facts.

Validated 11 Python protocol tests, 66 relevant CLI/MCP/provider tests, full static/docs gates, and official Linux Cutter 2.5.0 Qt5 workflows with a Unicode/#? ELF path. Same-path reload identity remains partial and native document switching is unverified.

Fixes #4497.
This commit is contained in:
Ysopwnable authored and GitHub committed 2026-10-16 23:12:18 +08:00
1 parent 9e7e13fb71
commit 2c1dee16df
2 files changed
+62 -3

No files matched your search

+2 -2
View File
@@ -333,8 +333,8 @@ class _RequestDispatcher(QObject):
@staticmethod
def _read_current_file() -> str | None:
try:
value = cutter.core().currentlyOpenFile()
return str(value) if value else None
value = cutter.core().getConfig("file.path")
return value if isinstance(value, str) and value else None
except Exception:
return None
+60 -1
View File
@@ -14,6 +14,7 @@ import types
import unittest
import uuid
from pathlib import Path
from unittest.mock import patch
def _install_host_stubs():
@@ -22,7 +23,7 @@ def _install_host_stubs():
sys.modules["cutter"] = cutter
qtcore = types.ModuleType("PySide6.QtCore")
qtcore.QObject = type("QObject", (), {})
qtcore.Signal = lambda *_args: object()
qtcore.Signal = lambda *_args: types.SimpleNamespace(connect=lambda _handler: None)
pyside = types.ModuleType("PySide6")
pyside.QtCore = qtcore
sys.modules["PySide6"] = pyside
@@ -221,6 +222,64 @@ def run_serve_mode():
server.join(timeout=3)
class DocumentIdentityTest(unittest.TestCase):
def test_document_config_drives_generation_and_command_admission(self):
first = "/tmp/Cutter 雪 #1?.elf"
second = "/tmp/another document.elf"
configuration = {"file.path": first}
core = types.SimpleNamespace(
getConfig=configuration.__getitem__,
getVersionInformation=lambda: "backend fixture",
)
commands = []
with patch.object(bridge.cutter, "core", lambda: core, create=True), patch.object(
bridge.cutter, "cmd", lambda command: commands.append(command) or "observed", create=True
):
dispatcher = bridge._RequestDispatcher()
self.assertEqual(dispatcher.current_file, first)
self.assertEqual(dispatcher.generation, 0)
def handle(**values):
request = {**values, "_event": threading.Event(), "_cancelled": threading.Event()}
reply = {}
dispatcher._handle(request, reply)
self.assertTrue(request["_event"].is_set())
return reply
same = handle(kind="status")
self.assertEqual(same["current_file"], first)
self.assertEqual(same["document_generation"], 0)
configuration["file.path"] = second
changed = handle(kind="status")
self.assertEqual(changed["current_file"], second)
self.assertEqual(changed["document_generation"], 1)
stale = handle(kind="command", request_id=str(uuid.uuid4()), expected_generation=0, command="iI")
self.assertEqual(stale["error"], "active-document-generation-changed")
self.assertEqual(stale["execution_state"], "not_started")
self.assertEqual(commands, [])
current = handle(kind="command", request_id=str(uuid.uuid4()), expected_generation=1, command="iI")
self.assertEqual(current["output"], "observed")
self.assertEqual(current["document_generation"], 1)
self.assertEqual(commands, ["iI"])
def test_absent_or_unreadable_document_config_remains_unknown(self):
class UnreadableCore:
def getConfig(self, _key):
raise RuntimeError("document config unavailable")
for core in [
types.SimpleNamespace(getConfig=lambda _key: ""),
types.SimpleNamespace(getConfig=lambda _key: None),
types.SimpleNamespace(getConfig=lambda _key: object()),
types.SimpleNamespace(),
UnreadableCore(),
]:
with self.subTest(core=type(core).__name__), patch.object(
bridge.cutter, "core", lambda: core, create=True
):
self.assertIsNone(bridge._RequestDispatcher._read_current_file())
class ProtocolProducerTest(unittest.TestCase):
def test_rejects_legacy_peers_before_ui_dispatch(self):
with socket.create_connection(("127.0.0.1", self.server.port), timeout=2) as connection: