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