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
2727
                                          request_references, request_code_actions, request_document_symbols, &
2828
                                          request_signature_help, request_formatting, request_rename, &
2929
                                          process_server_messages, filename_to_uri, &
30
-                                         get_server_with_capability, &
30
+                                         get_server_with_capability, notify_file_opened, &
3131
                                          CAP_COMPLETION, CAP_DEFINITION, CAP_REFERENCES, CAP_RENAME, &
3232
                                          CAP_CODE_ACTIONS, CAP_FORMATTING, CAP_HOVER, CAP_DOCUMENT_SYMBOLS
3333
     use rename_prompt_module, only: show_rename_prompt
@@ -188,13 +188,30 @@ contains
188188
             end if
189189
         end if
190190
 
191
-        ! Debug: log all incoming keys
192
-        block
193
-            integer :: dbg
194
-            open(newunit=dbg, file='/tmp/fac_keys_all.log', position='append', action='write')
195
-            write(dbg, '(A,A,A)') 'key_str = [', trim(key_str), ']'
196
-            close(dbg)
197
-        end block
191
+        ! Route keys to code actions panel when visible
192
+        if (is_code_actions_panel_visible(editor%code_actions_panel)) then
193
+            if (code_actions_panel_handle_key(editor%code_actions_panel, trim(key_str))) then
194
+                ! For Enter, we need to apply the code action here since panel just returns handled=true
195
+                if (trim(key_str) == 'enter') then
196
+                    call apply_selected_code_action(editor, buffer)
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
198215
 
199216
         select case(trim(key_str))
200217
         ! File operations
@@ -225,33 +242,7 @@ contains
225242
                 return
226243
             end if
227244
 
228
-            ! If diagnostics panel is visible, hide it
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
245
+            ! Other panels (diagnostics, code_actions, references, symbols) are handled in early routing
255246
 
256247
             ! ESC - Clear selections and return to single cursor mode
257248
             if (size(editor%cursors) > 1) then
@@ -357,33 +348,7 @@ contains
357348
                 return
358349
             end if
359350
 
360
-            ! If diagnostics panel is visible, navigate it
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
351
+            ! Other panels (diagnostics, code_actions, references, symbols) are handled in early routing
387352
 
388353
             if (size(editor%cursors) > 1) then
389354
                 ! Move all cursors
@@ -405,33 +370,7 @@ contains
405370
                 return
406371
             end if
407372
 
408
-            ! If diagnostics panel is visible, navigate it
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
373
+            ! Other panels (diagnostics, code_actions, references, symbols) are handled in early routing
435374
 
436375
             if (size(editor%cursors) > 1) then
437376
                 ! Move all cursors
@@ -743,71 +682,7 @@ contains
743682
             is_edit_action = .true.
744683
 
745684
         case('enter')
746
-            ! If code actions menu is visible, apply selected action
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
685
+            ! Code actions panel is handled in early routing above
811686
 
812687
             ! If symbols panel is visible, jump to selected symbol
813688
             if (is_symbols_panel_visible(editor%symbols_panel)) then
@@ -1269,13 +1144,6 @@ contains
12691144
 
12701145
         case('f10', 'alt-.')
12711146
             ! 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
12791147
             ! If panel is already visible, close it
12801148
             if (is_code_actions_panel_visible(editor%code_actions_panel)) then
12811149
                 call hide_code_actions_panel(editor%code_actions_panel)
@@ -1286,7 +1154,7 @@ contains
12861154
                     if (code_actions_server > 0) then
12871155
                         ! Request code actions for the current line
12881156
                         block
1289
-                            integer :: request_id, lsp_line, dbg, dbg_i
1157
+                            integer :: request_id, lsp_line
12901158
                             character(len=:), allocatable :: file_uri
12911159
                             type(diagnostic_t), allocatable :: line_diags(:)
12921160
                             type(json_value_t) :: diags_json
@@ -1300,58 +1168,12 @@ contains
13001168
                             ! This is critical for multi-LSP: Ruff should only see Ruff's diagnostics
13011169
                             file_uri = filename_to_uri(editor%tabs(editor%active_tab_index)%filename)
13021170
 
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
-
13101171
                             line_diags = get_diagnostics_for_line_by_server(editor%diagnostics, file_uri, &
13111172
                                 editor%cursors(editor%active_cursor)%line, code_actions_server)
