fortrangoingonforty/facsimile / cd73a1e

Browse files

establish fortress mode base

Authored by espadonne
SHA
cd73a1e1d5d0bf788894ebde4e687ef0d0378fbd
Parents
5fc322f
Tree
8102811

3 changed files

StatusFile+-
M src/fortress/filesystem/fortress_fs_module.f90 24 0
M src/fortress/fortress_navigator_module.f90 31 31
M src/fortress/ui/fortress_display_module.f90 53 92
src/fortress/filesystem/fortress_fs_module.f90modified
@@ -101,8 +101,32 @@ contains
101101
 
102102
         ! Cleanup temp files
103103
         call execute_command_line("rm -f " // trim(temp_file) // " " // trim(stat_file) // " 2>/dev/null")
104
+
105
+        ! Filter out "." and ".." entries
106
+        call filter_dot_entries(files, is_dir, is_exec, count)
104107
     end subroutine get_file_list
105108
 
109
+    !> Filter out "." and ".." from file list
110
+    subroutine filter_dot_entries(files, is_dir, is_exec, count)
111
+        character(len=*), dimension(*), intent(inout) :: files
112
+        logical, dimension(*), intent(inout) :: is_dir, is_exec
113
+        integer, intent(inout) :: count
114
+        integer :: i, j
115
+
116
+        j = 0
117
+        do i = 1, count
118
+            if (trim(files(i)) /= "." .and. trim(files(i)) /= "..") then
119
+                j = j + 1
120
+                if (i /= j) then
121
+                    files(j) = files(i)
122
+                    is_dir(j) = is_dir(i)
123
+                    is_exec(j) = is_exec(i)
124
+                end if
125
+            end if
126
+        end do
127
+        count = j
128
+    end subroutine filter_dot_entries
129
+
106130
     function get_pwd() result(path)
107131
         character(len=MAX_PATH) :: path
108132
         integer :: unit, ios
src/fortress/fortress_navigator_module.f90modified
@@ -5,6 +5,7 @@ module fortress_navigator_module
55
     use iso_fortran_env, only: output_unit, input_unit
66
     use fortress_fs_module
77
     use fortress_display_module
8
+    use terminal_io_module, only: terminal_read_char
89
     implicit none
910
     private
1011
 
@@ -31,7 +32,7 @@ contains
3132
         character(len=*), intent(in), optional :: initial_path
3233
         character(len=MAX_PATH) :: current_dir, parent_dir, temp_dir
3334
         character(len=1) :: key
34
-        integer :: rows, cols
35
+        integer :: rows, cols, ios
3536
         logical :: running
3637
 
3738
         ! Initialize state
@@ -56,6 +57,9 @@ contains
5657
             call get_file_list(parent_dir, parent_files, parent_is_dir, parent_is_exec, parent_count)
5758
             call get_file_list(current_dir, current_files, current_is_dir, current_is_exec, current_count)
5859
 
60
+            ! Find current directory in parent listing
61
+            parent_selected = find_in_parent(current_dir, parent_files, parent_count)
62
+
5963
             ! Get terminal size
6064
             call get_term_size(rows, cols)
6165
 
@@ -74,8 +78,15 @@ contains
7478
                                          parent_files, parent_is_dir, parent_is_exec, parent_count, &
7579
                                          selected, parent_selected, scroll_offset, parent_scroll_offset)
7680
 
77
-            ! Read key
78
-            read(input_unit, '(a1)') key
81
+            ! Read key using raw terminal input (blocking mode)
82
+            ! Keep trying until we get input to avoid tight redraw loop
83
+            do
84
+                ios = terminal_read_char()
85
+                if (ios >= 0) exit
86
+                ! Small delay to avoid busy-waiting
87
+                call sleep(0)
88
+            end do
89
+            key = achar(ios)
7990
 
8091
             ! Handle input
8192
             select case (key)
@@ -150,18 +161,22 @@ contains
150161
     function check_arrow_key(key) result(is_arrow)
151162
         character(len=1), intent(inout) :: key
152163
         logical :: is_arrow
153
-        character(len=1) :: next_char
154
-        integer :: ios
164
+        integer :: char_code
155165
 
156166
         is_arrow = .false.
157167
 
158168
         if (key == char(27)) then
159169
             ! Try to read next character
