fortrangoingonforty/facsimile / 5b62922

Browse files

i forget

Authored by espadonne
SHA
5b62922280795a014de88c186314129169913224
Parents
3c9948a
Tree
23b313f

9 changed files

StatusFile+-
M src/clipboard/clipboard_module.f90 1 1
M src/commands/command_handler_module.f90 170 32
M src/lsp/diagnostics_module.f90 3 0
M src/lsp/lsp_server_manager_module.f90 2 0
M src/terminal/renderer_module.f90 93 2
M src/ui/replace_prompt_module.f90 4 0
M src/utils/platform_module.f90 1 1
M src/utils/platform_wrapper.c 3 2
M src/workspace/file_tree_renderer_module.f90 34 12
src/clipboard/clipboard_module.f90modified
@@ -14,7 +14,7 @@ contains
1414
 
1515
     subroutine copy_to_clipboard(text)
1616
         character(len=*), intent(in) :: text
17
-        integer :: unit, ios, file_size
17
+        integer :: unit, ios
1818
         character(len=512) :: command
1919
         character(len=:), allocatable :: temp_dir, temp_file
2020
 
src/commands/command_handler_module.f90modified
@@ -6,7 +6,10 @@ module command_handler_module
66
                                    navigate_to_pane_left, navigate_to_pane_right, navigate_to_pane_up, navigate_to_pane_down, &
77
                                    sync_editor_to_pane, tab_t
88
     use text_buffer_module
9
-    use renderer_module, only: update_viewport, render_screen, render_screen_with_tree, tree_state
9
+    use renderer_module, only: update_viewport, render_screen, render_screen_with_tree, tree_state, &
10
+                               fuss_search_buffer, fuss_search_len, fuss_search_last_time, &
11
+                               fuss_fuzzy_jump, fuss_reset_search, get_time_ms, &
12
+                               fuss_git_prefix_active
1013
     use yank_stack_module
1114
     use clipboard_module
1215
     use help_display_module, only: show_help
@@ -4887,52 +4890,96 @@ contains
48874890
                 end if
48884891
             end if
48894892
 
4893
+        case('ctrl-g')
4894
+            ! Activate git prefix mode (Ctrl+g then a/u/m/p/f/l/t/d)
4895
+            fuss_git_prefix_active = .true.
4896
+
48904897
         case('a')
4891
-            ! Stage file
4892
-            if (allocated(editor%workspace_path)) then
4893
-                call tree_stage_file(tree_state, editor%workspace_path)
4898
+            ! Stage file (only with Ctrl+g prefix)
4899
+            if (fuss_git_prefix_active) then
4900
+                fuss_git_prefix_active = .false.
4901
+                if (allocated(editor%workspace_path)) then
4902
+                    call tree_stage_file(tree_state, editor%workspace_path)
4903
+                end if
4904
+            else
4905
+                call handle_fuss_fuzzy_search(key_str)
48944906
             end if
48954907
 
48964908
         case('u')
4897
-            ! Unstage file
4898
-            if (allocated(editor%workspace_path)) then
4899
-                call tree_unstage_file(tree_state, editor%workspace_path)
4909
+            ! Unstage file (only with Ctrl+g prefix)
4910
+            if (fuss_git_prefix_active) then
4911
+                fuss_git_prefix_active = .false.
4912
+                if (allocated(editor%workspace_path)) then
4913
+                    call tree_unstage_file(tree_state, editor%workspace_path)
4914
+                end if
4915
+            else
4916
+                call handle_fuss_fuzzy_search(key_str)
49004917
             end if
49014918
 
49024919
         case('m')
4903
-            ! Git commit with message
4904
-            if (allocated(editor%workspace_path)) then
4905
-                call handle_git_commit(editor)
4920
+            ! Git commit with message (only with Ctrl+g prefix)
4921
+            if (fuss_git_prefix_active) then
4922
+                fuss_git_prefix_active = .false.
4923
+                if (allocated(editor%workspace_path)) then
4924
+                    call handle_git_commit(editor)
4925
+                end if
4926
+            else
4927
+                call handle_fuss_fuzzy_search(key_str)
49064928
             end if
