From addc9b09ed3031bdfc57be3c303b9a5c9abbee44 Mon Sep 17 00:00:00 2001 From: "n loewen (aider)" Date: Sat, 7 Jun 2025 21:48:41 +0100 Subject: [PATCH] refactor: Improve multi-line text selection logic and handling --- gtm2.py | 85 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/gtm2.py b/gtm2.py index c89521e..7adf432 100755 --- a/gtm2.py +++ b/gtm2.py @@ -278,25 +278,41 @@ def draw_selection(stdscr, state): return start_x, start_y = state.selection_start_coord - _, end_y = state.selection_end_coord + end_x, end_y = state.selection_end_coord - y1, y2 = min(start_y, end_y), max(start_y, end_y) - # Determine pane from where selection started pane = 'left' if start_x < state.divider_col else 'right' - if pane == 'left': - x1, x2 = 0, state.divider_col - 1 + pane_x1, pane_x2 = 0, state.divider_col - 1 else: # right - x1, x2 = state.divider_col + 2, state.width - 1 + pane_x1, pane_x2 = state.divider_col + 2, state.width - 1 + + # Determine drag direction to handle multi-line selection correctly + if start_y < end_y or (start_y == end_y and start_x <= end_x): + drag_start_x, drag_start_y = start_x, start_y + drag_end_x, drag_end_y = end_x, end_y + else: # upward drag or right-to-left on same line + drag_start_x, drag_start_y = end_x, end_y + drag_end_x, drag_end_y = start_x, start_y + + for y in range(drag_start_y, drag_end_y + 1): + x1, x2 = -1, -1 + if drag_start_y == drag_end_y: # single line selection + x1, x2 = drag_start_x, drag_end_x + elif y == drag_start_y: # first line of multi-line selection + x1, x2 = drag_start_x, pane_x2 + elif y == drag_end_y: # last line of multi-line selection + x1, x2 = pane_x1, drag_end_x + else: # middle line of multi-line selection + x1, x2 = pane_x1, pane_x2 + + # Clamp selection to pane boundaries + x1 = max(x1, pane_x1) + x2 = min(x2, pane_x2) - for y in range(y1, y2 + 1): try: - # chgat can fail at the bottom-right corner of the screen - if x1 < state.width: - # `width-1` is the last valid column - end_col = min(x2, state.width - 1) - length = end_col - x1 + 1 + if x1 < state.width and x1 <= x2: + length = x2 - x1 + 1 if length > 0: stdscr.chgat(y, x1, length, curses.A_REVERSE) except curses.error: @@ -357,33 +373,54 @@ def copy_selection_to_clipboard(stdscr, state): return start_x, start_y = state.selection_start_coord - _, end_y = state.selection_end_coord - - y1, y2 = min(start_y, end_y), max(start_y, end_y) + end_x, end_y = state.selection_end_coord # Determine pane from where selection started pane = 'left' if start_x < state.divider_col else 'right' - if pane == 'left': - x1, x2 = 0, state.divider_col - 1 + pane_x1, pane_x2 = 0, state.divider_col - 1 else: # right - x1, x2 = state.divider_col + 2, state.width - 1 + pane_x1, pane_x2 = state.divider_col + 2, state.width - 1 + + # Determine drag direction to handle multi-line selection correctly + if start_y < end_y or (start_y == end_y and start_x <= end_x): + drag_start_x, drag_start_y = start_x, start_y + drag_end_x, drag_end_y = end_x, end_y + else: # upward drag or right-to-left on same line + drag_start_x, drag_start_y = end_x, end_y + drag_end_x, drag_end_y = start_x, start_y height, width = stdscr.getmaxyx() selected_text_parts = [] - for y in range(y1, y2 + 1): - if 0 <= y < height: - line_str = "" - end_col = min(x2, width - 1) - for x in range(x1, end_col + 1): + for y in range(drag_start_y, drag_end_y + 1): + if not (0 <= y < height): + continue + + x1, x2 = -1, -1 + if drag_start_y == drag_end_y: # single line selection + x1, x2 = drag_start_x, drag_end_x + elif y == drag_start_y: # first line of multi-line selection + x1, x2 = drag_start_x, pane_x2 + elif y == drag_end_y: # last line of multi-line selection + x1, x2 = pane_x1, drag_end_x + else: # middle line of multi-line selection + x1, x2 = pane_x1, pane_x2 + + # Clamp selection to pane boundaries and screen width + x1 = max(x1, pane_x1) + x2 = min(x2, pane_x2, width - 1) + + line_str = "" + if x1 <= x2: + for x in range(x1, x2 + 1): try: char_and_attr = stdscr.inch(y, x) char = char_and_attr & 0xFF line_str += chr(char) except curses.error: line_str += " " - selected_text_parts.append(line_str.rstrip()) + selected_text_parts.append(line_str) if selected_text_parts: text_to_copy = "\n".join(selected_text_parts)