From 4ab48a77ff4e7745e5585e4c7260de2db5c13b80 Mon Sep 17 00:00:00 2001 From: didericis Date: Thu, 25 Jun 2026 19:38:46 -0400 Subject: [PATCH] refactor(tui): flatten _multiselect_loop key handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interactive multiselect loop nested key dispatch up to six indent levels deep — the worst offender being the space-bar toggle (while > if focus > elif key > if filtered > if/else membership) and the long order-mode elif chain inside the focus branch. Extract two behaviour-identical helpers: - `_toggle_membership(items, item)` collapses the add/remove if/else, pulling the space branch back to four levels. - `_handle_order_key(key, selected, order_cursor)` moves the entire order-focus dispatch out of the loop, returning the new cursor. No control-flow or key-binding changes; the loop's early returns and focus toggling are untouched. (git_gate.py's deep-looking lines named in the issue are multiline call-argument continuations already under four levels of control nesting, so no change was warranted there.) Closes #288 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NkwFXLFff9PYPy4wgVBJp9 --- bot_bottle/cli/tui.py | 72 ++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/bot_bottle/cli/tui.py b/bot_bottle/cli/tui.py index 93fc3ed..03cca8e 100644 --- a/bot_bottle/cli/tui.py +++ b/bot_bottle/cli/tui.py @@ -301,6 +301,44 @@ def _run_multiselect( return result +def _toggle_membership(items: list[str], item: str) -> None: + """Add `item` if absent, remove it if present (in place).""" + if item in items: + items.remove(item) + else: + items.append(item) + + +def _handle_order_key(key: int, selected: list[str], order_cursor: int) -> int: + """Apply a keypress in 'order' focus: navigate, reorder, or remove the + item at `order_cursor`. Mutates `selected` in place and returns the new + order cursor.""" + if key in (curses.KEY_UP, ord("k")): + if order_cursor > 0: + order_cursor -= 1 + elif key in (curses.KEY_DOWN, ord("j")): + if order_cursor < len(selected) - 1: + order_cursor += 1 + elif key == ord("K"): + # Move selected item up (earlier in order). + if order_cursor > 0: + i = order_cursor + selected[i - 1], selected[i] = selected[i], selected[i - 1] + order_cursor -= 1 + elif key == ord("J"): + # Move selected item down (later in order). + if order_cursor < len(selected) - 1: + i = order_cursor + selected[i], selected[i + 1] = selected[i + 1], selected[i] + order_cursor += 1 + elif key in (curses.KEY_ENTER, _KEY_ENTER_ALT, ord("\r"), _KEY_SPACE): + # Remove item from selection while in order mode. + del selected[order_cursor] + if order_cursor >= len(selected) and order_cursor > 0: + order_cursor -= 1 + return order_cursor + + def _multiselect_loop( screen: Any, items: list[str], *, title: str, initial: list[str] ) -> Optional[list[str]]: @@ -362,11 +400,7 @@ def _multiselect_loop( elif key == _KEY_SPACE: if filtered: - item = filtered[cursor] - if item in selected: - selected.remove(item) - else: - selected.append(item) + _toggle_membership(selected, filtered[cursor]) elif key in (curses.KEY_UP, ord("k")): if cursor > 0: @@ -387,33 +421,7 @@ def _multiselect_loop( cursor = 0 else: # focus == "order" - if key in (curses.KEY_UP, ord("k")): - if order_cursor > 0: - order_cursor -= 1 - - elif key in (curses.KEY_DOWN, ord("j")): - if order_cursor < len(selected) - 1: - order_cursor += 1 - - elif key == ord("K"): - # Move selected item up (earlier in order). - if order_cursor > 0: - i = order_cursor - selected[i - 1], selected[i] = selected[i], selected[i - 1] - order_cursor -= 1 - - elif key == ord("J"): - # Move selected item down (later in order). - if order_cursor < len(selected) - 1: - i = order_cursor - selected[i], selected[i + 1] = selected[i + 1], selected[i] - order_cursor += 1 - - elif key in (curses.KEY_ENTER, _KEY_ENTER_ALT, ord("\r"), _KEY_SPACE): - # Remove item from selection while in order mode. - del selected[order_cursor] - if order_cursor >= len(selected) and order_cursor > 0: - order_cursor -= 1 + order_cursor = _handle_order_key(key, selected, order_cursor) def _render_multiselect(