fortrangoingonforty/facsimile / 03dbddb

Browse files

fix opened cwd not persisting workspace

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
03dbddb4da8873ffd9d3db3db493664fc39d1491
Parents
a675124
Tree
b74dfbc

4 changed files

StatusFile+-
M app/main.f90 67 6
M src/commands/command_handler_module.f90 12 3
M src/terminal/input_handler_module.f90 119 4
M src/workspace/workspace_module.f90 73 1
app/main.f90modified
@@ -112,42 +112,62 @@ program facsimile
112112
         ! User selected a workspace from welcome menu (not browse)
113113
         ! This handles favorites, recents, and CURRENT DIRECTORY
114114
         if (allocated(selected_path) .and. .not. is_browse) then
115
+            write(0, '(A)') '[DEBUG WELCOME] selected_path allocated, value: ' // selected_path
116
+            write(0, '(A,L1)') '[DEBUG WELCOME] is_browse: ', is_browse
117
+
115118
             ! Check if user selected CURRENT DIRECTORY option
116119
             if (selected_path == "CWD") then
120
+                write(0, '(A)') '[DEBUG WELCOME] CWD selected, getting workspace path'
117121
                 ! Get actual current working directory
118122
                 call get_workspace_path(selected_path)
123
+                write(0, '(A)') '[DEBUG WELCOME] Workspace path: ' // selected_path
119124
                 arg = selected_path
120125
             else
126
+                write(0, '(A)') '[DEBUG WELCOME] Not CWD, using selected_path directly'
121127
                 arg = selected_path
122128
             end if
123129
 
124130
             ! Check if it's a directory
131
+            write(0, '(A)') '[DEBUG WELCOME] Testing if directory: ' // trim(arg)
125132
             call execute_command_line("test -d '" // trim(arg) // &
126133
                 "' && echo 'Directory' > /tmp/.fac_filetype || " // &
127134
                 "echo 'File' > /tmp/.fac_filetype", wait=.true.)
128135
             call read_file_type(status)
136
+            write(0, '(A,I0)') '[DEBUG WELCOME] read_file_type status: ', status
129137
             if (status == 0) then
130138
                 ! Directory - workspace mode
139
+                write(0, '(A)') '[DEBUG WELCOME] Setting is_workspace_mode = TRUE'
131140
                 is_workspace_mode = .true.
132141
                 call workspace_get_path(trim(arg), workspace_dir)
142
+                write(0, '(A)') '[DEBUG WELCOME] workspace_dir: ' // trim(workspace_dir)
133143
             else
134144
                 ! Invalid selection (favorites/recents should only have directories)
135145
                 write(error_unit, '(A)') 'Error: Selected path is not a directory'
136146
                 stop 1
137147
             end if
148
+        else
149
+            write(0, '(A,L1)') '[DEBUG WELCOME] selected_path allocated: ', allocated(selected_path)
150
+            if (allocated(selected_path)) then
151
+                write(0, '(A)') '[DEBUG WELCOME] selected_path value: ' // selected_path
152
+            end if
153
+            write(0, '(A,L1)') '[DEBUG WELCOME] is_browse: ', is_browse
138154
         end if
139155
     end if
140156
 
141157
     ! Handle workspace mode
158
+    write(0, '(A,L1)') '[DEBUG WORKSPACE] is_workspace_mode: ', is_workspace_mode
142159
     if (is_workspace_mode) then
160
+        write(0, '(A)') '[DEBUG WORKSPACE] In workspace mode, workspace_dir: ' // trim(workspace_dir)
143161
         ! Check if workspace exists, create if not
144162
         if (.not. workspace_exists(workspace_dir)) then
163
+            write(0, '(A)') '[DEBUG WORKSPACE] Workspace does not exist, creating'
145164
             call workspace_init(workspace_dir, workspace_success)
146165
             if (.not. workspace_success) then
147166
                 write(error_unit, '(A)') 'Error: Failed to create workspace'
148167
                 stop 1
149168
             end if
150169
         else
170
+            write(0, '(A)') '[DEBUG WORKSPACE] Workspace exists, loading'
151171
             ! Load existing workspace
152172
             call workspace_load(workspace_dir, workspace_success)
153173
             if (.not. workspace_success) then