13121173
 
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
-
13331174
                             ! 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
-
13381175
                             diags_json = diagnostics_to_json(line_diags)
13391176
 
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
-
13551177
                             ! Request code actions for entire line with diagnostics context
13561178
                             request_id = request_code_actions(editor%lsp_manager, &
13571179
                                 code_actions_server, &
@@ -1360,10 +1182,6 @@ contains
13601182
                             lsp_line, 999, &
13611183
                             handle_code_actions_response_wrapper, &
13621184
                             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)
13671185
                             ! Panel will be shown when response arrives in handle_code_actions_response_impl
13681186
                         end block
13691187
                     end if
@@ -1372,14 +1190,6 @@ contains
13721190
 
13731191
         case('f12', 'ctrl-\\', 'alt-g')
13741192
             ! 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
13831193
             call terminal_move_cursor(editor%screen_rows, 1)
13841194
             block
13851195
                 integer :: def_server
@@ -1408,13 +1218,6 @@ contains
14081218
                             editor%tabs(editor%active_tab_index)%filename, &
14091219
                             lsp_line, lsp_char, handle_definition_response_wrapper)
14101220
 
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
-
14181221
                         if (request_id > 0) then
14191222
                             ! Response will be handled by callback
14201223
                             call terminal_write('Searching for definition...                ')
@@ -1429,12 +1232,6 @@ contains
14291232
 
14301233
         case('shift-f12', 'alt-r')
14311234
             ! 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
14381235
             block
14391236
                 integer :: refs_server
14401237
                 refs_server = get_lsp_server_for_cap(editor, CAP_REFERENCES)
@@ -1540,13 +1337,6 @@ contains
15401337
 
15411338
         case('f2')
15421339
             ! 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
15501340
             block
15511341
                 integer :: rename_server
15521342
                 rename_server = get_lsp_server_for_cap(editor, CAP_RENAME)
@@ -1572,17 +1362,6 @@ contains
15721362
                                 lsp_line = editor%cursors(editor%active_cursor)%line - 1
15731363
                                 lsp_char = editor%cursors(editor%active_cursor)%column - 1
15741364
 
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
-
15861365
                                 ! Save editor state for callback
15871366
                                 saved_editor_for_callback => editor
15881367
 
@@ -1597,26 +1376,16 @@ contains
15971376
 
15981377
                                     ! Poll for LSP response and render immediately when received
15991378
                                     block
1600
-                                        integer :: poll_count, max_polls, pane_idx, debug_unit
1379
+                                        integer :: poll_count, max_polls, pane_idx
16011380
                                         integer(8) :: start_time, end_time, count_rate, target_time
16021381
                                         max_polls = 100  ! Poll up to 100 times (1 second total)
16031382
 
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
-
16091383
                                         do poll_count = 1, max_polls
16101384
                                             ! Process any LSP messages
16111385
                                             call process_server_messages(editor%lsp_manager)
16121386
 
16131387
                                             ! Check if rename response modified the buffer
16141388
                                             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
-
16201389
                                                 ! Sync buffer from tab (LSP modified tab buffer)
16211390
                                                 call copy_buffer(buffer, editor%tabs(editor%active_tab_index)%buffer)
16221391
 
@@ -1649,13 +1418,6 @@ contains
16491418
                                                 if (end_time >= target_time) exit
16501419
                                             end do
16511420
                                         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
16591421
                                     end block
16601422
                                 end if
16611423
 
@@ -1704,12 +1466,6 @@ contains
17041466
 
17051467
         case('f4', 'alt-o')
17061468
             ! 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
17131469
             block
17141470
                 integer :: symbols_server
17151471
                 symbols_server = get_lsp_server_for_cap(editor, CAP_DOCUMENT_SYMBOLS)
@@ -1900,29 +1656,9 @@ contains
19001656
 
19011657
         case('f8', 'alt-e')
19021658
             ! 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
19101659
             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
19181660
             ! Re-render screen to show/hide the panel
19191661
             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
19261662
 
19271663
         case('alt-c')
19281664
             ! Toggle case sensitivity for match mode (ctrl-d)
@@ -1999,12 +1735,6 @@ contains
19991735
             end if
