Skip to content

Commit 7ceec70

Browse files
committed
[C2] Add ProjectManager.update_document_content + editor save
1 parent 269c0a5 commit 7ceec70

File tree

4 files changed

+102
-9
lines changed

4 files changed

+102
-9
lines changed

issues/2026-02-10_18-23-04-ui-autosave-streaming-v1.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
22
C1,Unify bottom status UI (remove duplicate editor status frame),"Remove EditorPanel internal bottom status frame to avoid double bottom bars and overlapping lines; ensure stats/cursor/doc status still update via main EnhancedStatusBar; adjust layout/styling if needed; update Qt layout test accordingly.","Only one bottom status area is shown; no overlapping/stacked lines; status info still updates; Qt layout test passes.","python -m pytest -q tests/test_status_bar_layout.py",none,DONE,DONE,TODO,"src/gui/editor/editor_panel.py | src/gui/status/status_bar.py | tests/test_status_bar_layout.py",none,"done_at:2026-02-10 | test:.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_status_bar_layout.py"
3-
C2,Add ProjectManager.update_document_content + editor save,"Implement ProjectManager.update_document_content wrapper (persist content via update_document); update IntelligentTextEditor manual save and autosave paths to persist when bound; add unit test covering update_document_content behavior and ensuring no AttributeError/crash.","Autosave/manual save persist content via ProjectManager.update_document_content when project/doc bound; unit test passes.","python -m pytest -q tests/test_project_update_document_content.py",none,TODO,TODO,TODO,"src/core/project.py | src/gui/editor/text_editor.py | tests/test_project_update_document_content.py",none,none
3+
C2,Add ProjectManager.update_document_content + editor save,"Implement ProjectManager.update_document_content wrapper (persist content via update_document); update IntelligentTextEditor manual save and autosave paths to persist when bound; add unit test covering update_document_content behavior and ensuring no AttributeError/crash.","Autosave/manual save persist content via ProjectManager.update_document_content when project/doc bound; unit test passes.","python -m pytest -q tests/test_project_update_document_content.py",none,DONE,DONE,TODO,"src/core/project.py | src/gui/editor/text_editor.py | tests/test_project_update_document_content.py",none,"done_at:2026-02-10 | test:.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_project_update_document_content.py"
44
C3,Bind EditorPanel documents to ProjectManager (autosave works),"Add EditorPanel.set_project_manager and ensure all editors created for project documents are bound to the active ProjectManager + correct document_id; update MainWindow wiring so selecting a document loads/updates via ProjectManager and manual save works; avoid users typing into an unbound scratch doc when a project is open; add integration test.","Editing a project document triggers autosave and persists into project DB; reopening project shows saved content; manual save works; integration test passes.","python -m pytest -q tests/test_editor_project_persistence.py",none,TODO,TODO,TODO,"src/gui/editor/editor_panel.py | src/gui/main_window_parts/ui.py | src/gui/main_window_parts/integrations.py | tests/test_editor_project_persistence.py",C2,none
55
C4,Restore last session (reopen recent project & last document),"Persist and restore last-open project/document when enabled: wire SettingsDialog restore_session/auto_save prefs into Config; on startup, if enabled, auto-open most recent project and re-open last document (fallback to first scene).","Restart restores the previous project and last document content instead of showing a blank scratch doc (when restore_session enabled).",manual,none,TODO,TODO,TODO,"src/main.py | src/core/config_schema.py | src/gui/dialogs/settings_dialog.py | src/core/config.py | src/gui/main_window_parts/ui.py | src/gui/main_window_parts/integrations.py",C3,"manual_checklist: 1) Create/open a project, edit a scene, wait for autosave, close app. 2) Reopen app; expect project auto-open and edited content present. 3) Disable restore_session in settings; restart; expect no auto-open."
66
C5,Streaming dispatch respects stream_response toggle,"Plumb config ai.stream_response (default on) through AICompletionService/AIRequestDispatcher to use QtAIClient.complete_stream_async; include context/request_id in chunk signals; ensure cancellation prevents stale chunks from updating UI; add unit test for dispatch selection.","When stream_response enabled, streaming client method is used and chunk signals are emitted; when disabled, non-stream method used; unit test passes.","python -m pytest -q tests/test_streaming_dispatcher.py",none,TODO,TODO,TODO,"src/application/ai_completion_service.py | src/core/ai_qt_client.py | src/gui/ai/enhanced_ai_manager.py | src/gui/main_window_parts/integrations.py | tests/test_streaming_dispatcher.py",none,none

