From 0b3179f4ff6426bbe2a7ee3dbac901dda2924908 Mon Sep 17 00:00:00 2001 From: Joakim Date: Mon, 12 Oct 2026 22:35:04 +0200 Subject: [PATCH] colors, ../ och -a --- CHANGELOG.md | 4 ++ tui/tui-shell.bash | 22 +++++++-- tui/tui.py | 115 ++++++++++++++++++++++++++------------------- 3 files changed, 87 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e877e86..a1802ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,3 +28,7 @@ - Cursor can now move to the empty line immediately below the last object. - `Ctrl+K K` now behaves like joe: the block end is the row immediately above the cursor. - On the bottom empty line, `Ctrl+K K` therefore selects through the last object. + +## v2 update +- The bottom empty row now visibly shows the cursor. +- When TUI exits normally, the shell integration changes the parent Bash shell to the directory where TUI was closed. diff --git a/tui/tui-shell.bash b/tui/tui-shell.bash index 7c27876..3c845ae 100644 --- a/tui/tui-shell.bash +++ b/tui/tui-shell.bash @@ -44,19 +44,31 @@ tui() { fi rc=$? - if (( rc != 42 )); then - rm -f "$command_file" "$cwd_file" - return "$rc" - fi - tui_cwd="$(<"$cwd_file")" if [[ -z "$tui_cwd" || ! -d "$tui_cwd" ]]; then + if (( rc != 42 )); then + rm -f "$command_file" "$cwd_file" + return "$rc" + fi echo "tui: invalid TUI working directory" >&2 rm -f "$command_file" "$cwd_file" return 1 fi + # A normal TUI exit means the user wants to leave TUI. Change the + # parent Bash shell to the directory where TUI was actually closed. + if (( rc != 42 )); then + if (( rc == 0 )); then + cd "$tui_cwd" || { + rm -f "$command_file" "$cwd_file" + return 1 + } + fi + rm -f "$command_file" "$cwd_file" + return "$rc" + fi + # Execute all commands belonging to one TUI action before starting # TUI again. Each command remains a normal Bash command and gets its # own history entry. If a command fails, stop the action and pause so diff --git a/tui/tui.py b/tui/tui.py index 36a59bb..ae78f16 100755 --- a/tui/tui.py +++ b/tui/tui.py @@ -443,10 +443,12 @@ def draw(stdscr, browser, status=""): text = entry.name + ("/" if entry.is_dir() else "") else: # One empty line after the final object, matching the cursor range. + # A literal space is needed here: curses does not draw attributes + # for an empty string, so the cursor would otherwise be invisible. entry = None is_marked = False attrs = curses.A_NORMAL - text = "" + text = " " if index == browser.cursor: attrs |= curses.A_UNDERLINE @@ -502,6 +504,18 @@ def run(stdscr, start_dir=".", show_all=False): status = "" + def save_cwd(): + cwd_file = os.environ.get("TUI_CWD_FILE") + if cwd_file: + try: + Path(cwd_file).write_text(str(browser.path), encoding="utf-8") + except OSError: + pass + + # Always leave the shell wrapper with the directory in which TUI was + # actually closed, not merely the directory of the last action. + save_cwd() + def configured_action(key): if key == curses.KEY_UP: return browser.resolve_key_action(["up"], browser.selected()) @@ -522,62 +536,65 @@ def run(stdscr, start_dir=".", show_all=False): return None - while True: - draw(stdscr, browser, status) - status = "" + try: + while True: + draw(stdscr, browser, status) + status = "" - key = stdscr.getch() + key = stdscr.getch() - if key == 11: # Ctrl-K - action, message = read_ctrl_k(stdscr, browser) + if key == 11: # Ctrl-K + action, message = read_ctrl_k(stdscr, browser) - if action == "mark-begin": - browser.mark_begin() - elif action == "mark-end": - browser.mark_end() + if action == "mark-begin": + browser.mark_begin() + elif action == "mark-end": + browser.mark_end() + elif action: + result = browser.execute_action( + action, + stdscr, + marked_only=True, + ) + if result == SHELL_HANDOFF: + return SHELL_HANDOFF + if result is False: + return + else: + status = message + continue + + # Left is navigation, not an action: first move the cursor to the + # parent entry; pressing left again enters that parent. This also + # makes the parent entry visible at the top of every directory. + if key == curses.KEY_LEFT: + if browser.cursor == 0: + if browser.parent() is False: + return + else: + browser.cursor = 0 + continue + + action = configured_action(key) + + if action == "up": + browser.move(-1) + elif action == "down": + browser.move(1) + elif action == "enter": + if browser.enter() is False: + return + elif action == "parent": + if browser.parent() is False: + return elif action: - result = browser.execute_action( - action, - stdscr, - marked_only=True, - ) + result = browser.execute_action(action, stdscr) if result == SHELL_HANDOFF: return SHELL_HANDOFF if result is False: return - else: - status = message - continue - - # Left is navigation, not an action: first move the cursor to the - # parent entry; pressing left again enters that parent. This also - # makes the parent entry visible at the top of every directory. - if key == curses.KEY_LEFT: - if browser.cursor == 0: - if browser.parent() is False: - return - else: - browser.cursor = 0 - continue - - action = configured_action(key) - - if action == "up": - browser.move(-1) - elif action == "down": - browser.move(1) - elif action == "enter": - if browser.enter() is False: - return - elif action == "parent": - if browser.parent() is False: - return - elif action: - result = browser.execute_action(action, stdscr) - if result == SHELL_HANDOFF: - return SHELL_HANDOFF - if result is False: - return + finally: + save_cwd() def main():