fortrangoingonforty/fortress / 061808d

Browse files

resolve some multiselect things on wezterm i am losing it

Authored by espadonne
SHA
061808d806cc9d79099f49faef8dabd73b11d509
Parents
d0d6ec0
Tree
4971bb0

3 changed files

StatusFile+-
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
5151
     logical, dimension(MAX_FILES) :: current_is_favorite, parent_is_favorite
5252
 
5353
     character(len=1) :: key
54
-    integer :: i, rows, cols, visible_height
54
+    integer :: i, rows, cols, visible_height, top_padding
5555
     logical :: is_shift_pressed
56
+    character(len=256) :: term_program
5657
 
5758
     ! Initialize
5859
     current_dir = get_pwd()
5960
     parent_dir = get_parent_path(current_dir)
6061
     call detect_git_repo(current_dir, in_git_repo, repo_name, branch_name)
6162
     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
+
6274
     call setup_raw_mode()
6375
     call enter_alt_screen()  ! Use alternate screen buffer to prevent scrolling issues
6476
     call hide_cursor()  ! Hide cursor for cleaner display
6577
 
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
7280
     flush(output_unit)
7381
 
7482
     ! Initialize selection array to false
@@ -118,9 +126,11 @@ program fortress
118126
         call mark_favorites_in_lists(parent_dir, parent_files, parent_count, parent_is_dir, &
119127
                                      favorite_dirs, favorite_count, parent_is_favorite)
120128
 
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
122132
         call get_term_size(rows, cols)
123
-        visible_height = rows - 3
133
+        visible_height = rows - top_padding - 3
124134
 
125135
         ! Handle navigation signals from previous iteration
126136
         if (selected == -1) then
@@ -169,12 +179,10 @@ program fortress
169179
             parent_scroll_offset = max(0, min(parent_scroll_offset, max(0, parent_count - visible_height)))
170180
         end if
171181
 
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
176184
         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, &
178186
                            current_is_staged, current_is_unstaged, current_is_untracked, current_has_incoming, &
179187
                            current_count, parent_files, parent_is_dir, parent_is_exec, parent_count, &
180188
                            selected, parent_selected, scroll_offset, parent_scroll_offset, &
@@ -193,9 +201,23 @@ program fortress
193201
 
194202
         ! Handle input
195203
         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
197206
             call read_arrow_key_with_shift(key, is_shift_pressed)
198207
 
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
+
199221
             if (move_mode) then
200222
                 ! In move mode, navigate directories only
201223
                 select case(key)
src/ui/display.f90modified
@@ -11,7 +11,7 @@ module ui_display
1111
 
1212
 contains
1313
 
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, &
1515
                               current_is_staged, current_is_unstaged, current_is_untracked, current_has_incoming, &
1616
                               current_count, parent_files, parent_is_dir, parent_is_exec, parent_count, &
1717
                               selected, parent_selected, scroll_offset, parent_scroll_offset, &
@@ -20,7 +20,7 @@ contains
2020
                               has_clipboard, clipboard_is_cut, clipboard_source_name, clipboard_count, &
2121
                               is_selected, selection_count, &
2222
                               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
2424
         integer, intent(in) :: scroll_offset, parent_scroll_offset
2525
         character(len=*), intent(in) :: current_dir, repo_name, branch_name
2626
         character(len=*), dimension(*), intent(in) :: current_files, parent_files
@@ -37,57 +37,49 @@ contains
3737
         logical, dimension(*), intent(in) :: is_selected
3838
         integer, intent(in) :: selection_count
3939
         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
4242
         character(len=20) :: color_code
4343
 
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
-
5344
         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))
5546
 
5647
         ! Add blank lines at top as padding for terminals that need it
5748
         do i = 1, top_padding
5849
             write(output_unit, '(a)') ""
5950
         end do
6051
 
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)
6256
         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
6558
         else if (selection_count > 0) then
6659
             ! 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
6961
         else if (has_clipboard) then
7062
             if (clipboard_count > 1) then
7163
                 ! Multiple items in clipboard
7264
                 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
7566
                 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
7868
                 end if
7969
             else
8070
                 ! Single item in clipboard
8171
                 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
8473
                 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
8775
                 end if
8876
             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
8980
         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)') " "
9183
         end if
9284
 
9385
         ! Files (render based on scroll offsets)
@@ -184,8 +176,19 @@ contains
184176
                                                   current_has_incoming(current_idx), .true.)
185177
                     end if
186178
                     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
187190
                 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
189192
                     write(output_unit, '(a)', advance='no') BOLD // UNDERLINE // trim(color_code) // trim(fname)
190193
                     ! Add git indicators if in repo
191194
                     if (in_git_repo) then
@@ -223,18 +226,15 @@ contains
223226
             end if
224227
         end do
225228
 
226
-        ! Footer
229
+        ! Footer - help text only (status moved to header)
227230
         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
230232
         else if (selection_count > 0) then
231233
             ! 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 // &
234235
                                      DIM // "→:enter ←:back ~:home /:root c:cd q:quit" // RESET
235236
         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
238238
         else
239239
             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
240240
         end if