fortrangoingonforty/facsimile / fd5ebde

Browse files

fix(lsp): code actions work on all tabs, fix Enter key on panels, syntax highlighting

- Fix Alt+. code actions for all tabs by sending LSP didOpen notification
when tabs are created via file tree, jump-to-definition, or workspace restore
- Fix Enter key double-processing on offcanvas panels (code actions, references,
symbols) - Enter was both executing action AND inserting newline in editor
- Fix Fortran syntax highlighting when switching between file types by auto-
updating the syntax highlighter when editor filename changes
- Enable CAP_CODE_ACTIONS for Fortran (fortls)
- Remove extensive debug logging from command_handler_module.f90 (550+ lines)
Authored by espadonne
SHA
fd5ebde64fd40b4c13a7100072f1ae1f35d53b78
Parents
1f6d571
Tree
c03e51f

4 changed files

StatusFile+-
M src/commands/command_handler_module.f90 128 551
M src/lsp/lsp_server_manager_module.f90 1 0
M src/terminal/renderer_module.f90 22 0
M src/workspace/workspace_module.f90 14 1
src/commands/command_handler_module.f90modified
@@ -27,7 +27,7 @@ module command_handler_module
27
                                          request_references, request_code_actions, request_document_symbols, &
27
                                          request_references, request_code_actions, request_document_symbols, &
28
                                          request_signature_help, request_formatting, request_rename, &
28
                                          request_signature_help, request_formatting, request_rename, &
29
                                          process_server_messages, filename_to_uri, &
29
                                          process_server_messages, filename_to_uri, &
30
-                                         get_server_with_capability, &
30
+                                         get_server_with_capability, notify_file_opened, &
31
                                          CAP_COMPLETION, CAP_DEFINITION, CAP_REFERENCES, CAP_RENAME, &
31
                                          CAP_COMPLETION, CAP_DEFINITION, CAP_REFERENCES, CAP_RENAME, &
32
                                          CAP_CODE_ACTIONS, CAP_FORMATTING, CAP_HOVER, CAP_DOCUMENT_SYMBOLS
32
                                          CAP_CODE_ACTIONS, CAP_FORMATTING, CAP_HOVER, CAP_DOCUMENT_SYMBOLS
33
     use rename_prompt_module, only: show_rename_prompt
33
     use rename_prompt_module, only: show_rename_prompt
@@ -188,13 +188,30 @@ contains
188
             end if
188
             end if
189
         end if
189
         end if
190
 
190
 
191
-        ! Debug: log all incoming keys
191
+        ! Route keys to code actions panel when visible
192
-        block
192
+        if (is_code_actions_panel_visible(editor%code_actions_panel)) then
193
-            integer :: dbg
193
+            if (code_actions_panel_handle_key(editor%code_actions_panel, trim(key_str))) then
194
-            open(newunit=dbg, file='/tmp/fac_keys_all.log', position='append', action='write')
194
+                ! For Enter, we need to apply the code action here since panel just returns handled=true
195
-            write(dbg, '(A,A,A)') 'key_str = [', trim(key_str), ']'
195
+                if (trim(key_str) == 'enter') then
196
-            close(dbg)
196
+                    call apply_selected_code_action(editor, buffer)
197
-        end block
197
+                end if
198
+                return
199
+            end if
200
+        end if
201
+
202
+        ! Route keys to references panel when visible
203
+        if (is_references_panel_visible(editor%references_panel)) then
204
+            if (references_panel_handle_key(editor%references_panel, trim(key_str))) then
205
+                return
206
+            end if
207
+        end if
208
+
209
+        ! Route keys to symbols panel when visible
210
+        if (is_symbols_panel_visible(editor%symbols_panel)) then
211
+            if (symbols_panel_handle_key(editor%symbols_panel, trim(key_str))) then
212
+                return
213
+            end if
214
+        end if
198
 
215
 
199
         select case(trim(key_str))
216
         select case(trim(key_str))
200
         ! File operations
217
         ! File operations
@@ -225,33 +242,7 @@ contains
225
                 return
242
                 return
226
             end if
243
             end if
227
 
244
 
228
-            ! If diagnostics panel is visible, hide it
245
+            ! Other panels (diagnostics, code_actions, references, symbols) are handled in early routing
229
-            if (is_diagnostics_panel_visible(editor%diagnostics_panel)) then
230
-                if (diagnostics_panel_handle_key(editor%diagnostics_panel, trim(key_str))) then
231
-                    return
232
-                end if
233
-            end if
234
-
235
-            ! If references panel is visible, hide it
236
-            if (is_references_panel_visible(editor%references_panel)) then
237
-                if (references_panel_handle_key(editor%references_panel, trim(key_str))) then
238
-                    return
239
-                end if
240
-            end if
241
-
242
-            ! If code actions menu is visible, hide it
243
-            if (is_code_actions_panel_visible(editor%code_actions_panel)) then
244
-                if (code_actions_panel_handle_key(editor%code_actions_panel, trim(key_str))) then
245
-                    return
246
-                end if
247
-            end if
248
-
249
-            ! If symbols panel is visible, hide it
250
-            if (is_symbols_panel_visible(editor%symbols_panel)) then
251
-                if (symbols_panel_handle_key(editor%symbols_panel, trim(key_str))) then
252
-                    return
253
-                end if
254
-            end if
255
 
246
 
256
             ! ESC - Clear selections and return to single cursor mode
247
             ! ESC - Clear selections and return to single cursor mode
257
             if (size(editor%cursors) > 1) then
248
             if (size(editor%cursors) > 1) then
@@ -357,33 +348,7 @@ contains
357
                 return
348
                 return
358
             end if
349
             end if
359
 
350
 
360
-            ! If diagnostics panel is visible, navigate it
351
+            ! Other panels (diagnostics, code_actions, references, symbols) are handled in early routing
361
-            if (is_diagnostics_panel_visible(editor%diagnostics_panel)) then
362
-                if (diagnostics_panel_handle_key(editor%diagnostics_panel, trim(key_str))) then
363
-                    return
364
-                end if
365
-            end if
366
-
367
-            ! If references panel is visible, navigate it
368
-            if (is_references_panel_visible(editor%references_panel)) then
369
-                if (references_panel_handle_key(editor%references_panel, trim(key_str))) then
370
-                    return
371
-                end if
372
-            end if
373
-
374
-            ! If code actions menu is visible, navigate it
375
-            if (is_code_actions_panel_visible(editor%code_actions_panel)) then
376
-                if (code_actions_panel_handle_key(editor%code_actions_panel, trim(key_str))) then
377
-                    return
378
-                end if
379
-            end if
380
-
381
-            ! If symbols panel is visible, navigate it
382
-            if (is_symbols_panel_visible(editor%symbols_panel)) then
383
-                if (symbols_panel_handle_key(editor%symbols_panel, trim(key_str))) then
384
-                    return
385
-                end if
386
-            end if
387
 
352
 
388
             if (size(editor%cursors) > 1) then
353
             if (size(editor%cursors) > 1) then
389
                 ! Move all cursors
354
                 ! Move all cursors
@@ -405,33 +370,7 @@ contains
405
                 return
370
                 return
406
             end if
371
             end if
407
 
372
 
408
-            ! If diagnostics panel is visible, navigate it
373
+            ! Other panels (diagnostics, code_actions, references, symbols) are handled in early routing
409
-            if (is_diagnostics_panel_visible(editor%diagnostics_panel)) then
410
-                if (diagnostics_panel_handle_key(editor%diagnostics_panel, trim(key_str))) then
411
-                    return
412
-                end if
413
-            end if
414
-
415
-            ! If references panel is visible, navigate it
416
-            if (is_references_panel_visible(editor%references_panel)) then
417
-                if (references_panel_handle_key(editor%references_panel, trim(key_str))) then
418
-                    return
419
-                end if
420
-            end if
421
-
422
-            ! If code actions menu is visible, navigate it
423
-            if (is_code_actions_panel_visible(editor%code_actions_panel)) then
424
-                if (code_actions_panel_handle_key(editor%code_actions_panel, trim(key_str))) then
425
-                    return
426
-                end if
427
-            end if
428
-
429
-            ! If symbols panel is visible, navigate it
430
-            if (is_symbols_panel_visible(editor%symbols_panel)) then
431
-                if (symbols_panel_handle_key(editor%symbols_panel, trim(key_str))) then
432
-                    return
433
-                end if
434
-            end if
435
 
374
 
436
             if (size(editor%cursors) > 1) then
