resolve some multiselect things on wezterm i am losing it
- SHA
061808d806cc9d79099f49faef8dabd73b11d509- Parents
-
d0d6ec0 - Tree
4971bb0
061808d
061808d806cc9d79099f49faef8dabd73b11d509d0d6ec0
4971bb0| Status | File | + | - |
|---|---|---|---|
| A |
AUR_PACKAGING.md
|
161 | 0 |
| M |
app/main.f90
|
37 | 15 |
| M |
src/ui/display.f90
|
36 | 36 |
AUR_PACKAGING.mdadded@@ -0,0 +1,161 @@ | ||
| 1 | +# AUR Packaging Guide for FORTRESS | |
| 2 | + | |
| 3 | +This guide explains how to package FORTRESS for the Arch User Repository (AUR). | |
| 4 | + | |
| 5 | +## Files Needed for AUR | |
| 6 | + | |
| 7 | +The following files are required in the AUR repository: | |
| 8 | + | |
| 9 | +1. **PKGBUILD** - Build script that describes how to build and install the package | |
| 10 | +2. **.SRCINFO** - Metadata file generated from PKGBUILD | |
| 11 | +3. **fortress.install** - Post-install messages and scripts | |
| 12 | + | |
| 13 | +## Preparing for AUR Submission | |
| 14 | + | |
| 15 | +### 1. Create a Release on GitHub | |
| 16 | + | |
| 17 | +First, create a tagged release on GitHub: | |
| 18 | + | |
| 19 | +```bash | |
| 20 | +# Tag the release | |
| 21 | +git tag -a v0.1.0 -m "Release v0.1.0" | |
| 22 | +git push origin v0.1.0 | |
| 23 | +``` | |
| 24 | + | |
| 25 | +Then create a release on GitHub using this tag. | |
| 26 | + | |
| 27 | +### 2. Generate Checksum | |
| 28 | + | |
| 29 | +Download the release tarball and generate its SHA256 checksum: | |
| 30 | + | |
| 31 | +```bash | |
| 32 | +# Download the release | |
| 33 | +wget https://github.com/FortranGoingOnForty/fortress/archive/v0.1.0.tar.gz | |
| 34 | + | |
| 35 | +# Generate checksum | |
| 36 | +sha256sum v0.1.0.tar.gz | |
| 37 | +``` | |
| 38 | + | |
| 39 | +Update the `sha256sums` line in PKGBUILD with this value. | |
| 40 | + | |
| 41 | +### 3. Test the PKGBUILD | |
| 42 | + | |
| 43 | +Before submitting to AUR, test the build locally: | |
| 44 | + | |
| 45 | +```bash | |
| 46 | +# Install dependencies (if not already installed) | |
| 47 | +sudo pacman -S fpm gcc-fortran base-devel | |
| 48 | + | |
| 49 | +# Test build | |
| 50 | +makepkg -sf | |
| 51 | + | |
| 52 | +# Test installation | |
| 53 | +sudo pacman -U fortress-0.1.0-1-x86_64.pkg.tar.zst | |
| 54 | + | |
| 55 | +# Test the installed package | |
| 56 | +fortress | |
| 57 | +``` | |
| 58 | + | |
| 59 | +### 4. Generate .SRCINFO | |
| 60 | + | |
| 61 | +The .SRCINFO file must be generated from PKGBUILD: | |
| 62 | + | |
| 63 | +```bash | |
| 64 | +makepkg --printsrcinfo > .SRCINFO | |
| 65 | +``` | |
| 66 | + | |
| 67 | +**Important**: Regenerate .SRCINFO every time you update PKGBUILD! | |
| 68 | + | |
| 69 | +### 5. Submit to AUR | |
| 70 | + | |
| 71 | +If this is your first time: | |
| 72 | + | |
| 73 | +```bash | |
| 74 | +# Clone the AUR repository (will be empty initially) | |
| 75 | +git clone ssh://aur@aur.archlinux.org/fortress.git aur-fortress | |
| 76 | +cd aur-fortress | |
| 77 | + | |
| 78 | +# Copy the required files | |
| 79 | +cp ../PKGBUILD . | |
| 80 | +cp ../fortress.install . | |
| 81 | +makepkg --printsrcinfo > .SRCINFO | |
| 82 | + | |
| 83 | +# Commit and push | |
| 84 | +git add PKGBUILD fortress.install .SRCINFO | |
| 85 | +git commit -m "Initial import of fortress v0.1.0" | |
| 86 | +git push | |
| 87 | +``` | |
| 88 | + | |
| 89 | +For updates: | |
| 90 | + | |
| 91 | +```bash | |
| 92 | +cd aur-fortress | |
| 93 | + | |
| 94 | +# Update PKGBUILD (bump pkgver, update sha256sums, etc.) | |
| 95 | +# Then regenerate .SRCINFO | |
| 96 | +makepkg --printsrcinfo > .SRCINFO | |
| 97 | + | |
| 98 | +# Commit and push | |
| 99 | +git add PKGBUILD .SRCINFO | |
| 100 | +git commit -m "Update to v0.2.0" | |
| 101 | +git push | |
| 102 | +``` | |
| 103 | + | |
| 104 | +## How Installation Works | |
| 105 | + | |
| 106 | +When users install fortress from AUR: | |
| 107 | + | |
| 108 | +1. **Binary Installation**: The executable is installed as `/usr/bin/fortress-bin` | |
| 109 | + | |
| 110 | +2. **Shell Integration** (automatic for most shells): | |
| 111 | + - **Bash**: Auto-loaded from `/etc/profile.d/fortress.sh` on shell startup | |
| 112 | + - **Fish**: Auto-loaded from `/usr/share/fish/vendor_functions.d/fortress.fish` | |
| 113 | + - **Zsh**: Users need to add `source /usr/share/fortress/fortress.sh` to `~/.zshrc` | |
| 114 | + | |
| 115 | +3. **Usage**: Users just run `fortress` and the cd-on-exit feature works automatically | |
| 116 | + | |
| 117 | +## Testing Shell Integration | |
| 118 | + | |
| 119 | +After installation, test each shell: | |
| 120 | + | |
| 121 | +```bash | |
| 122 | +# Bash | |
| 123 | +bash | |
| 124 | +fortress # Should work, press 'c' to cd | |
| 125 | + | |
| 126 | +# Fish | |
| 127 | +fish | |
| 128 | +fortress # Should work, press 'c' to cd | |
| 129 | + | |
| 130 | +# Zsh (after sourcing) | |
| 131 | +zsh | |
| 132 | +source /usr/share/fortress/fortress.sh | |
| 133 | +fortress # Should work, press 'c' to cd | |
| 134 | +``` | |
| 135 | + | |
| 136 | +## Maintaining the AUR Package | |
| 137 | + | |
| 138 | +### Version Updates | |
| 139 | + | |
| 140 | +1. Update `pkgver` in PKGBUILD | |
| 141 | +2. Update `sha256sums` with new release checksum | |
| 142 | +3. Test build: `makepkg -sf` | |
| 143 | +4. Regenerate .SRCINFO: `makepkg --printsrcinfo > .SRCINFO` | |
| 144 | +5. Commit and push to AUR | |
| 145 | + | |
| 146 | +### Common Issues | |
| 147 | + | |
| 148 | +**fpm not in official repos**: Users need to install `fpm` from AUR first: | |
| 149 | +```bash | |
| 150 | +yay -S fpm | |
| 151 | +``` | |
| 152 | + | |
| 153 | +**Build fails**: Check that all makedepends are correct and the build() function works | |
| 154 | + | |
| 155 | +**Shell integration doesn't work**: Verify files are installed to correct locations and users have restarted their shell | |
| 156 | + | |
| 157 | +## Resources | |
| 158 | + | |
| 159 | +- [AUR Submission Guidelines](https://wiki.archlinux.org/title/AUR_submission_guidelines) | |
| 160 | +- [PKGBUILD Manual](https://wiki.archlinux.org/title/PKGBUILD) | |
| 161 | +- [Creating Packages](https://wiki.archlinux.org/title/Creating_packages) | |
app/main.f90modified@@ -51,24 +51,32 @@ program fortress | ||
| 51 | 51 | logical, dimension(MAX_FILES) :: current_is_favorite, parent_is_favorite |
| 52 | 52 | |
| 53 | 53 | character(len=1) :: key |
| 54 | - integer :: i, rows, cols, visible_height | |
| 54 | + integer :: i, rows, cols, visible_height, top_padding | |
| 55 | 55 | logical :: is_shift_pressed |
| 56 | + character(len=256) :: term_program | |
| 56 | 57 | |
| 57 | 58 | ! Initialize |
| 58 | 59 | current_dir = get_pwd() |
| 59 | 60 | parent_dir = get_parent_path(current_dir) |
| 60 | 61 | call detect_git_repo(current_dir, in_git_repo, repo_name, branch_name) |
| 61 | 62 | call load_favorites(favorite_dirs, favorite_count) |
| 63 | + | |
| 64 | + ! Detect terminal type once for consistent padding throughout | |
| 65 | + call get_environment_variable("TERM_PROGRAM", term_program) | |
| 66 | + if (index(term_program, "WezTerm") > 0 .or. index(term_program, "ghostty") > 0) then | |
| 67 | + top_padding = 2 ! WezTerm/Ghostty need 2 lines to prevent top cutoff | |
| 68 | + else if (index(term_program, "Apple_Terminal") > 0 .or. index(term_program, "iTerm") > 0) then | |
| 69 | + top_padding = 2 ! Terminal.app and iTerm2 need 2 lines | |
| 70 | + else | |
| 71 | + top_padding = 1 ! Other terminals need 1 line | |
| 72 | + end if | |
| 73 | + | |
| 62 | 74 | call setup_raw_mode() |
| 63 | 75 | call enter_alt_screen() ! Use alternate screen buffer to prevent scrolling issues |
| 64 | 76 | call hide_cursor() ! Hide cursor for cleaner display |
| 65 | 77 | |
| 66 | - ! Reset scroll region and clear completely | |
| 67 | - write(output_unit, '(a)', advance='no') ESC // "[r" ! Disable scroll region | |
| 68 | - write(output_unit, '(a)', advance='no') ESC // "[1;1H" ! Position at 1,1 | |
| 69 | - write(output_unit, '(a)', advance='no') ESC // "[2J" ! Clear screen | |
| 70 | - write(output_unit, '(a)') "" ! Write a blank line to ensure we're at row 2 | |
| 71 | - write(output_unit, '(a)', advance='no') ESC // "[1;1H" ! Go back to 1,1 | |
| 78 | + ! Clear screen and position at home | |
| 79 | + write(output_unit, '(a)', advance='no') CLEAR | |
| 72 | 80 | flush(output_unit) |
| 73 | 81 | |
| 74 | 82 | ! Initialize selection array to false |
@@ -118,9 +126,11 @@ program fortress | ||
| 118 | 126 | call mark_favorites_in_lists(parent_dir, parent_files, parent_count, parent_is_dir, & |
| 119 | 127 | favorite_dirs, favorite_count, parent_is_favorite) |
| 120 | 128 | |
| 121 | - ! Get terminal size | |
| 129 | + ! Get terminal size and calculate visible height accounting for padding and 2-line header | |
| 130 | + ! Layout: top_padding + header(2 lines) + vis_h + footer(1 line) = rows | |
| 131 | + ! So: vis_h = rows - top_padding - 3 | |
| 122 | 132 | call get_term_size(rows, cols) |
| 123 | - visible_height = rows - 3 | |
| 133 | + visible_height = rows - top_padding - 3 | |
| 124 | 134 | |
| 125 | 135 | ! Handle navigation signals from previous iteration |
| 126 | 136 | if (selected == -1) then |
@@ -169,12 +179,10 @@ program fortress | ||
| 169 | 179 | parent_scroll_offset = max(0, min(parent_scroll_offset, max(0, parent_count - visible_height))) |
| 170 | 180 | end if |
| 171 | 181 | |
| 172 | - ! Draw - reset scroll region, position at 1,1, then clear | |
| 173 | - write(output_unit, '(a)', advance='no') ESC // "[r" ! Reset scroll region | |
| 174 | - write(output_unit, '(a)', advance='no') ESC // "[1;1H" ! Position at 1,1 | |
| 175 | - write(output_unit, '(a)', advance='no') ESC // "[2J" ! Clear screen | |
| 182 | + ! Draw - use CLEAR which does move home + clear in one operation | |
| 183 | + write(output_unit, '(a)', advance='no') CLEAR | |
| 176 | 184 | flush(output_unit) |
| 177 | - call draw_interface(rows, cols, current_dir, current_files, current_is_dir, current_is_exec, & | |
| 185 | + call draw_interface(rows, cols, top_padding, current_dir, current_files, current_is_dir, current_is_exec, & | |
| 178 | 186 | current_is_staged, current_is_unstaged, current_is_untracked, current_has_incoming, & |
| 179 | 187 | current_count, parent_files, parent_is_dir, parent_is_exec, parent_count, & |
| 180 | 188 | selected, parent_selected, scroll_offset, parent_scroll_offset, & |
@@ -193,9 +201,23 @@ program fortress | ||
| 193 | 201 | |
| 194 | 202 | ! Handle input |
| 195 | 203 | select case(ichar(key)) |
| 196 | - case(27) ! ESC - arrow keys or Shift+arrow keys | |
| 204 | + case(27) ! ESC - could be arrow keys, Shift+arrow, or standalone ESC | |
| 205 | + ! Read the arrow key sequence first to determine what was pressed | |
| 197 | 206 | call read_arrow_key_with_shift(key, is_shift_pressed) |
| 198 | 207 | |
| 208 | + ! If it's not an arrow key (A/B/C/D), it was a standalone ESC press | |
| 209 | + if (key /= 'A' .and. key /= 'B' .and. key /= 'C' .and. key /= 'D') then | |
| 210 | + ! Standalone ESC - exit multi-select mode if active | |
| 211 | + if (selection_count > 0) then | |
| 212 | + call clear_all_selections(is_selected, selection_count, in_selection_mode) | |
| 213 | + selection_anchor = -1 | |
| 214 | + has_disjoint_selection = .false. | |
| 215 | + end if | |
| 216 | + cycle ! Redraw and wait for next input | |
| 217 | + end if | |
| 218 | + | |
| 219 | + ! If we get here, it's an arrow key - continue with normal arrow handling | |
| 220 | + | |
| 199 | 221 | if (move_mode) then |
| 200 | 222 | ! In move mode, navigate directories only |
| 201 | 223 | select case(key) |
src/ui/display.f90modified@@ -11,7 +11,7 @@ module ui_display | ||
| 11 | 11 | |
| 12 | 12 | contains |
| 13 | 13 | |
| 14 | - subroutine draw_interface(r, c, current_dir, current_files, current_is_dir, current_is_exec, & | |
| 14 | + subroutine draw_interface(r, c, top_padding, current_dir, current_files, current_is_dir, current_is_exec, & | |
| 15 | 15 | current_is_staged, current_is_unstaged, current_is_untracked, current_has_incoming, & |
| 16 | 16 | current_count, parent_files, parent_is_dir, parent_is_exec, parent_count, & |
| 17 | 17 | selected, parent_selected, scroll_offset, parent_scroll_offset, & |
@@ -20,7 +20,7 @@ contains | ||
| 20 | 20 | has_clipboard, clipboard_is_cut, clipboard_source_name, clipboard_count, & |
| 21 | 21 | is_selected, selection_count, & |
| 22 | 22 | current_is_favorite, parent_is_favorite) |
| 23 | - integer, intent(in) :: r, c, current_count, parent_count, selected, parent_selected | |
| 23 | + integer, intent(in) :: r, c, top_padding, current_count, parent_count, selected, parent_selected | |
| 24 | 24 | integer, intent(in) :: scroll_offset, parent_scroll_offset |
| 25 | 25 | character(len=*), intent(in) :: current_dir, repo_name, branch_name |
| 26 | 26 | character(len=*), dimension(*), intent(in) :: current_files, parent_files |
@@ -37,57 +37,49 @@ contains | ||
| 37 | 37 | logical, dimension(*), intent(in) :: is_selected |
| 38 | 38 | integer, intent(in) :: selection_count |
| 39 | 39 | logical, dimension(*), intent(in) :: current_is_favorite, parent_is_favorite |
| 40 | - integer :: left_w, i, parent_idx, current_idx, vis_h, display_len, top_padding | |
| 41 | - character(len=256) :: fname, term_program | |
| 40 | + integer :: left_w, i, parent_idx, current_idx, vis_h, display_len | |
| 41 | + character(len=256) :: fname | |
| 42 | 42 | character(len=20) :: color_code |
| 43 | 43 | |
| 44 | - ! Detect terminal emulator and add appropriate padding | |
| 45 | - ! Most terminals need 1 line, WezTerm/Ghostty need 2 | |
| 46 | - call get_environment_variable("TERM_PROGRAM", term_program) | |
| 47 | - if (index(term_program, "WezTerm") > 0 .or. index(term_program, "ghostty") > 0) then | |
| 48 | - top_padding = 2 ! WezTerm/Ghostty need 2 lines of padding | |
| 49 | - else | |
| 50 | - top_padding = 1 ! Most other terminals need 1 line | |
| 51 | - end if | |
| 52 | - | |
| 53 | 44 | left_w = c * 3 / 10 |
| 54 | - vis_h = r - 3 - top_padding ! Visible height (reduced by padding if needed) | |
| 45 | + vis_h = r - top_padding - 3 ! Visible height: rows - (top_padding + header(2) + footer(1)) | |
| 55 | 46 | |
| 56 | 47 | ! Add blank lines at top as padding for terminals that need it |
| 57 | 48 | do i = 1, top_padding |
| 58 | 49 | write(output_unit, '(a)') "" |
| 59 | 50 | end do |
| 60 | 51 | |
| 61 | - ! Header | |
| 52 | + ! Header - Line 1: Always show path | |
| 53 | + write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) | |
| 54 | + | |
| 55 | + ! Header - Line 2: Status info (always present to prevent shifting) | |
| 62 | 56 | if (move_mode) then |
| 63 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) // & | |
| 64 | - " | " // RED // "MOVE: " // trim(move_source_name) // RESET | |
| 57 | + write(output_unit, '(a)') RED // "MOVE: " // trim(move_source_name) // RESET | |
| 65 | 58 | else if (selection_count > 0) then |
| 66 | 59 | ! Show selection count |
| 67 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) // & | |
| 68 | - " | " // BLUE // trim(adjustl(itoa(selection_count))) // " selected" // RESET | |
| 60 | + write(output_unit, '(a)') BLUE // trim(adjustl(itoa(selection_count))) // " selected" // RESET | |
| 69 | 61 | else if (has_clipboard) then |
| 70 | 62 | if (clipboard_count > 1) then |
| 71 | 63 | ! Multiple items in clipboard |
| 72 | 64 | if (clipboard_is_cut) then |
| 73 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) // & | |
| 74 | - " | " // YELLOW // "CUT: " // trim(adjustl(itoa(clipboard_count))) // " items" // RESET | |
| 65 | + write(output_unit, '(a)') YELLOW // "CUT: " // trim(adjustl(itoa(clipboard_count))) // " items" // RESET | |
| 75 | 66 | else |
| 76 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) // & | |
| 77 | - " | " // GREEN // "COPY: " // trim(adjustl(itoa(clipboard_count))) // " items" // RESET | |
| 67 | + write(output_unit, '(a)') GREEN // "COPY: " // trim(adjustl(itoa(clipboard_count))) // " items" // RESET | |
| 78 | 68 | end if |
| 79 | 69 | else |
| 80 | 70 | ! Single item in clipboard |
| 81 | 71 | if (clipboard_is_cut) then |
| 82 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) // & | |
| 83 | - " | " // YELLOW // "CUT: " // trim(clipboard_source_name) // RESET | |
| 72 | + write(output_unit, '(a)') YELLOW // "CUT: " // trim(clipboard_source_name) // RESET | |
| 84 | 73 | else |
| 85 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) // & | |
| 86 | - " | " // GREEN // "COPY: " // trim(clipboard_source_name) // RESET | |
| 74 | + write(output_unit, '(a)') GREEN // "COPY: " // trim(clipboard_source_name) // RESET | |
| 87 | 75 | end if |
| 88 | 76 | end if |
| 77 | + else if (in_git_repo) then | |
| 78 | + ! Show git repo info when no other status | |
| 79 | + write(output_unit, '(a)') DIM // trim(repo_name) // ":" // trim(branch_name) // RESET | |
| 89 | 80 | else |
| 90 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) | |
| 81 | + ! Empty status line to maintain consistent spacing - write a space not empty string | |
| 82 | + write(output_unit, '(a)') " " | |
| 91 | 83 | end if |
| 92 | 84 | |
| 93 | 85 | ! Files (render based on scroll offsets) |
@@ -184,8 +176,19 @@ contains | ||
| 184 | 176 | current_has_incoming(current_idx), .true.) |
| 185 | 177 | end if |
| 186 | 178 | write(output_unit, '(a)') RESET |
| 179 | + else if (current_idx == selected .and. .not. move_mode .and. is_selected(current_idx)) then | |
| 180 | + ! Cursor on a selected item - use bold+underline with original color | |
| 181 | + write(output_unit, '(a)', advance='no') BOLD // UNDERLINE // trim(color_code) // trim(fname) | |
| 182 | + ! Add git indicators if in repo | |
| 183 | + if (in_git_repo) then | |
| 184 | + call write_git_indicators(current_is_staged(current_idx), & | |
| 185 | + current_is_unstaged(current_idx), & | |
| 186 | + current_is_untracked(current_idx), & | |
| 187 | + current_has_incoming(current_idx), .true.) | |
| 188 | + end if | |
| 189 | + write(output_unit, '(a)') RESET | |
| 187 | 190 | else if (current_idx == selected .and. .not. move_mode) then |
| 188 | - ! Normal selection cursor (not in move mode) - use bold+underline with original color | |
| 191 | + ! Normal selection cursor (not selected) - use bold+underline with original color | |
| 189 | 192 | write(output_unit, '(a)', advance='no') BOLD // UNDERLINE // trim(color_code) // trim(fname) |
| 190 | 193 | ! Add git indicators if in repo |
| 191 | 194 | if (in_git_repo) then |
@@ -223,18 +226,15 @@ contains | ||
| 223 | 226 | end if |
| 224 | 227 | end do |
| 225 | 228 | |
| 226 | - ! Footer | |
| 229 | + ! Footer - help text only (status moved to header) | |
| 227 | 230 | if (move_mode) then |
| 228 | - write(output_unit, '(a)') RED // "MOVE MODE: " // RESET // & | |
| 229 | - DIM // "↑↓:next/prev dir →:enter dir ←:parent ~:home /:root v:move here q:cancel" // RESET | |
| 231 | + write(output_unit, '(a)') DIM // "↑↓:next/prev dir →:enter dir ←:parent ~:home /:root v:move here q:cancel" // RESET | |
| 230 | 232 | else if (selection_count > 0) then |
| 231 | 233 | ! Selection mode footer - show multi-select help |
| 232 | - write(output_unit, '(a)') BLUE // "MULTI-SELECT: " // RESET // & | |
| 233 | - DIM // "Space:toggle Shift+↑↓:block select y:copy x:cut p:paste r:delete | " // RESET // & | |
| 234 | + write(output_unit, '(a)') DIM // "ESC:exit Space:toggle Shift+↑↓:block y:copy x:cut p:paste r:delete | " // RESET // & | |
| 234 | 235 | DIM // "→:enter ←:back ~:home /:root c:cd q:quit" // RESET |
| 235 | 236 | else if (in_git_repo) then |
| 236 | - write(output_unit, '(a)') DIM // trim(repo_name) // ":" // trim(branch_name) // " | " // RESET // & | |
| 237 | - DIM // "Space:select Shift+↑↓:block | ↑↓:nav →:enter ←:back ~:home /:root s:search 8:favorites *:star o:open n:rename r:remove v:move y:copy x:cut p:paste .:hidden a:add u:unstage m:commit d:diff f:fetch l:pull h:push c:cd q:quit" // RESET | |
| 237 | + write(output_unit, '(a)') DIM // "Space:select Shift+↑↓:block | ↑↓:nav →:enter ←:back ~:home /:root s:search 8:favorites *:star o:open n:rename r:remove v:move y:copy x:cut p:paste .:hidden a:add u:unstage m:commit d:diff f:fetch l:pull h:push c:cd q:quit" // RESET | |
| 238 | 238 | else |
| 239 | 239 | write(output_unit, '(a)') DIM // "Space:select Shift+↑↓:block | ↑↓:nav →:enter ←:back ~:home /:root s:search 8:favorites *:star o:open n:rename r:remove v:move y:copy x:cut p:paste .:hidden c:cd q:quit" // RESET |
| 240 | 240 | end if |