fortrangoingonforty/fortsh / 853c41e

Browse files

add Ctrl+V paste from clipboard with kill-buffer fallback

Authored by espadonne
SHA
853c41e44a96a0e68eaea578d8a135301e9bd4e7
Parents
dd2b33c
Tree
b22ef96

2 changed files

StatusFile+-
M src/io/readline.f90 51 1
M tests/interactive/utils/keys.py 2 1
src/io/readline.f90modified
@@ -47,6 +47,7 @@ module readline
47
   integer, parameter :: KEY_TAB = 9
47
   integer, parameter :: KEY_TAB = 9
48
   integer, parameter :: KEY_CTRL_C = 3
48
   integer, parameter :: KEY_CTRL_C = 3
49
   integer, parameter :: KEY_CTRL_D = 4
49
   integer, parameter :: KEY_CTRL_D = 4
50
+  integer, parameter :: KEY_CTRL_V = 22   ! Paste from system clipboard / kill buffer
50
   integer, parameter :: KEY_CTRL_X = 24   ! Process kill mode
51
   integer, parameter :: KEY_CTRL_X = 24   ! Process kill mode
51
   integer, parameter :: KEY_CTRL_A = 1    ! Home (beginning of line)
52
   integer, parameter :: KEY_CTRL_A = 1    ! Home (beginning of line)
52
   integer, parameter :: KEY_CTRL_E = 5    ! End (end of line)
53
   integer, parameter :: KEY_CTRL_E = 5    ! End (end of line)
@@ -1673,6 +1674,13 @@ contains
1673
             call handle_kill_word(module_input_state)
1674
             call handle_kill_word(module_input_state)
1674
           end if
1675
           end if
1675
           
1676
           
1677
+        case(KEY_CTRL_V)
1678
+          ! Ctrl+V — paste (Sprint 5). Reads from the system clipboard
1679
+          ! first; falls back to the in-session kill_buffer if no
1680
+          ! clipboard tool is available or the clipboard is empty.
1681
+          ! If a selection is active, it's deleted first (paste-over).
1682
+          if (.not. module_input_state%in_search) call handle_paste(module_input_state)
1683
+
1676
         case(KEY_CTRL_Y)
1684
         case(KEY_CTRL_Y)
1677
           ! Yank - no-op in search mode
1685
           ! Yank - no-op in search mode
1678
           if (.not. module_input_state%in_search) call handle_yank(module_input_state)
1686
           if (.not. module_input_state%in_search) call handle_yank(module_input_state)
@@ -7514,7 +7522,49 @@ contains
7514
     input_state%cursor_pos = input_state%cursor_pos + insert_len
7522
     input_state%cursor_pos = input_state%cursor_pos + insert_len
7515
     input_state%dirty = .true.
7523
     input_state%dirty = .true.
7516
   end subroutine
7524
   end subroutine
7517
-  
7525
+
7526
+  ! Ctrl+V paste handler (Sprint 5). Reads from the system clipboard;
7527
+  ! if the clipboard is empty or no tool is available, falls back to
7528
+  ! yanking from the in-session kill_buffer. If a selection is active
7529
+  ! it is deleted first (paste-over behavior, same as Ctrl+Y).
7530
+  subroutine handle_paste(input_state)
7531
+    type(input_state_t), intent(inout) :: input_state
7532
+    character(len=MAX_LINE_LEN) :: paste_buf
7533
+    integer :: paste_len, insert_len, i, j
7534
+
7535
+    ! Delete active selection first (paste-over).
7536
+    if (input_state%selection_active) call delete_selection(input_state)
7537
+
7538
+    ! Try the system clipboard.
7539
+    paste_len = 0
7540
+    call clipboard_paste(paste_buf, MAX_LINE_LEN, paste_len)
7541
+
7542
+    if (paste_len > 0) then
7543
+      ! Truncate to available space.
7544
+      insert_len = min(paste_len, MAX_LINE_LEN - input_state%length)
7545
+      if (insert_len <= 0) return
7546
+
7547
+      ! Shift existing text right.
7548
+      do i = input_state%length, input_state%cursor_pos + 1, -1
7549
+        if (i + insert_len <= MAX_LINE_LEN) then
7550
+          call state_buffer_set_char(input_state, i + insert_len, state_buffer_get_char(input_state, i))
7551
+        end if
7552
+      end do
7553
+
7554
+      ! Insert clipboard text at cursor.
7555
+      do j = 1, insert_len
7556
+        call state_buffer_set_char(input_state, input_state%cursor_pos + j, paste_buf(j:j))
7557
+      end do
7558
+
7559
+      input_state%length = input_state%length + insert_len
7560
+      input_state%cursor_pos = input_state%cursor_pos + insert_len
7561
+      input_state%dirty = .true.
7562
+    else
7563
+      ! Clipboard empty or unavailable — fall back to kill buffer (same as C-y).
7564
+      call handle_yank(input_state)
7565
+    end if
7566
+  end subroutine handle_paste
7567
+
7518
   subroutine handle_clear_screen(input_state, prompt)
7568
   subroutine handle_clear_screen(input_state, prompt)
7519
     type(input_state_t), intent(inout) :: input_state
7569
     type(input_state_t), intent(inout) :: input_state
7520
     character(len=*), intent(in) :: prompt
7570
     character(len=*), intent(in) :: prompt
tests/interactive/utils/keys.pymodified
@@ -55,7 +55,7 @@ CTRL_R = "\x12" # Reverse search
55
 CTRL_S = "\x13"  # Forward search / Suspend output
55
 CTRL_S = "\x13"  # Forward search / Suspend output
56
 CTRL_T = "\x14"  # Transpose characters
56
 CTRL_T = "\x14"  # Transpose characters
57
 CTRL_U = "\x15"  # Kill to beginning of line
57
 CTRL_U = "\x15"  # Kill to beginning of line
58
-CTRL_V = "\x16"  # Quoted insert
58
+CTRL_V = "\x16"  # Paste from clipboard / kill buffer
59
 CTRL_W = "\x17"  # Kill word backward
59
 CTRL_W = "\x17"  # Kill word backward
60
 CTRL_X = "\x18"  # Prefix
60
 CTRL_X = "\x18"  # Prefix
61
 CTRL_Y = "\x19"  # Yank
61
 CTRL_Y = "\x19"  # Yank
@@ -154,6 +154,7 @@ KEYS = {
154
     "C-s": CTRL_S,
154
     "C-s": CTRL_S,
155
     "C-t": CTRL_T,
155
     "C-t": CTRL_T,
156
     "C-u": CTRL_U,
156
     "C-u": CTRL_U,
157
+    "C-v": CTRL_V,
157
     "C-w": CTRL_W,
158
     "C-w": CTRL_W,
158
     "C-y": CTRL_Y,
159
     "C-y": CTRL_Y,
159
     "C-z": CTRL_Z,
160
     "C-z": CTRL_Z,