refactor: split sort into independent direction and grouping axes
This commit is contained in:
parent
87c9a6f641
commit
b88ecce6f5
4 changed files with 70 additions and 40 deletions
|
|
@ -21,7 +21,8 @@ class Input:
|
||||||
"enter": self._enter,
|
"enter": self._enter,
|
||||||
"back": self._back,
|
"back": self._back,
|
||||||
"parent": self._parent,
|
"parent": self._parent,
|
||||||
"sort": self._sort,
|
"sort_direction": self._sort_direction,
|
||||||
|
"sort_grouping": self._sort_grouping,
|
||||||
"new_folder": self._new_folder,
|
"new_folder": self._new_folder,
|
||||||
"delete_folder": self._delete_folder,
|
"delete_folder": self._delete_folder,
|
||||||
"new_file": self._new_file,
|
"new_file": self._new_file,
|
||||||
|
|
@ -96,8 +97,12 @@ class Input:
|
||||||
self._maybe_show_error()
|
self._maybe_show_error()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _sort(self) -> bool:
|
def _sort_direction(self) -> bool:
|
||||||
self.app.navigator.cycle_sort()
|
self.app.navigator.cycle_sort_direction()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _sort_grouping(self) -> bool:
|
||||||
|
self.app.navigator.cycle_sort_grouping()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _new_folder(self) -> bool:
|
def _new_folder(self) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ DEFAULT_KEYBINDS: dict[str, list[str]] = {
|
||||||
"enter": ["l", "KEY_RIGHT", "\n", "KEY_ENTER"],
|
"enter": ["l", "KEY_RIGHT", "\n", "KEY_ENTER"],
|
||||||
"back": ["-", "KEY_LEFT", "KEY_BACKSPACE", "\x7f", "\x08"],
|
"back": ["-", "KEY_LEFT", "KEY_BACKSPACE", "\x7f", "\x08"],
|
||||||
"parent": ["h"],
|
"parent": ["h"],
|
||||||
"sort": ["i"],
|
"sort_direction": ["r"],
|
||||||
|
"sort_grouping": ["i"],
|
||||||
"new_folder": ["d"],
|
"new_folder": ["d"],
|
||||||
"delete_folder": ["D"],
|
"delete_folder": ["D"],
|
||||||
"new_file": ["%"],
|
"new_file": ["%"],
|
||||||
|
|
@ -25,10 +26,12 @@ DEFAULT_SCROLL_OFFSET = 4
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, keybinds: dict[str, list[str]], cursor_reset: str,
|
def __init__(self, keybinds: dict[str, list[str]], cursor_reset: str,
|
||||||
sort_mode: str, theme: Theme, scroll_offset: int) -> None:
|
sort_direction: str, sort_grouping: str, theme: Theme,
|
||||||
|
scroll_offset: int) -> None:
|
||||||
self._keybinds = keybinds
|
self._keybinds = keybinds
|
||||||
self._cursor_reset = cursor_reset
|
self._cursor_reset = cursor_reset
|
||||||
self._sort_mode = sort_mode
|
self._sort_direction = sort_direction
|
||||||
|
self._sort_grouping = sort_grouping
|
||||||
self._theme = theme
|
self._theme = theme
|
||||||
self._scroll_offset = scroll_offset
|
self._scroll_offset = scroll_offset
|
||||||
self._key_to_action: dict[str, str] = {
|
self._key_to_action: dict[str, str] = {
|
||||||
|
|
@ -53,11 +56,19 @@ class Config:
|
||||||
keybinds = dict(DEFAULT_KEYBINDS)
|
keybinds = dict(DEFAULT_KEYBINDS)
|
||||||
keybinds.update(data.get("keybinds", {}))
|
keybinds.update(data.get("keybinds", {}))
|
||||||
cursor_reset = data.get("cursor", {}).get("reset", DEFAULT_CURSOR_RESET)
|
cursor_reset = data.get("cursor", {}).get("reset", DEFAULT_CURSOR_RESET)
|
||||||
sort_mode = data.get("sort", {}).get("mode", Sort.DEFAULT_MODE)
|
|
||||||
if sort_mode not in Sort.MODES:
|
sort_data = data.get("sort", {})
|
||||||
print(f"pyexplorer: unknown sort mode {sort_mode!r}, "
|
sort_direction = sort_data.get("direction", Sort.DEFAULT_DIRECTION)
|
||||||
f"falling back to {Sort.DEFAULT_MODE!r}", file=sys.stderr)
|
if sort_direction not in Sort.DIRECTIONS:
|
||||||
sort_mode = Sort.DEFAULT_MODE
|
print(f"pyexplorer: unknown sort direction {sort_direction!r}, "
|
||||||
|
f"falling back to {Sort.DEFAULT_DIRECTION!r}", file=sys.stderr)
|
||||||
|
sort_direction = Sort.DEFAULT_DIRECTION
|
||||||
|
sort_grouping = sort_data.get("grouping", Sort.DEFAULT_GROUPING)
|
||||||
|
if sort_grouping not in Sort.GROUPINGS:
|
||||||
|
print(f"pyexplorer: unknown sort grouping {sort_grouping!r}, "
|
||||||
|
f"falling back to {Sort.DEFAULT_GROUPING!r}", file=sys.stderr)
|
||||||
|
sort_grouping = Sort.DEFAULT_GROUPING
|
||||||
|
|
||||||
theme = Theme.load(data)
|
theme = Theme.load(data)
|
||||||
scroll_offset = data.get("scroll", {}).get("offset",
|
scroll_offset = data.get("scroll", {}).get("offset",
|
||||||
DEFAULT_SCROLL_OFFSET)
|
DEFAULT_SCROLL_OFFSET)
|
||||||
|
|
@ -65,7 +76,8 @@ class Config:
|
||||||
print(f"pyexplorer: invalid scroll offset {scroll_offset!r}, "
|
print(f"pyexplorer: invalid scroll offset {scroll_offset!r}, "
|
||||||
f"falling back to {DEFAULT_SCROLL_OFFSET}", file=sys.stderr)
|
f"falling back to {DEFAULT_SCROLL_OFFSET}", file=sys.stderr)
|
||||||
scroll_offset = DEFAULT_SCROLL_OFFSET
|
scroll_offset = DEFAULT_SCROLL_OFFSET
|
||||||
return cls(keybinds, cursor_reset, sort_mode, theme, scroll_offset)
|
return cls(keybinds, cursor_reset, sort_direction, sort_grouping,
|
||||||
|
theme, scroll_offset)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _default_path() -> Path:
|
def _default_path() -> Path:
|
||||||
|
|
@ -83,8 +95,12 @@ class Config:
|
||||||
return self._cursor_reset
|
return self._cursor_reset
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sort_mode(self) -> str:
|
def sort_direction(self) -> str:
|
||||||
return self._sort_mode
|
return self._sort_direction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sort_grouping(self) -> str:
|
||||||
|
return self._sort_grouping
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def theme(self) -> Theme:
|
def theme(self) -> Theme:
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ class Navigator:
|
||||||
|
|
||||||
def __init__(self, path: Path, config: Config) -> None:
|
def __init__(self, path: Path, config: Config) -> None:
|
||||||
self._config = config
|
self._config = config
|
||||||
self._sort_mode = config.sort_mode
|
self._sort_direction = config.sort_direction
|
||||||
|
self._sort_grouping = config.sort_grouping
|
||||||
self._history = [path]
|
self._history = [path]
|
||||||
self._cursor = Cursor()
|
self._cursor = Cursor()
|
||||||
self._current: Folder | None = None
|
self._current: Folder | None = None
|
||||||
|
|
@ -25,7 +26,8 @@ class Navigator:
|
||||||
try:
|
try:
|
||||||
self._current = Folder(self._history[-1])
|
self._current = Folder(self._history[-1])
|
||||||
self._items = Sort.sort_items(self._current.children(),
|
self._items = Sort.sort_items(self._current.children(),
|
||||||
self._sort_mode)
|
self._sort_direction,
|
||||||
|
self._sort_grouping)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self._items = []
|
self._items = []
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
|
|
@ -35,9 +37,17 @@ class Navigator:
|
||||||
def refresh(self) -> None:
|
def refresh(self) -> None:
|
||||||
self._load()
|
self._load()
|
||||||
|
|
||||||
def cycle_sort(self) -> None:
|
def _resort(self) -> None:
|
||||||
self._sort_mode = Sort.next_mode(self._sort_mode)
|
self._items = Sort.sort_items(self._items, self._sort_direction,
|
||||||
self._items = Sort.sort_items(self._items, self._sort_mode)
|
self._sort_grouping)
|
||||||
|
|
||||||
|
def cycle_sort_direction(self) -> None:
|
||||||
|
self._sort_direction = Sort.next_direction(self._sort_direction)
|
||||||
|
self._resort()
|
||||||
|
|
||||||
|
def cycle_sort_grouping(self) -> None:
|
||||||
|
self._sort_grouping = Sort.next_grouping(self._sort_grouping)
|
||||||
|
self._resort()
|
||||||
|
|
||||||
def _reset_cursor(self) -> None:
|
def _reset_cursor(self) -> None:
|
||||||
if self._config.cursor_reset == "keep":
|
if self._config.cursor_reset == "keep":
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,32 @@
|
||||||
from ..items.AItems import AItem
|
from ..items.AItems import AItem
|
||||||
|
|
||||||
MODES = [
|
DIRECTIONS = ["asc", "desc"]
|
||||||
"name_asc",
|
GROUPINGS = ["mixed", "dirs_first", "files_first"]
|
||||||
"name_desc",
|
|
||||||
"dirs_first_asc",
|
|
||||||
"files_first_asc",
|
|
||||||
"dirs_first_desc",
|
|
||||||
"files_first_desc",
|
|
||||||
]
|
|
||||||
|
|
||||||
DEFAULT_MODE = "name_asc"
|
DEFAULT_DIRECTION = "asc"
|
||||||
|
DEFAULT_GROUPING = "mixed"
|
||||||
_DESC_MODES = ("name_desc", "dirs_first_desc", "files_first_desc")
|
|
||||||
_DIRS_FIRST_MODES = ("dirs_first_asc", "dirs_first_desc")
|
|
||||||
_FILES_FIRST_MODES = ("files_first_asc", "files_first_desc")
|
|
||||||
|
|
||||||
|
|
||||||
def sort_items(items: list[AItem], mode: str) -> list[AItem]:
|
def sort_items(items: list[AItem], direction: str, grouping: str) -> list[AItem]:
|
||||||
items = sorted(items, key=lambda item: item.name, reverse=mode in _DESC_MODES)
|
items = sorted(items, key=lambda item: item.name, reverse=direction == "desc")
|
||||||
if mode in _DIRS_FIRST_MODES:
|
if grouping == "dirs_first":
|
||||||
items = sorted(items, key=lambda item: not item.is_navigable())
|
items = sorted(items, key=lambda item: not item.is_navigable())
|
||||||
elif mode in _FILES_FIRST_MODES:
|
elif grouping == "files_first":
|
||||||
items = sorted(items, key=lambda item: item.is_navigable())
|
items = sorted(items, key=lambda item: item.is_navigable())
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def next_mode(mode: str) -> str:
|
def next_direction(direction: str) -> str:
|
||||||
try:
|
try:
|
||||||
i = MODES.index(mode)
|
i = DIRECTIONS.index(direction)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return DEFAULT_MODE
|
return DEFAULT_DIRECTION
|
||||||
return MODES[(i + 1) % len(MODES)]
|
return DIRECTIONS[(i + 1) % len(DIRECTIONS)]
|
||||||
|
|
||||||
|
|
||||||
|
def next_grouping(grouping: str) -> str:
|
||||||
|
try:
|
||||||
|
i = GROUPINGS.index(grouping)
|
||||||
|
except ValueError:
|
||||||
|
return DEFAULT_GROUPING
|
||||||
|
return GROUPINGS[(i + 1) % len(GROUPINGS)]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue