diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dce2677..e728098c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Bug Fixes + +- If the cursor is after the final semicolon in the query editor, and there is only whitespace after the semicolon, Harlequin will now execute the last query before the semicolon, instead of doing nothing when clicking Run Query or pressing ctrl+j. + ## [1.16.1] - 2024-03-27 ### Bug Fixes diff --git a/src/harlequin/app.py b/src/harlequin/app.py index 514ccd64..83ff1b2a 100644 --- a/src/harlequin/app.py +++ b/src/harlequin/app.py @@ -819,7 +819,7 @@ def _validate_selection(self) -> str: """ if self.editor is None: return "" - selection = self.editor.selected_text + selection = self.editor.selected_text.strip() if self.connection is None: return selection if selection: diff --git a/src/harlequin/components/code_editor.py b/src/harlequin/components/code_editor.py index 1ce64c07..0bf331cf 100644 --- a/src/harlequin/components/code_editor.py +++ b/src/harlequin/components/code_editor.py @@ -62,7 +62,7 @@ def current_query(self) -> str: after = (lno, len(self.text_input.document.get_line(lno))) return self.text_input.get_text_range( start=(before[0], before[1]), end=(after[0], after[1]) - ) + ).strip() @property def previous_query(self) -> str: @@ -82,7 +82,7 @@ def previous_query(self) -> str: return self.text_input.get_text_range( start=(first[0], first[1]), end=(second[0], second[1]) - ) + ).strip() def on_mount(self) -> None: self.post_message(EditorCollection.EditorSwitched(active_editor=self)) diff --git a/tests/functional_tests/test_app.py b/tests/functional_tests/test_app.py index 772bbce6..55151e4d 100644 --- a/tests/functional_tests/test_app.py +++ b/tests/functional_tests/test_app.py @@ -206,6 +206,57 @@ async def test_multiple_queries( assert all(snap_results) +@pytest.mark.asyncio +async def test_single_query_terminated_with_semicolon( + app_all_adapters: Harlequin, +) -> None: + app = app_all_adapters + messages: list[Message] = [] + async with app.run_test(message_hook=messages.append) as pilot: + await app.workers.wait_for_complete() + while app.editor is None: + await pilot.pause() + q = "select 1; \n\t\n" + app.editor.text = q + await pilot.press("ctrl+j") + + # should only run current query + await app.workers.wait_for_complete() + await pilot.pause() + [query_submitted_message] = [ + m for m in messages if isinstance(m, QuerySubmitted) + ] + assert query_submitted_message.query_text == "select 1;" + assert app.results_viewer.tab_count == 1 + + app.editor.focus() + await pilot.press("ctrl+a") + await pilot.press("ctrl+j") + + # should not run whitespace query, even though included + # in selection. + await app.workers.wait_for_complete() + await pilot.pause() + [_, query_submitted_message] = [ + m for m in messages if isinstance(m, QuerySubmitted) + ] + assert query_submitted_message.query_text == "select 1;" + assert app.results_viewer.tab_count == 1 + + app.editor.focus() + await pilot.press("ctrl+end") + await pilot.press("ctrl+j") + # should run previous query + assert not app.editor.current_query + await app.workers.wait_for_complete() + await pilot.pause() + [*_, query_submitted_message] = [ + m for m in messages if isinstance(m, QuerySubmitted) + ] + assert query_submitted_message.query_text == "select 1;" + assert app.results_viewer.tab_count == 1 + + @pytest.mark.asyncio @pytest.mark.parametrize( "bad_query",