49074929
 
49084930
         case('p')
4909
-            ! Git push
4910
-            if (allocated(editor%workspace_path)) then
4911
-                call handle_git_push(editor)
4931
+            ! Git push (only with Ctrl+g prefix)
4932
+            if (fuss_git_prefix_active) then
4933
+                fuss_git_prefix_active = .false.
4934
+                if (allocated(editor%workspace_path)) then
4935
+                    call handle_git_push(editor)
4936
+                end if
4937
+            else
4938
+                call handle_fuss_fuzzy_search(key_str)
49124939
             end if
49134940
 
49144941
         case('f')
4915
-            ! Git fetch
4916
-            if (allocated(editor%workspace_path)) then
4917
-                call handle_git_fetch(editor)
4942
+            ! Git fetch (only with Ctrl+g prefix)
4943
+            if (fuss_git_prefix_active) then
4944
+                fuss_git_prefix_active = .false.
4945
+                if (allocated(editor%workspace_path)) then
4946
+                    call handle_git_fetch(editor)
4947
+                end if
4948
+            else
4949
+                call handle_fuss_fuzzy_search(key_str)
49184950
             end if
49194951
 
49204952
         case('l')
4921
-            ! Git pull
4922
-            if (allocated(editor%workspace_path)) then
4923
-                call handle_git_pull(editor)
4953
+            ! Git pull (only with Ctrl+g prefix)
4954
+            if (fuss_git_prefix_active) then
4955
+                fuss_git_prefix_active = .false.
4956
+                if (allocated(editor%workspace_path)) then
4957
+                    call handle_git_pull(editor)
4958
+                end if
4959
+            else
4960
+                call handle_fuss_fuzzy_search(key_str)
49244961
             end if
49254962
 
49264963
         case('t')
4927
-            ! Git tag
4928
-            if (allocated(editor%workspace_path)) then
4929
-                call handle_git_tag(editor)
4964
+            ! Git tag (only with Ctrl+g prefix)
4965
+            if (fuss_git_prefix_active) then
4966
+                fuss_git_prefix_active = .false.
4967
+                if (allocated(editor%workspace_path)) then
4968
+                    call handle_git_tag(editor)
4969
+                end if
4970
+            else
4971
+                call handle_fuss_fuzzy_search(key_str)
49304972
             end if
49314973
 
49324974
         case('d')
4933
-            ! Git diff
4934
-            if (allocated(editor%workspace_path)) then
4935
-                call handle_git_diff(editor, buffer)
4975
+            ! Git diff (only with Ctrl+g prefix)
4976
+            if (fuss_git_prefix_active) then
4977
+                fuss_git_prefix_active = .false.
4978
+                if (allocated(editor%workspace_path)) then
4979
+                    call handle_git_diff(editor, buffer)
4980
+                end if
4981
+            else
4982
+                call handle_fuss_fuzzy_search(key_str)
49364983
             end if
49374984
 
49384985
         case('enter', 'o')
@@ -4947,7 +4994,21 @@ contains
49474994
             end if
49484995
 
49494996
         case('v')
4950
-            ! Open file in vertical split (only for files, not directories)
4997
+            ! Fuzzy search (vsplit moved to alt-v)
4998
+            if (fuss_git_prefix_active) then
4999
+                fuss_git_prefix_active = .false.
5000
+            end if
5001
+            call handle_fuss_fuzzy_search(key_str)
5002
+
5003
+        case('s')
5004
+            ! Fuzzy search (hsplit moved to alt-s)
5005
+            if (fuss_git_prefix_active) then
5006
+                fuss_git_prefix_active = .false.
5007
+            end if
5008
+            call handle_fuss_fuzzy_search(key_str)
5009
+
5010
+        case('alt-v')
5011
+            ! Open file in vertical split (direct shortcut)
49515012
             if (tree_state%selected_index >= 1 .and. tree_state%selected_index <= tree_state%n_selectable) then
49525013
                 if (.not. tree_state%selectable_files(tree_state%selected_index)%is_directory) then
