The XeFM Navigation System handles directory traversal, cursor positioning, and navigation state management. This document covers the implementation details of navigation behaviors and optimizations.
When navigating from a child directory to its parent directory using the Backspace key, the system implements intelligent cursor positioning to improve user experience.
xefm/app.pyelif key == curses.KEY_BACKSPACE or key == KEY_BACKSPACE_2 or key == KEY_BACKSPACE_1: # Backspace - go to parent directory
if current_pane['path'] != current_pane['path'].parent:
try:
# Save current cursor position before changing directory
self.save_cursor_position(current_pane)
# Remember the child directory name we're leaving
child_directory_name = current_pane['path'].name
current_pane['path'] = current_pane['path'].parent
current_pane['selected_index'] = 0
current_pane['scroll_offset'] = 0
current_pane['selected_files'].clear() # Clear selections when changing directory
self.refresh_files(current_pane)
# Try to set cursor to the child directory we just came from
cursor_set = False
for i, file_path in enumerate(current_pane['files']):
if file_path.name == child_directory_name and file_path.is_dir():
current_pane['selected_index'] = i
# Adjust scroll offset to keep selection visible
self.adjust_scroll_for_selection(current_pane)
cursor_set = True
break
# If we couldn't find the child directory, try to restore cursor position from history
if not cursor_set and not self.restore_cursor_position(current_pane):
# If no history found, default to first item
current_pane['selected_index'] = 0
current_pane['scroll_offset'] = 0
self.needs_full_redraw = True
except PermissionError:
self.show_error("Permission denied")
self.needs_full_redraw = True
child_directory_name)The implementation includes robust fallback mechanisms:
If the child directory is deleted while the user is in it, the fallback mechanisms ensure graceful handling:
When already at the root directory, the condition current_pane['path'] != current_pane['path'].parent prevents unnecessary processing.
Permission errors during navigation are caught and displayed to the user with appropriate error messages.
The adjust_scroll_for_selection() method ensures that when the cursor is positioned on the child directory, it remains visible even if it’s outside the current scroll view.
go_root)go_root (default \) takes the active pane to the top of whatever it is
showing. Issue #353 asked for it as a Windows feature and assumed the TUI
would need a per-OS branch, because “root” means something different on each
platform. It does not: every PathImpl already answers anchor, so one
implementation is correct everywhere.
| Pane is showing | anchor |
|---|---|
C:\Users\foo\src |
C:\ |
/home/foo/src |
/ |
s3://bucket/logs/2026/ |
s3://bucket/ |
ssh://host/var/log |
ssh://host/ |
| Inside an archive | the root of the filesystem holding the archive |
XeFMApp._go_root reads pane["path"].anchor, bails with an “Already at …”
log line when the pane is there already (unless the pane is virtual, where the
jump still has to leave the result set), and otherwise hands the jump to
_go_to_dir, which exits virtual mode and re-lists on a worker.
_top_level_name mirrors what _go_parent does with the child it came from:
it walks parent upward until the next step is the root and returns that
branch’s name, so jumping from /home/foo/src to / lands the cursor on
home. The walk is bounded (256 steps) rather than trusting every backend’s
chain to terminate — it runs on the UI thread, and a path implementation whose
parents never reach a fixed point would otherwise hang the app. A path whose
chain never meets the root simply lands at the top of the listing.
S3PathImpl.anchor returned the bare s3://, which names no listable
location — XeFM has no bucket enumeration — while parent/parents already
stopped at the bucket root. The bucket is S3’s drive, the way the host is
SFTP’s, so the anchor is s3://{bucket}/.SSHPathImpl.parents looped forever. It walked upward while comparing
each step against the starting path, which never matches again once the walk
moves off it, so at the host root (its own parent) it appended without end.
It now stops at the fixed point, and all four backends agree: parents ends
at the anchor and is empty at the root.save_cursor_position(current_pane) stores cursor state before navigationrestore_cursor_position(current_pane) retrieves saved positionsMAX_HISTORY_ENTRIES configuration for memory managementadjust_scroll_for_selection(current_pane) keeps selections visiblecurrent_pane['path'].name for cross-platform directory name extractioncurrent_pane['path'].parent for reliable parent directory accessfile_path.is_dir() for consistent directory identificationThe navigation system includes comprehensive unit tests in test/test_parent_directory_navigation.py:
MAX_HISTORY_ENTRIES for fallback cursor history