20001736
 
20011737
         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
-
20081738
             ! Check for mouse events
20091739
             if (index(key_str, 'mouse-') == 1) then
20101740
                 call handle_mouse_event_action(key_str, editor, buffer)
@@ -5238,6 +4968,18 @@ contains
52384968
                 allocate(character(len=len_trim(full_path)) :: editor%filename)
52394969
                 editor%filename = full_path
52404970
 
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
+
52414983
                 ! Reset cursor to top of file
52424984
                 editor%cursors(editor%active_cursor)%line = 1
52434985
                 editor%cursors(editor%active_cursor)%column = 1
@@ -5989,13 +5731,6 @@ contains
59895731
         integer, intent(in) :: request_id
59905732
         type(lsp_message_t), intent(in) :: response
59915733
 
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
-
59995734
         ! Call the actual handler with saved editor state
60005735
         if (associated(saved_editor_for_callback)) then
60015736
             call handle_references_response_impl(saved_editor_for_callback, response)
@@ -6122,27 +5857,14 @@ contains
61225857
         type(lsp_message_t), intent(in) :: response
61235858
         type(json_value_t) :: result_array, action_obj, edit_obj
61245859
         type(code_action_t), allocatable :: actions(:)
6125
-        integer :: num_actions, i, dbg
6126
-        character(len=:), allocatable :: title, kind, action_json, result_str
5860
+        integer :: num_actions, i
5861
+        character(len=:), allocatable :: title, kind, action_json
61275862
         logical :: is_preferred
61285863
 
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
-
61385864
         ! The result is directly in response%result for LSP responses
61395865
         result_array = response%result
61405866
         num_actions = json_array_size(result_array)
61415867
 
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
-
61465868
         if (num_actions == 0) then
61475869
             ! No actions available - don't show panel
61485870
             return
@@ -6206,13 +5928,6 @@ contains
62065928
         integer, intent(in) :: request_id
62075929
         type(lsp_message_t), intent(in) :: response
62085930
 
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
-
62165931
         ! Call the actual handler with saved editor state
62175932
         if (associated(saved_editor_for_callback)) then
62185933
             call handle_symbols_response_impl(saved_editor_for_callback, response)
@@ -6238,26 +5953,6 @@ contains
62385953
         result_array = response%result
62395954
         num_symbols = json_array_size(result_array)
62405955
 
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
-
62615956
         if (num_symbols == 0) then
62625957
             call clear_symbols(editor%symbols_panel)
62635958
             call terminal_move_cursor(editor%screen_rows, 1)
@@ -6406,24 +6101,12 @@ contains
64066101
 
64076102
         character(len=:), allocatable :: result_str
64086103
         integer :: changes_applied
6409
-        integer :: debug_unit
64106104
 
64116105
         if (.not. associated(saved_editor_for_callback)) return
64126106
 
64136107
         ! Convert result to string for apply_workspace_edit
64146108
         result_str = json_stringify(response%result)
64156109
 
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
-
64276110
         if (.not. allocated(result_str) .or. result_str == 'null' .or. len_trim(result_str) == 0) then
64286111
             call terminal_move_cursor(saved_editor_for_callback%screen_rows, 1)
64296112
             call terminal_write('Rename failed or not supported                ')
@@ -6434,21 +6117,6 @@ contains
64346117
         ! Apply workspace edit
64356118
         call apply_workspace_edit(saved_editor_for_callback, result_str, changes_applied)
64366119
 
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
-
64526120
         call terminal_move_cursor(saved_editor_for_callback%screen_rows, 1)
64536121
         if (changes_applied > 0) then
64546122
             block
@@ -6533,6 +6201,52 @@ contains
65336201
         end if
65346202
     end subroutine handle_formatting_response_wrapper
65356203
 
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
+
65366250
     ! Apply a workspace edit from LSP
65376251
     subroutine apply_workspace_edit(editor, edit_json, changes_applied)
65386252
         use json_module, only: json_parse, json_value_t, json_get_array, json_array_size, &
@@ -6549,15 +6263,6 @@ contains
65496263
 
65506264
         changes_applied = 0
65516265
 
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
-
65616266
         ! Parse the edit JSON
65626267
         edit_obj = json_parse(edit_json)
65636268
 
