@@ -178,6 +178,7 @@ contains |
| 178 | integer :: term_height, viewport_offset, visible_items, top_padding | 178 | integer :: term_height, viewport_offset, visible_items, top_padding |
| 179 | integer :: prev_selected, prev_viewport | 179 | integer :: prev_selected, prev_viewport |
| 180 | logical :: needs_full_redraw | 180 | logical :: needs_full_redraw |
| | 181 | + character(len=10) :: mode ! "normal" or "git" mode |
| 181 | type(tree_node), pointer :: tree_root | 182 | type(tree_node), pointer :: tree_root |
| 182 | | 183 | |
| 183 | ! Initialize tree pointer | 184 | ! Initialize tree pointer |
@@ -243,6 +244,7 @@ contains |
| 243 | selected = 1 | 244 | selected = 1 |
| 244 | viewport_offset = 1 | 245 | viewport_offset = 1 |
| 245 | running = .true. | 246 | running = .true. |
| | 247 | + mode = 'normal' ! Start in normal mode |
| 246 | | 248 | |
| 247 | ! Partial redraw optimization: initialize tracking state | 249 | ! Partial redraw optimization: initialize tracking state |
| 248 | prev_selected = 0 ! Force initial draw | 250 | prev_selected = 0 ! Force initial draw |
@@ -271,14 +273,14 @@ contains |
| 271 | ! Full redraw needed: viewport scrolled or forced refresh | 273 | ! Full redraw needed: viewport scrolled or forced refresh |
| 272 | call clear_screen() | 274 | call clear_screen() |
| 273 | call draw_interactive_tree(tree_root, items, n_items, selected, & | 275 | call draw_interactive_tree(tree_root, items, n_items, selected, & |
| 274 | - repo_name, branch_name, viewport_offset, visible_items, top_padding) | 276 | + repo_name, branch_name, viewport_offset, visible_items, top_padding, mode) |
| 275 | needs_full_redraw = .false. | 277 | needs_full_redraw = .false. |
| 276 | else if (selected /= prev_selected) then | 278 | else if (selected /= prev_selected) then |
| 277 | ! Only selection changed within same viewport - still need full redraw for now | 279 | ! Only selection changed within same viewport - still need full redraw for now |
| 278 | ! TODO: Could optimize this with partial line updates in the future | 280 | ! TODO: Could optimize this with partial line updates in the future |
| 279 | call clear_screen() | 281 | call clear_screen() |
| 280 | call draw_interactive_tree(tree_root, items, n_items, selected, & | 282 | call draw_interactive_tree(tree_root, items, n_items, selected, & |
| 281 | - repo_name, branch_name, viewport_offset, visible_items, top_padding) | 283 | + repo_name, branch_name, viewport_offset, visible_items, top_padding, mode) |
| 282 | end if | 284 | end if |
| 283 | | 285 | |
| 284 | ! Update tracking state | 286 | ! Update tracking state |
@@ -288,6 +290,30 @@ contains |
| 288 | ! Read key | 290 | ! Read key |
| 289 | call read_key(key) | 291 | call read_key(key) |
| 290 | | 292 | |
| | 293 | + ! Check for alt-g to toggle git mode |
| | 294 | + ! alt-g is encoded as achar(1 + ichar('g') - ichar('a')) = achar(7) |
| | 295 | + if (key == achar(7)) then |
| | 296 | + ! Toggle between normal and git mode |
| | 297 | + if (mode == 'normal') then |
| | 298 | + mode = 'git' |
| | 299 | + else |
| | 300 | + mode = 'normal' |
| | 301 | + end if |
| | 302 | + needs_full_redraw = .true. |
| | 303 | + cycle ! Skip rest of key handling |
| | 304 | + end if |
| | 305 | + |
| | 306 | + ! Handle ESC key - exit git mode if active |
| | 307 | + if (key == achar(27)) then |
| | 308 | + if (mode == 'git') then |
| | 309 | + mode = 'normal' |
| | 310 | + needs_full_redraw = .true. |
| | 311 | + cycle |
| | 312 | + end if |
| | 313 | + ! In normal mode, ESC does nothing for now |
| | 314 | + cycle |
| | 315 | + end if |
| | 316 | + |
| 291 | ! Handle input | 317 | ! Handle input |
| 292 | select case (key) | 318 | select case (key) |
| 293 | case ('j', 'B') ! j or down arrow - navigate to next sibling (skip nested items) | 319 | case ('j', 'B') ! j or down arrow - navigate to next sibling (skip nested items) |
@@ -309,7 +335,9 @@ contains |
| 309 | ! Force full redraw after tree structure change | 335 | ! Force full redraw after tree structure change |
| 310 | needs_full_redraw = .true. | 336 | needs_full_redraw = .true. |
| 311 | end if | 337 | end if |
| | 338 | + ! Git operations - only available in git mode |
| 312 | case ('a') ! Stage file or directory (lowercase to avoid conflict with arrow A) | 339 | case ('a') ! Stage file or directory (lowercase to avoid conflict with arrow A) |
| | 340 | + if (mode == 'git') then |
| 313 | ! Check if it's a directory - stage all files in it | 341 | ! Check if it's a directory - stage all files in it |
| 314 | if (.not. items(selected)%is_file) then | 342 | if (.not. items(selected)%is_file) then |
| 315 | call git_stage_directory(items(selected)%path) | 343 | call git_stage_directory(items(selected)%path) |
@@ -325,8 +353,9 @@ contains |
| 325 | hide_dotfiles, selected, running, exit_if_empty=.true., force_refresh=.true.) | 353 | hide_dotfiles, selected, running, exit_if_empty=.true., force_refresh=.true.) |
| 326 | needs_full_redraw = .true. | 354 | needs_full_redraw = .true. |
| 327 | end if | 355 | end if |
| | 356 | + end if |
| 328 | case ('u') ! Unstage file (lowercase) | 357 | case ('u') ! Unstage file (lowercase) |
| 329 | - if (items(selected)%is_file .and. items(selected)%is_staged) then | 358 | + if (mode == 'git' .and. items(selected)%is_file .and. items(selected)%is_staged) then |
| 330 | call git_unstage_file(items(selected)%path) | 359 | call git_unstage_file(items(selected)%path) |
| 331 | ! Refresh files after git unstage | 360 | ! Refresh files after git unstage |
| 332 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 361 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -334,42 +363,57 @@ contains |
| 334 | needs_full_redraw = .true. | 363 | needs_full_redraw = .true. |
| 335 | end if | 364 | end if |
| 336 | case ('S') ! Stage all (Shift+S to avoid conflict with up arrow 'A') | 365 | case ('S') ! Stage all (Shift+S to avoid conflict with up arrow 'A') |
| | 366 | + if (mode == 'git') then |
| 337 | call git_stage_all() | 367 | call git_stage_all() |
| 338 | ! Refresh files after staging all | 368 | ! Refresh files after staging all |
| 339 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 369 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 340 | hide_dotfiles, selected, running, exit_if_empty=.true., force_refresh=.true.) | 370 | hide_dotfiles, selected, running, exit_if_empty=.true., force_refresh=.true.) |
| 341 | needs_full_redraw = .true. | 371 | needs_full_redraw = .true. |
| | 372 | + end if |
| 342 | case ('U') ! Unstage all (Shift+U) | 373 | case ('U') ! Unstage all (Shift+U) |
| | 374 | + if (mode == 'git') then |
| 343 | call git_unstage_all() | 375 | call git_unstage_all() |
| 344 | ! Refresh files after unstaging all | 376 | ! Refresh files after unstaging all |
| 345 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 377 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 346 | hide_dotfiles, selected, running, force_refresh=.true.) | 378 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 347 | needs_full_redraw = .true. | 379 | needs_full_redraw = .true. |
| | 380 | + end if |
| 348 | case ('m') ! Commit (lowercase) | 381 | case ('m') ! Commit (lowercase) |
| | 382 | + if (mode == 'git') then |
| 349 | call commit_prompt() | 383 | call commit_prompt() |
| 350 | ! Refresh files after commit | 384 | ! Refresh files after commit |
| 351 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 385 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 352 | hide_dotfiles, selected, running, force_refresh=.true.) | 386 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 353 | needs_full_redraw = .true. | 387 | needs_full_redraw = .true. |
| | 388 | + end if |
| 354 | case ('M') ! Amend last commit (Shift+m) | 389 | case ('M') ! Amend last commit (Shift+m) |
| | 390 | + if (mode == 'git') then |
| 355 | call amend_commit_prompt() | 391 | call amend_commit_prompt() |
| 356 | ! Refresh files after amend commit | 392 | ! Refresh files after amend commit |
| 357 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 393 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 358 | hide_dotfiles, selected, running, force_refresh=.true.) | 394 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 359 | needs_full_redraw = .true. | 395 | needs_full_redraw = .true. |
| | 396 | + end if |
| 360 | case ('s') ! Show git status (lowercase) | 397 | case ('s') ! Show git status (lowercase) |
| | 398 | + if (mode == 'git') then |
| 361 | call show_status_view() | 399 | call show_status_view() |
| 362 | needs_full_redraw = .true. | 400 | needs_full_redraw = .true. |
| | 401 | + end if |
| 363 | case ('p') ! Push (lowercase) | 402 | case ('p') ! Push (lowercase) |
| | 403 | + if (mode == 'git') then |
| 364 | call push_prompt() | 404 | call push_prompt() |
| 365 | ! Refresh files after push | 405 | ! Refresh files after push |
| 366 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 406 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 367 | hide_dotfiles, selected, running, force_refresh=.true.) | 407 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 368 | needs_full_redraw = .true. | 408 | needs_full_redraw = .true. |
| | 409 | + end if |
| 369 | case ('t') ! Tag (lowercase) | 410 | case ('t') ! Tag (lowercase) |
| | 411 | + if (mode == 'git') then |
| 370 | call tag_prompt() | 412 | call tag_prompt() |
| 371 | needs_full_redraw = .true. | 413 | needs_full_redraw = .true. |
| | 414 | + end if |
| 372 | case ('b') ! Switch branch | 415 | case ('b') ! Switch branch |
| | 416 | + if (mode == 'git') then |
| 373 | call branch_switch_prompt() | 417 | call branch_switch_prompt() |
| 374 | ! Refresh files after branch switch | 418 | ! Refresh files after branch switch |
| 375 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 419 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -377,7 +421,9 @@ contains |
| 377 | needs_full_redraw = .true. | 421 | needs_full_redraw = .true. |
| 378 | ! Update branch name display | 422 | ! Update branch name display |
| 379 | call get_repo_info(repo_name, branch_name) | 423 | call get_repo_info(repo_name, branch_name) |
| | 424 | + end if |
| 380 | case ('n') ! Create new branch | 425 | case ('n') ! Create new branch |
| | 426 | + if (mode == 'git') then |
| 381 | call branch_create_prompt() | 427 | call branch_create_prompt() |
| 382 | ! Refresh files after branch creation | 428 | ! Refresh files after branch creation |
| 383 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 429 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -385,33 +431,38 @@ contains |
| 385 | needs_full_redraw = .true. | 431 | needs_full_redraw = .true. |
| 386 | ! Update branch name display | 432 | ! Update branch name display |
| 387 | call get_repo_info(repo_name, branch_name) | 433 | call get_repo_info(repo_name, branch_name) |
| | 434 | + end if |
| 388 | case ('R') ! Delete branch (Shift+r, since 'r' is used for delete file) | 435 | case ('R') ! Delete branch (Shift+r, since 'r' is used for delete file) |
| | 436 | + if (mode == 'git') then |
| 389 | call branch_delete_prompt() | 437 | call branch_delete_prompt() |
| 390 | needs_full_redraw = .true. | 438 | needs_full_redraw = .true. |
| 391 | ! No need to refresh files or update branch name (stays on current branch) | 439 | ! No need to refresh files or update branch name (stays on current branch) |
| | 440 | + end if |
| 392 | case ('f') ! Git fetch | 441 | case ('f') ! Git fetch |
| | 442 | + if (mode == 'git') then |
| 393 | call git_fetch() | 443 | call git_fetch() |
| 394 | ! Refresh files after fetch and include files with incoming changes | 444 | ! Refresh files after fetch and include files with incoming changes |
| 395 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 445 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 396 | hide_dotfiles, selected, running, include_incoming=.true., force_refresh=.true.) | 446 | hide_dotfiles, selected, running, include_incoming=.true., force_refresh=.true.) |
| 397 | needs_full_redraw = .true. | 447 | needs_full_redraw = .true. |
| | 448 | + end if |
| 398 | case ('d') ! Git diff with less | 449 | case ('d') ! Git diff with less |
| 399 | - if (items(selected)%is_file) then | 450 | + if (mode == 'git' .and. items(selected)%is_file) then |
| 400 | call git_diff_file(items(selected)%path, items(selected)%has_incoming) | 451 | call git_diff_file(items(selected)%path, items(selected)%has_incoming) |
| 401 | needs_full_redraw = .true. | 452 | needs_full_redraw = .true. |
| 402 | end if | 453 | end if |
| 403 | case ('c') ! View file contents (cat/bat/less) | 454 | case ('c') ! View file contents (cat/bat/less) |
| 404 | - if (items(selected)%is_file) then | 455 | + if (mode == 'git' .and. items(selected)%is_file) then |
| 405 | call view_file(items(selected)%path) | 456 | call view_file(items(selected)%path) |
| 406 | needs_full_redraw = .true. | 457 | needs_full_redraw = .true. |
| 407 | end if | 458 | end if |
| 408 | case ('w') ! Git blame (who changed this line) | 459 | case ('w') ! Git blame (who changed this line) |
| 409 | - if (items(selected)%is_file) then | 460 | + if (mode == 'git' .and. items(selected)%is_file) then |
| 410 | call blame_prompt(items(selected)%path) | 461 | call blame_prompt(items(selected)%path) |
| 411 | needs_full_redraw = .true. | 462 | needs_full_redraw = .true. |
| 412 | end if | 463 | end if |
| 413 | case ('r') ! Remove/delete file | 464 | case ('r') ! Remove/delete file |
| 414 | - if (items(selected)%is_file) then | 465 | + if (mode == 'git' .and. items(selected)%is_file) then |
| 415 | call delete_prompt(items(selected)%path, items(selected)%is_untracked) | 466 | call delete_prompt(items(selected)%path, items(selected)%is_untracked) |
| 416 | ! Refresh files after delete | 467 | ! Refresh files after delete |
| 417 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 468 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -419,7 +470,7 @@ contains |
| 419 | needs_full_redraw = .true. | 470 | needs_full_redraw = .true. |
| 420 | end if | 471 | end if |
| 421 | case ('x', 'X') ! Discard changes | 472 | case ('x', 'X') ! Discard changes |
| 422 | - if (items(selected)%is_file .and. (items(selected)%is_staged .or. items(selected)%is_unstaged .or. items(selected)%is_untracked)) then | 473 | + if (mode == 'git' .and. items(selected)%is_file .and. (items(selected)%is_staged .or. items(selected)%is_unstaged .or. items(selected)%is_untracked)) then |
| 423 | call discard_prompt(items(selected)%path, items(selected)%is_staged, items(selected)%is_untracked) | 474 | call discard_prompt(items(selected)%path, items(selected)%is_staged, items(selected)%is_untracked) |
| 424 | ! Refresh files after discard | 475 | ! Refresh files after discard |
| 425 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 476 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -427,6 +478,7 @@ contains |
| 427 | needs_full_redraw = .true. | 478 | needs_full_redraw = .true. |
| 428 | end if | 479 | end if |
| 429 | case ('l') ! Git pull | 480 | case ('l') ! Git pull |
| | 481 | + if (mode == 'git') then |
| 430 | call git_pull() | 482 | call git_pull() |
| 431 | ! Refresh files after pull (incoming indicators will automatically clear) | 483 | ! Refresh files after pull (incoming indicators will automatically clear) |
| 432 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 484 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -434,37 +486,51 @@ contains |
| 434 | needs_full_redraw = .true. | 486 | needs_full_redraw = .true. |
| 435 | ! Note: After successful pull, git diff will show no upstream differences | 487 | ! Note: After successful pull, git diff will show no upstream differences |
| 436 | ! so has_incoming will be .false. for all files automatically | 488 | ! so has_incoming will be .false. for all files automatically |
| | 489 | + end if |
| 437 | case ('z') ! Stash push (save changes) | 490 | case ('z') ! Stash push (save changes) |
| | 491 | + if (mode == 'git') then |
| 438 | call stash_push_prompt() | 492 | call stash_push_prompt() |
| 439 | ! Refresh files after stash | 493 | ! Refresh files after stash |
| 440 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 494 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 441 | hide_dotfiles, selected, running, exit_if_empty=.true., force_refresh=.true.) | 495 | hide_dotfiles, selected, running, exit_if_empty=.true., force_refresh=.true.) |
| 442 | needs_full_redraw = .true. | 496 | needs_full_redraw = .true. |
| | 497 | + end if |
| 443 | case ('Z') ! Stash pop/apply (restore changes) | 498 | case ('Z') ! Stash pop/apply (restore changes) |
| | 499 | + if (mode == 'git') then |
| 444 | call stash_pop_apply_prompt() | 500 | call stash_pop_apply_prompt() |
| 445 | ! Refresh files after stash pop/apply | 501 | ! Refresh files after stash pop/apply |
| 446 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 502 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 447 | hide_dotfiles, selected, running, force_refresh=.true.) | 503 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 448 | needs_full_redraw = .true. | 504 | needs_full_redraw = .true. |
| | 505 | + end if |
| 449 | case ('y') ! Cherry-pick (yank commit) | 506 | case ('y') ! Cherry-pick (yank commit) |
| | 507 | + if (mode == 'git') then |
| 450 | call cherry_pick_prompt() | 508 | call cherry_pick_prompt() |
| 451 | ! Refresh files after cherry-pick | 509 | ! Refresh files after cherry-pick |
| 452 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 510 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 453 | hide_dotfiles, selected, running, force_refresh=.true.) | 511 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 454 | needs_full_redraw = .true. | 512 | needs_full_redraw = .true. |
| | 513 | + end if |
| 455 | case ('v') ! Revert commit | 514 | case ('v') ! Revert commit |
| | 515 | + if (mode == 'git') then |
| 456 | call revert_commit_prompt() | 516 | call revert_commit_prompt() |
| 457 | ! Refresh files after revert | 517 | ! Refresh files after revert |
| 458 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 518 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 459 | hide_dotfiles, selected, running, force_refresh=.true.) | 519 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 460 | needs_full_redraw = .true. | 520 | needs_full_redraw = .true. |
| | 521 | + end if |
| 461 | case ('h') ! Show commit history | 522 | case ('h') ! Show commit history |
| | 523 | + if (mode == 'git') then |
| 462 | call history_browser_prompt() | 524 | call history_browser_prompt() |
| 463 | needs_full_redraw = .true. | 525 | needs_full_redraw = .true. |
| | 526 | + end if |
| 464 | case ('L') ! Show reflog (Shift+l) | 527 | case ('L') ! Show reflog (Shift+l) |
| | 528 | + if (mode == 'git') then |
| 465 | call reflog_browser_prompt() | 529 | call reflog_browser_prompt() |
| 466 | needs_full_redraw = .true. | 530 | needs_full_redraw = .true. |
| | 531 | + end if |
| 467 | case ('G') ! Merge branch (Shift+g) | 532 | case ('G') ! Merge branch (Shift+g) |
| | 533 | + if (mode == 'git') then |
| 468 | call merge_branch_prompt() | 534 | call merge_branch_prompt() |
| 469 | ! Refresh files after merge | 535 | ! Refresh files after merge |
| 470 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 536 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
@@ -472,18 +538,23 @@ contains |
| 472 | needs_full_redraw = .true. | 538 | needs_full_redraw = .true. |
| 473 | ! Update branch name display in case we merged | 539 | ! Update branch name display in case we merged |
| 474 | call get_repo_info(repo_name, branch_name) | 540 | call get_repo_info(repo_name, branch_name) |
| | 541 | + end if |
| 475 | case ('O') ! Reset (Shift+o - "Oh no, undo!") | 542 | case ('O') ! Reset (Shift+o - "Oh no, undo!") |
| | 543 | + if (mode == 'git') then |
| 476 | call reset_prompt() | 544 | call reset_prompt() |
| 477 | ! Refresh files after reset | 545 | ! Refresh files after reset |
| 478 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 546 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 479 | hide_dotfiles, selected, running, force_refresh=.true.) | 547 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 480 | needs_full_redraw = .true. | 548 | needs_full_redraw = .true. |
| | 549 | + end if |
| 481 | case ('I') ! Interactive rebase (Shift+i) | 550 | case ('I') ! Interactive rebase (Shift+i) |
| | 551 | + if (mode == 'git') then |
| 482 | call rebase_prompt() | 552 | call rebase_prompt() |
| 483 | ! Refresh files after rebase | 553 | ! Refresh files after rebase |
| 484 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & | 554 | call refresh_and_rebuild(show_all, files, n_files, items, n_items, tree_root, & |
| 485 | hide_dotfiles, selected, running, force_refresh=.true.) | 555 | hide_dotfiles, selected, running, force_refresh=.true.) |
| 486 | needs_full_redraw = .true. | 556 | needs_full_redraw = .true. |
| | 557 | + end if |
| 487 | case ('.') ! Toggle hiding dotfiles and gitignored files | 558 | case ('.') ! Toggle hiding dotfiles and gitignored files |
| 488 | hide_dotfiles = .not. hide_dotfiles | 559 | hide_dotfiles = .not. hide_dotfiles |
| 489 | ! Rebuild item list with new filter | 560 | ! Rebuild item list with new filter |
@@ -497,8 +568,15 @@ contains |
| 497 | visible_items = term_height - top_padding - 6 | 568 | visible_items = term_height - top_padding - 6 |
| 498 | if (visible_items < 3) visible_items = 3 | 569 | if (visible_items < 3) visible_items = 3 |
| 499 | if (visible_items > n_items) visible_items = n_items | 570 | if (visible_items > n_items) visible_items = n_items |
| 500 | - case ('q', 'Q') ! Quit | 571 | + case ('q', 'Q') ! Quit or exit git mode |
| | 572 | + if (mode == 'git') then |
| | 573 | + ! In git mode: q exits to normal mode |
| | 574 | + mode = 'normal' |
| | 575 | + needs_full_redraw = .true. |
| | 576 | + else |
| | 577 | + ! In normal mode: q quits the application |
| 501 | running = .false. | 578 | running = .false. |
| | 579 | + end if |
| 502 | end select | 580 | end select |
| 503 | end do | 581 | end do |
| 504 | | 582 | |