Source code for simvx.ide.panels.file_browser
"""IDE File Browser -- project directory tree sidebar."""
from pathlib import Path
from typing import TYPE_CHECKING
from simvx.core.git_status import GitStatusProvider
from simvx.core.ui.file_browser import FileBrowserPanel as _BaseFileBrowserPanel
if TYPE_CHECKING:
from ..state import State
__all__ = ["FileBrowserPanel"]
[docs]
class FileBrowserPanel(_BaseFileBrowserPanel):
"""IDE file browser that opens files via goto_requested.
Constructs a ``GitStatusProvider`` rooted at ``state.project_root`` when
that path exists, wiring ``state.dirty_paths`` so unsaved buffers show as
yellow ``Status.MODIFIED_UNSAVED`` dots. Pass ``git_status=`` to override.
"""
def __init__(
self,
state: State,
*,
git_status: GitStatusProvider | None = None,
**kwargs,
):
if git_status is None and state.project_root:
project_root = Path(state.project_root)
if project_root.is_dir():
# The provider tolerates corrupt/partial .git directories
# (worktree pointers, fresh init with no HEAD, missing git
# binary) internally — see GitStatusProvider docs.
git_status = GitStatusProvider(
project_root,
dirty_paths_callback=state.dirty_paths,
)
super().__init__(
title="Files",
show_icons=True,
drag_enabled=False,
git_status=git_status,
**kwargs,
)
self.name = "Files"
self._state = state
# Wire file activation to IDE goto
self.file_activated.connect(self._on_file_activated)
def _on_file_activated(self, path: str):
"""Open file in IDE editor via goto_requested."""
self._state.goto_requested.emit(path, 0, 0)
# Override _on_item_selected to also match original IDE behaviour
# where single-click on a file triggers file_selected + goto_requested
def _on_item_activated(self, item):
"""IDE-compat: single-click on file triggers file_selected and goto.
Called from tests that simulate the original IDE item_selected callback.
"""
if not item.data:
return
path = item.data.get("path", "")
is_dir = item.data.get("is_dir", False)
if not is_dir and path:
self.file_selected.emit(path)
self._state.goto_requested.emit(path, 0, 0)
[docs]
def exit_tree(self):
"""Stop the polling thread when the panel is removed from the tree."""
provider = self.__dict__.get("_git_status")
if provider is not None:
provider.stop()
if hasattr(super(), "exit_tree"):
super().exit_tree()