49535014
                     selected_path = get_selected_item_path(tree_state)
@@ -4957,8 +5018,8 @@ contains
49575018
                 end if
49585019
             end if
49595020
 
4960
-        case('s')
4961
-            ! Open file in horizontal split (only for files, not directories)
5021
+        case('alt-s')
5022
+            ! Open file in horizontal split (direct shortcut)
49625023
             if (tree_state%selected_index >= 1 .and. tree_state%selected_index <= tree_state%n_selectable) then
49635024
                 if (.not. tree_state%selectable_files(tree_state%selected_index)%is_directory) then
49645025
                     selected_path = get_selected_item_path(tree_state)
@@ -4977,14 +5038,75 @@ contains
49775038
             editor%fuss_hints_expanded = .not. editor%fuss_hints_expanded
49785039
 
49795040
         case('esc')
4980
-            ! Exit fuss mode
4981
-            editor%fuss_mode_active = .false.
4982
-            editor%fuss_hints_expanded = .false.  ! Reset to collapsed
4983
-            call cleanup_tree_state(tree_state)
5041
+            ! Exit fuss mode (or cancel git prefix mode)
5042
+            if (fuss_git_prefix_active) then
5043
+                fuss_git_prefix_active = .false.
5044
+            else
5045
+                editor%fuss_mode_active = .false.
5046
+                editor%fuss_hints_expanded = .false.  ! Reset to collapsed
5047
+                fuss_git_prefix_active = .false.
5048
+                call fuss_reset_search()
5049
+                call cleanup_tree_state(tree_state)
5050
+            end if
5051
+
5052
+        case default
5053
+            ! Reset git prefix mode if invalid key in prefix mode
5054
+            if (fuss_git_prefix_active) then
5055
+                fuss_git_prefix_active = .false.
5056
+            end if
5057
+            ! Fuzzy search: accumulate typed characters and jump to match
5058
+            ! Only handle single printable characters (letters, digits)
5059
+            if (len_trim(key_str) == 1) then
5060
+                call handle_fuss_fuzzy_search(key_str)
5061
+            end if
49845062
 
49855063
         end select
49865064
     end subroutine handle_fuss_input
49875065
 