@@ -6566,13 +6271,6 @@ contains
65666271
             doc_changes_arr = json_get_array(edit_obj, 'documentChanges')
65676272
             num_files = json_array_size(doc_changes_arr)
65686273
 
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
-
65766274
             do i = 0, num_files - 1  ! 0-based index
65776275
                 file_change_obj = json_get_array_element(doc_changes_arr, i)
65786276
 
@@ -6580,33 +6278,11 @@ contains
65806278
                 if (json_has_key(file_change_obj, 'textDocument')) then
65816279
                     text_doc_obj = json_get_object(file_change_obj, 'textDocument')
65826280
                     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
65966281
                 end if
65976282
 
65986283
                 ! Get edits array
65996284
                 if (json_has_key(file_change_obj, 'edits') .and. allocated(uri)) then
66006285
                     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
-
66106286
                     call apply_file_edits_obj(editor, uri, edits_arr, changes_applied)
66116287
                     deallocate(uri)
66126288
                 end if
@@ -6656,38 +6332,10 @@ contains
66566332
             filename = uri
66576333
         end if
66586334
 
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
-
66706335
         ! Find the tab with this file
66716336
         tab_idx = 0
66726337
         do j = 1, size(editor%tabs)
66736338
             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
-
66916339
                 ! Try exact match first, then check if the absolute path ends with the relative path
66926340
                 if (trim(editor%tabs(j)%filename) == trim(filename)) then
66936341
                     tab_idx = j
@@ -6702,13 +6350,6 @@ contains
67026350
             end if
67036351
         end do
67046352
 
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
-
67126353
         if (tab_idx == 0) then
67136354
             ! File not open - skip for now
67146355
             if (allocated(filename)) deallocate(filename)
@@ -6758,75 +6399,22 @@ contains
67586399
 
67596400
         integer :: start_pos, end_pos, delete_count
67606401
 
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
-
67726402
         ! Calculate buffer positions
67736403
         start_pos = get_buffer_position(buffer, start_line, start_char)
67746404
         end_pos = get_buffer_position(buffer, end_line, end_char)
67756405
 
6776
-        block
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
6406
+        if (start_pos <= 0 .or. end_pos <= 0) return
67936407
 
67946408
         ! Delete the old text
67956409
         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
-
68036410
         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
68106411
             call buffer_delete(buffer, start_pos, delete_count)
68116412
         end if
68126413
 
68136414
         ! Insert the new text
68146415
         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
68216416
             call buffer_insert(buffer, start_pos, new_text)
68226417
         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
68306418
     end subroutine apply_single_edit
68316419
 
68326420
     ! Execute a command from the command palette
@@ -7048,39 +6636,9 @@ contains
70486636
         integer :: target_line, target_col, i, num_locations
70496637
         logical :: found_file
70506638
 
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
-
70736639
         ! Try to treat result as array first
70746640
         num_locations = json_array_size(response%result)
70756641
 
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
-
70846642
         if (num_locations > 0) then
70856643
             ! Array of locations - take first one
70866644
             location_obj = json_get_array_element(response%result, 0)
@@ -7089,12 +6647,6 @@ contains
70896647
             location_obj = response%result
70906648
         else
70916649
             ! 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
70986650
             call terminal_move_cursor(editor%screen_rows, 1)
70996651
             call terminal_write('No definition found                           ')
71006652
             if (associated(saved_buffer_for_callback)) then
@@ -7132,23 +6684,31 @@ contains
71326684
         target_line = int(line_real) + 1
71336685
         target_col = int(col_real) + 1
71346686
 
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
-
71446687
         ! Check if the file is already open in a tab
71456688
         found_file = .false.
71466689
         do i = 1, size(editor%tabs)
71476690
             if (allocated(editor%tabs(i)%filename)) then
6691
+                ! Check for exact match or suffix match (handles relative vs absolute paths)
71486692
                 if (trim(editor%tabs(i)%filename) == trim(filepath)) then
7149
-                    ! Switch to this tab
7150
-                    editor%active_tab_index = i
71516693
                     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)
71526712
                     exit
71536713
                 end if
71546714
             end if
@@ -7173,6 +6733,19 @@ contains
71736733
                         call copy_buffer(editor%tabs(new_tab_idx)%panes(1)%buffer, editor%tabs(new_tab_idx)%buffer)
