don't perform eol behavior on cursor up on empty line
- SHA
909f06257f6b720323efb69086e0263a353c92cc- Parents
-
c4b4cfb - Tree
19dcf56
909f062
909f06257f6b720323efb69086e0263a353c92ccc4b4cfb
19dcf56| Status | File | + | - |
|---|---|---|---|
| M |
app/main.f90
|
8 | 0 |
| M |
src/commands/command_handler_module.f90
|
33 | 27 |
| M |
src/editor_state_module.f90
|
10 | 0 |
| M |
src/terminal/renderer_module.f90
|
20 | 0 |
| M |
src/ui/command_palette_module.f90
|
28 | 12 |
| M |
src/ui/unified_search_module.f90
|
3 | 4 |
app/main.f90modified@@ -20,6 +20,8 @@ program facsimile | ||
| 20 | 20 | notify_file_closed, process_server_messages, & |
| 21 | 21 | set_diagnostics_handler, set_lsp_workspace_root |
| 22 | 22 | use lsp_protocol_module, only: lsp_message_t |
| 23 | + use app_state_module, only: is_first_run, mark_first_run_complete | |
| 24 | + use lsp_server_installer_panel_module, only: show_lsp_server_installer_panel | |
| 23 | 25 | implicit none |
| 24 | 26 | |
| 25 | 27 | type(editor_state_t) :: editor |
@@ -352,6 +354,12 @@ program facsimile | ||
| 352 | 354 | ! Save initial file state for undo (position 0) |
| 353 | 355 | call save_initial_state_for_undo(buffer, editor) |
| 354 | 356 | |
| 357 | + ! First-run experience: show LSP server installer panel | |
| 358 | + if (is_first_run()) then | |
| 359 | + call show_lsp_server_installer_panel(editor%lsp_installer_panel) | |
| 360 | + call mark_first_run_complete() | |
| 361 | + end if | |
| 362 | + | |
| 355 | 363 | ! Initial render |
| 356 | 364 | call render_screen(buffer, editor, allocated(search_pattern), match_case_sensitive) |
| 357 | 365 | |
src/commands/command_handler_module.f90modified@@ -65,6 +65,12 @@ module command_handler_module | ||
| 65 | 65 | use diagnostics_module, only: get_diagnostics_for_line, get_diagnostics_for_line_by_server, & |
| 66 | 66 | diagnostics_to_json, diagnostic_t |
| 67 | 67 | use json_module, only: json_value_t |
| 68 | + use lsp_server_installer_panel_module, only: show_lsp_server_installer_panel, & | |
| 69 | + hide_lsp_server_installer_panel, & | |
| 70 | + is_lsp_server_installer_panel_visible, & | |
| 71 | + lsp_server_installer_panel_handle_key, & | |
| 72 | + render_lsp_server_installer_panel, & | |
| 73 | + refresh_server_status | |
| 68 | 74 | implicit none |
| 69 | 75 | private |
| 70 | 76 | |
@@ -252,6 +258,15 @@ contains | ||
| 252 | 258 | end if |
| 253 | 259 | end if |
| 254 | 260 | |
| 261 | + ! Route keys to LSP server installer panel when visible | |
| 262 | + if (is_lsp_server_installer_panel_visible(editor%lsp_installer_panel)) then | |
| 263 | + if (lsp_server_installer_panel_handle_key(editor%lsp_installer_panel, trim(key_str))) then | |
| 264 | + call render_lsp_server_installer_panel(editor%lsp_installer_panel, & | |
| 265 | + editor%screen_rows, editor%screen_cols) | |
| 266 | + return | |
| 267 | + end if | |
| 268 | + end if | |
| 269 | + | |
| 255 | 270 | select case(trim(key_str)) |
| 256 | 271 | ! File operations |
| 257 | 272 | case('ctrl-q') |
@@ -1719,6 +1734,15 @@ contains | ||
| 1719 | 1734 | ! Re-render screen to show/hide the panel |
| 1720 | 1735 | call render_screen(buffer, editor) |
| 1721 | 1736 | |
| 1737 | + case('alt-m') | |
| 1738 | + ! Toggle LSP server installer panel (Alt+M for Manager) | |
| 1739 | + if (is_lsp_server_installer_panel_visible(editor%lsp_installer_panel)) then | |
| 1740 | + call hide_lsp_server_installer_panel(editor%lsp_installer_panel) | |
| 1741 | + else | |
| 1742 | + call show_lsp_server_installer_panel(editor%lsp_installer_panel) | |
| 1743 | + end if | |
| 1744 | + call render_screen(buffer, editor) | |
| 1745 | + | |
| 1722 | 1746 | case('alt-c') |
| 1723 | 1747 | ! Toggle case sensitivity for match mode (ctrl-d) |
| 1724 | 1748 | ! Only has effect when in active match mode (search_pattern allocated) |
@@ -1860,28 +1884,19 @@ contains | ||
| 1860 | 1884 | subroutine move_cursor_up(cursor, buffer) |
| 1861 | 1885 | type(cursor_t), intent(inout) :: cursor |
| 1862 | 1886 | type(buffer_t), intent(in) :: buffer |
| 1863 | - character(len=:), allocatable :: current_line, target_line | |
| 1887 | + character(len=:), allocatable :: target_line | |
| 1864 | 1888 | |
| 1865 | 1889 | cursor%has_selection = .false. ! Clear selection |
| 1866 | 1890 | if (cursor%line > 1) then |
| 1867 | - current_line = buffer_get_line(buffer, cursor%line) | |
| 1868 | 1891 | cursor%line = cursor%line - 1 |
| 1869 | 1892 | target_line = buffer_get_line(buffer, cursor%line) |
| 1870 | 1893 | |
| 1871 | - ! If on empty line with no goal column established, go to end of target line | |
| 1872 | - ! Otherwise, use the goal column | |
| 1873 | - if (len(current_line) == 0 .and. cursor%desired_column == 1) then | |
| 1894 | + ! Always use goal column, clamped to line bounds (standard editor behavior) | |
| 1895 | + cursor%column = cursor%desired_column | |
| 1896 | + if (cursor%column > len(target_line) + 1) then | |
| 1874 | 1897 | cursor%column = len(target_line) + 1 |
| 1875 | - cursor%desired_column = cursor%column | |
| 1876 | - else | |
| 1877 | - ! Use goal column, clamped to line bounds | |
| 1878 | - cursor%column = cursor%desired_column | |
| 1879 | - if (cursor%column > len(target_line) + 1) then | |
| 1880 | - cursor%column = len(target_line) + 1 | |
| 1881 | - end if | |
| 1882 | 1898 | end if |
| 1883 | 1899 | |
| 1884 | - if (allocated(current_line)) deallocate(current_line) | |
| 1885 | 1900 | if (allocated(target_line)) deallocate(target_line) |
| 1886 | 1901 | end if |
| 1887 | 1902 | end subroutine move_cursor_up |
@@ -1890,28 +1905,19 @@ contains | ||
| 1890 | 1905 | type(cursor_t), intent(inout) :: cursor |
| 1891 | 1906 | type(buffer_t), intent(in) :: buffer |
| 1892 | 1907 | integer, intent(in) :: line_count |
| 1893 | - character(len=:), allocatable :: current_line, target_line | |
| 1908 | + character(len=:), allocatable :: target_line | |
| 1894 | 1909 | |
| 1895 | 1910 | cursor%has_selection = .false. ! Clear selection |
| 1896 | 1911 | if (cursor%line < line_count) then |
| 1897 | - current_line = buffer_get_line(buffer, cursor%line) | |
| 1898 | 1912 | cursor%line = cursor%line + 1 |
| 1899 | 1913 | target_line = buffer_get_line(buffer, cursor%line) |
| 1900 | 1914 | |
| 1901 | - ! If on empty line with no goal column established, go to col 1 of target line | |
| 1902 | - ! Otherwise, use the goal column | |
| 1903 | - if (len(current_line) == 0 .and. cursor%desired_column == 1) then | |
| 1904 | - cursor%column = 1 | |
| 1905 | - ! desired_column stays 1 | |
| 1906 | - else | |
| 1907 | - ! Use goal column, clamped to line bounds | |
| 1908 | - cursor%column = cursor%desired_column | |
| 1909 | - if (cursor%column > len(target_line) + 1) then | |
| 1910 | - cursor%column = len(target_line) + 1 | |
| 1911 | - end if | |
| 1915 | + ! Always use goal column, clamped to line bounds (standard editor behavior) | |
| 1916 | + cursor%column = cursor%desired_column | |
| 1917 | + if (cursor%column > len(target_line) + 1) then | |
| 1918 | + cursor%column = len(target_line) + 1 | |
| 1912 | 1919 | end if |
| 1913 | 1920 | |
| 1914 | - if (allocated(current_line)) deallocate(current_line) | |
| 1915 | 1921 | if (allocated(target_line)) deallocate(target_line) |
| 1916 | 1922 | end if |
| 1917 | 1923 | end subroutine move_cursor_down |
src/editor_state_module.f90modified@@ -31,6 +31,9 @@ module editor_state_module | ||
| 31 | 31 | cleanup_document_sync |
| 32 | 32 | use jump_stack_module, only: jump_stack_t, init_jump_stack, & |
| 33 | 33 | cleanup_jump_stack |
| 34 | + use lsp_server_installer_panel_module, only: lsp_server_installer_panel_t, & | |
| 35 | + init_lsp_server_installer_panel, & | |
| 36 | + cleanup_lsp_server_installer_panel | |
| 34 | 37 | implicit none |
| 35 | 38 | private |
| 36 | 39 | |
@@ -135,6 +138,7 @@ module editor_state_module | ||
| 135 | 138 | type(signature_tooltip_t) :: signature_tooltip |
| 136 | 139 | type(command_palette_t) :: command_palette |
| 137 | 140 | type(workspace_symbols_panel_t) :: workspace_symbols_panel |
| 141 | + type(lsp_server_installer_panel_t) :: lsp_installer_panel | |
| 138 | 142 | |
| 139 | 143 | ! Navigation |
| 140 | 144 | type(jump_stack_t) :: jump_stack |
@@ -199,6 +203,9 @@ contains | ||
| 199 | 203 | |
| 200 | 204 | ! Initialize jump stack |
| 201 | 205 | call init_jump_stack(editor%jump_stack) |
| 206 | + | |
| 207 | + ! Initialize LSP server installer panel | |
| 208 | + call init_lsp_server_installer_panel(editor%lsp_installer_panel) | |
| 202 | 209 | end subroutine init_editor |
| 203 | 210 | |
| 204 | 211 | subroutine cleanup_editor(editor) |
@@ -252,6 +259,9 @@ contains | ||
| 252 | 259 | |
| 253 | 260 | ! Cleanup jump stack |
| 254 | 261 | call cleanup_jump_stack(editor%jump_stack) |
| 262 | + | |
| 263 | + ! Cleanup LSP server installer panel | |
| 264 | + call cleanup_lsp_server_installer_panel(editor%lsp_installer_panel) | |
| 255 | 265 | end subroutine cleanup_editor |
| 256 | 266 | |
| 257 | 267 | ! Helper to cleanup a single tab |
src/terminal/renderer_module.f90modified@@ -16,6 +16,8 @@ module renderer_module | ||
| 16 | 16 | use code_actions_panel_module, only: render_code_actions_panel |
| 17 | 17 | use symbols_panel_module, only: render_symbols_panel |
| 18 | 18 | use unified_search_module, only: get_matches_on_line, search_mode_active |
| 19 | + use lsp_server_installer_panel_module, only: render_lsp_server_installer_panel, & | |
| 20 | + is_lsp_server_installer_panel_visible | |
| 19 | 21 | implicit none |
| 20 | 22 | private |
| 21 | 23 | |
@@ -214,6 +216,12 @@ contains | ||
| 214 | 216 | ! Render symbols panel if visible (for panes path) |
| 215 | 217 | call render_symbols_panel(editor%symbols_panel, editor%screen_rows) |
| 216 | 218 | |
| 219 | + ! Render LSP server installer panel if visible (for panes path) | |
| 220 | + if (is_lsp_server_installer_panel_visible(editor%lsp_installer_panel)) then | |
| 221 | + call render_lsp_server_installer_panel(editor%lsp_installer_panel, & | |
| 222 | + editor%screen_rows, editor%screen_cols) | |
| 223 | + end if | |
| 224 | + | |
| 217 | 225 | ! Position cursor for panes |
| 218 | 226 | call render_cursor_for_panes(editor) |
| 219 | 227 | return ! Exit after rendering panes |
@@ -285,6 +293,12 @@ contains | ||
| 285 | 293 | ! Render symbols panel if visible |
| 286 | 294 | call render_symbols_panel(editor%symbols_panel, editor%screen_rows) |
| 287 | 295 | |
| 296 | + ! Render LSP server installer panel if visible | |
| 297 | + if (is_lsp_server_installer_panel_visible(editor%lsp_installer_panel)) then | |
| 298 | + call render_lsp_server_installer_panel(editor%lsp_installer_panel, & | |
| 299 | + editor%screen_rows, editor%screen_cols) | |
| 300 | + end if | |
| 301 | + | |
| 288 | 302 | ! Position cursor for panes or regular view |
| 289 | 303 | if (size(editor%tabs) > 0 .and. editor%active_tab_index > 0 .and. & |
| 290 | 304 | editor%active_tab_index <= size(editor%tabs)) then |
@@ -891,6 +905,12 @@ contains | ||
| 891 | 905 | ! Render symbols panel if visible |
| 892 | 906 | call render_symbols_panel(editor%symbols_panel, editor%screen_rows) |
| 893 | 907 | |
| 908 | + ! Render LSP server installer panel if visible | |
| 909 | + if (is_lsp_server_installer_panel_visible(editor%lsp_installer_panel)) then | |
| 910 | + call render_lsp_server_installer_panel(editor%lsp_installer_panel, & | |
| 911 | + editor%screen_rows, editor%screen_cols) | |
| 912 | + end if | |
| 913 | + | |
| 894 | 914 | ! Position cursor in editor pane (use appropriate method based on pane count) |
| 895 | 915 | if (size(editor%tabs(editor%active_tab_index)%panes) > 1) then |
| 896 | 916 | ! Multiple panes: use pane-aware cursor rendering with tree offset |
src/ui/command_palette_module.f90modified@@ -323,17 +323,21 @@ contains | ||
| 323 | 323 | ! Draw header line with cyan title |
| 324 | 324 | row = start_row + 1 |
| 325 | 325 | call terminal_move_cursor(row, start_col) |
| 326 | - call terminal_write('│' // CYAN // ' Command Palette' // RESET) | |
| 327 | - display_width = 17 ! " Command Palette" length | |
| 328 | - call terminal_write(repeat(' ', content_width - display_width - 2) // '│') | |
| 326 | + call terminal_write('│') | |
| 327 | + call terminal_write(CYAN // ' Command Palette' // RESET) | |
| 328 | + display_width = 16 ! " Command Palette" visible length | |
| 329 | + call terminal_write(repeat(' ', max(0, content_width - 2 - display_width))) | |
| 330 | + call terminal_write('│') | |
| 329 | 331 | |
| 330 | 332 | ! Draw search query line with yellow prompt |
| 331 | 333 | row = row + 1 |
| 332 | 334 | call terminal_move_cursor(row, start_col) |
| 333 | - call terminal_write('│' // YELLOW // ' > ' // RESET) | |
| 335 | + call terminal_write('│') | |
| 336 | + call terminal_write(YELLOW // ' > ' // RESET) | |
| 334 | 337 | call terminal_write(trim(palette%search_query)) |
| 335 | - display_width = 3 + len_trim(palette%search_query) | |
| 336 | - call terminal_write(repeat(' ', content_width - display_width - 2) // '│') | |
| 338 | + display_width = 3 + len_trim(palette%search_query) ! " > " + query length | |
| 339 | + call terminal_write(repeat(' ', max(0, content_width - 2 - display_width))) | |
| 340 | + call terminal_write('│') | |
| 337 | 341 | |
| 338 | 342 | ! Draw separator |
| 339 | 343 | row = row + 1 |
@@ -352,7 +356,7 @@ contains | ||
| 352 | 356 | call terminal_move_cursor(row, start_col) |
| 353 | 357 | call terminal_write('│') |
| 354 | 358 | |
| 355 | - ! Build line with category, name, and shortcut | |
| 359 | + ! Build line with category, name, and shortcut (for display_width calculation) | |
| 356 | 360 | if (len_trim(cmd%category) > 0) then |
| 357 | 361 | write(category_tag, '(A,A,A)') '[', trim(cmd%category), '] ' |
| 358 | 362 | else |
@@ -366,17 +370,29 @@ contains | ||
| 366 | 370 | write(line, '(A,A,A)') trim(line), ' ', trim(cmd%shortcut) |
| 367 | 371 | end if |
| 368 | 372 | |
| 369 | - ! Truncate if too long | |
| 370 | - display_width = min(len_trim(line), content_width - 3) | |
| 373 | + ! Calculate visible width (actual characters, no ANSI codes) | |
| 374 | + display_width = len_trim(line) | |
| 375 | + | |
| 376 | + ! Ensure it fits | |
| 377 | + if (display_width > content_width - 2) then | |
| 378 | + display_width = content_width - 2 | |
| 379 | + end if | |
| 380 | + | |
| 381 | + ! Calculate padding | |
| 382 | + display_width = max(0, min(display_width, content_width - 2)) | |
| 371 | 383 | |
| 372 | 384 | if (i == palette%selected_index) then |
| 373 | 385 | ! Highlight selected item with inverse colors |
| 374 | - call terminal_write(INVERSE // line(1:display_width)) | |
| 375 | - call terminal_write(repeat(' ', content_width - display_width - 2) // RESET // '│') | |
| 386 | + call terminal_write(INVERSE) | |
| 387 | + call terminal_write(line(1:display_width)) | |
| 388 | + call terminal_write(repeat(' ', max(0, content_width - 2 - display_width))) | |
| 389 | + call terminal_write(RESET) | |
| 376 | 390 | else |
| 377 | 391 | call terminal_write(line(1:display_width)) |
| 378 | - call terminal_write(repeat(' ', content_width - display_width - 2) // '│') | |
| 392 | + call terminal_write(repeat(' ', max(0, content_width - 2 - display_width))) | |
| 379 | 393 | end if |
| 394 | + | |
| 395 | + call terminal_write('│') | |
| 380 | 396 | end do |
| 381 | 397 | |
| 382 | 398 | ! Fill remaining visible slots with empty rows |
src/ui/unified_search_module.f90modified@@ -4,7 +4,6 @@ module unified_search_module | ||
| 4 | 4 | use editor_state_module, only: editor_state_t, cursor_t, sync_editor_to_pane |
| 5 | 5 | use text_buffer_module |
| 6 | 6 | use regex_module |
| 7 | - use renderer_module, only: render_screen | |
| 8 | 7 | implicit none |
| 9 | 8 | private |
| 10 | 9 | |
@@ -239,10 +238,10 @@ contains | ||
| 239 | 238 | call search_forward(editor, buffer) |
| 240 | 239 | end if |
| 241 | 240 | |
| 242 | - ! Re-render screen to show cursor at new match position | |
| 243 | - call render_screen(buffer, editor) | |
| 241 | + ! Note: Screen re-rendering happens in command_handler after search exits | |
| 242 | + ! The match highlighting and cursor position will be visible after exiting search | |
| 244 | 243 | |
| 245 | - ! Update prompt with match count (redraw prompt over rendered screen) | |
| 244 | + ! Update prompt with match count | |
| 246 | 245 | call build_unified_prompt(prompt, find_buffer, find_pos, replace_buffer, replace_pos) |
| 247 | 246 | call display_prompt(editor, prompt, find_pos, replace_pos) |
| 248 | 247 | end if |