375
             if (size(editor%cursors) > 1) then
437
                 ! Move all cursors
376
                 ! Move all cursors
@@ -743,71 +682,7 @@ contains
743
             is_edit_action = .true.
682
             is_edit_action = .true.
744
 
683
 
745
         case('enter')
684
         case('enter')
746
-            ! If code actions menu is visible, apply selected action
685
+            ! Code actions panel is handled in early routing above
747
-            if (is_code_actions_panel_visible(editor%code_actions_panel)) then
748
-                block
749
-                    use json_module, only: json_parse, json_value_t, json_get_object, &
750
-                                           json_has_key, json_stringify
751
-                    character(len=:), allocatable :: action_json, edit_json
752
-                    type(json_value_t) :: action_obj, edit_obj
753
-                    integer :: changes_applied
754
-
755
-                    if (get_selected_action(editor%code_actions_panel, action_json)) then
756
-                        ! Debug: log the action JSON
757
-                        block
758
-                            integer :: dbg
759
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
760
-                            write(dbg, '(A)') '=== APPLYING CODE ACTION ==='
761
-                            write(dbg, '(A)') 'action_json:'
762
-                            write(dbg, '(A)') action_json(1:min(1000, len(action_json)))
763
-                            close(dbg)
764
-                        end block
765
-
766
-                        ! Parse the action JSON to extract the edit
767
-                        action_obj = json_parse(action_json)
768
-
769
-                        if (json_has_key(action_obj, 'edit')) then
770
-                            ! Get the edit object and convert to string for apply_workspace_edit
771
-                            edit_obj = json_get_object(action_obj, 'edit')
772
-                            edit_json = json_stringify(edit_obj)
773
-
774
-                            ! Debug: log the edit JSON
775
-                            block
776
-                                integer :: dbg
777
-                                open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
778
-                                write(dbg, '(A)') 'edit_json:'
779
-                                write(dbg, '(A)') edit_json(1:min(1000, len(edit_json)))
780
-                                close(dbg)
781
-                            end block
782
-
783
-                            ! Apply the workspace edit
784
-                            call apply_workspace_edit(editor, edit_json, changes_applied)
785
-
786
-                            if (changes_applied > 0) then
787
-                                ! Sync modified tab buffer back to the buffer parameter
788
-                                if (editor%active_tab_index > 0 .and. &
789
-                                    editor%active_tab_index <= size(editor%tabs)) then
790
-                                    call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%buffer)
791
-                                end if
792
-                                ! Re-render screen to show the applied changes
793
-                                call render_screen(buffer, editor)
794
-                                call terminal_move_cursor(editor%screen_rows, 1)
795
-                                call terminal_write('Code action applied                        ')
796
-                            else
797
-                                call terminal_move_cursor(editor%screen_rows, 1)
798
-                                call terminal_write('No changes from code action                ')
799
-                            end if
800
-                        else
801
-                            call terminal_move_cursor(editor%screen_rows, 1)
802
-                            call terminal_write('Code action has no edit                    ')
803
-                        end if
804
-
805
-                        ! Hide menu after selection
806
-                        call hide_code_actions_panel(editor%code_actions_panel)
807
-                    end if
808
-                end block
809
-                return
810
-            end if
811
 
686
 
812
             ! If symbols panel is visible, jump to selected symbol
687
             ! If symbols panel is visible, jump to selected symbol
813
             if (is_symbols_panel_visible(editor%symbols_panel)) then
688
             if (is_symbols_panel_visible(editor%symbols_panel)) then
@@ -1269,13 +1144,6 @@ contains
1269
 
1144
 
1270
         case('f10', 'alt-.')
1145
         case('f10', 'alt-.')
1271
             ! Trigger code actions (F10 or Alt+.) - toggle behavior
1146
             ! Trigger code actions (F10 or Alt+.) - toggle behavior
1272
-            block
1273
-                integer :: dbg
1274
-                open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1275
-                write(dbg, '(A)') '=== F10/ALT-. KEY DETECTED ==='
1276
-                write(dbg, '(A,L1)') 'panel visible = ', is_code_actions_panel_visible(editor%code_actions_panel)
1277
-                close(dbg)
1278
-            end block
1279
             ! If panel is already visible, close it
1147
             ! If panel is already visible, close it
1280
             if (is_code_actions_panel_visible(editor%code_actions_panel)) then
1148
             if (is_code_actions_panel_visible(editor%code_actions_panel)) then
1281
                 call hide_code_actions_panel(editor%code_actions_panel)
1149
                 call hide_code_actions_panel(editor%code_actions_panel)
@@ -1286,7 +1154,7 @@ contains
1286
                     if (code_actions_server > 0) then
1154
                     if (code_actions_server > 0) then
1287
                         ! Request code actions for the current line
1155
                         ! Request code actions for the current line
1288
                         block
1156
                         block
1289
-                            integer :: request_id, lsp_line, dbg, dbg_i
1157
+                            integer :: request_id, lsp_line
1290
                             character(len=:), allocatable :: file_uri
1158
                             character(len=:), allocatable :: file_uri
1291
                             type(diagnostic_t), allocatable :: line_diags(:)
1159
                             type(diagnostic_t), allocatable :: line_diags(:)
1292
                             type(json_value_t) :: diags_json
1160
                             type(json_value_t) :: diags_json
@@ -1300,58 +1168,12 @@ contains
1300
                             ! This is critical for multi-LSP: Ruff should only see Ruff's diagnostics
1168
                             ! This is critical for multi-LSP: Ruff should only see Ruff's diagnostics
1301
                             file_uri = filename_to_uri(editor%tabs(editor%active_tab_index)%filename)
1169
                             file_uri = filename_to_uri(editor%tabs(editor%active_tab_index)%filename)
1302
 
1170
 
1303
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1304
-                            write(dbg, '(A,A)') 'file_uri = ', trim(file_uri)
1305
-                            write(dbg, '(A,I0)') 'cursor line (1-based) = ', editor%cursors(editor%active_cursor)%line
1306
-                            write(dbg, '(A,I0)') 'lsp_line (0-based) = ', lsp_line
1307
-                            write(dbg, '(A,I0)') 'code_actions_server = ', code_actions_server
1308
-                            close(dbg)
1309
-
1310
                             line_diags = get_diagnostics_for_line_by_server(editor%diagnostics, file_uri, &
1171
                             line_diags = get_diagnostics_for_line_by_server(editor%diagnostics, file_uri, &
1311
                                 editor%cursors(editor%active_cursor)%line, code_actions_server)
1172
                                 editor%cursors(editor%active_cursor)%line, code_actions_server)
1312
 
1173
 
1313
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1314
-                            write(dbg, '(A,I0)') 'Found diagnostics on line: ', size(line_diags)
1315
-                            close(dbg)
1316
-
1317
-                            ! Debug: check each diagnostic before calling diagnostics_to_json
1318
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1319
-                            write(dbg, '(A)') 'About to check diagnostics...'
1320
-                            do dbg_i = 1, size(line_diags)
1321
-                                write(dbg, '(A,I0)') 'Checking diagnostic ', dbg_i
1322
-                                write(dbg, '(A,L1)') '  message allocated: ', allocated(line_diags(dbg_i)%message)
1323
-                                write(dbg, '(A,L1)') '  source allocated: ', allocated(line_diags(dbg_i)%source)
1324
-                                write(dbg, '(A,L1)') '  code allocated: ', allocated(line_diags(dbg_i)%code)
1325
-                                write(dbg, '(A,L1)') '  data allocated: ', allocated(line_diags(dbg_i)%data)
1326
-                                if (allocated(line_diags(dbg_i)%message)) then
1327
-                                    write(dbg, '(A,I0)') '  message len: ', len(line_diags(dbg_i)%message)
1328
-                                end if
1329
-                            end do
1330
-                            write(dbg, '(A)') 'Diagnostics check complete'
1331
-                            close(dbg)
1332
-
1333
                             ! Convert diagnostics to JSON for request
1174
                             ! Convert diagnostics to JSON for request
1334
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1335
-                            write(dbg, '(A)') 'Calling diagnostics_to_json...'
1336
-                            close(dbg)
1337
-
1338
                             diags_json = diagnostics_to_json(line_diags)
1175
                             diags_json = diagnostics_to_json(line_diags)
1339
 
1176
 