71746734
                     end if
71756735
 
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
+
71766749
                     ! Switch to the new tab
71776750
                     call switch_to_tab(editor, new_tab_idx)
71786751
 
@@ -7208,10 +6781,14 @@ contains
72086781
         ! File already open in tabs - jump to the line and column
72096782
         editor%cursors(editor%active_cursor)%line = target_line
72106783
         editor%cursors(editor%active_cursor)%column = target_col
6784
+        editor%cursors(editor%active_cursor)%desired_column = target_col
72116785
 
72126786
         ! Center viewport on target
72136787
         editor%viewport_line = max(1, target_line - editor%screen_rows / 2)
72146788
 
6789
+        ! Sync cursor changes back to pane
6790
+        call sync_editor_to_pane(editor)
6791
+
72156792
         call terminal_move_cursor(editor%screen_rows, 1)
72166793
         call terminal_write('Jumped to definition                          ')
72176794
         if (associated(saved_buffer_for_callback)) then
src/lsp/lsp_server_manager_module.f90modified
@@ -282,6 +282,7 @@ contains
282282
         caps(CAP_HOVER) = .true.
283283
         caps(CAP_DIAGNOSTICS) = .true.
284284
         caps(CAP_DOCUMENT_SYMBOLS) = .true.
285
+        caps(CAP_CODE_ACTIONS) = .true.  ! fortls has limited code action support
285286
         call add_config(manager, "fortran", "fortls", "fortls", "*.f90,*.f95,*.f03,*.f08", caps)
286287
 
287288
         ! TODO: Load from config file
src/terminal/renderer_module.f90modified
@@ -23,6 +23,7 @@ module renderer_module
2323
     public :: show_line_numbers, LINE_NUMBER_WIDTH
2424
     public :: render_screen_with_tree, render_screen_with_lsp_panel
2525
     public :: tree_state
26
+    public :: update_syntax_highlighter
2627
 
2728
     ! Configuration
2829
     logical :: show_line_numbers = .true.
@@ -50,6 +51,7 @@ module renderer_module
5051
 
5152
     ! Syntax highlighting state
5253
     type(syntax_highlighter_t) :: syntax_highlighter
54
+    character(len=512) :: last_highlighted_filename = ""
5355
 
5456
 contains
5557
 
@@ -70,8 +72,10 @@ contains
7072
         ! Initialize syntax highlighter if filename provided
7173
         if (present(filename)) then
7274
             call init_highlighter(syntax_highlighter, filename)
75
+            last_highlighted_filename = trim(filename)
7376
         else
7477
             call init_highlighter(syntax_highlighter)
78
+            last_highlighted_filename = ""
7579
         end if
7680
     end subroutine init_renderer
7781
 
@@ -80,6 +84,19 @@ contains
8084
         call cleanup_highlighter(syntax_highlighter)
8185
     end subroutine cleanup_renderer
8286
 
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
+
83100
     subroutine render_screen(buffer, editor, match_mode_active, match_case_sens)
84101
         type(buffer_t), intent(in) :: buffer
85102
         type(editor_state_t), intent(inout) :: editor
@@ -94,6 +111,11 @@ contains
94111
         logical :: found_match
95112
         type(cursor_t) :: cursor
96113
 
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
+
97119
         call terminal_hide_cursor()
98120
 
99121
         ! Render tab bar if there are any tabs
src/workspace/workspace_module.f90modified
@@ -4,7 +4,8 @@
44
 module workspace_module
55
     use iso_c_binding, only: c_int
66
     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
89
     use recents_module, only: recents_add_or_update
910
     implicit none
1011
     private
@@ -623,6 +624,18 @@ contains
623624
                             call buffer_load_file(editor%tabs(tab_idx)%buffer, trim(full_path), load_status)
624625
                             write(0, '(A,I0)') '[DEBUG RESTORE WS] Buffer loaded, status: ', load_status
625626
 
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
+
626639
                             ! Set cursor and viewport in first pane
627640
                             if (allocated(editor%tabs(tab_idx)%panes) .and. size(editor%tabs(tab_idx)%panes) > 0) then
628641
                                 call buffer_load_file(editor%tabs(tab_idx)%panes(1)%buffer, trim(full_path), load_status)