From e616763fd1965ef39c469070e79ea00654fab8ef Mon Sep 17 00:00:00 2001 From: Marthin Lachira Date: Tue, 10 Feb 2026 18:10:11 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20feat(query-history):=20append=20?= =?UTF-8?q?history=20queries=20without=20replacing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/ui/mixins/query_execution.py | 28 +++++++++++++++++++ .../domains/query/ui/screens/query_history.py | 24 +++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/sqlit/domains/query/ui/mixins/query_execution.py b/sqlit/domains/query/ui/mixins/query_execution.py index 08ba29f..739c78c 100644 --- a/sqlit/domains/query/ui/mixins/query_execution.py +++ b/sqlit/domains/query/ui/mixins/query_execution.py @@ -754,6 +754,28 @@ def _apply_history_query(self: QueryMixinHost, query: str) -> None: # Focus query input - this triggers on_descendant_focus which updates footer bindings self.query_input.focus() + def _append_history_query(self: QueryMixinHost, query: str) -> None: + """Append a query to the end of the editor without replacing text.""" + if query is None: + return + + current_text = self.query_input.text + if current_text: + trimmed_current = current_text.rstrip("\n") + separator = "\n" if trimmed_current else "" + new_text = f"{trimmed_current}{separator}{query}" + else: + new_text = query + + self._push_undo_state() + self.query_input.text = new_text + + lines = new_text.split("\n") + last_line = len(lines) - 1 + last_col = len(lines[-1]) if lines else 0 + self.query_input.cursor_location = (last_line, last_col) + self.query_input.focus() + def action_show_history(self: QueryMixinHost) -> None: """Show query history for the current connection.""" if not self.current_config: @@ -781,6 +803,8 @@ def _handle_history_result(self: QueryMixinHost, result: Any) -> None: action, data = result if action == "select": self._apply_history_query(data) + elif action == "append": + self._append_history_query(data) elif action == "delete": self._delete_history_entry(data) self.action_show_history() @@ -853,6 +877,10 @@ def _handle_telescope_result(self: QueryMixinHost, result: Any) -> None: connection_name = data.get("connection_name", "") database = data.get("database", "") self._run_telescope_query(connection_name, query, database=database) + elif action == "append": + query = data.get("query", "") + if query: + self._append_history_query(query) elif action == "delete": timestamp = data.get("timestamp", "") connection_name = data.get("connection_name", "") diff --git a/sqlit/domains/query/ui/screens/query_history.py b/sqlit/domains/query/ui/screens/query_history.py index 01a31e2..342585f 100644 --- a/sqlit/domains/query/ui/screens/query_history.py +++ b/sqlit/domains/query/ui/screens/query_history.py @@ -26,6 +26,7 @@ class QueryHistoryScreen(ModalScreen): Binding("escape", "cancel", "Cancel", priority=True), Binding("q", "cancel", "Cancel"), Binding("enter", "select", "Select"), + Binding("a", "append", "Append"), Binding("d", "delete", "Delete"), Binding("asterisk", "toggle_star", "Star"), Binding("slash", "open_filter", "Filter"), @@ -177,7 +178,12 @@ def compose(self) -> ComposeResult: else: title = f"Query History - {self.connection_name}" empty_message = "No query history for this connection" - shortcuts = [("Select", ""), ("Star", "*"), ("Delete", "D")] + shortcuts = [ + ("Select", ""), + ("Append", "a"), + ("Star", "*"), + ("Delete", "D"), + ] self._merged_entries = self._merge_entries() @@ -240,6 +246,22 @@ def action_select(self) -> None: except Exception: self.dismiss(None) + def action_append(self) -> None: + entries = self._get_display_entries() + if not entries: + self.dismiss(None) + return + + try: + option_list = self.query_one("#history-list", OptionList) + idx = option_list.highlighted + if idx is not None and idx < len(entries): + self.dismiss(self._build_action_result("append", entries[idx])) + else: + self.dismiss(None) + except Exception: + self.dismiss(None) + def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None: if event.option_list.id == "history-list": idx = event.option_list.highlighted