1340
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1341
-                            write(dbg, '(A)') 'diagnostics_to_json returned'
1342
-                            close(dbg)
1343
-
1344
-                            ! Debug: log the diagnostics JSON
1345
-                            block
1346
-                                use json_module, only: json_stringify
1347
-                                character(len=:), allocatable :: diags_str
1348
-                                diags_str = json_stringify(diags_json)
1349
-                                open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1350
-                                write(dbg, '(A)') 'Diagnostics JSON being sent:'
1351
-                                write(dbg, '(A)') diags_str
1352
-                                close(dbg)
1353
-                            end block
1354
-
1355
                             ! Request code actions for entire line with diagnostics context
1177
                             ! Request code actions for entire line with diagnostics context
1356
                             request_id = request_code_actions(editor%lsp_manager, &
1178
                             request_id = request_code_actions(editor%lsp_manager, &
1357
                                 code_actions_server, &
1179
                                 code_actions_server, &
@@ -1360,10 +1182,6 @@ contains
1360
                             lsp_line, 999, &
1182
                             lsp_line, 999, &
1361
                             handle_code_actions_response_wrapper, &
1183
                             handle_code_actions_response_wrapper, &
1362
                             diags_json)
1184
                             diags_json)
1363
-
1364
-                            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
1365
-                            write(dbg, '(A,I0)') 'request_id = ', request_id
1366
-                            close(dbg)
1367
                             ! Panel will be shown when response arrives in handle_code_actions_response_impl
1185
                             ! Panel will be shown when response arrives in handle_code_actions_response_impl
1368
                         end block
1186
                         end block
1369
                     end if
1187
                     end if
@@ -1372,14 +1190,6 @@ contains
1372
 
1190
 
1373
         case('f12', 'ctrl-\\', 'alt-g')
1191
         case('f12', 'ctrl-\\', 'alt-g')
1374
             ! Go to definition (F12, Ctrl+\, or Alt+G)
1192
             ! Go to definition (F12, Ctrl+\, or Alt+G)
1375
-            block
1376
-                integer :: debug_unit, def_server
1377
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1378
-                write(debug_unit, '(A)') '>>> INSIDE F12/ALT-G HANDLER <<<'
1379
-                write(debug_unit, '(A,I0)') 'active_tab_index = ', editor%active_tab_index
1380
-                write(debug_unit, '(A,I0)') 'size(tabs) = ', size(editor%tabs)
1381
-                close(debug_unit)
1382
-            end block
1383
             call terminal_move_cursor(editor%screen_rows, 1)
1193
             call terminal_move_cursor(editor%screen_rows, 1)
1384
             block
1194
             block
1385
                 integer :: def_server
1195
                 integer :: def_server
@@ -1408,13 +1218,6 @@ contains
1408
                             editor%tabs(editor%active_tab_index)%filename, &
1218
                             editor%tabs(editor%active_tab_index)%filename, &
1409
                             lsp_line, lsp_char, handle_definition_response_wrapper)
1219
                             lsp_line, lsp_char, handle_definition_response_wrapper)
1410
 
1220
 
1411
-                        block
1412
-                            integer :: debug_unit
1413
-                            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1414
-                            write(debug_unit, '(A,I0)') 'request_definition returned request_id = ', request_id
1415
-                            close(debug_unit)
1416
-                        end block
1417
-
1418
                         if (request_id > 0) then
1221
                         if (request_id > 0) then
1419
                             ! Response will be handled by callback
1222
                             ! Response will be handled by callback
1420
                             call terminal_write('Searching for definition...                ')
1223
                             call terminal_write('Searching for definition...                ')
@@ -1429,12 +1232,6 @@ contains
1429
 
1232
 
1430
         case('shift-f12', 'alt-r')
1233
         case('shift-f12', 'alt-r')
1431
             ! Find all references (Shift+F12 or Alt+R)
1234
             ! Find all references (Shift+F12 or Alt+R)
1432
-            block
1433
-                integer :: debug_unit
1434
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1435
-                write(debug_unit, '(A)') '>>> INSIDE SHIFT-F12/ALT-R HANDLER <<<'
1436
-                close(debug_unit)
1437
-            end block
1438
             block
1235
             block
1439
                 integer :: refs_server
1236
                 integer :: refs_server
1440
                 refs_server = get_lsp_server_for_cap(editor, CAP_REFERENCES)
1237
                 refs_server = get_lsp_server_for_cap(editor, CAP_REFERENCES)
@@ -1540,13 +1337,6 @@ contains
1540
 
1337
 
1541
         case('f2')
1338
         case('f2')
1542
             ! Rename symbol
1339
             ! Rename symbol
1543
-            block
1544
-                integer :: debug_unit
1545
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1546
-                write(debug_unit, '(A)') '>>> F2 KEY DETECTED <<<'
1547
-                write(debug_unit, '(A,I0)') 'active_tab_index: ', editor%active_tab_index
1548
-                close(debug_unit)
1549
-            end block
1550
             block
1340
             block
1551
                 integer :: rename_server
1341
                 integer :: rename_server
1552
                 rename_server = get_lsp_server_for_cap(editor, CAP_RENAME)
1342
                 rename_server = get_lsp_server_for_cap(editor, CAP_RENAME)
@@ -1572,17 +1362,6 @@ contains
1572
                                 lsp_line = editor%cursors(editor%active_cursor)%line - 1
1362
                                 lsp_line = editor%cursors(editor%active_cursor)%line - 1
1573
                                 lsp_char = editor%cursors(editor%active_cursor)%column - 1
1363
                                 lsp_char = editor%cursors(editor%active_cursor)%column - 1
1574
 
1364
 
1575
-                                ! Debug logging
1576
-                                block
1577
-                                    integer :: debug_unit
1578
-                                    open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1579
-                                    write(debug_unit, '(A)') '>>> SENDING RENAME REQUEST <<<'
1580
-                                    write(debug_unit, '(A)') 'Old name: ' // trim(old_name)
1581
-                                    write(debug_unit, '(A)') 'New name: ' // trim(new_name)
1582
-                                    write(debug_unit, '(A,I0,A,I0)') 'Position: line=', lsp_line, ' char=', lsp_char
1583
-                                    close(debug_unit)
1584
-                                end block
1585
-
1586
                                 ! Save editor state for callback
1365
                                 ! Save editor state for callback
1587
                                 saved_editor_for_callback => editor
1366
                                 saved_editor_for_callback => editor
1588
 
1367
 
@@ -1597,26 +1376,16 @@ contains
1597
 
1376
 
1598
                                     ! Poll for LSP response and render immediately when received
1377
                                     ! Poll for LSP response and render immediately when received
1599
                                     block
1378
                                     block
1600
-                                        integer :: poll_count, max_polls, pane_idx, debug_unit
1379
+                                        integer :: poll_count, max_polls, pane_idx
1601
                                         integer(8) :: start_time, end_time, count_rate, target_time
1380
                                         integer(8) :: start_time, end_time, count_rate, target_time
1602
                                         max_polls = 100  ! Poll up to 100 times (1 second total)
1381
                                         max_polls = 100  ! Poll up to 100 times (1 second total)
1603
 
1382
 
1604
-                                        ! Debug: Start polling
1605
-                                        open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1606
-                                        write(debug_unit, '(A)') '>>> STARTING RENAME POLLING <<<'
1607
-                                        close(debug_unit)
1608
-
1609
                                         do poll_count = 1, max_polls
1383
                                         do poll_count = 1, max_polls
1610
                                             ! Process any LSP messages
1384
                                             ! Process any LSP messages
1611
                                             call process_server_messages(editor%lsp_manager)
1385
                                             call process_server_messages(editor%lsp_manager)
1612
 
1386
 
1613
                                             ! Check if rename response modified the buffer
1387
                                             ! Check if rename response modified the buffer
1614
                                             if (g_lsp_modified_buffer) then
1388
                                             if (g_lsp_modified_buffer) then
1615
-                                                ! Debug: Flag detected!
1616
-                                                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1617
-                                                write(debug_unit, '(A,I0,A)') '>>> FLAG DETECTED at poll ', poll_count, ' - RENDERING NOW <<<'
1618
-                                                close(debug_unit)
1619
-
1620
                                                 ! Sync buffer from tab (LSP modified tab buffer)
1389
                                                 ! Sync buffer from tab (LSP modified tab buffer)
1621
                                                 call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%buffer)
1390
                                                 call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%buffer)
1622
 
1391
 
@@ -1649,13 +1418,6 @@ contains
1649
                                                 if (end_time >= target_time) exit
1418
                                                 if (end_time >= target_time) exit
1650
                                             end do
1419
                                             end do
1651
                                         end do
1420
                                         end do
