diff --git a/sqlit/shared/ui/widgets_text_area.py b/sqlit/shared/ui/widgets_text_area.py index 13475d5e..71475d85 100644 --- a/sqlit/shared/ui/widgets_text_area.py +++ b/sqlit/shared/ui/widgets_text_area.py @@ -149,16 +149,23 @@ async def _on_key(self, event: Key) -> None: if event.key == "tab": if not self._is_insert_mode(): return - # If the autocomplete dropdown is open, let the app's key router - # accept the suggestion only when Tab is actually bound to the - # autocomplete_accept action. Otherwise Tab should insert a tab. + # If the autocomplete dropdown is open, let the app's action system + # handle Tab when the autocomplete keymap claims it. Otherwise Tab + # should insert a tab character. app = cast("AutocompleteProtocol", self.app) if getattr(app, "_autocomplete_visible", False): from sqlit.core.keymap import get_keymap keymap = get_keymap() - if "tab" in keymap.keys_for_action("autocomplete_accept"): - return + for binding in keymap.get_action_keys(): + if ( + binding.key == event.key + and binding.context == "autocomplete" + and await self.app.run_action(binding.action) + ): + event.prevent_default() + event.stop() + return self._push_undo_if_changed() tab = self._tab_insert_string() selection = self.selection diff --git a/tests/ui/test_query_tab_insert.py b/tests/ui/test_query_tab_insert.py index 146ed3b5..0d1300cf 100644 --- a/tests/ui/test_query_tab_insert.py +++ b/tests/ui/test_query_tab_insert.py @@ -150,3 +150,50 @@ async def test_tab_inserts_when_autocomplete_accept_is_rebound(self) -> None: assert app.query_input.text == "sel\t" finally: reset_keymap() + + @pytest.mark.asyncio + async def test_tab_honors_other_autocomplete_action_rebinding(self) -> None: + app = _make_app() + + try: + async with app.run_test(size=(100, 35)) as pilot: + app.action_focus_query() + await pilot.press("i") + await pilot.pause() + + defaults = DefaultKeymapProvider() + action_keys = [ + binding + for binding in defaults.get_action_keys() + if not ( + binding.key == "tab" + and binding.context == "autocomplete" + ) + ] + action_keys.append( + ActionKeyDef("tab", "autocomplete_next", "autocomplete") + ) + set_keymap( + FileBasedKeymapProvider( + "rebound-autocomplete-next", + defaults.get_leader_commands(), + action_keys, + ) + ) + + app.query_input.text = "s" + app.query_input.cursor_location = (0, 1) + app._show_autocomplete( + ["select", "set", "session", "sequence", "security"], + "s", + ) + await pilot.pause() + assert app.autocomplete_dropdown.selected_index == 0 + + await pilot.press("tab") + await pilot.pause() + + assert app.query_input.text == "s" + assert app.autocomplete_dropdown.selected_index == 1 + finally: + reset_keymap()