fortrangoingonforty/fuss / f7bf5a2

Browse files

shore up keyboard problems on rename mode

Authored by espadonne
SHA
f7bf5a269ccd4c73301309c53a4b9664c12f8599
Parents
73f447a
Tree
e58fa89

3 changed files

StatusFile+-
M src/display_module.f90 1 1
M src/fuss_main.f90 25 16
M src/terminal_module.f90 65 24
src/display_module.f90modified
@@ -180,7 +180,7 @@ contains
180180
         print '(A)', ''
181181
         if (in_rename_mode) then
182182
             ! Rename mode help - show in cyan
183
-            print '(A)', achar(27) // '[36mRENAME MODE: Type name (caps A/B ok, C/D use arrows) | ←/→:cursor | Backspace:del | Tab:save | ESC:cancel' // achar(27) // '[0m'
183
+            print '(A)', achar(27) // '[36mRENAME MODE: Type name (all letters/numbers ok) | ←/→:cursor | Backspace:del | Tab:save | ESC:cancel' // achar(27) // '[0m'
184184
         else if (mode == 'git') then
185185
             ! Git mode help - show in yellow tint
186186
             print '(A)', achar(27) // '[33mLegend: ' // achar(27) // '[32m↑' // achar(27) // '[0m=staged ' // &
src/fuss_main.f90modified
@@ -465,24 +465,26 @@ contains
465465
                         needs_full_redraw = .true.
466466
                     end if
467467
                     cycle
468
-                ! NOTE: Arrow keys (after escape processing) are indistinguishable from uppercase A/B/C/D
469
-                ! Trade-off: Prioritize arrow functionality over uppercase C/D letters
470
-                ! Uppercase A/B work fine, C/D reserved for arrows
471
-                else if (key == 'C') then
472
-                    ! Right arrow - move cursor right (also blocks uppercase C)
468
+                ! Arrow keys are now encoded as control codes: Up=28, Down=29, Right=30, Left=31
469
+                ! This allows uppercase C and D to work in rename mode
470
+                else if (key == achar(30)) then
471
+                    ! Right arrow - move cursor right
473472
                     if (rename_cursor_pos < len_trim(rename_buffer)) then
474473
                         rename_cursor_pos = rename_cursor_pos + 1
475474
                         needs_full_redraw = .true.
476475
                     end if
477476
                     cycle
478
-                else if (key == 'D') then
479
-                    ! Left arrow - move cursor left (also blocks uppercase D)
477
+                else if (key == achar(31)) then
478
+                    ! Left arrow - move cursor left
480479
                     if (rename_cursor_pos > 0) then
481480
                         rename_cursor_pos = rename_cursor_pos - 1
482481
                         needs_full_redraw = .true.
483482
                     end if
484483
                     cycle