1652
-
1653
-                                        ! Debug: Polling finished without detecting flag
1654
-                                        if (.not. g_lsp_modified_buffer) then
1655
-                                            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1656
-                                            write(debug_unit, '(A)') '>>> POLLING TIMEOUT - FLAG NEVER SET <<<'
1657
-                                            close(debug_unit)
1658
-                                        end if
1659
                                     end block
1421
                                     end block
1660
                                 end if
1422
                                 end if
1661
 
1423
 
@@ -1704,12 +1466,6 @@ contains
1704
 
1466
 
1705
         case('f4', 'alt-o')
1467
         case('f4', 'alt-o')
1706
             ! Document symbols outline (F4 or Alt+O)
1468
             ! Document symbols outline (F4 or Alt+O)
1707
-            block
1708
-                integer :: debug_unit
1709
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1710
-                write(debug_unit, '(A)') '>>> INSIDE F4/ALT-O HANDLER <<<'
1711
-                close(debug_unit)
1712
-            end block
1713
             block
1469
             block
1714
                 integer :: symbols_server
1470
                 integer :: symbols_server
1715
                 symbols_server = get_lsp_server_for_cap(editor, CAP_DOCUMENT_SYMBOLS)
1471
                 symbols_server = get_lsp_server_for_cap(editor, CAP_DOCUMENT_SYMBOLS)
@@ -1900,29 +1656,9 @@ contains
1900
 
1656
 
1901
         case('f8', 'alt-e')
1657
         case('f8', 'alt-e')
1902
             ! Toggle diagnostics panel (F8 or Alt+E for errors)
1658
             ! Toggle diagnostics panel (F8 or Alt+E for errors)
1903
-            block
1904
-                integer :: debug_unit
1905
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1906
-                write(debug_unit, '(A)') '>>> INSIDE F8/ALT-E HANDLER <<<'
1907
-                write(debug_unit, '(A)') 'Calling toggle_diagnostics_panel...'
1908
-                close(debug_unit)
1909
-            end block
1910
             call toggle_panel(editor%diagnostics_panel)
1659
             call toggle_panel(editor%diagnostics_panel)
1911
-            block
1912
-                integer :: debug_unit
1913
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1914
-                write(debug_unit, '(A)') 'toggle_diagnostics_panel returned'
1915
-                write(debug_unit, '(A)') '>>> ABOUT TO CALL render_screen <<<'
1916
-                close(debug_unit)
1917
-            end block
1918
             ! Re-render screen to show/hide the panel
1660
             ! Re-render screen to show/hide the panel
1919
             call render_screen(buffer, editor)
1661
             call render_screen(buffer, editor)
1920
-            block
1921
-                integer :: debug_unit
1922
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
1923
-                write(debug_unit, '(A)') '>>> render_screen COMPLETED <<<'
1924
-                close(debug_unit)
1925
-            end block
1926
 
1662
 
1927
         case('alt-c')
1663
         case('alt-c')
1928
             ! Toggle case sensitivity for match mode (ctrl-d)
1664
             ! Toggle case sensitivity for match mode (ctrl-d)
@@ -1999,12 +1735,6 @@ contains
1999
             end if
1735
             end if
2000
 
1736
 
2001
         case default
1737
         case default