@@ -155,30 +175,68 @@ program facsimile
155175
                 stop 1
156176
             end if
157177
         end if
178
+    else
179
+        write(0, '(A)') '[DEBUG WORKSPACE] NOT in workspace mode'
158180
     end if
159181
 
160182
     ! Initialize editor
161183
     call init_editor(editor)
162184
     running = .true.
163185
 
186
+    ! Initialize terminal early (needed for workspace restoration warnings)
187
+    call terminal_init()
188
+    call terminal_clear_screen()
189
+
190
+    ! Initialize main buffer early (needed for workspace restoration)
191
+    call init_buffer(buffer)
192
+
164193
     ! Set workspace path
194
+    write(0, '(A,L1)') '[DEBUG RESTORE CHECK] is_workspace_mode: ', is_workspace_mode
165195
     if (is_workspace_mode) then
196
+        write(0, '(A)') '[DEBUG RESTORE CHECK] workspace_dir: ' // trim(workspace_dir)
166197
         ! Use detected/created workspace directory
167198
         allocate(character(len=len_trim(workspace_dir)) :: editor%workspace_path)
168199
         editor%workspace_path = trim(workspace_dir)
169200
 
201
+        ! DEBUG: Print before restoration (unit 0 = stderr)
202
+        write(0, '(A)') '[DEBUG RESTORE] About to restore workspace from: ' // trim(editor%workspace_path)
203
+        write(0, '(A)') '[DEBUG RESTORE] Workspace JSON path: ' // trim(editor%workspace_path) // '/.fac/workspace.json'
204
+
170205
         ! Restore workspace state (tabs, cursor positions, etc.)
171206
         call workspace_restore_state(editor, editor%workspace_path, workspace_success)
172
-        ! Silently ignore restore failures for now
207
+
208
+        ! DEBUG: Print restoration results
209
+        write(0, '(A,L1)') '[DEBUG RESTORE] Workspace restore success: ', workspace_success
210
+        if (allocated(editor%tabs)) then
211
+            write(0, '(A,I0)') '[DEBUG RESTORE] Number of tabs restored: ', size(editor%tabs)
212
+        else
213
+            write(0, '(A)') '[DEBUG RESTORE] No tabs allocated after restore'
214
+        end if
215
+        write(0, '(A,I0)') '[DEBUG RESTORE] Active tab index: ', editor%active_tab_index
216
+
217
+        ! Sync restored active tab's buffer to main buffer
218
+        if (workspace_success .and. allocated(editor%tabs) .and. editor%active_tab_index > 0) then
219
+            if (editor%active_tab_index <= size(editor%tabs)) then
220
+                if (allocated(editor%tabs(editor%active_tab_index)%panes) .and. &
221
+                    size(editor%tabs(editor%active_tab_index)%panes) > 0) then
222
+                    ! Copy active pane's buffer to main buffer (replaces the empty init)
223
+                    write(0, '(A)') '[DEBUG RESTORE] Copying restored tab buffer to main buffer'
224
+                    call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%panes(1)%buffer)
225
+                else
226
+                    write(0, '(A)') '[DEBUG RESTORE] Active tab has no panes!'
227
+                end if
228
+            else
229
+                write(0, '(A,I0,A,I0)') '[DEBUG RESTORE] Active tab index ', editor%active_tab_index, &
230
+                    ' exceeds tab count ', size(editor%tabs)
231
+            end if
232
+        else
233
+            write(0, '(A)') '[DEBUG RESTORE] Skipping buffer sync - conditions not met'
234
+        end if
173235
     else
174236
         ! Single-file mode - use current directory
175237
         call get_workspace_path(editor%workspace_path)
176238
     end if
177239
 
178
-    ! Initialize terminal (before backup restore prompts)
179
-    call terminal_init()
180
-    call terminal_clear_screen()
181
-
182240
     ! Check for backups and offer restoration (after workspace load, after terminal init)
183241
     if (is_workspace_mode .and. backup_detect(editor%workspace_path)) then
184242
         call handle_backup_restoration(editor, buffer)
@@ -259,7 +317,10 @@ program facsimile
259317
             editor%filename = trim(filename)
260318
         end if
261319
     else