485
-                ! A and B (up/down arrows) are allowed as uppercase letters - arrows ignored
484
+                ! Up/Down arrows (28, 29) are ignored in rename mode
485
+                else if (key == achar(28) .or. key == achar(29)) then
486
+                    ! Ignore up/down arrows in rename mode
487
+                    cycle
486488
                 else if ((key >= 'a' .and. key <= 'z') .or. &
487489
                          (key >= 'A' .and. key <= 'Z') .or. &
488490
                          (key >= '0' .and. key <= '9') .or. &
@@ -507,10 +509,10 @@ contains
507509
             end if
508510
 
509511
             ! Fuzzy search in normal mode - handle any printable character
510
-            ! Exclude A, B, C, D since those are arrow key codes after escape sequence processing
512
+            ! Arrow keys are now control codes (28-31), so all letters A-Z work in search
511513
             if (mode == 'normal') then
512514
                 if ((key >= 'a' .and. key <= 'z') .or. &
513
-                    ((key >= 'E' .and. key <= 'Z') .or. (key >= '0' .and. key <= '9')) .or. &
515
+                    ((key >= 'A' .and. key <= 'Z') .or. (key >= '0' .and. key <= '9')) .or. &
514516
                     key == '_' .or. key == '-' .or. key == '.') then
515517
 
516518
                     ! Check if timeout elapsed since last keypress - if so, start fresh search
@@ -558,36 +560,42 @@ contains
558560
             end if
559561
 
560562
             ! Handle input
561
-            select case (key)
562
-            case ('j', 'B')  ! j or down arrow - navigate to next sibling (skip nested items)
563
+            ! Note: Arrow keys are now control codes (28-31), checked with if-else before select case
564
+            if (key == 'j' .or. key == achar(29)) then
565
+                ! j or down arrow (29) - navigate to next sibling (skip nested items)
563566
                 ! Clear search buffer on navigation
564567
                 if (search_length > 0) then
565568
                     search_length = 0
566569
                     search_buffer = ''
567570
                 end if
568571
                 call navigate_down(items, n_items, selected)
569
-            case ('k', 'A')  ! k or up arrow - navigate to previous sibling (skip nested items)
572
+            else if (key == 'k' .or. key == achar(28)) then
573
+                ! k or up arrow (28) - navigate to previous sibling (skip nested items)
570574
                 ! Clear search buffer on navigation
571575
                 if (search_length > 0) then
572576
                     search_length = 0
573577
                     search_buffer = ''
574578
                 end if
575579
                 call navigate_up(items, n_items, selected)
576
-            case ('D')  ! Left arrow - navigate to parent directory
580
+            else if (key == achar(31)) then
581
+                ! Left arrow (31) - navigate to parent directory
577582
                 ! Clear search buffer on navigation
578583
                 if (search_length > 0) then
579584
                     search_length = 0
580585
                     search_buffer = ''
581586
                 end if
582587
                 call navigate_left(items, n_items, selected)
583
-            case ('C')  ! Right arrow - enter directory
588
+            else if (key == achar(30)) then
589
+                ! Right arrow (30) - enter directory
584590
                 ! Clear search buffer on navigation
585591
                 if (search_length > 0) then
586592
                     search_length = 0
587593
                     search_buffer = ''
588594
                 end if
589595
                 call navigate_right(items, n_items, selected, tree_root, hide_dotfiles)
590
-            case (' ')  ! Space bar - toggle expand/collapse
596
+            else
597
+                select case (key)
598
+                case (' ')  ! Space bar - toggle expand/collapse
591599
                 ! Clear search buffer on navigation
592600
                 if (search_length > 0) then
593601
                     search_length = 0
@@ -848,6 +856,7 @@ contains
848856
                 ! Unhandled keys - do nothing
849857
                 continue
850858
             end select
859
+            end if  ! End of arrow key if-else chain
851860
         end do
852861
 
853862
         ! Restore terminal to normal state
src/terminal_module.f90modified
@@ -132,41 +132,82 @@ contains
132132
 
133133
     subroutine read_key(key)
134134
         character(len=1), intent(out) :: key
135
-        character(len=3) :: escape_seq
136
-        integer :: iostat, tty_unit
135
+        integer :: status, unit_num, iostat
136
+        character(len=256) :: cmd
137
+
138
+        ! Use bash with timeout to read a character (avoids blocking on ESC)
139
+        ! This prevents the ESC key from requiring a second keypress
140
+        write(cmd, '(A)') 'bash -c "read -t 0.1 -n 1 -s key < /dev/tty && echo -n $key" > ' // FUSS_TEMP // ' 2>/dev/null'
141
+        call execute_command_line(trim(cmd), exitstat=status)
142
+
143
+        if (status /= 0) then
144
+            ! Timeout or error - treat as quit
145
+            key = 'q'
146
+            return
147
+        end if
137148
 
138
-        ! Open /dev/tty for reading
139
-        open(newunit=tty_unit, file='/dev/tty', status='old', action='read', iostat=iostat)
149
+        ! Read the character from temp file
150
+        open(newunit=unit_num, file=FUSS_TEMP, status='old', action='read', iostat=iostat)
140151
         if (iostat /= 0) then
141
-            key = 'q'  ! If we can't open tty, quit
152
+            key = 'q'
142153
             return
143154
         end if
155
+        read(unit_num, '(A1)', iostat=iostat) key
156
+        close(unit_num, status='delete')
144157
 
145
-        ! Read one character
146
-        read(tty_unit, '(A1)', iostat=iostat, advance='no') key
158
+        if (iostat /= 0) then
159
+            key = 'q'
160
+            return
161
+        end if
147162
 
148163
         ! Check for escape sequence (arrow keys or alt-key combos)
149164
         if (key == achar(27)) then
150
-            ! Read just the first character after ESC
151
-            read(tty_unit, '(A1)', iostat=iostat, advance='no') escape_seq(1:1)
152
-
153
-            if (escape_seq(1:1) == '[') then
154
-                ! Arrow key sequence: ESC[A/B/C/D - need to read one more char
155
-                read(tty_unit, '(A1)', iostat=iostat, advance='no') escape_seq(2:2)
156
-                key = escape_seq(2:2)  ! Return A, B, C, or D
157
-            else if (escape_seq(1:1) >= 'a' .and. escape_seq(1:1) <= 'z') then
158
-                ! Alt-letter sequence: ESC followed by letter
159
-                ! Encode as ASCII control characters (1-26 for alt-a through alt-z)
160
-                ! This keeps us in valid ASCII range [0-127]
161
-                ! e.g., alt-g returns achar(7) = ASCII BEL
162
-                key = achar(1 + ichar(escape_seq(1:1)) - ichar('a'))
163
-            else if (iostat /= 0) then
164
-                ! Just ESC key alone (no following character or timeout)
165
+            ! Detected ESC - try to read next char quickly with timeout
166
+            write(cmd, '(A)') 'bash -c "read -t 0.05 -n 1 -s key < /dev/tty && echo -n $key" > ' // &
167
+                              FUSS_TEMP // ' 2>/dev/null'
168
+            call execute_command_line(trim(cmd), exitstat=status)
169
+
170
+            if (status == 0) then
171
+                ! Got a following character - check what it is
172
+                open(newunit=unit_num, file=FUSS_TEMP, status='old', action='read', iostat=iostat)
173
+                if (iostat == 0) then
174
+                    read(unit_num, '(A1)', iostat=iostat) key
175
+                    close(unit_num, status='delete')
176
+
177
+                    ! Check for arrow keys (ESC [ A/B/C/D) or alt-keys (ESC letter)
178
+                    if (key == '[') then
179
+                        ! Arrow key sequence - read final character
180
+                        write(cmd, '(A)') 'bash -c "read -t 0.05 -n 1 -s key < /dev/tty && echo -n $key" > ' // &
181
+                                          FUSS_TEMP // ' 2>/dev/null'
182
+                        call execute_command_line(trim(cmd), exitstat=status)
183
+                        if (status == 0) then
184
+                            open(newunit=unit_num, file=FUSS_TEMP, status='old', action='read', iostat=iostat)
185
+                            if (iostat == 0) then
186
+                                read(unit_num, '(A1)', iostat=iostat) key
187
+                                close(unit_num, status='delete')
188
+                                ! Encode arrow keys as unique control codes to avoid conflict with uppercase letters
189
+                                ! Up=28, Down=29, Right=30, Left=31
190
+                                if (key == 'A') then
191
+                                    key = achar(28)  ! Up arrow
192
+                                else if (key == 'B') then
193
+                                    key = achar(29)  ! Down arrow
194
+                                else if (key == 'C') then
195
+                                    key = achar(30)  ! Right arrow
196
+                                else if (key == 'D') then
197
+                                    key = achar(31)  ! Left arrow
198
+                                end if
199
+                            end if
200
+                        end if
201
+                    else if (key >= 'a' .and. key <= 'z') then
202
+                        ! Alt-letter: encode as control char (1-26 for alt-a through alt-z)
203
+                        key = achar(1 + ichar(key) - ichar('a'))
204
+                    end if
205
+                end if
206
+            else
207
+                ! Just ESC alone (timeout, no following character)
165208
                 key = achar(27)
166209
             end if
167210
         end if
168
-
169
-        close(tty_unit)
170211
     end subroutine read_key
171212
 
172213
     subroutine read_line(prompt, line)