160
-            read(input_unit, '(a1)', advance='no', iostat=ios) next_char
161
-            if (ios == 0 .and. next_char == '[') then
162
-                ! It's an arrow key sequence
163
-                read(input_unit, '(a1)') key
164
-                is_arrow = .true.
170
+            char_code = terminal_read_char()
171
+            if (char_code >= 0) then
172
+                if (achar(char_code) == '[') then
173
+                    ! It's an arrow key sequence - read the direction
174
+                    char_code = terminal_read_char()
175
+                    if (char_code >= 0) then
176
+                        key = achar(char_code)
177
+                        is_arrow = .true.
178
+                    end if
179
+                end if
165180
             end if
166181
         end if
167182
     end function check_arrow_key
@@ -197,31 +212,16 @@ contains
197212
         end select
198213
     end subroutine handle_arrow_key
199214
 
200
-    !> Get terminal size using tput
215
+    !> Get terminal size using fac's terminal module
201216
     subroutine get_term_size(rows, cols)
217
+        use terminal_io_module, only: terminal_get_size
202218
         integer, intent(out) :: rows, cols
203
-        integer :: unit, ios
204
-        character(len=MAX_PATH) :: temp_file
205
-
206
-        call get_environment_variable("HOME", temp_file)
207
-        temp_file = trim(temp_file) // "/.fac_term_size"
208219
 