262
-        call init_buffer(buffer)
320
+        ! Only initialize empty buffer if we don't have restored tabs
321
+        if (.not. (allocated(editor%tabs) .and. editor%active_tab_index > 0)) then
322
+            call init_buffer(buffer)
323
+        end if
263324
     end if
264325
 
265326
     ! Save initial file state for undo (position 0)
src/commands/command_handler_module.f90modified
@@ -93,8 +93,9 @@ contains
9393
             match_case_sensitive = .true.  ! Reset to default
9494
         end if
9595
 
96
-        ! Route input when in fuss mode (except ctrl-b and ctrl-q which work in both modes)
97
-        if (editor%fuss_mode_active .and. trim(key_str) /= 'ctrl-b' .and. trim(key_str) /= 'ctrl-q') then
96
+        ! Route input when in fuss mode (except ctrl-b/F2 and ctrl-q which work in both modes)
97
+        if (editor%fuss_mode_active .and. trim(key_str) /= 'ctrl-b' .and. &
98
+            trim(key_str) /= 'f2' .and. trim(key_str) /= 'ctrl-q') then
9899
             call handle_fuss_input(key_str, editor, buffer)
99100
             return
100101
         end if
@@ -104,7 +105,7 @@ contains
104105
         case('ctrl-q')
105106
             should_quit = .true.
106107
 
107
-        case('ctrl-b')
108
+        case('ctrl-b', 'f2')
108109
             ! Toggle fuss mode (file tree)
109110
             call toggle_fuss_mode(editor)
110111
 
@@ -4107,6 +4108,14 @@ contains
41074108
             end if
41084109
 
41094110
             if (status == 0) then
4111
+                ! Copy tab's buffer to pane's buffer
4112
+                if (allocated(editor%tabs(editor%active_tab_index)%panes) .and. &
4113
+                    editor%tabs(editor%active_tab_index)%active_pane_index > 0) then
4114
+                    call copy_buffer(editor%tabs(editor%active_tab_index)%panes( &
4115
+                        editor%tabs(editor%active_tab_index)%active_pane_index)%buffer, &
4116
+                        editor%tabs(editor%active_tab_index)%buffer)
4117
+                end if
4118
+
41104119
                 ! Copy tab's buffer to main buffer so it's displayed
41114120
                 call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%buffer)
41124121
 
src/terminal/input_handler_module.f90modified
@@ -83,7 +83,7 @@ contains
8383
 
8484
     subroutine handle_escape_sequence(key_str)
8585
         character(len=*), intent(out) :: key_str
86
-        character :: ch1, ch2, ch3, modifier_ch
86
+        character :: ch, ch1, ch2, ch3, modifier_ch
8787
         integer :: char_code, ios
8888
 
8989
         key_str = 'esc'
@@ -176,9 +176,102 @@ contains
176176
                     call handle_modified_special_key(key_str, 6)
177177
                 end if
178178
             case('1')
