From 0330db0b7a33a2315f3c113865998f62d6dae087 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:59:58 +0000 Subject: [PATCH] Add tests for triple/double click mouse selection behaviors; fix extension_in_progress not being cleared on selection reset Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/3ee2e09c-5400-46f6-a556-0bba0e9dcff8 Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com> --- kitty/screen.c | 1 + kitty_tests/mouse.py | 70 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/kitty/screen.c b/kitty/screen.c index 8ad15cea6..d95a1c65f 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -46,6 +46,7 @@ static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECA static void clear_selection(Selections *selections) { selections->in_progress = false; + selections->extension_in_progress = false; selections->extend_mode = EXTEND_CELL; selections->count = 0; } diff --git a/kitty_tests/mouse.py b/kitty_tests/mouse.py index 081e7422f..6b4ec5078 100644 --- a/kitty_tests/mouse.py +++ b/kitty_tests/mouse.py @@ -8,6 +8,7 @@ from kitty.fast_data_types import ( GLFW_MOD_CONTROL, GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_RIGHT, + MOUSE_SELECTION_WORD_AND_LINE_FROM_POINT, create_mock_window, mock_mouse_selection, send_mock_mouse_event_to_window, @@ -83,11 +84,11 @@ class TestMouse(BaseTest): from kitty.window import as_text self.ae(sl, q, f'{sl!r} != {q!r} after movement to x={x} y={y}. Screen contents: {as_text(s)!r}') - def multi_click(x=0, y=0, count=2): + def multi_click(x=0, y=0, count=2, modifiers=0): clear_click_queue = True while count > 0: count -= 1 - ev(GLFW_MOUSE_BUTTON_LEFT, x=x, y=y, clear_click_queue=clear_click_queue) + ev(GLFW_MOUSE_BUTTON_LEFT, x=x, y=y, modifiers=modifiers, clear_click_queue=clear_click_queue) clear_click_queue = False def scroll(x=0, y=0, up=True): @@ -313,3 +314,68 @@ class TestMouse(BaseTest): s.draw('12345') press(x=0, y=0) move(x=2, y=2, q='abcde\n\n12') + + # Line from begin select (alt+triple click): selects from column 0 + # (including any leading whitespace), unlike normal triple click which strips it + s.reset() + s.draw(' 123') + s.linefeed(), s.carriage_return() + s.draw(' 456') + s.linefeed(), s.carriage_return() + multi_click(x=1, count=3, modifiers=GLFW_MOD_ALT) + self.ae(sel(), ' 123') + multi_click(x=1, count=3) + self.ae(sel(), '123') + multi_click(x=1, count=3, modifiers=GLFW_MOD_ALT) + move(y=1) + self.ae(sel(), ' 123\n 456') + release() + + # Line from point select (ctrl+alt+triple click): selects from click + # position to end of line; cannot start before the first non-blank char + s.reset() + s.draw(' 123') + s.linefeed(), s.carriage_return() + s.draw(' 456') + s.linefeed(), s.carriage_return() + multi_click(x=2, count=3, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL) + self.ae(sel(), '23') + # click before first non-blank: selection starts at first non-blank + multi_click(x=0, count=3, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL) + self.ae(sel(), '123') + # click past last non-blank char: no selection created + multi_click(x=4, count=3, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL) + self.ae(sel(), '') + # drag to next line + multi_click(x=2, count=3, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL) + move(y=1) + self.ae(sel(), '23\n 456') + release() + + # Word and line from point: selects the word at the click point and + # extends the selection to the end of the line + sel_word_and_line = MOUSE_SELECTION_WORD_AND_LINE_FROM_POINT + s.reset() + s.draw('ab cd') + press(x=0) + mock_mouse_selection(w, GLFW_MOUSE_BUTTON_LEFT, sel_word_and_line) + self.ae(sel(), 'ab cd') + press(x=3) + mock_mouse_selection(w, GLFW_MOUSE_BUTTON_LEFT, sel_word_and_line) + self.ae(sel(), 'cd') + # click on space: no word, selects from space to end of line + press(x=2) + mock_mouse_selection(w, GLFW_MOUSE_BUTTON_LEFT, sel_word_and_line) + self.ae(sel(), ' cd') + # click past last non-blank char: no selection created + press(x=4) + mock_mouse_selection(w, GLFW_MOUSE_BUTTON_LEFT, sel_word_and_line) + self.ae(sel(), '') + + # Double-click on whitespace: no word found, no selection created + s.reset() + s.draw('ab cd') + multi_click(x=2) + self.ae(sel(), '') + multi_click(x=2.4) + self.ae(sel(), '')