2002
-            ! DEBUG: Show unhandled function keys
2003
-            if (index(key_str, 'f') == 1 .or. index(key_str, 'shift-f') == 1) then
2004
-                call terminal_move_cursor(editor%screen_rows, 1)
2005
-                call terminal_write('[DEBUG] Unhandled key: ' // trim(key_str) // '                ')
2006
-            end if
2007
-
2008
             ! Check for mouse events
1738
             ! Check for mouse events
2009
             if (index(key_str, 'mouse-') == 1) then
1739
             if (index(key_str, 'mouse-') == 1) then
2010
                 call handle_mouse_event_action(key_str, editor, buffer)
1740
                 call handle_mouse_event_action(key_str, editor, buffer)
@@ -5238,6 +4968,18 @@ contains
5238
                 allocate(character(len=len_trim(full_path)) :: editor%filename)
4968
                 allocate(character(len=len_trim(full_path)) :: editor%filename)
5239
                 editor%filename = full_path
4969
                 editor%filename = full_path
5240
 
4970
 
4971
+                ! Send LSP didOpen notification to ALL active servers
4972
+                if (editor%tabs(editor%active_tab_index)%num_lsp_servers > 0) then
4973
+                    block
4974
+                        integer :: srv_i
4975
+                        do srv_i = 1, editor%tabs(editor%active_tab_index)%num_lsp_servers
4976
+                            call notify_file_opened(editor%lsp_manager, &
4977
+                                editor%tabs(editor%active_tab_index)%lsp_server_indices(srv_i), &
4978
+                                full_path, buffer_to_string(editor%tabs(editor%active_tab_index)%buffer))
4979
+                        end do
4980
+                    end block
4981
+                end if
4982
+
5241
                 ! Reset cursor to top of file
4983
                 ! Reset cursor to top of file
5242
                 editor%cursors(editor%active_cursor)%line = 1
4984
                 editor%cursors(editor%active_cursor)%line = 1
5243
                 editor%cursors(editor%active_cursor)%column = 1
4985
                 editor%cursors(editor%active_cursor)%column = 1
@@ -5989,13 +5731,6 @@ contains
5989
         integer, intent(in) :: request_id
5731
         integer, intent(in) :: request_id
5990
         type(lsp_message_t), intent(in) :: response
5732
         type(lsp_message_t), intent(in) :: response
5991
 
5733
 
5992
-        block
5993
-            integer :: debug_unit
5994
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
5995
-            write(debug_unit, '(A)') '>>> REFERENCES RESPONSE RECEIVED <<<'
5996
-            close(debug_unit)
5997
-        end block
5998
-
5999
         ! Call the actual handler with saved editor state
5734
         ! Call the actual handler with saved editor state
6000
         if (associated(saved_editor_for_callback)) then
5735
         if (associated(saved_editor_for_callback)) then
6001
             call handle_references_response_impl(saved_editor_for_callback, response)
5736
             call handle_references_response_impl(saved_editor_for_callback, response)
@@ -6122,27 +5857,14 @@ contains
6122
         type(lsp_message_t), intent(in) :: response
5857
         type(lsp_message_t), intent(in) :: response
6123
         type(json_value_t) :: result_array, action_obj, edit_obj
5858
         type(json_value_t) :: result_array, action_obj, edit_obj
6124
         type(code_action_t), allocatable :: actions(:)
5859
         type(code_action_t), allocatable :: actions(:)
6125
-        integer :: num_actions, i, dbg
5860
+        integer :: num_actions, i
6126
-        character(len=:), allocatable :: title, kind, action_json, result_str
5861
+        character(len=:), allocatable :: title, kind, action_json
6127
         logical :: is_preferred
5862
         logical :: is_preferred
6128
 
5863
 
6129
-        ! Debug: Log that callback was invoked
6130
-        open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
6131
-        write(dbg, '(A)') '=== CODE ACTIONS RESPONSE RECEIVED ==='
6132
-        write(dbg, '(A,I0)') 'response%id = ', response%id
6133
-        result_str = json_stringify(response%result)
6134
-        write(dbg, '(A)') 'response%result (first 500 chars):'
6135
-        write(dbg, '(A)') result_str(1:min(500, len(result_str)))
6136
-        close(dbg)
6137
-
6138
         ! The result is directly in response%result for LSP responses
5864
         ! The result is directly in response%result for LSP responses
6139
         result_array = response%result
5865
         result_array = response%result
6140
         num_actions = json_array_size(result_array)
5866
         num_actions = json_array_size(result_array)
6141
 
5867
 
6142
-        open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
6143
-        write(dbg, '(A,I0)') 'num_actions from json_array_size = ', num_actions
6144
-        close(dbg)
6145
-
6146
         if (num_actions == 0) then
5868
         if (num_actions == 0) then
6147
             ! No actions available - don't show panel
5869
             ! No actions available - don't show panel
6148
             return
5870
             return
@@ -6206,13 +5928,6 @@ contains
6206
         integer, intent(in) :: request_id
5928
         integer, intent(in) :: request_id
6207
         type(lsp_message_t), intent(in) :: response
5929
         type(lsp_message_t), intent(in) :: response
6208
 
5930
 
6209
-        block
6210
-            integer :: debug_unit
6211
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6212
-            write(debug_unit, '(A)') '>>> SYMBOLS RESPONSE RECEIVED <<<'
6213
-            close(debug_unit)
6214
-        end block
6215
-
6216
         ! Call the actual handler with saved editor state
5931
         ! Call the actual handler with saved editor state
6217
         if (associated(saved_editor_for_callback)) then
5932
         if (associated(saved_editor_for_callback)) then
6218
             call handle_symbols_response_impl(saved_editor_for_callback, response)
5933
             call handle_symbols_response_impl(saved_editor_for_callback, response)
@@ -6238,26 +5953,6 @@ contains
6238
         result_array = response%result
5953
         result_array = response%result
6239
         num_symbols = json_array_size(result_array)
5954
         num_symbols = json_array_size(result_array)
6240
 
5955
 
6241
-        block
6242
-            integer :: debug_unit
6243
-            character(len=:), allocatable :: result_str, error_str
6244
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6245
-            write(debug_unit, '(A)') '>>> SYMBOLS RESPONSE RECEIVED <<<'
6246
-
6247
-            ! Check for error
6248
-            if (json_has_key(response%error, "message")) then
6249
-                error_str = json_get_string(response%error, "message")
6250
-                write(debug_unit, '(A)') 'ERROR: ' // trim(error_str)
6251
-            end if
6252
-
6253
-            write(debug_unit, '(A,I0)') 'num_symbols = ', num_symbols
6254
-            result_str = json_stringify(response%result)
6255
-            if (allocated(result_str)) then
6256
-                write(debug_unit, '(A)') 'Result JSON: ' // result_str(1:min(500,len(result_str)))
6257
-            end if
6258
-            close(debug_unit)
6259
-        end block
6260
-
6261
         if (num_symbols == 0) then
5956
         if (num_symbols == 0) then
6262
             call clear_symbols(editor%symbols_panel)
5957
             call clear_symbols(editor%symbols_panel)
6263
             call terminal_move_cursor(editor%screen_rows, 1)
5958
             call terminal_move_cursor(editor%screen_rows, 1)
@@ -6406,24 +6101,12 @@ contains
6406
 
6101
 
6407
         character(len=:), allocatable :: result_str
6102
         character(len=:), allocatable :: result_str
6408
         integer :: changes_applied
6103
         integer :: changes_applied
6409
-        integer :: debug_unit
6410
 
6104
 
6411
         if (.not. associated(saved_editor_for_callback)) return
6105
         if (.not. associated(saved_editor_for_callback)) return
6412
 
6106
 
6413
         ! Convert result to string for apply_workspace_edit
6107
         ! Convert result to string for apply_workspace_edit
6414
         result_str = json_stringify(response%result)
6108
         result_str = json_stringify(response%result)
6415
 
6109
 
6416
-        ! Debug logging
6417
-        open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6418
-        write(debug_unit, '(A)') '>>> RENAME RESPONSE <<<'
6419
-        if (allocated(result_str)) then
6420
-            write(debug_unit, '(A,I0)') 'Result length: ', len(result_str)
6421
-            write(debug_unit, '(A)') 'Result (first 500 chars): ' // result_str(1:min(500, len(result_str)))
6422
-        else
6423
-            write(debug_unit, '(A)') 'Result: NOT ALLOCATED'
6424
-        end if
6425
-        close(debug_unit)
6426
-
6427
         if (.not. allocated(result_str) .or. result_str == 'null' .or. len_trim(result_str) == 0) then
6110
         if (.not. allocated(result_str) .or. result_str == 'null' .or. len_trim(result_str) == 0) then
6428
             call terminal_move_cursor(saved_editor_for_callback%screen_rows, 1)
6111
             call terminal_move_cursor(saved_editor_for_callback%screen_rows, 1)
6429
             call terminal_write('Rename failed or not supported                ')
6112
             call terminal_write('Rename failed or not supported                ')
@@ -6434,21 +6117,6 @@ contains
6434
         ! Apply workspace edit
6117
         ! Apply workspace edit
6435
         call apply_workspace_edit(saved_editor_for_callback, result_str, changes_applied)
6118
         call apply_workspace_edit(saved_editor_for_callback, result_str, changes_applied)
6436
 
6119
 
6437
-        ! Debug: verify edits were applied
6438
-        if (changes_applied > 0) then
6439
-            block
6440
-                integer :: tab_idx, debug_unit
6441
-                tab_idx = saved_editor_for_callback%active_tab_index
6442
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6443
-                write(debug_unit, '(A)') '>>> AFTER ALL EDITS APPLIED <<<'
6444
-                write(debug_unit, '(A,I0)') 'Active tab index: ', tab_idx
6445
-                write(debug_unit, '(A,I0)') 'Changes applied: ', changes_applied
6446
-                write(debug_unit, '(A,L1)') 'Buffer modified flag: ', saved_editor_for_callback%tabs(tab_idx)%buffer%modified
6447
-                write(debug_unit, '(A)') 'NOTE: Screen will be rendered by main loop'
6448
-                close(debug_unit)
6449
-            end block
6450
-        end if
6451
-
6452
         call terminal_move_cursor(saved_editor_for_callback%screen_rows, 1)
6120
         call terminal_move_cursor(saved_editor_for_callback%screen_rows, 1)
6453
         if (changes_applied > 0) then
6121
         if (changes_applied > 0) then
6454
             block
6122
             block
@@ -6533,6 +6201,52 @@ contains
6533
         end if
6201
         end if
6534
     end subroutine handle_formatting_response_wrapper
6202
     end subroutine handle_formatting_response_wrapper
6535
 
6203
 
6204
+    ! Apply the selected code action from the panel
6205
+    subroutine apply_selected_code_action(editor, buffer)
6206
+        use json_module, only: json_parse, json_value_t, json_get_object, &
6207
+                               json_has_key, json_stringify
6208
+        type(editor_state_t), intent(inout) :: editor
6209
+        type(buffer_t), intent(inout) :: buffer
6210
+        character(len=:), allocatable :: action_json, edit_json
6211
+        type(json_value_t) :: action_obj, edit_obj
6212
+        integer :: changes_applied
6213
+
6214
+        if (get_selected_action(editor%code_actions_panel, action_json)) then
6215
+            ! Parse the action JSON to extract the edit
6216
+            action_obj = json_parse(action_json)
6217
+
6218
+            if (json_has_key(action_obj, 'edit')) then
6219
+                ! Get the edit object and convert to string for apply_workspace_edit
6220
+                edit_obj = json_get_object(action_obj, 'edit')
6221
+                edit_json = json_stringify(edit_obj)
6222
+
6223
+                ! Apply the workspace edit
6224
+                call apply_workspace_edit(editor, edit_json, changes_applied)
6225
+
6226
+                if (changes_applied > 0) then
6227
+                    ! Sync modified tab buffer back to the buffer parameter
6228
+                    if (editor%active_tab_index > 0 .and. &
6229
+                        editor%active_tab_index <= size(editor%tabs)) then
6230
+                        call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%buffer)
6231
+                    end if
6232
+                    ! Re-render screen to show the applied changes
6233
+                    call render_screen(buffer, editor)
6234
+                    call terminal_move_cursor(editor%screen_rows, 1)
6235
+                    call terminal_write('Code action applied                        ')
6236
+                else
6237
+                    call terminal_move_cursor(editor%screen_rows, 1)
6238
+                    call terminal_write('No changes from code action                ')
6239
+                end if
6240
+            else
6241
+                call terminal_move_cursor(editor%screen_rows, 1)
6242
+                call terminal_write('Code action has no edit                    ')
6243
+            end if
6244
+
6245
+            ! Hide menu after selection
6246
+            call hide_code_actions_panel(editor%code_actions_panel)
6247
+        end if
6248
+    end subroutine apply_selected_code_action
6249
+
6536
     ! Apply a workspace edit from LSP
6250
     ! Apply a workspace edit from LSP
6537
     subroutine apply_workspace_edit(editor, edit_json, changes_applied)
6251
     subroutine apply_workspace_edit(editor, edit_json, changes_applied)
6538
         use json_module, only: json_parse, json_value_t, json_get_array, json_array_size, &
6252
         use json_module, only: json_parse, json_value_t, json_get_array, json_array_size, &
@@ -6549,15 +6263,6 @@ contains
6549
 
6263
 
6550
         changes_applied = 0
6264
         changes_applied = 0
6551
 
6265
 