5066
+    ! Handle fuzzy search character input in fuss mode
5067
+    subroutine handle_fuss_fuzzy_search(key_str)
5068
+        use iso_fortran_env, only: int64
5069
+        character(len=*), intent(in) :: key_str
5070
+        integer(int64) :: current_time, elapsed
5071
+        character(len=1) :: ch
5072
+        logical :: found
5073
+
5074
+        ch = key_str(1:1)
5075
+
5076
+        ! Only accept printable characters (letters, digits, some punctuation)
5077
+        if (ichar(ch) < 32 .or. ichar(ch) > 126) return
5078
+
5079
+        ! Get current time
5080
+        current_time = get_time_ms()
5081
+
5082
+        ! Check for timeout (500ms) - reset if too long since last keystroke
5083
+        if (fuss_search_last_time > 0) then
5084
+            elapsed = current_time - fuss_search_last_time
5085
+            if (elapsed > 500) then
5086
+                ! Timeout - reset search buffer
5087
+                fuss_search_buffer = ''
5088
+                fuss_search_len = 0
5089
+            end if
5090
+        end if
5091
+
5092
+        ! Add character to search buffer (if there's room)
5093
+        if (fuss_search_len < 64) then
5094
+            fuss_search_len = fuss_search_len + 1
5095
+            fuss_search_buffer(fuss_search_len:fuss_search_len) = ch
5096
+        end if
5097
+
5098
+        ! Update timestamp
5099
+        fuss_search_last_time = current_time
5100
+
5101
+        ! Try to jump to a match
5102
+        found = fuss_fuzzy_jump(fuss_search_buffer(1:fuss_search_len))
5103
+
5104
+        ! Update viewport to keep selection visible
5105
+        if (found) then
5106
+            call update_tree_viewport(tree_state, 18)
5107
+        end if
5108
+    end subroutine handle_fuss_fuzzy_search
5109
+
49885110
     ! Open a file in the editor
49895111
     subroutine open_file_in_editor(file_path, editor, buffer)
49905112
         use editor_state_module, only: create_tab
@@ -5810,6 +5932,8 @@ contains
58105932
         integer, intent(in) :: unused_request_id
58115933
         type(lsp_message_t), intent(in) :: response
58125934
 
5935
+        if (.false.) print *, unused_request_id  ! Silence unused warning
5936
+
58135937
         ! Call the actual handler with saved editor state
58145938
         if (associated(saved_editor_for_callback)) then
58155939
             call handle_references_response_impl(saved_editor_for_callback, response)
@@ -5919,6 +6043,8 @@ contains
59196043
         integer, intent(in) :: unused_request_id
59206044
         type(lsp_message_t), intent(in) :: response
59216045
 
6046
+        if (.false.) print *, unused_request_id  ! Silence unused warning
6047
+
59226048
         ! Call the actual handler with saved editor state
59236049
         if (associated(saved_editor_for_callback)) then
59246050
             call handle_code_actions_response_impl(saved_editor_for_callback, response)
@@ -6006,6 +6132,8 @@ contains
60066132
         integer, intent(in) :: unused_request_id
60076133
         type(lsp_message_t), intent(in) :: response
60086134
 
6135
+        if (.false.) print *, unused_request_id  ! Silence unused warning
6136
+
60096137
         ! Call the actual handler with saved editor state
60106138
         if (associated(saved_editor_for_callback)) then
60116139
             call handle_symbols_response_impl(saved_editor_for_callback, response)
@@ -6167,6 +6295,8 @@ contains
61676295
         integer, intent(in) :: unused_request_id
61686296
         type(lsp_message_t), intent(in) :: response
61696297
 
6298
+        if (.false.) print *, unused_request_id  ! Silence unused warning
6299
+
61706300
         ! Call the actual handler with saved editor state
61716301
         if (associated(saved_editor_for_callback)) then
61726302
             call handle_signature_response(saved_editor_for_callback%signature_tooltip, response)
@@ -6183,6 +6313,8 @@ contains
61836313
         character(len=:), allocatable :: result_str
61846314
         integer :: changes_applied
61856315
 
6316
+        if (.false.) print *, unused_request_id  ! Silence unused warning
6317
+
61866318
         if (.not. associated(saved_editor_for_callback)) return
61876319
 
61886320
         ! Convert result to string for apply_workspace_edit
@@ -6226,6 +6358,8 @@ contains
62266358
         integer :: start_line, start_char, end_line, end_char
62276359
         integer :: changes_applied
62286360
 
6361
+        if (.false.) print *, unused_request_id  ! Silence unused warning
6362
+
62296363
         if (.not. associated(saved_editor_for_callback)) return
62306364
 
62316365
         tab_idx = saved_editor_for_callback%active_tab_index
@@ -6691,6 +6825,8 @@ contains
66916825
         character(len=:), allocatable :: name, container, uri
66926826
         real(8) :: line_num, char_num, kind_num
66936827
 
6828
+        if (.false.) print *, unused_request_id  ! Silence unused warning
6829
+
66946830
         num_symbols = json_array_size(response%result)
66956831
         if (num_symbols == 0) return
66966832
         allocate(symbols(num_symbols))
@@ -6955,6 +7091,8 @@ contains
69557091
         integer, intent(in) :: unused_request_id
69567092
         type(lsp_message_t), intent(in) :: response
69577093
 
7094
+        if (.false.) print *, unused_request_id  ! Silence unused warning
7095
+
69587096
         ! Call actual handler with saved editor state
69597097
         if (associated(saved_editor_for_callback)) then
69607098
             call handle_definition_response_impl(saved_editor_for_callback, response)
src/lsp/diagnostics_module.f90modified
@@ -202,6 +202,9 @@ contains
202202
         type(diagnostic_t) :: diag
203203
         type(diagnostic_t), allocatable :: old_items(:)
204204
 
205
+        ! Initialize old_items to prevent uninitialized warning
206
+        allocate(old_items(0))
207
+
205208
         ! Get URI
206209
         if (.not. json_has_key(params, "uri")) return
207210
         uri = json_get_string(params, "uri")
src/lsp/lsp_server_manager_module.f90modified
@@ -771,6 +771,8 @@ contains
771771
 
772772
         ! Servers can send requests too (like workspace/configuration)
773773
         ! TODO: Handle server requests
774
+        ! Silence unused argument warnings (stub for future implementation)
775
+        if (.false.) print *, manager%num_servers, server%initialized, msg%id
774776
     end subroutine handle_request
775777
 
776778
     subroutine track_request(server, request_id)
src/terminal/renderer_module.f90modified
@@ -1,5 +1,5 @@
11
 module renderer_module
2
-    use iso_fortran_env, only: int32, output_unit
2
+    use iso_fortran_env, only: int32, int64, output_unit
33
     use terminal_io_module
44
     use text_buffer_module
55
     use utf8_module
@@ -28,6 +28,9 @@ module renderer_module
2828
     public :: tree_state
2929
     public :: update_syntax_highlighter
3030
     public :: render_cursor_only  ! Fast path for cursor-only updates
31
+    public :: fuss_search_buffer, fuss_search_len, fuss_search_last_time
32
+    public :: fuss_fuzzy_jump, fuss_reset_search, get_time_ms
33
+    public :: fuss_git_prefix_active
3134
 
3235
     ! Configuration
3336
     logical :: show_line_numbers = .true.
@@ -53,6 +56,14 @@ module renderer_module
5356
     ! File tree state (for fuss mode)
5457
     type(tree_state_t) :: tree_state
5558
 
59
+    ! Fuzzy search state for fuss mode (500ms timeout)
60
+    character(len=64) :: fuss_search_buffer = ''
61
+    integer :: fuss_search_len = 0
62
+    integer(int64) :: fuss_search_last_time = 0
63
+
64
+    ! Git prefix mode for fuss (Ctrl+g followed by command key)
65
+    logical :: fuss_git_prefix_active = .false.
66
+
5667
     ! Syntax highlighting state
5768
     type(syntax_highlighter_t) :: syntax_highlighter
5869
     character(len=512) :: last_highlighted_filename = ""
@@ -928,7 +939,8 @@ contains
928939
         call render_tab_bar(editor, editor_start_col, editor_width)
929940
 
930941
         ! Render file tree in left pane (start at row 2 for tab bar)
931
-        call render_file_tree(tree_state, 2, editor%screen_rows - 1, 2, tree_width - 2, editor%fuss_hints_expanded)
942
+        call render_file_tree(tree_state, 2, editor%screen_rows - 1, 2, tree_width - 2, &
943
+                              editor%fuss_hints_expanded, fuss_git_prefix_active)
932944
 
933945
         ! Render vertical separator (start at row 2 for tab bar)
934946
         call render_vertical_separator(separator_col, 2, editor%screen_rows - 1)
@@ -2282,4 +2294,83 @@ contains
22822294
         end if
22832295
     end function get_basename_str
22842296
 
2297
+    ! Get current time in milliseconds (for fuzzy search timeout)
2298
+    function get_time_ms() result(ms)
2299
+        integer(int64) :: ms
2300
+        integer(int64) :: count, count_rate
2301
+
2302
+        call system_clock(count, count_rate)
2303
+        if (count_rate > 0) then
2304
+            ms = (count * 1000_int64) / count_rate
2305
+        else
2306
+            ms = 0
2307
+        end if
2308
+    end function get_time_ms
2309
+
2310
+    ! Reset fuzzy search buffer
2311
+    subroutine fuss_reset_search()
2312
+        fuss_search_buffer = ''
2313
+        fuss_search_len = 0
2314
+        fuss_search_last_time = 0
2315
+    end subroutine fuss_reset_search
2316
+
2317
+    ! Fuzzy jump to matching entry in fuss mode
2318
+    ! Returns true if a match was found and jumped to
2319
+    function fuss_fuzzy_jump(search_str) result(found)
2320
+        character(len=*), intent(in) :: search_str
2321
+        logical :: found
2322
+        integer :: i, start_idx, search_len
2323
+        character(len=256) :: item_name, search_lower, name_lower
2324
+
2325
+        found = .false.
2326
+        search_len = len_trim(search_str)
2327
+        if (search_len == 0) return
2328
+        if (tree_state%n_selectable == 0) return
2329
+
2330
+        ! Convert search string to lowercase for case-insensitive matching
2331
+        search_lower = to_lower(trim(search_str))
2332
+
2333
+        ! Start searching from current position + 1, wrap around
2334
+        start_idx = tree_state%selected_index
2335
+        do i = 1, tree_state%n_selectable
2336
+            ! Wrap around index
2337
+            start_idx = start_idx + 1
2338
+            if (start_idx > tree_state%n_selectable) start_idx = 1
2339
+
2340
+            ! Get item name (extract basename from path for files)
2341
+            if (associated(tree_state%selectable_files(start_idx)%node)) then
2342
+                item_name = trim(tree_state%selectable_files(start_idx)%node%name)
2343
+            else
2344
+                item_name = trim(tree_state%selectable_files(start_idx)%path)
2345
+            end if
2346
+
2347
+            ! Convert to lowercase for comparison
2348
+            name_lower = to_lower(trim(item_name))
2349
+
2350
+            ! Check if name starts with search string (prefix match)
2351
+            if (len_trim(name_lower) >= search_len) then
2352
+                if (name_lower(1:search_len) == search_lower(1:search_len)) then
2353
+                    tree_state%selected_index = start_idx
2354
+                    found = .true.
2355
+                    return
2356
+                end if
2357
+            end if
2358
+        end do
2359
+    end function fuss_fuzzy_jump
2360
+
2361
+    ! Convert string to lowercase
2362
+    function to_lower(str) result(lower_str)
2363
+        character(len=*), intent(in) :: str
2364
+        character(len=256) :: lower_str
2365
+        integer :: i, ic
2366
+
2367
+        lower_str = str
2368
+        do i = 1, len_trim(str)
2369
+            ic = ichar(str(i:i))
2370
+            if (ic >= ichar('A') .and. ic <= ichar('Z')) then
2371
+                lower_str(i:i) = char(ic + 32)
2372
+            end if
2373
+        end do
2374
+    end function to_lower
2375
+
22852376
 end module renderer_module
src/ui/replace_prompt_module.f90modified
@@ -22,6 +22,10 @@ contains
2222
         integer :: replace_count
2323
         character(len=:), allocatable :: find_pattern, replace_text
2424
 
25
+        ! Initialize allocatables to prevent "may be uninitialized" warning
26
+        allocate(character(len=0) :: find_pattern)
27
+        allocate(character(len=0) :: replace_text)
28
+
2529
         ! Initialize
2630
         find_buffer = ''
2731
         replace_buffer = ''
src/utils/platform_module.f90modified
@@ -121,7 +121,7 @@ contains
121121
 
122122
     function platform_paste_from_clipboard() result(text)
123123
         character(len=:), allocatable :: text
124
-        character(len=100000) :: buffer
124
+        character(len=100000), save :: buffer  ! save prevents stack overflow warning
125125
         integer(c_int) :: result_len, res
126126
 
127127
         res = paste_from_clipboard_c(buffer, 100000_c_int, result_len)
src/utils/platform_wrapper.cmodified
@@ -180,7 +180,8 @@ void get_temp_dir_f(char* buffer, int buffer_len, int* result_len) {
180180
     int copy_len = len + needs_slash;
181181
     if (copy_len >= buffer_len) copy_len = buffer_len - 1;
182182
 
183
-    strncpy(buffer, tmp, len < buffer_len ? len : buffer_len - 1);
183
+    int bytes_to_copy = len < buffer_len - 1 ? len : buffer_len - 1;
184
+    memcpy(buffer, tmp, bytes_to_copy);
184185
     if (needs_slash && len < buffer_len - 1) {
185186
         buffer[len] = '/';
186187
         buffer[len + 1] = '\0';
@@ -242,7 +243,7 @@ void get_config_dir_f(char* buffer, int buffer_len, int* result_len) {
242243
     if (config && strlen(config) > 0) {
243244
         int len = strlen(config);
244245
         int copy_len = len < buffer_len - 1 ? len : buffer_len - 1;
245
-        strncpy(buffer, config, copy_len);
246
+        memcpy(buffer, config, copy_len);
246247
         buffer[copy_len] = '\0';
247248
         *result_len = copy_len;
248249
         return;
src/workspace/file_tree_renderer_module.f90modified
@@ -14,14 +14,23 @@ module file_tree_renderer_module
1414
 
1515
 contains
1616
 
17
-    subroutine render_file_tree(state, start_row, end_row, start_col, width, hints_expanded)
17
+    subroutine render_file_tree(state, start_row, end_row, start_col, width, hints_expanded, git_prefix_active)
1818
         type(tree_state_t), intent(in) :: state
1919
         integer, intent(in) :: start_row, end_row, start_col, width
2020
         logical, intent(in) :: hints_expanded
21
+        logical, intent(in), optional :: git_prefix_active
22
+        logical :: git_mode
2123
         integer :: current_row, item_idx, row
2224
         character(len=512) :: status_line
2325
         character(len=:), allocatable :: padding
2426
 
27
+        ! Handle optional git_prefix_active parameter
28
+        if (present(git_prefix_active)) then
29
+            git_mode = git_prefix_active
30
+        else
31
+            git_mode = .false.
32
+        end if
33
+
2534
         ! First, clear all rows in the tree pane
2635
         padding = repeat(' ', width)
2736
         do row = start_row, end_row
@@ -71,17 +80,30 @@ contains
7180
                 call terminal_write('j/k:nav →:in ←:up o:open .:hide spc:toggle')
7281
                 call terminal_write(ESC // '[0m')
7382
 
74
-                ! Second row: git operations (staging/basic)
75
-                call terminal_move_cursor(end_row - 2, start_col)
76
-                call terminal_write(ESC // '[90m') ! Gray
77
-                call terminal_write('a:stage u:unstage d:diff m:commit')
78
-                call terminal_write(ESC // '[0m')
79
-
80
-                ! Third row: git operations (remote)
81
-                call terminal_move_cursor(end_row - 1, start_col)
82
-                call terminal_write(ESC // '[90m') ! Gray
83
-                call terminal_write('p:push f:fetch l:pull t:tag')
84
-                call terminal_write(ESC // '[0m')
83
+                ! Second and third rows: git operations (only shown when git mode active)
84
+                if (git_mode) then
85
+                    ! Git mode active - show git bindings in yellow
86
+                    call terminal_move_cursor(end_row - 2, start_col)
87
+                    call terminal_write(ESC // '[1;33m') ! Bright yellow
88
+                    call terminal_write('a:stage u:unstage d:diff m:commit')
89
+                    call terminal_write(ESC // '[0m')
90
+
91
+                    call terminal_move_cursor(end_row - 1, start_col)
92
+                    call terminal_write(ESC // '[1;33m') ! Bright yellow
93
+                    call terminal_write('p:push f:fetch l:pull t:tag  esc:cancel')
94
+                    call terminal_write(ESC // '[0m')
95
+                else
96
+                    ! Normal mode - show shortcuts and fuzzy search hint
97
+                    call terminal_move_cursor(end_row - 2, start_col)
98
+                    call terminal_write(ESC // '[90m') ! Gray
99
+                    call terminal_write('alt-v:vsplit alt-s:hsplit ctrl-g:git')
100
+                    call terminal_write(ESC // '[0m')
101
+
102
+                    call terminal_move_cursor(end_row - 1, start_col)
103
+                    call terminal_write(ESC // '[90m') ! Gray
104
+                    call terminal_write('type to fuzzy search files')
105
+                    call terminal_write(ESC // '[0m')
106
+                end if
85107
 
86108
                 ! Fourth row: exit
87109
                 call terminal_move_cursor(end_row, start_col)