diff --git a/presentation/graph/structure_graph_view.py b/presentation/graph/structure_graph_view.py index 9c5856d..8cc86f1 100644 --- a/presentation/graph/structure_graph_view.py +++ b/presentation/graph/structure_graph_view.py @@ -105,6 +105,32 @@ class StructureGraphView(QWidget): # sau — nên panel phải bật theo, không thì lượt quét đó lại im lặng. self.renderer.project_changed.connect(self._on_project_scan_started) + def _ensure_scan_path(self) -> None: + """Điền ô đường dẫn từ project đang hoạt động nếu nó đang rỗng. + + ``GraphRenderer.auto_scan_and_fit`` thoát sớm khi ô đường dẫn rỗng, + trong khi chính nút Scan lại có đường lùi (``path_edit.text() or + Path.cwd()``). Bất đối xứng đó nghĩa là: vào màn thì không làm gì, bấm + Scan thì chạy — đúng thứ người dùng phàn nàn. + + Điền từ project đang chọn chứ KHÔNG lấy ``cwd()`` làm đường lùi như nút + Scan: quét thư mục làm việc của tiến trình là quét một cây không liên + quan gì tới project, và nó có thể rất lớn. + """ + if self.renderer.path_edit.text().strip(): + return + ctx = getattr(self, "ctx", None) + pid = (getattr(ctx, "active_project_id", "") or "").strip() + if not pid or pid == "default": + return + try: + from ...core.projects import load_project + project = load_project(pid) + except Exception: # noqa: BLE001 + return + if project is not None: + self.renderer.path_edit.setText(str(project.workspace_dir())) + def _on_project_scan_started(self) -> None: """Renderer vừa đổi project. Nó chỉ quét ngay khi màn này đang mở.""" if self.renderer.isVisible(): @@ -156,6 +182,7 @@ class StructureGraphView(QWidget): ``graph_rendered`` phát — bao trọn cả lượt quét chạy ở luồng nền phía sau, chứ không tắt ngay khi hàm này trả về. """ + self._ensure_scan_path() da_dung_khung = self.renderer.web is not None self._show_busy("structure.scanning" if da_dung_khung else "structure.loading_view") try: diff --git a/tests/ui/test_graphrag_busy_panel.py b/tests/ui/test_graphrag_busy_panel.py index 234a39e..8dc308c 100644 --- a/tests/ui/test_graphrag_busy_panel.py +++ b/tests/ui/test_graphrag_busy_panel.py @@ -55,6 +55,7 @@ def _dung(qapp, ViewCls, web=None, path="C:/tmp"): v.renderer = _Renderer(qapp, web=web, path=path) v.renderer._view = v v._workspace_project = None + v.ctx = type("C", (), {"active_project_id": "default"})() v.resize(800, 600) v._build_busy_panel() return v @@ -199,3 +200,62 @@ def test_doi_project_khi_man_dang_an_thi_khong_bat_panel(qapp, view): assert v._busy.isHidden() is True finally: v.deleteLater() + + +# ---- đường dẫn quét rỗng thì lấy từ project đang hoạt động --------------- + +def test_o_duong_dan_rong_thi_dien_tu_project_dang_chon(qapp, view, monkeypatch, tmp_path): + """GraphRenderer.auto_scan_and_fit thoát sớm khi ô đường dẫn rỗng, trong khi + nút Scan lại có đường lùi — vào màn không làm gì, bấm Scan thì chạy.""" + import cowork_local.core.projects as projects + + ws = tmp_path / "ws" + ws.mkdir() + + class _P: + project_id = "p1" + def workspace_dir(self, base=None): + return ws + + monkeypatch.setattr(projects, "load_project", lambda pid: _P() if pid == "p1" else None) + + v = _dung(qapp, view, path="") + v.ctx = type("C", (), {"active_project_id": "p1"})() + v.renderer.path_edit = type("E", (), { + "_t": "", + "text": lambda s: s._t, + "setText": lambda s, t: setattr(s, "_t", t)})() + try: + v._ensure_scan_path() + + assert v.renderer.path_edit.text() == str(ws) + finally: + v.deleteLater() + + +def test_da_co_duong_dan_thi_khong_ghi_de(qapp, view): + v = _dung(qapp, view, path="C:/da-chon") + v.ctx = type("C", (), {"active_project_id": "p1"})() + try: + v._ensure_scan_path() + + assert v.renderer.path_edit.text() == "C:/da-chon" + finally: + v.deleteLater() + + +def test_chua_chon_project_thi_khong_bia_duong_dan(qapp, view): + """Không lấy cwd() làm đường lùi: quét thư mục làm việc của tiến trình là + quét một cây không liên quan gì tới project.""" + v = _dung(qapp, view, path="") + v.ctx = type("C", (), {"active_project_id": "default"})() + v.renderer.path_edit = type("E", (), { + "_t": "", + "text": lambda s: s._t, + "setText": lambda s, t: setattr(s, "_t", t)})() + try: + v._ensure_scan_path() + + assert v.renderer.path_edit.text() == "" + finally: + v.deleteLater()