6552
-        ! Debug logging
6553
-        block
6554
-            integer :: dbg
6555
-            open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
6556
-            write(dbg, '(A)') '=== apply_workspace_edit called ==='
6557
-            write(dbg, '(A,I0)') 'edit_json length = ', len(edit_json)
6558
-            close(dbg)
6559
-        end block
6560
-
6561
         ! Parse the edit JSON
6266
         ! Parse the edit JSON
6562
         edit_obj = json_parse(edit_json)
6267
         edit_obj = json_parse(edit_json)
6563
 
6268
 
@@ -6566,13 +6271,6 @@ contains
6566
             doc_changes_arr = json_get_array(edit_obj, 'documentChanges')
6271
             doc_changes_arr = json_get_array(edit_obj, 'documentChanges')
6567
             num_files = json_array_size(doc_changes_arr)
6272
             num_files = json_array_size(doc_changes_arr)
6568
 
6273
 
6569
-            block
6570
-                integer :: dbg
6571
-                open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
6572
-                write(dbg, '(A,I0)') 'Found documentChanges, num_files = ', num_files
6573
-                close(dbg)
6574
-            end block
6575
-
6576
             do i = 0, num_files - 1  ! 0-based index
6274
             do i = 0, num_files - 1  ! 0-based index
6577
                 file_change_obj = json_get_array_element(doc_changes_arr, i)
6275
                 file_change_obj = json_get_array_element(doc_changes_arr, i)
6578
 
6276
 
@@ -6580,33 +6278,11 @@ contains
6580
                 if (json_has_key(file_change_obj, 'textDocument')) then
6278
                 if (json_has_key(file_change_obj, 'textDocument')) then
6581
                     text_doc_obj = json_get_object(file_change_obj, 'textDocument')
6279
                     text_doc_obj = json_get_object(file_change_obj, 'textDocument')
6582
                     uri = json_get_string(text_doc_obj, 'uri')
6280
                     uri = json_get_string(text_doc_obj, 'uri')
6583
-
6584
-                    block
6585
-                        integer :: dbg
6586
-                        open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
6587
-                        write(dbg, '(A,I0)') 'Processing file ', i
6588
-                        if (allocated(uri)) then
6589
-                            write(dbg, '(A,A)') 'uri = ', trim(uri)
6590
-                        else
6591
-                            write(dbg, '(A)') 'uri NOT allocated'
6592
-                        end if
6593
-                        write(dbg, '(A,L1)') 'has edits key = ', json_has_key(file_change_obj, 'edits')
6594
-                        close(dbg)
6595
-                    end block
6596
                 end if
6281
                 end if
6597
 
6282
 
6598
                 ! Get edits array
6283
                 ! Get edits array
6599
                 if (json_has_key(file_change_obj, 'edits') .and. allocated(uri)) then
6284
                 if (json_has_key(file_change_obj, 'edits') .and. allocated(uri)) then
6600
                     edits_arr = json_get_array(file_change_obj, 'edits')
6285
                     edits_arr = json_get_array(file_change_obj, 'edits')
6601
-
6602
-                    block
6603
-                        integer :: dbg, num_edits
6604
-                        num_edits = json_array_size(edits_arr)
6605
-                        open(newunit=dbg, file='/tmp/fac_code_actions.log', position='append', action='write')
6606
-                        write(dbg, '(A,I0)') 'num_edits = ', num_edits
6607
-                        close(dbg)
6608
-                    end block
6609
-
6610
                     call apply_file_edits_obj(editor, uri, edits_arr, changes_applied)
6286
                     call apply_file_edits_obj(editor, uri, edits_arr, changes_applied)
6611
                     deallocate(uri)
6287
                     deallocate(uri)
6612
                 end if
6288
                 end if
@@ -6656,38 +6332,10 @@ contains
6656
             filename = uri
6332
             filename = uri
6657
         end if
6333
         end if
6658
 
6334
 
6659
-        ! Debug logging
6660
-        block
6661
-            integer :: debug_unit
6662
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6663
-            write(debug_unit, '(A)') '>>> APPLY_FILE_EDITS_OBJ <<<'
6664
-            write(debug_unit, '(A)') 'URI: ' // trim(uri)
6665
-            write(debug_unit, '(A)') 'Extracted filename: ' // trim(filename)
6666
-            write(debug_unit, '(A,I0)') 'Number of tabs: ', size(editor%tabs)
6667
-            close(debug_unit)
6668
-        end block
6669
-
6670
         ! Find the tab with this file
6335
         ! Find the tab with this file
6671
         tab_idx = 0
6336
         tab_idx = 0
6672
         do j = 1, size(editor%tabs)
6337
         do j = 1, size(editor%tabs)
6673
             if (allocated(editor%tabs(j)%filename)) then
6338
             if (allocated(editor%tabs(j)%filename)) then
6674
-                ! Debug logging for each tab
6675
-                block
6676
-                    integer :: debug_unit
6677
-                    logical :: exact_match, ends_with_match
6678
-                    open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6679
-                    write(debug_unit, '(A,I0,A)') 'Tab ', j, ': ' // trim(editor%tabs(j)%filename)
6680
-                    exact_match = trim(editor%tabs(j)%filename) == trim(filename)
6681
-                    ! Check if filename ends with tab filename (for absolute vs relative path matching)
6682
-                    ends_with_match = .false.
6683
-                    if (len(filename) >= len(editor%tabs(j)%filename)) then
6684
-                        ends_with_match = filename(len(filename)-len(editor%tabs(j)%filename)+1:) == editor%tabs(j)%filename
6685
-                    end if
6686
-                    write(debug_unit, '(A,L1)') '  Exact match: ', exact_match
6687
-                    write(debug_unit, '(A,L1)') '  Ends-with match: ', ends_with_match
6688
-                    close(debug_unit)
6689
-                end block
6690
-
6691
                 ! Try exact match first, then check if the absolute path ends with the relative path
6339
                 ! Try exact match first, then check if the absolute path ends with the relative path
6692
                 if (trim(editor%tabs(j)%filename) == trim(filename)) then
6340
                 if (trim(editor%tabs(j)%filename) == trim(filename)) then
6693
                     tab_idx = j
6341
                     tab_idx = j
@@ -6702,13 +6350,6 @@ contains
6702
             end if
6350
             end if
6703
         end do
6351
         end do
6704
 
6352
 
6705
-        block
6706
-            integer :: debug_unit
6707
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6708
-            write(debug_unit, '(A,I0)') 'Found tab_idx: ', tab_idx
6709
-            close(debug_unit)
6710
-        end block
6711
-
6712
         if (tab_idx == 0) then
6353
         if (tab_idx == 0) then
6713
             ! File not open - skip for now
6354
             ! File not open - skip for now
6714
             if (allocated(filename)) deallocate(filename)
6355
             if (allocated(filename)) deallocate(filename)
@@ -6758,75 +6399,22 @@ contains
6758
 
6399
 
6759
         integer :: start_pos, end_pos, delete_count
6400
         integer :: start_pos, end_pos, delete_count
6760
 
6401
 
6761
-        ! Debug logging
6762
-        block
6763
-            integer :: debug_unit
6764
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6765
-            write(debug_unit, '(A)') '>>> APPLY_SINGLE_EDIT <<<'
6766
-            write(debug_unit, '(A,I0,A,I0)') 'Start: line=', start_line, ', char=', start_char
6767
-            write(debug_unit, '(A,I0,A,I0)') 'End:   line=', end_line, ', char=', end_char
6768
-            write(debug_unit, '(A)') 'New text: ' // trim(new_text)
6769
-            close(debug_unit)
6770
-        end block
6771
-
6772
         ! Calculate buffer positions
6402
         ! Calculate buffer positions
6773
         start_pos = get_buffer_position(buffer, start_line, start_char)
6403
         start_pos = get_buffer_position(buffer, start_line, start_char)
6774
         end_pos = get_buffer_position(buffer, end_line, end_char)
6404
         end_pos = get_buffer_position(buffer, end_line, end_char)
6775
 
6405
 
6776
-        block
6406
+        if (start_pos <= 0 .or. end_pos <= 0) return
6777
-            integer :: debug_unit
6778
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6779
-            write(debug_unit, '(A,I0)') 'Calculated start_pos: ', start_pos
6780
-            write(debug_unit, '(A,I0)') 'Calculated end_pos: ', end_pos
6781
-            close(debug_unit)
6782
-        end block
6783
-
6784
-        if (start_pos <= 0 .or. end_pos <= 0) then
6785
-            block
6786
-                integer :: debug_unit
6787
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6788
-                write(debug_unit, '(A)') 'EARLY RETURN: start_pos or end_pos <= 0'
6789
-                close(debug_unit)
6790
-            end block
6791
-            return
6792
-        end if
6793
 
