diff --git a/README.md b/README.md index f445225..9b9a2e0 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Working in the terminal often means: - 🖥 Native terminal interface - 🔁 Works with Bash, Zsh and other shells - ⚡ Returns the selected path to the shell +- 🔎 Press `.` to toggle hidden directories ## Quick Demo @@ -102,6 +103,8 @@ Then use: cd_ ``` +- Dentro de `cd_browser`, presiona `.` para alternar la visualización de carpetas ocultas. + ## Uninstall If the project was installed with `pip`, remove it with: diff --git a/src/app/filesystem.py b/src/app/filesystem.py index 26e5d62..2862272 100644 --- a/src/app/filesystem.py +++ b/src/app/filesystem.py @@ -13,28 +13,41 @@ class DirectoryEntry: has_children: bool -def list_directories(path: Path) -> list[DirectoryEntry]: +def list_directories(path: Path, show_hidden: bool = False) -> list[DirectoryEntry]: """Return direct child directories sorted by name.""" directory_path = path.expanduser().resolve() entries: list[DirectoryEntry] = [] for child in directory_path.iterdir(): - if child.is_dir(): - entries.append( - DirectoryEntry( - name=child.name, - path=child, - has_children=has_subdirectories(child), - ) + if not child.is_dir(): + continue + if not show_hidden and child.name.startswith("."): + continue + + entries.append( + DirectoryEntry( + name=child.name, + path=child, + has_children=has_subdirectories(child, show_hidden=show_hidden), ) + ) return sorted(entries, key=lambda entry: entry.name.casefold()) -def has_subdirectories(path: Path) -> bool: +def has_subdirectories(path: Path, show_hidden: bool = False) -> bool: """Return whether the directory contains at least one subdirectory.""" directory_path = path.expanduser().resolve() - return any(child.is_dir() for child in directory_path.iterdir()) + for child in directory_path.iterdir(): + if not child.is_dir(): + continue + + if not show_hidden and child.name.startswith("."): + continue + + return True + + return False diff --git a/src/app/navigator.py b/src/app/navigator.py index d93e3c4..5adbcc8 100644 --- a/src/app/navigator.py +++ b/src/app/navigator.py @@ -28,6 +28,7 @@ class Navigator: current_path: Path = field(init=False) selected_index: int = field(init=False, default=0) _expanded_paths: set[Path] = field(init=False, default_factory=set) + show_hidden: bool = False def __post_init__(self) -> None: self.current_path = self.start_path.expanduser().resolve() @@ -144,6 +145,13 @@ class Navigator: self._set_current_path(path) return path + def toggle_hidden(self) -> None: + """Toggle showing/hiding hidden directory entries.""" + + self.show_hidden = not self.show_hidden + self.selected_index = 0 + self._expanded_paths.clear() + def select_history_entry(self, index: int) -> Path: """Jump to a directory stored in session history.""" @@ -154,7 +162,7 @@ class Navigator: def _build_entries(self, path: Path, depth: int) -> list[VisibleEntry]: entries: list[VisibleEntry] = [] - for directory in list_directories(path): + for directory in list_directories(path, show_hidden=self.show_hidden): is_expanded = directory.path in self._expanded_paths entries.append( VisibleEntry( diff --git a/src/app/ui.py b/src/app/ui.py index ed09cf5..b6312fc 100644 --- a/src/app/ui.py +++ b/src/app/ui.py @@ -145,7 +145,8 @@ class TerminalUI: stdscr.refresh() return - path_text = str(navigator.current_path) + hidden_status = "on" if navigator.show_hidden else "off" + path_text = f"{navigator.current_path} [hidden: {hidden_status}]" stdscr.addnstr(0, 0, path_text, width - 1) lines = build_render_lines(navigator) @@ -282,6 +283,11 @@ class TerminalUI: self._tree_scroll_offset = 0 return None + if key == ord("."): + navigator.toggle_hidden() + self._tree_scroll_offset = 0 + return None + if key in (curses.KEY_ENTER, 10, 13): return navigator.selected_entry.path diff --git a/tests/test_cli.py b/tests/test_cli.py index f1172a4..16ce8a1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,9 +4,10 @@ import pytest from app.cli import run_cli from app.navigator import Navigator +from app.ui import TerminalUI -class StubUI: +class StubUI(TerminalUI): def __init__(self, result: Path) -> None: self.result = result self.navigator: Navigator | None = None diff --git a/tests/test_navigator.py b/tests/test_navigator.py index 804ea51..5d48efa 100644 --- a/tests/test_navigator.py +++ b/tests/test_navigator.py @@ -25,6 +25,31 @@ def test_navigator_initial_state_includes_parent_entry(tmp_path: Path) -> None: ] +def test_toggle_hidden_directories(tmp_path: Path) -> None: + root = tmp_path / "root" + root.mkdir() + visible = root / "visible" + visible.mkdir() + hidden = root / ".hidden" + hidden.mkdir() + + navigator = Navigator(root) + + assert [entry.name for entry in navigator.visible_entries] == ["..", "visible"] + + navigator.toggle_hidden() + assert navigator.show_hidden is True + assert [entry.name for entry in navigator.visible_entries] == [ + "..", + ".hidden", + "visible", + ] + + navigator.toggle_hidden() + assert navigator.show_hidden is False + assert [entry.name for entry in navigator.visible_entries] == ["..", "visible"] + + def test_expand_selected_directory_reveals_nested_entries(tmp_path: Path) -> None: current = tmp_path / "workspace" current.mkdir()