@@ -299,11 +299,11 @@ contains |
| 299 | 299 | prev_selected = selected |
| 300 | 300 | prev_viewport = viewport_offset |
| 301 | 301 | |
| 302 | | - ! Check search timeout (1 second) |
| 302 | + ! Check search timeout (0.5 seconds) |
| 303 | 303 | if (search_length > 0) then |
| 304 | 304 | call system_clock(current_tick) |
| 305 | | - ! Check if 1 second has elapsed (clock_rate ticks per second) |
| 306 | | - if (current_tick - last_search_tick > clock_rate) then |
| 305 | + ! Check if 0.5 seconds has elapsed (clock_rate/2 ticks) |
| 306 | + if (current_tick - last_search_tick > clock_rate / 2) then |
| 307 | 307 | search_length = 0 |
| 308 | 308 | search_buffer = '' |
| 309 | 309 | needs_full_redraw = .true. |
@@ -363,6 +363,21 @@ contains |
| 363 | 363 | if ((key >= 'a' .and. key <= 'z') .or. & |
| 364 | 364 | ((key >= 'E' .and. key <= 'Z') .or. (key >= '0' .and. key <= '9')) .or. & |
| 365 | 365 | key == '_' .or. key == '-' .or. key == '.') then |
| 366 | + |
| 367 | + ! Check if timeout elapsed since last keypress - if so, start fresh search |
| 368 | + if (search_length > 0) then |
| 369 | + call system_clock(current_tick) |
| 370 | + if (current_tick - last_search_tick > clock_rate / 2) then |
| 371 | + ! Timeout elapsed (0.5 seconds) - clear buffer and start new search |
| 372 | + search_length = 0 |
| 373 | + search_buffer = '' |
| 374 | + ! DEBUG |
| 375 | + open(99, file='/tmp/fuss_debug.log', position='append') |
| 376 | + write(99, '(A)') 'TIMEOUT: Starting fresh search (0.5s elapsed)' |
| 377 | + close(99) |
| 378 | + end if |
| 379 | + end if |
| 380 | + |
| 366 | 381 | ! Add to search buffer |
| 367 | 382 | if (search_length < 32) then |
| 368 | 383 | search_length = search_length + 1 |
@@ -686,6 +701,8 @@ contains |
| 686 | 701 | ! In normal mode: q quits the application |
| 687 | 702 | running = .false. |
| 688 | 703 | end if |
| 704 | + case (achar(17)) ! Ctrl-Q - force quit from any mode |
| 705 | + running = .false. |
| 689 | 706 | case default |
| 690 | 707 | ! Unhandled keys - do nothing |
| 691 | 708 | continue |
@@ -1512,6 +1529,7 @@ contains |
| 1512 | 1529 | |
| 1513 | 1530 | subroutine fuzzy_jump_to_match(items, n_items, pattern, selected) |
| 1514 | 1531 | ! Jump to first item that fuzzy matches the pattern |
| 1532 | + ! Prioritizes matching item names (basename) over full paths |
| 1515 | 1533 | ! Searches from NEXT position (skips current item to allow cycling) |
| 1516 | 1534 | type(selectable_item), intent(in) :: items(:) |
| 1517 | 1535 | integer, intent(in) :: n_items |
@@ -1523,6 +1541,28 @@ contains |
| 1523 | 1541 | start_pos = selected + 1 |
| 1524 | 1542 | if (start_pos > n_items) start_pos = 1 |
| 1525 | 1543 | |
| 1544 | + ! PASS 1: Try to match item NAME first (e.g., "src" matches "src/" before "src/file.f90") |
| 1545 | + ! Search from next position forward |
| 1546 | + do i = start_pos, n_items |
| 1547 | + if (associated(items(i)%node)) then |
| 1548 | + if (fuzzy_match(pattern, items(i)%node%name)) then |
| 1549 | + selected = i |
| 1550 | + return |
| 1551 | + end if |
| 1552 | + end if |
| 1553 | + end do |
| 1554 | + |
| 1555 | + ! Wrap around: search from beginning to current position (inclusive) |
| 1556 | + do i = 1, selected |
| 1557 | + if (associated(items(i)%node)) then |
| 1558 | + if (fuzzy_match(pattern, items(i)%node%name)) then |
| 1559 | + selected = i |
| 1560 | + return |
| 1561 | + end if |
| 1562 | + end if |
| 1563 | + end do |
| 1564 | + |
| 1565 | + ! PASS 2: If no name match, try matching full path |
| 1526 | 1566 | ! Search from next position forward |
| 1527 | 1567 | do i = start_pos, n_items |
| 1528 | 1568 | if (fuzzy_match(pattern, items(i)%path)) then |