6407
 
6794
         ! Delete the old text
6408
         ! Delete the old text
6795
         delete_count = end_pos - start_pos
6409
         delete_count = end_pos - start_pos
6796
-        block
6797
-            integer :: debug_unit
6798
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6799
-            write(debug_unit, '(A,I0)') 'Delete count: ', delete_count
6800
-            close(debug_unit)
6801
-        end block
6802
-
6803
         if (delete_count > 0) then
6410
         if (delete_count > 0) then
6804
-            block
6805
-                integer :: debug_unit
6806
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6807
-                write(debug_unit, '(A,I0,A,I0)') 'Calling buffer_delete(pos=', start_pos, ', count=', delete_count, ')'
6808
-                close(debug_unit)
6809
-            end block
6810
             call buffer_delete(buffer, start_pos, delete_count)
6411
             call buffer_delete(buffer, start_pos, delete_count)
6811
         end if
6412
         end if
6812
 
6413
 
6813
         ! Insert the new text
6414
         ! Insert the new text
6814
         if (len(new_text) > 0) then
6415
         if (len(new_text) > 0) then
6815
-            block
6816
-                integer :: debug_unit
6817
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6818
-                write(debug_unit, '(A,I0,A)') 'Calling buffer_insert(pos=', start_pos, ', text="' // trim(new_text) // '")'
6819
-                close(debug_unit)
6820
-            end block
6821
             call buffer_insert(buffer, start_pos, new_text)
6416
             call buffer_insert(buffer, start_pos, new_text)
6822
         end if
6417
         end if
6823
-
6824
-        block
6825
-            integer :: debug_unit
6826
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
6827
-            write(debug_unit, '(A)') 'apply_single_edit COMPLETED'
6828
-            close(debug_unit)
6829
-        end block
6830
     end subroutine apply_single_edit
6418
     end subroutine apply_single_edit
6831
 
6419
 
6832
     ! Execute a command from the command palette
6420
     ! Execute a command from the command palette
@@ -7048,39 +6636,9 @@ contains
7048
         integer :: target_line, target_col, i, num_locations
6636
         integer :: target_line, target_col, i, num_locations
7049
         logical :: found_file
6637
         logical :: found_file
7050
 
6638
 
7051
-        ! Log response for debugging
7052
-        block
7053
-            integer :: debug_unit
7054
-            character(len=:), allocatable :: result_str, error_str
7055
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
7056
-            write(debug_unit, '(A)') '>>> DEFINITION RESPONSE RECEIVED <<<'
7057
-
7058
-            ! Check for error
7059
-            if (json_has_key(response%error, "message")) then
7060
-                error_str = json_get_string(response%error, "message")
7061
-                write(debug_unit, '(A)') 'ERROR: ' // trim(error_str)
7062
-            end if
7063
-
7064
-            result_str = json_stringify(response%result)
7065
-            if (allocated(result_str)) then
7066
-                write(debug_unit, '(A)') 'Result JSON: ' // result_str(1:min(500,len(result_str)))
7067
-            else
7068
-                write(debug_unit, '(A)') 'Result JSON: (not allocated)'
7069
-            end if
7070
-            close(debug_unit)
7071
-        end block
7072
-
7073
         ! Try to treat result as array first
6639
         ! Try to treat result as array first
7074
         num_locations = json_array_size(response%result)
6640
         num_locations = json_array_size(response%result)
7075
 
6641
 
7076
-        block
7077
-            integer :: debug_unit
7078
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
7079
-            write(debug_unit, '(A,I0)') 'num_locations = ', num_locations
7080
-            write(debug_unit, '(A,L1)') 'has uri key = ', json_has_key(response%result, "uri")
7081
-            close(debug_unit)
7082
-        end block
7083
-
7084
         if (num_locations > 0) then
6642
         if (num_locations > 0) then
7085
             ! Array of locations - take first one
6643
             ! Array of locations - take first one
7086
             location_obj = json_get_array_element(response%result, 0)
6644
             location_obj = json_get_array_element(response%result, 0)
@@ -7089,12 +6647,6 @@ contains
7089
             location_obj = response%result
6647
             location_obj = response%result
7090
         else
6648
         else
7091
             ! No definition found
6649
             ! No definition found
7092
-            block
7093
-                integer :: debug_unit
7094
-                open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
7095
-                write(debug_unit, '(A)') '>>> NO DEFINITION FOUND (empty response) <<<'
7096
-                close(debug_unit)
7097
-            end block
7098
             call terminal_move_cursor(editor%screen_rows, 1)
6650
             call terminal_move_cursor(editor%screen_rows, 1)
7099
             call terminal_write('No definition found                           ')
6651
             call terminal_write('No definition found                           ')
7100
             if (associated(saved_buffer_for_callback)) then
6652
             if (associated(saved_buffer_for_callback)) then
@@ -7132,23 +6684,31 @@ contains
7132
         target_line = int(line_real) + 1
6684
         target_line = int(line_real) + 1
7133
         target_col = int(col_real) + 1
6685
         target_col = int(col_real) + 1
7134
 
6686
 
7135
-        ! Log details
7136
-        block
7137
-            integer :: debug_unit
7138
-            open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write')
7139
-            write(debug_unit, '(A)') 'File: ' // trim(filepath)
7140
-            write(debug_unit, '(A,I0,A,I0)') 'Position: line=', target_line, ', col=', target_col
7141
-            close(debug_unit)
7142
-        end block
7143
-
7144
         ! Check if the file is already open in a tab
6687
         ! Check if the file is already open in a tab
7145
         found_file = .false.
6688
         found_file = .false.
7146
         do i = 1, size(editor%tabs)
6689
         do i = 1, size(editor%tabs)
7147
             if (allocated(editor%tabs(i)%filename)) then
6690
             if (allocated(editor%tabs(i)%filename)) then
6691
+                ! Check for exact match or suffix match (handles relative vs absolute paths)
7148
                 if (trim(editor%tabs(i)%filename) == trim(filepath)) then
6692
                 if (trim(editor%tabs(i)%filename) == trim(filepath)) then
7149
-                    ! Switch to this tab
7150
-                    editor%active_tab_index = i
7151
                     found_file = .true.
6693
                     found_file = .true.
6694
+                else if (len_trim(filepath) > len_trim(editor%tabs(i)%filename)) then
6695
+                    ! Check if filepath ends with tab filename
6696
+                    if (filepath(len_trim(filepath)-len_trim(editor%tabs(i)%filename)+1:) == &
6697
+                        trim(editor%tabs(i)%filename)) then
6698
+                        found_file = .true.
6699
+                    end if
6700
+                else if (len_trim(editor%tabs(i)%filename) > len_trim(filepath)) then
6701
+                    ! Check if tab filename ends with filepath
6702
+                    if (editor%tabs(i)%filename(len_trim(editor%tabs(i)%filename)-len_trim(filepath)+1:) == &
6703
+                        trim(filepath)) then
6704
+                        found_file = .true.
6705
+                    end if
6706
+                end if
6707
+
6708
+                if (found_file) then
6709
+                    ! Properly switch to this tab
6710
+                    call switch_to_tab(editor, i)
6711
+                    call sync_pane_to_editor(editor, i, editor%tabs(i)%active_pane_index)
7152
                     exit
6712
                     exit
7153
                 end if
6713
                 end if
7154
             end if
6714
             end if
@@ -7173,6 +6733,19 @@ contains
7173
                         call copy_buffer(editor%tabs(new_tab_idx)%panes(1)%buffer, editor%tabs(new_tab_idx)%buffer)
6733
                         call copy_buffer(editor%tabs(new_tab_idx)%panes(1)%buffer, editor%tabs(new_tab_idx)%buffer)
7174
                     end if
6734
                     end if
7175
 
6735
 
6736
+                    ! Send LSP didOpen notification to all active servers for this tab
6737
+                    if (editor%tabs(new_tab_idx)%num_lsp_servers > 0) then
6738
+                        block
6739
+                            use text_buffer_module, only: buffer_to_string
6740
+                            integer :: srv_i
6741
+                            do srv_i = 1, editor%tabs(new_tab_idx)%num_lsp_servers
6742
+                                call notify_file_opened(editor%lsp_manager, &
6743
+                                    editor%tabs(new_tab_idx)%lsp_server_indices(srv_i), &
6744
+                                    filepath, buffer_to_string(editor%tabs(new_tab_idx)%buffer))
6745
+                            end do
6746
+                        end block
6747
+                    end if
6748
+
7176
                     ! Switch to the new tab