src/core/project.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,14 @@ def update_document(self, doc_id: str, save: bool = True, **kwargs) -> Optional[
441441
logger.info(f"Document updated: {doc.name}")
442442
return doc
443443

444+
def update_document_content(self, doc_id: str, content: str, save: bool = True) -> bool:
445+
"""更新文档内容(便捷方法,供编辑器自动保存/手动保存使用)"""
446+
if not self._current_project or doc_id not in self._current_project.documents:
447+
return False
448+
449+
doc = self.update_document(doc_id, save=save, content=content)
450+
return doc is not None
451+
444452
def get_document(self, doc_id: str) -> Optional[ProjectDocument]:
445453
if self._current_project:
446454
return self._current_project.documents.get(doc_id)

src/gui/editor/text_editor.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -886,14 +886,29 @@ def is_modified(self) -> bool:
886886
"""检查是否已修改"""
887887
return self._is_modified
888888

889-
def save_document(self):
890-
"""保存文档"""
891-
if self._is_modified:
892-
content = self.toPlainText()
893-
self._last_save_content = content
894-
self._is_modified = False
895-
self.autoSaveTriggered.emit(content)
896-
logger.info("Document saved manually")
889+
def save_document(self) -> bool:
890+
"""保存文档(优先持久化到项目数据库)。"""
891+
if not self._is_modified:
892+
return True
893+
894+
content = self.toPlainText()
895+
896+
# If bound to a project document, persist via ProjectManager.
897+
if self._current_document_id and self._project_manager:
898+
try:
899+
success = bool(self._project_manager.update_document_content(self._current_document_id, content))
900+
except Exception as e: # noqa: BLE001
901+
logger.warning(f"Failed to save via project manager: {e}")
902+
success = False
903+
904+
if not success:
905+
return False
906+
907+
self._last_save_content = content
908+
self._is_modified = False
909+
self.autoSaveTriggered.emit(content)
910+
logger.info("Document saved manually")
911+
return True
897912

898913
def insert_text_at_cursor(self, text: str):
899914
"""在光标位置插入文本"""
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
9+
10+
pytest.importorskip("PyQt6")
11+
pytest.importorskip("pytestqt")
12+
13+
from core.config import Config
14+
from core.project import ProjectManager
15+
from core.shared import Shared
16+
from gui.editor.text_editor import IntelligentTextEditor
17+
18+
19+
def test_project_manager_update_document_content_persists(tmp_path: Path) -> None:
20+
config = Config()
21+
shared = Shared(config)
22+
pm = ProjectManager(config=config, shared=shared)
23+
24+
project_dir = tmp_path / "proj"
25+
assert pm.create_project("Proj", str(project_dir)) is True
26+
assert pm._current_project is not None
27+
assert pm._current_project.documents
28+
29+
doc_id = next(iter(pm._current_project.documents.keys()))
30+
31+
assert pm.update_document_content(doc_id, "hello") is True
32+
assert pm.get_document_content(doc_id) == "hello"
33+
34+
pm.close_project()
35+
assert pm.open_project(str(project_dir)) is True
36+
assert pm.get_document_content(doc_id) == "hello"
37+
38+
39+
class _DummyProjectManager:
40+
def __init__(self) -> None:
41+
self.calls: list[tuple[str, str]] = []
42+
43+
def update_document_content(self, doc_id: str, content: str) -> bool: # noqa: D401
44+
self.calls.append((doc_id, content))
45+
return True
46+
47+
48+
def test_text_editor_save_and_autosave_use_project_manager(qtbot) -> None:
49+
config = Config()
50+
shared = Shared(config)
51+
editor = IntelligentTextEditor(config=config, shared=shared)
52+
qtbot.addWidget(editor)
53+
54+
dummy_pm = _DummyProjectManager()
55+
editor.set_project_manager(dummy_pm)
56+
editor.set_document_content("start", "doc1")
57+
58+
editor.setPlainText("changed")
59+
assert editor.is_modified() is True
60+
61+
assert editor.save_document() is True
62+
assert dummy_pm.calls == [("doc1", "changed")]
63+
assert editor.is_modified() is False
64+
65+
editor.setPlainText("changed again")
66+
assert editor.is_modified() is True
67+
68+
editor._trigger_auto_save()
69+
assert dummy_pm.calls[-1] == ("doc1", "changed again")
70+
assert editor.is_modified() is False

0 commit comments

Comments
 (0)