209
-        call execute_command_line("echo ""$(tput lines) $(tput cols)"" > " // trim(temp_file) // " 2>/dev/null", wait=.true.)
210
-
211
-        open(newunit=unit, file=temp_file, status='old', iostat=ios)
212
-        if (ios == 0) then
213
-            read(unit, *, iostat=ios) rows, cols
214
-            close(unit)
215
-            if (ios /= 0) then
216
-                rows = 24
217
-                cols = 80
218
-            end if
219
-        else
220
-            rows = 24
221
-            cols = 80
222
-        end if
220
+        call terminal_get_size(rows, cols)
223221
 
224
-        call execute_command_line("rm -f " // trim(temp_file) // " 2>/dev/null")
222
+        ! Sanity check
223
+        if (rows <= 0) rows = 24
224
+        if (cols <= 0) cols = 80
225225
     end subroutine get_term_size
226226
 
227227
 end module fortress_navigator_module
src/fortress/ui/fortress_display_module.f90modified
@@ -1,5 +1,4 @@
1
-! UI display for Fortress navigator (adapted for fac - Phase 1 simplified)
2
-! Original source: fortress/src/ui/display.f90
1
+! UI display for Fortress navigator (simplified for fac)
32
 
43
 module fortress_display_module
54
     use iso_fortran_env, only: output_unit
@@ -7,9 +6,9 @@ module fortress_display_module
76
     implicit none
87
     private
98
 
10
-    public :: draw_fortress_interface, get_file_color
9
+    public :: draw_fortress_interface
1110
 
12
-    ! ANSI escape codes (matching fortress terminal_control)
11
+    ! ANSI escape codes
1312
     character(len=*), parameter :: ESC = char(27)
1413
     character(len=*), parameter :: BOLD = ESC // "[1m"
1514
     character(len=*), parameter :: DIM = ESC // "[2m"
@@ -17,10 +16,8 @@ module fortress_display_module
1716
     character(len=*), parameter :: RESET = ESC // "[0m"
1817
     character(len=*), parameter :: BLUE = ESC // "[34m"
1918
     character(len=*), parameter :: GREEN = ESC // "[32m"
20
-    character(len=*), parameter :: RED = ESC // "[31m"
2119
     character(len=*), parameter :: GREY = ESC // "[90m"
2220
     character(len=*), parameter :: WHITE = ESC // "[37m"
23
-    character(len=*), parameter :: YELLOW = ESC // "[33m"
2421
 
2522
 contains
2623
 
@@ -33,128 +30,92 @@ contains
3330
         character(len=*), dimension(*), intent(in) :: current_files, parent_files
3431
         logical, dimension(*), intent(in) :: current_is_dir, parent_is_dir
3532
         logical, dimension(*), intent(in) :: current_is_exec, parent_is_exec
36
-        integer :: left_w, i, parent_idx, current_idx, vis_h, display_len, inner_w
37
-        character(len=256) :: fname
38
-        character(len=20) :: color_code
33
+        integer :: left_w, i, j, parent_idx, current_idx, vis_h
34
+        character(len=256) :: parent_name, current_name
35
+        character(len=256) :: line
3936
 
37
+        ! Calculate layout
4038
         left_w = c * 3 / 10
41
-        vis_h = r - 4  ! Visible height (top border + header + footer + bottom border)
42
-        inner_w = c - 4  ! Inner width (minus border chars and padding)
39
+        vis_h = r - 3
4340
 
44
-        ! Clear screen and move to top
45
-        write(output_unit, '(a)', advance='no') ESC // "[H" // ESC // "[J"
46
-        flush(output_unit)
47
-
48
-        ! Top border with rounded corners
49
-        write(output_unit, '(a)') DIM // "╭─" // repeat("─", inner_w) // "─╮" // RESET
41
+        ! Clear screen and hide cursor
42
+        write(output_unit, '(a)', advance='no') ESC // "[?25l" // ESC // "[2J" // ESC // "[H"
5043
 
51
-        ! Header - path with border
52
-        write(output_unit, '(a)', advance='no') DIM // "│ " // RESET
53
-        write(output_unit, '(a)', advance='no') BOLD // trim(current_dir) // RESET
54
-        write(output_unit, '(a)', advance='no') repeat(" ", max(0, inner_w - len_trim(current_dir)))
55
-        write(output_unit, '(a)') DIM // " │" // RESET
44
+        ! Header
45
+        write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir)
46
+        write(output_unit, '(a)') ""
5647
 
57
-        ! Files (render based on scroll offsets) with border
48
+        ! Render each line - write complete lines at once
5849
         do i = 1, vis_h
5950
             parent_idx = i + parent_scroll_offset
6051
             current_idx = i + scroll_offset
6152
 
62
-            ! Left border
63
-            write(output_unit, '(a)', advance='no') DIM // "│ " // RESET
53
+            ! Start with cursor at beginning of line
54
+            write(output_unit, '(a)', advance='no') ESC // "[1G"
6455
 
65
-            ! Parent pane (left 30%)
56
+            ! === Parent pane (left 30%) ===
6657
             if (parent_idx >= 1 .and. parent_idx <= parent_count) then
67
-                fname = parent_files(parent_idx)
58
+                ! Clean the filename - remove any control characters
59
+                parent_name = trim(adjustl(parent_files(parent_idx)))
6860
 
69
-                ! Add trailing slash for directories
70
-                if (parent_is_dir(parent_idx) .and. parent_files(parent_idx) /= "." .and. parent_files(parent_idx) /= "..") then
71
-                    fname = trim(fname) // "/"
61
+                ! Remove any newlines or carriage returns
62
+                j = scan(parent_name, char(10)//char(13))
63
+                if (j > 0) then
64
+                    parent_name = parent_name(1:j-1)
7265
                 end if
7366
 
74
-                ! Get color for parent file
75
-                color_code = get_file_color(parent_files(parent_idx), parent_is_dir(parent_idx), parent_is_exec(parent_idx))
67
+                if (parent_is_dir(parent_idx)) parent_name = trim(parent_name) // "/"
7668
 
77
-                ! Calculate visual width
78
-                display_len = min(len_trim(fname), left_w)
69
+                ! Truncate if too long
70
+                if (len_trim(parent_name) > left_w) then
71
+                    parent_name = parent_name(1:left_w)
72
+                end if
7973
 
80
-                ! Highlight if selected in parent pane
74
+                ! Write parent item with color
8175
                 if (parent_idx == parent_selected) then
82
-                    write(output_unit, '(a)', advance='no') DIM // BOLD // trim(color_code) // &
83
-                        fname(1:min(len_trim(fname), left_w)) // RESET
76
+                    write(output_unit, '(a)', advance='no') DIM // BOLD // BLUE // trim(parent_name) // RESET
8477
                 else
85
-                    write(output_unit, '(a)', advance='no') DIM // trim(color_code) // &
86
-                        fname(1:min(len_trim(fname), left_w)) // RESET
78
+                    write(output_unit, '(a)', advance='no') DIM // GREY // trim(parent_name) // RESET
8779
                 end if
88
-                write(output_unit, '(a)', advance='no') repeat(" ", max(0, left_w - display_len))
89
-            else
90
-                write(output_unit, '(a)', advance='no') repeat(" ", left_w)
9180
             end if
9281
 
93
-            ! RESET before separator
94
-            write(output_unit, '(a)', advance='no') RESET
95
-
96
-            ! Separator
97
-            write(output_unit, '(a)', advance='no') " │ "
82
+            ! Use explicit cursor positioning for separator - build the position string
83
+            write(line, '(a,i0,a)') ESC // "[", left_w + 1, "G"
84
+            write(output_unit, '(a)', advance='no') trim(line) // " │ "
9885
 
99
-            ! Current pane (right 70%)
86
+            ! === Current pane (right) ===
10087
             if (current_idx >= 1 .and. current_idx <= current_count) then
101
-                fname = current_files(current_idx)
88
+                ! Clean the filename - remove any control characters
89
+                current_name = trim(adjustl(current_files(current_idx)))
10290
 
103
-                ! Add trailing slash for directories
104
-                if (current_is_dir(current_idx) .and. current_files(current_idx) /= "." .and. current_files(current_idx) /= "..") then
105
-                    fname = trim(fname) // "/"
91
+                ! Remove any newlines or carriage returns by finding first occurrence
92
+                j = scan(current_name, char(10)//char(13))
93
+                if (j > 0) then
94
+                    current_name = current_name(1:j-1)
10695
                 end if
10796
 
108
-                ! Get color for current file
109
-                color_code = get_file_color(current_files(current_idx), current_is_dir(current_idx), current_is_exec(current_idx))
97
+                if (current_is_dir(current_idx)) current_name = trim(current_name) // "/"
11098
 
111
-                ! Highlight selected item
11299
                 if (current_idx == selected) then
113
-                    write(output_unit, '(a)', advance='no') BOLD // UNDERLINE // trim(color_code) // trim(fname) // RESET
100
+                    write(output_unit, '(a)') BOLD // UNDERLINE // WHITE // trim(current_name) // RESET
101
+                else if (current_is_dir(current_idx)) then
102
+                    write(output_unit, '(a)') BLUE // trim(current_name) // RESET
103
+                else if (current_is_exec(current_idx)) then
104
+                    write(output_unit, '(a)') GREEN // trim(current_name) // RESET
114105
                 else
115
-                    write(output_unit, '(a)', advance='no') trim(color_code) // trim(fname) // RESET
106
+                    write(output_unit, '(a)') trim(current_name) // RESET
116107
                 end if
117
-
118
-                ! Pad to right border
119
-                write(output_unit, '(a)', advance='no') repeat(" ", max(0, inner_w - left_w - 3 - len_trim(fname)))
120108
             else
121
-                write(output_unit, '(a)', advance='no') repeat(" ", max(0, inner_w - left_w - 3))
109
+                write(output_unit, '(a)') ""
122110
             end if
123
-
124
-            ! Right border
125
-            write(output_unit, '(a)') DIM // " │" // RESET
126111
         end do
127112
 
128
-        ! Bottom border with ESC hint
129
-        write(output_unit, '(a)', advance='no') DIM // "╰─" // RESET
130
-        write(output_unit, '(a)', advance='no') DIM // " ESC to exit " // RESET
131
-        write(output_unit, '(a)') DIM // repeat("─", max(0, inner_w - 13)) // "─╯" // RESET
113
+        ! Footer
114
+        write(output_unit, '(a)') DIM // "arrows:nav enter:open esc:quit" // RESET
132115
 
116
+        ! Show cursor again
117
+        write(output_unit, '(a)', advance='no') ESC // "[?25h"
133118
         flush(output_unit)
134119
     end subroutine draw_fortress_interface
135120
 
136
-    function get_file_color(filename, is_dir, is_exec) result(color)
137
-        character(len=*), intent(in) :: filename
138
-        logical, intent(in) :: is_dir, is_exec
139
-        character(len=20) :: color
140
-
141
-        ! Directories: Blue and bold
142
-        if (is_dir) then
143
-            color = BOLD // BLUE
144
-        ! Dotfiles: Grey
145
-        else if (len_trim(filename) > 0) then
146
-            if (filename(1:1) == '.') then
147
-                color = GREY
148
-            ! Executable files: Green
149
-            else if (is_exec) then
150
-                color = GREEN
151
-            ! All other files: White
152
-            else
153
-                color = WHITE
154
-            end if
155
-        else
156
-            color = WHITE
157
-        end if
158
-    end function get_file_color
159
-
160121
 end module fortress_display_module