6749
                     ! Switch to the new tab
7177
                     call switch_to_tab(editor, new_tab_idx)
6750
                     call switch_to_tab(editor, new_tab_idx)
7178
 
6751
 
@@ -7208,10 +6781,14 @@ contains
7208
         ! File already open in tabs - jump to the line and column
6781
         ! File already open in tabs - jump to the line and column
7209
         editor%cursors(editor%active_cursor)%line = target_line
6782
         editor%cursors(editor%active_cursor)%line = target_line
7210
         editor%cursors(editor%active_cursor)%column = target_col
6783
         editor%cursors(editor%active_cursor)%column = target_col
6784
+        editor%cursors(editor%active_cursor)%desired_column = target_col
7211
 
6785
 
7212
         ! Center viewport on target
6786
         ! Center viewport on target
7213
         editor%viewport_line = max(1, target_line - editor%screen_rows / 2)
6787
         editor%viewport_line = max(1, target_line - editor%screen_rows / 2)
7214
 
6788
 
6789
+        ! Sync cursor changes back to pane
6790
+        call sync_editor_to_pane(editor)
6791
+
7215
         call terminal_move_cursor(editor%screen_rows, 1)
6792
         call terminal_move_cursor(editor%screen_rows, 1)
7216
         call terminal_write('Jumped to definition                          ')
6793
         call terminal_write('Jumped to definition                          ')
7217
         if (associated(saved_buffer_for_callback)) then
6794
         if (associated(saved_buffer_for_callback)) then
src/lsp/lsp_server_manager_module.f90modified
@@ -282,6 +282,7 @@ contains
282
         caps(CAP_HOVER) = .true.
282
         caps(CAP_HOVER) = .true.
283
         caps(CAP_DIAGNOSTICS) = .true.
283
         caps(CAP_DIAGNOSTICS) = .true.
284
         caps(CAP_DOCUMENT_SYMBOLS) = .true.
284
         caps(CAP_DOCUMENT_SYMBOLS) = .true.
285
+        caps(CAP_CODE_ACTIONS) = .true.  ! fortls has limited code action support
285
         call add_config(manager, "fortran", "fortls", "fortls", "*.f90,*.f95,*.f03,*.f08", caps)
286
         call add_config(manager, "fortran", "fortls", "fortls", "*.f90,*.f95,*.f03,*.f08", caps)
286
 
287
 
287
         ! TODO: Load from config file
288
         ! TODO: Load from config file
src/terminal/renderer_module.f90modified
@@ -23,6 +23,7 @@ module renderer_module
23
     public :: show_line_numbers, LINE_NUMBER_WIDTH
23
     public :: show_line_numbers, LINE_NUMBER_WIDTH
24
     public :: render_screen_with_tree, render_screen_with_lsp_panel
24
     public :: render_screen_with_tree, render_screen_with_lsp_panel
25
     public :: tree_state
25
     public :: tree_state
26
+    public :: update_syntax_highlighter
26
 
27
 
27
     ! Configuration
28
     ! Configuration
28
     logical :: show_line_numbers = .true.
29
     logical :: show_line_numbers = .true.
@@ -50,6 +51,7 @@ module renderer_module
50
 
51
 
51
     ! Syntax highlighting state
52
     ! Syntax highlighting state
52
     type(syntax_highlighter_t) :: syntax_highlighter
53
     type(syntax_highlighter_t) :: syntax_highlighter
54
+    character(len=512) :: last_highlighted_filename = ""
53
 
55
 
54
 contains
56
 contains
55
 
57
 
@@ -70,8 +72,10 @@ contains
70
         ! Initialize syntax highlighter if filename provided
72
         ! Initialize syntax highlighter if filename provided
71
         if (present(filename)) then
73
         if (present(filename)) then
72
             call init_highlighter(syntax_highlighter, filename)
74
             call init_highlighter(syntax_highlighter, filename)
75
+            last_highlighted_filename = trim(filename)
73
         else
76
         else
74
             call init_highlighter(syntax_highlighter)
77
             call init_highlighter(syntax_highlighter)
78
+            last_highlighted_filename = ""
75
         end if
79
         end if
76
     end subroutine init_renderer
80
     end subroutine init_renderer
77
 
81
 
@@ -80,6 +84,19 @@ contains
80
         call cleanup_highlighter(syntax_highlighter)
84
         call cleanup_highlighter(syntax_highlighter)
81
     end subroutine cleanup_renderer
85
     end subroutine cleanup_renderer
82
 
86
 
87
+    ! Update syntax highlighter for a new filename/language
88
+    subroutine update_syntax_highlighter(filename)
89
+        character(len=*), intent(in) :: filename
90
+
91
+        ! Only update if filename has changed
92
+        if (trim(filename) == trim(last_highlighted_filename)) return
93
+
94
+        ! Cleanup old language definition and re-initialize
95
+        call cleanup_highlighter(syntax_highlighter)
96
+        call init_highlighter(syntax_highlighter, filename)
97
+        last_highlighted_filename = trim(filename)
98
+    end subroutine update_syntax_highlighter
99
+
83
     subroutine render_screen(buffer, editor, match_mode_active, match_case_sens)
100
     subroutine render_screen(buffer, editor, match_mode_active, match_case_sens)
84
         type(buffer_t), intent(in) :: buffer
101
         type(buffer_t), intent(in) :: buffer
85
         type(editor_state_t), intent(inout) :: editor
102
         type(editor_state_t), intent(inout) :: editor
@@ -94,6 +111,11 @@ contains
94
         logical :: found_match
111
         logical :: found_match
95
         type(cursor_t) :: cursor
112
         type(cursor_t) :: cursor
96
 
113
 
114
+        ! Auto-update syntax highlighter if filename changed
115
+        if (allocated(editor%filename)) then
116
+            call update_syntax_highlighter(editor%filename)
117
+        end if
118
+
97
         call terminal_hide_cursor()
119
         call terminal_hide_cursor()
98
 
120
 
99
         ! Render tab bar if there are any tabs
121
         ! Render tab bar if there are any tabs
src/workspace/workspace_module.f90modified
@@ -4,7 +4,8 @@
4
 module workspace_module
4
 module workspace_module
5
     use iso_c_binding, only: c_int
5
     use iso_c_binding, only: c_int
6
     use editor_state_module, only: editor_state_t, create_tab, sync_pane_to_editor
6
     use editor_state_module, only: editor_state_t, create_tab, sync_pane_to_editor
7
-    use text_buffer_module, only: buffer_t, init_buffer
7
+    use text_buffer_module, only: buffer_t, init_buffer, buffer_to_string
8
+    use lsp_server_manager_module, only: notify_file_opened
8
     use recents_module, only: recents_add_or_update
9
     use recents_module, only: recents_add_or_update
9
     implicit none
10
     implicit none
10
     private
11
     private
@@ -623,6 +624,18 @@ contains
623
                             call buffer_load_file(editor%tabs(tab_idx)%buffer, trim(full_path), load_status)
624
                             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
625
                             write(0, '(A,I0)') '[DEBUG RESTORE WS] Buffer loaded, status: ', load_status
625
 
626
 
627
+                            ! Send LSP didOpen notification for restored tabs
628
+                            if (load_status == 0 .and. editor%tabs(tab_idx)%num_lsp_servers > 0) then
629
+                                block
630
+                                    integer :: srv_i
631
+                                    do srv_i = 1, editor%tabs(tab_idx)%num_lsp_servers
632
+                                        call notify_file_opened(editor%lsp_manager, &
633
+                                            editor%tabs(tab_idx)%lsp_server_indices(srv_i), &
634
+                                            trim(full_path), buffer_to_string(editor%tabs(tab_idx)%buffer))
635
+                                    end do
636
+                                end block
637
+                            end if
638
+
626
                             ! Set cursor and viewport in first pane
639
                             ! Set cursor and viewport in first pane
627
                             if (allocated(editor%tabs(tab_idx)%panes) .and. size(editor%tabs(tab_idx)%panes) > 0) then
640
                             if (allocated(editor%tabs(tab_idx)%panes) .and. size(editor%tabs(tab_idx)%panes) > 0) then
628
                                 call buffer_load_file(editor%tabs(tab_idx)%panes(1)%buffer, trim(full_path), load_status)
641
                                 call buffer_load_file(editor%tabs(tab_idx)%panes(1)%buffer, trim(full_path), load_status)