i forget
- SHA
5b62922280795a014de88c186314129169913224- Parents
-
3c9948a - Tree
23b313f
5b62922
5b62922280795a014de88c1863141291699132243c9948a
23b313f| Status | File | + | - |
|---|---|---|---|
| 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 | ||
| 14 | 14 | |
| 15 | 15 | subroutine copy_to_clipboard(text) |
| 16 | 16 | character(len=*), intent(in) :: text |
| 17 | - integer :: unit, ios, file_size | |
| 17 | + integer :: unit, ios | |
| 18 | 18 | character(len=512) :: command |
| 19 | 19 | character(len=:), allocatable :: temp_dir, temp_file |
| 20 | 20 | |
src/commands/command_handler_module.f90modified@@ -6,7 +6,10 @@ module command_handler_module | ||
| 6 | 6 | navigate_to_pane_left, navigate_to_pane_right, navigate_to_pane_up, navigate_to_pane_down, & |
| 7 | 7 | sync_editor_to_pane, tab_t |
| 8 | 8 | 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 | |
| 10 | 13 | use yank_stack_module |
| 11 | 14 | use clipboard_module |
| 12 | 15 | use help_display_module, only: show_help |
@@ -4887,52 +4890,96 @@ contains | ||
| 4887 | 4890 | end if |
| 4888 | 4891 | end if |
| 4889 | 4892 | |
| 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 | + | |
| 4890 | 4897 | 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) | |
| 4894 | 4906 | end if |
| 4895 | 4907 | |
| 4896 | 4908 | 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) | |
| 4900 | 4917 | end if |
| 4901 | 4918 | |
| 4902 | 4919 | 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) | |
| 4906 | 4928 | end if |
| 4907 | 4929 | |
| 4908 | 4930 | 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) | |
| 4912 | 4939 | end if |
| 4913 | 4940 | |
| 4914 | 4941 | 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) | |
| 4918 | 4950 | end if |
| 4919 | 4951 | |
| 4920 | 4952 | 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) | |
| 4924 | 4961 | end if |
| 4925 | 4962 | |
| 4926 | 4963 | 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) | |
| 4930 | 4972 | end if |
| 4931 | 4973 | |
| 4932 | 4974 | 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) | |
| 4936 | 4983 | end if |
| 4937 | 4984 | |
| 4938 | 4985 | case('enter', 'o') |
@@ -4947,7 +4994,21 @@ contains | ||
| 4947 | 4994 | end if |
| 4948 | 4995 | |
| 4949 | 4996 | 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) | |
| 4951 | 5012 | if (tree_state%selected_index >= 1 .and. tree_state%selected_index <= tree_state%n_selectable) then |
| 4952 | 5013 | if (.not. tree_state%selectable_files(tree_state%selected_index)%is_directory) then |
| 4953 | 5014 | selected_path = get_selected_item_path(tree_state) |
@@ -4957,8 +5018,8 @@ contains | ||
| 4957 | 5018 | end if |
| 4958 | 5019 | end if |
| 4959 | 5020 | |
| 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) | |
| 4962 | 5023 | if (tree_state%selected_index >= 1 .and. tree_state%selected_index <= tree_state%n_selectable) then |
| 4963 | 5024 | if (.not. tree_state%selectable_files(tree_state%selected_index)%is_directory) then |
| 4964 | 5025 | selected_path = get_selected_item_path(tree_state) |
@@ -4977,14 +5038,75 @@ contains | ||
| 4977 | 5038 | editor%fuss_hints_expanded = .not. editor%fuss_hints_expanded |
| 4978 | 5039 | |
| 4979 | 5040 | 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 | |
| 4984 | 5062 | |
| 4985 | 5063 | end select |
| 4986 | 5064 | end subroutine handle_fuss_input |
| 4987 | 5065 | |
| 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 | + | |
| 4988 | 5110 | ! Open a file in the editor |
| 4989 | 5111 | subroutine open_file_in_editor(file_path, editor, buffer) |
| 4990 | 5112 | use editor_state_module, only: create_tab |
@@ -5810,6 +5932,8 @@ contains | ||
| 5810 | 5932 | integer, intent(in) :: unused_request_id |
| 5811 | 5933 | type(lsp_message_t), intent(in) :: response |
| 5812 | 5934 | |
| 5935 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 5936 | + | |
| 5813 | 5937 | ! Call the actual handler with saved editor state |
| 5814 | 5938 | if (associated(saved_editor_for_callback)) then |
| 5815 | 5939 | call handle_references_response_impl(saved_editor_for_callback, response) |
@@ -5919,6 +6043,8 @@ contains | ||
| 5919 | 6043 | integer, intent(in) :: unused_request_id |
| 5920 | 6044 | type(lsp_message_t), intent(in) :: response |
| 5921 | 6045 | |
| 6046 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 6047 | + | |
| 5922 | 6048 | ! Call the actual handler with saved editor state |
| 5923 | 6049 | if (associated(saved_editor_for_callback)) then |
| 5924 | 6050 | call handle_code_actions_response_impl(saved_editor_for_callback, response) |
@@ -6006,6 +6132,8 @@ contains | ||
| 6006 | 6132 | integer, intent(in) :: unused_request_id |
| 6007 | 6133 | type(lsp_message_t), intent(in) :: response |
| 6008 | 6134 | |
| 6135 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 6136 | + | |
| 6009 | 6137 | ! Call the actual handler with saved editor state |
| 6010 | 6138 | if (associated(saved_editor_for_callback)) then |
| 6011 | 6139 | call handle_symbols_response_impl(saved_editor_for_callback, response) |
@@ -6167,6 +6295,8 @@ contains | ||
| 6167 | 6295 | integer, intent(in) :: unused_request_id |
| 6168 | 6296 | type(lsp_message_t), intent(in) :: response |
| 6169 | 6297 | |
| 6298 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 6299 | + | |
| 6170 | 6300 | ! Call the actual handler with saved editor state |
| 6171 | 6301 | if (associated(saved_editor_for_callback)) then |
| 6172 | 6302 | call handle_signature_response(saved_editor_for_callback%signature_tooltip, response) |
@@ -6183,6 +6313,8 @@ contains | ||
| 6183 | 6313 | character(len=:), allocatable :: result_str |
| 6184 | 6314 | integer :: changes_applied |
| 6185 | 6315 | |
| 6316 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 6317 | + | |
| 6186 | 6318 | if (.not. associated(saved_editor_for_callback)) return |
| 6187 | 6319 | |
| 6188 | 6320 | ! Convert result to string for apply_workspace_edit |
@@ -6226,6 +6358,8 @@ contains | ||
| 6226 | 6358 | integer :: start_line, start_char, end_line, end_char |
| 6227 | 6359 | integer :: changes_applied |
| 6228 | 6360 | |
| 6361 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 6362 | + | |
| 6229 | 6363 | if (.not. associated(saved_editor_for_callback)) return |
| 6230 | 6364 | |
| 6231 | 6365 | tab_idx = saved_editor_for_callback%active_tab_index |
@@ -6691,6 +6825,8 @@ contains | ||
| 6691 | 6825 | character(len=:), allocatable :: name, container, uri |
| 6692 | 6826 | real(8) :: line_num, char_num, kind_num |
| 6693 | 6827 | |
| 6828 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 6829 | + | |
| 6694 | 6830 | num_symbols = json_array_size(response%result) |
| 6695 | 6831 | if (num_symbols == 0) return |
| 6696 | 6832 | allocate(symbols(num_symbols)) |
@@ -6955,6 +7091,8 @@ contains | ||
| 6955 | 7091 | integer, intent(in) :: unused_request_id |
| 6956 | 7092 | type(lsp_message_t), intent(in) :: response |
| 6957 | 7093 | |
| 7094 | + if (.false.) print *, unused_request_id ! Silence unused warning | |
| 7095 | + | |
| 6958 | 7096 | ! Call actual handler with saved editor state |
| 6959 | 7097 | if (associated(saved_editor_for_callback)) then |
| 6960 | 7098 | call handle_definition_response_impl(saved_editor_for_callback, response) |
src/lsp/diagnostics_module.f90modified@@ -202,6 +202,9 @@ contains | ||
| 202 | 202 | type(diagnostic_t) :: diag |
| 203 | 203 | type(diagnostic_t), allocatable :: old_items(:) |
| 204 | 204 | |
| 205 | + ! Initialize old_items to prevent uninitialized warning | |
| 206 | + allocate(old_items(0)) | |
| 207 | + | |
| 205 | 208 | ! Get URI |
| 206 | 209 | if (.not. json_has_key(params, "uri")) return |
| 207 | 210 | uri = json_get_string(params, "uri") |
src/lsp/lsp_server_manager_module.f90modified@@ -771,6 +771,8 @@ contains | ||
| 771 | 771 | |
| 772 | 772 | ! Servers can send requests too (like workspace/configuration) |
| 773 | 773 | ! TODO: Handle server requests |
| 774 | + ! Silence unused argument warnings (stub for future implementation) | |
| 775 | + if (.false.) print *, manager%num_servers, server%initialized, msg%id | |
| 774 | 776 | end subroutine handle_request |
| 775 | 777 | |
| 776 | 778 | subroutine track_request(server, request_id) |
src/terminal/renderer_module.f90modified@@ -1,5 +1,5 @@ | ||
| 1 | 1 | module renderer_module |
| 2 | - use iso_fortran_env, only: int32, output_unit | |
| 2 | + use iso_fortran_env, only: int32, int64, output_unit | |
| 3 | 3 | use terminal_io_module |
| 4 | 4 | use text_buffer_module |
| 5 | 5 | use utf8_module |
@@ -28,6 +28,9 @@ module renderer_module | ||
| 28 | 28 | public :: tree_state |
| 29 | 29 | public :: update_syntax_highlighter |
| 30 | 30 | 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 | |
| 31 | 34 | |
| 32 | 35 | ! Configuration |
| 33 | 36 | logical :: show_line_numbers = .true. |
@@ -53,6 +56,14 @@ module renderer_module | ||
| 53 | 56 | ! File tree state (for fuss mode) |
| 54 | 57 | type(tree_state_t) :: tree_state |
| 55 | 58 | |
| 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 | + | |
| 56 | 67 | ! Syntax highlighting state |
| 57 | 68 | type(syntax_highlighter_t) :: syntax_highlighter |
| 58 | 69 | character(len=512) :: last_highlighted_filename = "" |
@@ -928,7 +939,8 @@ contains | ||
| 928 | 939 | call render_tab_bar(editor, editor_start_col, editor_width) |
| 929 | 940 | |
| 930 | 941 | ! 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) | |
| 932 | 944 | |
| 933 | 945 | ! Render vertical separator (start at row 2 for tab bar) |
| 934 | 946 | call render_vertical_separator(separator_col, 2, editor%screen_rows - 1) |
@@ -2282,4 +2294,83 @@ contains | ||
| 2282 | 2294 | end if |
| 2283 | 2295 | end function get_basename_str |
| 2284 | 2296 | |
| 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 | + | |
| 2285 | 2376 | end module renderer_module |
src/ui/replace_prompt_module.f90modified@@ -22,6 +22,10 @@ contains | ||
| 22 | 22 | integer :: replace_count |
| 23 | 23 | character(len=:), allocatable :: find_pattern, replace_text |
| 24 | 24 | |
| 25 | + ! Initialize allocatables to prevent "may be uninitialized" warning | |
| 26 | + allocate(character(len=0) :: find_pattern) | |
| 27 | + allocate(character(len=0) :: replace_text) | |
| 28 | + | |
| 25 | 29 | ! Initialize |
| 26 | 30 | find_buffer = '' |
| 27 | 31 | replace_buffer = '' |
src/utils/platform_module.f90modified@@ -121,7 +121,7 @@ contains | ||
| 121 | 121 | |
| 122 | 122 | function platform_paste_from_clipboard() result(text) |
| 123 | 123 | character(len=:), allocatable :: text |
| 124 | - character(len=100000) :: buffer | |
| 124 | + character(len=100000), save :: buffer ! save prevents stack overflow warning | |
| 125 | 125 | integer(c_int) :: result_len, res |
| 126 | 126 | |
| 127 | 127 | 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) { | ||
| 180 | 180 | int copy_len = len + needs_slash; |
| 181 | 181 | if (copy_len >= buffer_len) copy_len = buffer_len - 1; |
| 182 | 182 | |
| 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); | |
| 184 | 185 | if (needs_slash && len < buffer_len - 1) { |
| 185 | 186 | buffer[len] = '/'; |
| 186 | 187 | buffer[len + 1] = '\0'; |
@@ -242,7 +243,7 @@ void get_config_dir_f(char* buffer, int buffer_len, int* result_len) { | ||
| 242 | 243 | if (config && strlen(config) > 0) { |
| 243 | 244 | int len = strlen(config); |
| 244 | 245 | int copy_len = len < buffer_len - 1 ? len : buffer_len - 1; |
| 245 | - strncpy(buffer, config, copy_len); | |
| 246 | + memcpy(buffer, config, copy_len); | |
| 246 | 247 | buffer[copy_len] = '\0'; |
| 247 | 248 | *result_len = copy_len; |
| 248 | 249 | return; |
src/workspace/file_tree_renderer_module.f90modified@@ -14,14 +14,23 @@ module file_tree_renderer_module | ||
| 14 | 14 | |
| 15 | 15 | contains |
| 16 | 16 | |
| 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) | |
| 18 | 18 | type(tree_state_t), intent(in) :: state |
| 19 | 19 | integer, intent(in) :: start_row, end_row, start_col, width |
| 20 | 20 | logical, intent(in) :: hints_expanded |
| 21 | + logical, intent(in), optional :: git_prefix_active | |
| 22 | + logical :: git_mode | |
| 21 | 23 | integer :: current_row, item_idx, row |
| 22 | 24 | character(len=512) :: status_line |
| 23 | 25 | character(len=:), allocatable :: padding |
| 24 | 26 | |
| 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 | + | |
| 25 | 34 | ! First, clear all rows in the tree pane |
| 26 | 35 | padding = repeat(' ', width) |
| 27 | 36 | do row = start_row, end_row |
@@ -71,17 +80,30 @@ contains | ||
| 71 | 80 | call terminal_write('j/k:nav →:in ←:up o:open .:hide spc:toggle') |
| 72 | 81 | call terminal_write(ESC // '[0m') |
| 73 | 82 | |
| 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 | |
| 85 | 107 | |
| 86 | 108 | ! Fourth row: exit |
| 87 | 109 | call terminal_move_cursor(end_row, start_col) |