179
-                ! Modified arrow key or home/end: ESC [ 1 ; 2 A format
180
-                call handle_modified_key(key_str)
181
-            case('2', '4', '7', '8')
179
+                ! Could be function key (F1-F9) or modified arrow/home/end
180
+                ! Check next character
181
+                char_code = terminal_read_char()
182
+                if (char_code >= 0) then
183
+                    ch3 = achar(char_code)
184
+                    if (ch3 == '~') then
185
+                        ! F1: ESC [ 1 1 ~ (alternate format)
186
+                        key_str = 'f1'
187
+                    else if (ch3 == '0') then
188
+                        ! F10 might be ESC [ 2 1 ~, check for tilde
189
+                        char_code = terminal_read_char()
190
+                        if (char_code >= 0 .and. achar(char_code) == '~') then
191
+                            key_str = 'f10'
192
+                        end if
193
+                    else if (ch3 == '1' .or. ch3 == '2' .or. ch3 == '3' .or. ch3 == '4' .or. &
194
+                             ch3 == '5' .or. ch3 == '7' .or. ch3 == '8' .or. ch3 == '9') then
195
+                        ! Function keys F1-F9: ESC [ 1 X ~
196
+                        char_code = terminal_read_char()
197
+                        if (char_code >= 0 .and. achar(char_code) == '~') then
198
+                            select case(ch3)
199
+                            case('1')
200
+                                key_str = 'f1'
201
+                            case('2')
202
+                                key_str = 'f2'
203
+                            case('3')
204
+                                key_str = 'f3'
205
+                            case('4')
206
+                                key_str = 'f4'
207
+                            case('5')
208
+                                key_str = 'f5'
209
+                            case('7')
210
+                                key_str = 'f6'
211
+                            case('8')
212
+                                key_str = 'f7'
213
+                            case('9')
214
+                                key_str = 'f8'
215
+                            end select
216
+                        end if
217
+                    else if (ch3 == ';') then
218
+                        ! Modified arrow key or home/end: ESC [ 1 ; 2 A format
219
+                        call handle_modified_key(key_str)
220
+                    end if
221
+                end if
222
+            case('2')
223
+                ! Could be F9-F12 or alternate modified keys
224
+                char_code = terminal_read_char()
225
+                if (char_code >= 0) then
226
+                    ch3 = achar(char_code)
227
+                    if (ch3 == '0' .or. ch3 == '1' .or. ch3 == '3' .or. ch3 == '4') then
228
+                        ! Function keys F9-F12: ESC [ 2 X ~
229
+                        char_code = terminal_read_char()
230
+                        if (char_code >= 0 .and. achar(char_code) == '~') then
231
+                            select case(ch3)
232
+                            case('0')
233
+                                key_str = 'f9'
234
+                            case('1')
235
+                                key_str = 'f10'
236
+                            case('3')
237
+                                key_str = 'f11'
238
+                            case('4')
239
+                                key_str = 'f12'
240
+                            end select
241
+                        end if
242
+                    else if (ch3 == ';') then
243
+                        ! ESC [ 2 ; A format (shift+arrow)
244
+                        char_code = terminal_read_char()
245
+                        if (char_code >= 0) then
246
+                            ch = achar(char_code)
247
+                            key_str = 'shift-'
248
+                            select case(ch)
249
+                            case('A')
250
+                                key_str = trim(key_str) // 'up'
251
+                            case('B')
252
+                                key_str = trim(key_str) // 'down'
253
+                            case('C')
254
+                                key_str = trim(key_str) // 'right'
255
+                            case('D')
256
+                                key_str = trim(key_str) // 'left'
257
+                            end select
258
+                        end if
259
+                    else
260
+                        ! Direct format ESC [ 2 A (we already read the 'A' in ch3)
261
+                        key_str = 'shift-'
262
+                        select case(ch3)
263
+                        case('A')
264
+                            key_str = trim(key_str) // 'up'
265
+                        case('B')
266
+                            key_str = trim(key_str) // 'down'
267
+                        case('C')
268
+                            key_str = trim(key_str) // 'right'
269
+                        case('D')
270
+                            key_str = trim(key_str) // 'left'
271
+                        end select
272
+                    end if
273
+                end if
274
+            case('4', '7', '8')
182275
                 ! Alternate format: ESC [ 2 A (modifier directly, no '1')
183276
                 ! This is sent by some terminals for shift+arrows
184277
                 call handle_alternate_modified_key(key_str, ch2)
@@ -186,6 +279,28 @@ contains
186279
                 ! Mouse event in SGR mode
187280
                 call handle_mouse_event(key_str)
188281
             end select
282
+        else if (ch1 == 'O') then
283
+            ! SS3 sequence (e.g., function keys F1-F4)
284
+            char_code = terminal_read_char()
285
+            if (char_code < 0) then
286
+                ! Timeout - this is just Alt+O
287
+                key_str = 'alt-o'
288
+                return
289
+            end if
290
+            ch2 = achar(char_code)
291
+            select case(ch2)
292
+            case('P')
293
+                key_str = 'f1'
294
+            case('Q')
295
+                key_str = 'f2'
296
+            case('R')
297
+                key_str = 'f3'
298
+            case('S')
299
+                key_str = 'f4'
300
+            case default
301
+                ! Unknown SS3 sequence - return as Alt+ch2
302
+                write(key_str, '(a,a)') 'alt-', ch2
303
+            end select
189304
         else if (ch1 == achar(27)) then
190305
             ! ESC ESC - likely Alt+something
191306
             char_code = terminal_read_char()
src/workspace/workspace_module.f90modified
@@ -230,9 +230,25 @@ contains
230230
         success = .false.
231231
         workspace_file = trim(dir_path) // "/.fac/workspace.json"
232232
 
233
+        ! DEBUG: Write to stderr (unit 0) so it doesn't interfere
234
+        write(0, '(A)') '[DEBUG SAVE] workspace_save_state called'
235
+        write(0, '(A)') '[DEBUG SAVE] Workspace file: ' // trim(workspace_file)
236
+        if (allocated(editor%tabs)) then
237
+            write(0, '(A,I0)') '[DEBUG SAVE] Tabs allocated, size: ', size(editor%tabs)
238
+            if (size(editor%tabs) > 0) then
239
+                write(0, '(A)') '[DEBUG SAVE] First tab filename: ' // trim(editor%tabs(1)%filename)
240
+            end if
241
+        else
242
+            write(0, '(A)') '[DEBUG SAVE] ERROR: Tabs not allocated!'
243
+        end if
244
+        write(0, '(A,I0)') '[DEBUG SAVE] Active tab index: ', editor%active_tab_index
245
+
233246
         ! Open file for writing
234247
         open(newunit=unit, file=workspace_file, status='replace', iostat=ios)
235
-        if (ios /= 0) return
248
+        if (ios /= 0) then
249
+            write(0, '(A,I0)') '[DEBUG SAVE] ERROR: Failed to open file, ios=', ios
250
+            return
251
+        end if
236252
 
237253
         ! Get current timestamp (simplified)
238254
         call date_and_time(timestamp)
@@ -246,6 +262,7 @@ contains
246262
 
247263
         ! Write tabs
248264
         if (allocated(editor%tabs)) then
265
+            write(0, '(A,I0)') '[DEBUG SAVE] Writing ', size(editor%tabs), ' tabs'
249266
             ws_len = len_trim(dir_path)
250267
             do i = 1, size(editor%tabs)
251268
                 ! Write tab start - must be on its own line for parser
@@ -410,13 +427,20 @@ contains
410427
         character(len=20) :: value_str
411428
         character(len=512) :: warning_msg
412429
 
430
+        ! DEBUG: Start of restoration
431
+        write(0, '(A)') '[DEBUG RESTORE WS] workspace_restore_state called'
432
+        write(0, '(A)') '[DEBUG RESTORE WS] Dir path: ' // trim(dir_path)
433
+
413434
         success = .false.
414435
         workspace_file = trim(dir_path) // "/.fac/workspace.json"
415436
 
437
+        write(0, '(A)') '[DEBUG RESTORE WS] Workspace file: ' // trim(workspace_file)
438
+
416439
         ! Open workspace file
417440
         open(newunit=unit, file=workspace_file, status='old', iostat=ios)
418441
         if (ios /= 0) then
419442
             ! Workspace file doesn't exist or can't be read (Phase 7: error handling)
443
+            write(0, '(A,I0)') '[DEBUG RESTORE WS] ERROR: Failed to open workspace.json, ios=', ios
420444
             warning_msg = "Warning: Could not open workspace.json - using empty workspace"
421445
             call terminal_write(trim(warning_msg))
422446
             ! Brief pause so user can see the warning
@@ -426,6 +450,8 @@ contains
426450
             return
427451
         end if
428452
 
453
+        write(0, '(A)') '[DEBUG RESTORE WS] Workspace file opened successfully'
454
+
429455
         ! Parse JSON line by line (simple parser for our specific format)
430456
         in_tabs_array = .false.
431457
         reading_tab = .false.
@@ -435,6 +461,7 @@ contains
435461
         pane_filename = ""
436462
         is_orphan = .false.
437463
         pane_count = 0
464
+        editor%active_tab_index = 1  ! Default to first tab
438465
 
439466
 
440467
         do
@@ -449,6 +476,23 @@ contains
449476
                 cycle
450477
             end if
451478
 
479
+            ! Parse active_tab index (outside tabs array)
480
+            if (.not. in_tabs_array .and. index(line, '"active_tab":') > 0) then
481
+                colon_pos = index(line, ':')
482
+                comma_pos = index(line, ',')
483
+                if (colon_pos > 0) then
484
+                    if (comma_pos > colon_pos) then
485
+                        value_str = adjustl(line(colon_pos+1:comma_pos-1))
486
+                    else
487
+                        value_str = adjustl(line(colon_pos+1:))
488
+                    end if
489
+                    read(value_str, *, iostat=ios) editor%active_tab_index
490
+                    ! Ensure it's at least 1 if tabs were restored
491
+                    if (editor%active_tab_index < 1) editor%active_tab_index = 1
492
+                end if
493
+                cycle
494
+            end if
495
+
452496
             ! Check if we're exiting the tabs array
453497
             if (in_tabs_array .and. index(line, '],') > 0 .and. .not. in_panes_array) then
454498
                 in_tabs_array = .false.
@@ -567,14 +611,17 @@ contains
567611
                         end if
568612
 
569613
                         ! Create tab
614
+                        write(0, '(A)') '[DEBUG RESTORE WS] Creating tab for file: ' // trim(full_path)
570615
                         call create_tab(editor, trim(full_path))
571616
                         tab_idx = editor%active_tab_index
572617
 
573618
                         ! Set orphan flag and load file
574619
                         if (allocated(editor%tabs) .and. tab_idx > 0) then
620
+                            write(0, '(A,I0)') '[DEBUG RESTORE WS] Tab created successfully, index: ', tab_idx
575621
                             editor%tabs(tab_idx)%is_orphan = is_orphan
576622
 
577623
                             call buffer_load_file(editor%tabs(tab_idx)%buffer, trim(full_path), load_status)
624
+                            write(0, '(A,I0)') '[DEBUG RESTORE WS] Buffer loaded, status: ', load_status
578625
 
579626
                             ! Set cursor and viewport in first pane
580627
                             if (allocated(editor%tabs(tab_idx)%panes) .and. size(editor%tabs(tab_idx)%panes) > 0) then
@@ -739,6 +786,29 @@ contains
739786
 
740787
         close(unit)
741788
 
789
+        write(0, '(A)') '[DEBUG RESTORE WS] Finished parsing JSON'
790
+
791
+        ! Clamp active_tab_index to valid range
792
+        if (allocated(editor%tabs)) then
793
+            write(0, '(A,I0)') '[DEBUG RESTORE WS] Tabs allocated, count: ', size(editor%tabs)
794
+            if (size(editor%tabs) > 0) then
795
+                if (editor%active_tab_index > size(editor%tabs)) then
796
+                    editor%active_tab_index = size(editor%tabs)
797
+                end if
798
+                if (editor%active_tab_index < 1) then
799
+                    editor%active_tab_index = 1
800
+                end if
801
+            else
802
+                write(0, '(A)') '[DEBUG RESTORE WS] No tabs in array!'
803
+                editor%active_tab_index = 0  ! No tabs
804
+            end if
805
+        else
806
+            write(0, '(A)') '[DEBUG RESTORE WS] Tabs NOT allocated!'
807
+            editor%active_tab_index = 0  ! No tabs
808
+        end if
809
+
810
+        write(0, '(A,I0)') '[DEBUG RESTORE WS] Final active_tab_index: ', editor%active_tab_index
811
+
742812
         ! Sync the active pane to editor state so status bar shows correct filename
743813
         if (allocated(editor%tabs) .and. editor%active_tab_index > 0) then
744814
             if (editor%active_tab_index <= size(editor%tabs)) then
@@ -746,6 +816,7 @@ contains
746816
                     if (editor%tabs(editor%active_tab_index)%active_pane_index > 0 .and. &
747817
                         editor%tabs(editor%active_tab_index)%active_pane_index <= &
748818
                         size(editor%tabs(editor%active_tab_index)%panes)) then
819
+                        write(0, '(A)') '[DEBUG RESTORE WS] Syncing active pane to editor'
749820
                         call sync_pane_to_editor(editor, editor%active_tab_index, &
750821
                                                 editor%tabs(editor%active_tab_index)%active_pane_index)
751822
                     end if
@@ -754,6 +825,7 @@ contains
754825
         end if
755826
 
756827
         success = .true.
828
+        write(0, '(A)') '[DEBUG RESTORE WS] workspace_restore_state completed successfully'
757829
     end subroutine workspace_restore_state
758830
 
759831
     !> Track workspace in recents (helper function)