lsp callback handlers ready for tests
- SHA
d3256e4a75ece88eb6045460247ebad7e20ebbcc- Parents
-
038f1e3 - Tree
c05bea0
d3256e4
d3256e4a75ece88eb6045460247ebad7e20ebbcc038f1e3
c05bea0| Status | File | + | - |
|---|---|---|---|
| M |
docs/KEYBINDINGS.md
|
12 | 11 |
| M |
docs/LSP_GUIDE.md
|
24 | 24 |
| M |
src/commands/command_handler_module.f90
|
206 | 15 |
| M |
src/lsp/lsp_server_manager_module.f90
|
19 | 16 |
| M |
src/syntax/syntax_highlighter_module.f90
|
9 | 9 |
| M |
src/terminal/input_handler_module.f90
|
130 | 32 |
| M |
src/ui/help_display_module.f90
|
5 | 1 |
| A |
test_keys.sh
|
25 | 0 |
| A |
test_raw_keys.sh
|
32 | 0 |
| A |
tests/scratch_files/README.md
|
268 | 0 |
| A |
tests/scratch_files/calculator.py
|
68 | 0 |
| A |
tests/scratch_files/main.c
|
79 | 0 |
| A |
tests/scratch_files/main.py
|
71 | 0 |
| A |
tests/scratch_files/math_utils.f90
|
89 | 0 |
| A |
tests/scratch_files/test_program.f90
|
43 | 0 |
| A |
tests/scratch_files/utils.c
|
76 | 0 |
docs/KEYBINDINGS.mdmodified@@ -8,13 +8,14 @@ Complete keyboard shortcut reference for `fac` editor. | ||
| 8 | 8 | |
| 9 | 9 | | Keybinding | Command | Description | |
| 10 | 10 | |------------|---------|-------------| |
| 11 | -| `F12` | Go to Definition | Jump to where a symbol is defined | | |
| 12 | -| `Shift+F12` | Find References | Find all usages of a symbol | | |
| 11 | +| `F12` or `Ctrl+\` | Go to Definition | Jump to where a symbol is defined | | |
| 12 | +| `Shift+F12` or `Ctrl+Shift+R` | Find References | Find all usages of a symbol | | |
| 13 | 13 | | `F2` | Rename Symbol | Rename symbol across entire project | |
| 14 | 14 | | `Ctrl+.` | Code Actions | Quick fixes and refactorings | |
| 15 | -| `Ctrl+Shift+D` | Diagnostics Panel | Show all errors and warnings | | |
| 16 | -| `Ctrl+Shift+O` | Document Symbols | Navigate symbols in current file | | |
| 17 | -| `Ctrl+Shift+T` | Workspace Symbols | Search symbols across all files | | |
| 15 | +| `F8` or `Ctrl+Shift+D` | Diagnostics Panel | Show all errors and warnings | | |
| 16 | +| `F4` or `Ctrl+Shift+O` | Document Symbols | Navigate symbols in current file | | |
| 17 | +| `F6` or `Ctrl+Shift+T` | Workspace Symbols | Search symbols across all files | | |
| 18 | +| `Ctrl+P` | Command Palette | Search and execute any command | | |
| 18 | 19 | | `Shift+Alt+F` | Format Document | Auto-format current file | |
| 19 | 20 | | `Alt+,` | Jump Back | Return to previous location (jump stack) | |
| 20 | 21 | |
@@ -77,7 +78,7 @@ Complete keyboard shortcut reference for `fac` editor. | ||
| 77 | 78 | | `Ctrl+T` | New Tab | Create new empty tab | |
| 78 | 79 | | `Ctrl+W` | Close Tab | Close current tab | |
| 79 | 80 | | `Ctrl+Tab` | Next Tab | Switch to next tab | |
| 80 | -| `Ctrl+Shift+Tab` | Previous Tab | Switch to previous tab | | |
| 81 | +| `F6ab` | Previous Tab | Switch to previous tab | | |
| 81 | 82 | | `Alt+1` to `Alt+9` | Jump to Tab | Switch to specific tab number | |
| 82 | 83 | | `Ctrl+\` | Split Vertical | Split current pane vertically | |
| 83 | 84 | | `Ctrl+Shift+\` | Split Horizontal | Split current pane horizontally | |
@@ -127,7 +128,7 @@ Complete keyboard shortcut reference for `fac` editor. | ||
| 127 | 128 | |
| 128 | 129 | | Keybinding | Command | Description | |
| 129 | 130 | |------------|---------|-------------| |
| 130 | -| `Ctrl+Shift+P` | Command Palette | Search and execute any command | | |
| 131 | +| `Ctrl+P` | Command Palette | Search and execute any command | | |
| 131 | 132 | | `Ctrl+?` or `F1` | Help | Show help screen | |
| 132 | 133 | | `Ctrl+Shift+F` | File Tree | Toggle file explorer (Fortress mode) | |
| 133 | 134 | | `Esc` | Cancel/Close | Close panels, cancel operations | |
@@ -218,14 +219,14 @@ Active when `Ctrl+Shift+F` is pressed: | ||
| 218 | 219 | ### Keybinding Conflicts |
| 219 | 220 | - Some keybindings may conflict with terminal emulator shortcuts |
| 220 | 221 | - If a key doesn't work, check your terminal's keyboard settings |
| 221 | -- Common conflicts: `Ctrl+Shift+T` (new terminal tab), `Ctrl+W` (close terminal tab) | |
| 222 | +- Common conflicts: `F6` (new terminal tab), `Ctrl+W` (close terminal tab) | |
| 222 | 223 | |
| 223 | 224 | ### Customization |
| 224 | 225 | - Keybindings are currently hardcoded |
| 225 | 226 | - Future versions will support custom keybindings |
| 226 | 227 | |
| 227 | 228 | ### Discovering Commands |
| 228 | -- Use `Ctrl+Shift+P` (Command Palette) to see all available commands | |
| 229 | +- Use `Ctrl+P` (Command Palette) to see all available commands | |
| 229 | 230 | - Commands show their keybindings in the palette |
| 230 | 231 | |
| 231 | 232 | ### Vim Users |
@@ -242,13 +243,13 @@ Some vim-style keybindings work: | ||
| 242 | 243 | ## 🚀 Most Useful Combos |
| 243 | 244 | |
| 244 | 245 | **Exploring code:** |
| 245 | -1. `Ctrl+Shift+T` - Find any symbol in project | |
| 246 | +1. `F6` - Find any symbol in project | |
| 246 | 247 | 2. `F12` - Jump to definition |
| 247 | 248 | 3. `Shift+F12` - See all usages |
| 248 | 249 | 4. `Alt+,` - Jump back |
| 249 | 250 | |
| 250 | 251 | **Fixing errors:** |
| 251 | -1. `Ctrl+Shift+D` - See all errors | |
| 252 | +1. `F8` - See all errors | |
| 252 | 253 | 2. Navigate to error |
| 253 | 254 | 3. `Ctrl+.` - Apply quick fix |
| 254 | 255 | 4. `Ctrl+S` - Save |
docs/LSP_GUIDE.mdmodified@@ -96,7 +96,7 @@ That's it! If you have `pylsp` installed, `fac` will automatically: | ||
| 96 | 96 | ### Step 3: Try It Out |
| 97 | 97 | |
| 98 | 98 | 1. **See errors**: Look for `E` (error) or `W` (warning) in the left gutter |
| 99 | -2. **View all diagnostics**: Press `Ctrl+Shift+D` to open the diagnostics panel | |
| 99 | +2. **View all diagnostics**: Press `F8` to open the diagnostics panel | |
| 100 | 100 | 3. **Jump to definition**: Put cursor on a function name and press `F12` |
| 101 | 101 | 4. **Find references**: Press `Shift+F12` to see everywhere a symbol is used |
| 102 | 102 | 5. **Rename**: Press `F2` to rename a variable across all files |
@@ -114,7 +114,7 @@ That's it! If you have `pylsp` installed, `fac` will automatically: | ||
| 114 | 114 | - Errors appear with `E` in the gutter (red) |
| 115 | 115 | - Warnings appear with `W` in the gutter (yellow) |
| 116 | 116 | - Put your cursor on a line with an error to see the message in the status bar |
| 117 | -- Press `Ctrl+Shift+D` to open the **Diagnostics Panel** showing all issues | |
| 117 | +- Press `F8` to open the **Diagnostics Panel** showing all issues | |
| 118 | 118 | |
| 119 | 119 | **Example:** |
| 120 | 120 | ```python |
@@ -125,7 +125,7 @@ def greet(name): | ||
| 125 | 125 | ``` |
| 126 | 126 | |
| 127 | 127 | **Keybinding:** |
| 128 | -- `Ctrl+Shift+D` - Open/close diagnostics panel | |
| 128 | +- `F8` or `Ctrl+Shift+D` - Open/close diagnostics panel | |
| 129 | 129 | - `j`/`k` or `↑`/`↓` - Navigate issues in panel |
| 130 | 130 | - `Enter` - Jump to selected issue |
| 131 | 131 | - `Esc` - Close panel |
@@ -155,7 +155,7 @@ def calculate_total(items): # ← You land here | ||
| 155 | 155 | ``` |
| 156 | 156 | |
| 157 | 157 | **Keybindings:** |
| 158 | -- `F12` - Go to definition | |
| 158 | +- `F12` or `Ctrl+\` - Go to definition | |
| 159 | 159 | - `Alt+,` - Jump back (navigate backward in jump history) |
| 160 | 160 | |
| 161 | 161 | **Cross-file navigation:** If the definition is in another file, `fac` automatically opens it in a new tab! |
@@ -186,7 +186,7 @@ References to 'calculate_total': | ||
| 186 | 186 | ``` |
| 187 | 187 | |
| 188 | 188 | **Keybindings:** |
| 189 | -- `Shift+F12` - Find all references | |
| 189 | +- `Shift+F12` or `Ctrl+Shift+R` - Find all references | |
| 190 | 190 | - `j`/`k` or `↑`/`↓` - Navigate references |
| 191 | 191 | - `Enter` - Jump to selected reference |
| 192 | 192 | - `Esc` - Close panel |
@@ -275,12 +275,12 @@ total = calculate(10, 20) | ||
| 275 | 275 | |
| 276 | 276 | --- |
| 277 | 277 | |
| 278 | -### 6. Document Symbols Outline (Ctrl+Shift+O) | |
| 278 | +### 6. Document Symbols Outline (F4) | |
| 279 | 279 | |
| 280 | 280 | **What it does:** See an outline of all functions, classes, and variables in the current file. |
| 281 | 281 | |
| 282 | 282 | **How to use:** |
| 283 | -1. Press `Ctrl+Shift+O` to open the symbols panel | |
| 283 | +1. Press `F4` to open the symbols panel | |
| 284 | 284 | 2. Type to filter symbols (fuzzy search) |
| 285 | 285 | 3. Press `Enter` to jump to a symbol |
| 286 | 286 | |
@@ -297,7 +297,7 @@ Document Symbols: | ||
| 297 | 297 | ``` |
| 298 | 298 | |
| 299 | 299 | **Keybindings:** |
| 300 | -- `Ctrl+Shift+O` - Open document symbols panel | |
| 300 | +- `F4` or `Ctrl+Shift+O` - Open document symbols panel | |
| 301 | 301 | - Type to search (fuzzy matching) |
| 302 | 302 | - `j`/`k` or `↑`/`↓` - Navigate symbols |
| 303 | 303 | - `Enter` - Jump to selected symbol |
@@ -305,12 +305,12 @@ Document Symbols: | ||
| 305 | 305 | |
| 306 | 306 | --- |
| 307 | 307 | |
| 308 | -### 7. Workspace Symbols (Ctrl+Shift+T) | |
| 308 | +### 7. Workspace Symbols (F6) | |
| 309 | 309 | |
| 310 | 310 | **What it does:** Search for any symbol across your **entire project** (all files). |
| 311 | 311 | |
| 312 | 312 | **How to use:** |
| 313 | -1. Press `Ctrl+Shift+T` to open workspace symbols | |
| 313 | +1. Press `F6` to open workspace symbols | |
| 314 | 314 | 2. Type part of a symbol name (fuzzy search) |
| 315 | 315 | 3. Navigate and press `Enter` to jump to it |
| 316 | 316 | |
@@ -331,7 +331,7 @@ Workspace Symbols: | ||
| 331 | 331 | - `calctot` matches `calculate_total` (consecutive) |
| 332 | 332 | |
| 333 | 333 | **Keybindings:** |
| 334 | -- `Ctrl+Shift+T` - Open workspace symbols | |
| 334 | +- `F6` or `Ctrl+Shift+T` - Open workspace symbols | |
| 335 | 335 | - Type to search across all files |
| 336 | 336 | - `j`/`k` or `↑`/`↓` - Navigate results |
| 337 | 337 | - `Enter` - Jump to symbol (opens file if needed) |
@@ -414,12 +414,12 @@ def greet(name, age): | ||
| 414 | 414 | |
| 415 | 415 | --- |
| 416 | 416 | |
| 417 | -### 10. Command Palette (Ctrl+Shift+P) | |
| 417 | +### 10. Command Palette (Ctrl+P) | |
| 418 | 418 | |
| 419 | 419 | **What it does:** Quick access to all LSP commands without remembering keybindings. |
| 420 | 420 | |
| 421 | 421 | **How to use:** |
| 422 | -1. Press `Ctrl+Shift+P` | |
| 422 | +1. Press `Ctrl+P` | |
| 423 | 423 | 2. Type to search commands (fuzzy search) |
| 424 | 424 | 3. Press `Enter` to execute |
| 425 | 425 | |
@@ -439,7 +439,7 @@ Command Palette: | ||
| 439 | 439 | ``` |
| 440 | 440 | |
| 441 | 441 | **Keybindings:** |
| 442 | -- `Ctrl+Shift+P` - Open command palette | |
| 442 | +- `Ctrl+P` - Open command palette | |
| 443 | 443 | - Type to search commands |
| 444 | 444 | - `Enter` - Execute selected command |
| 445 | 445 | - `Esc` - Close palette |
@@ -705,14 +705,14 @@ After using F12 to jump to a definition: | ||
| 705 | 705 | ### 2. Combine Search Features |
| 706 | 706 | |
| 707 | 707 | - `Ctrl+F` - Search text in current file |
| 708 | -- `Ctrl+Shift+O` - Search symbols in current file | |
| 709 | -- `Ctrl+Shift+T` - Search symbols across project | |
| 708 | +- `F4` - Search symbols in current file | |
| 709 | +- `F6` - Search symbols across project | |
| 710 | 710 | - `Shift+F12` - Find all usages of current symbol |
| 711 | 711 | |
| 712 | 712 | ### 3. Fix Errors Faster |
| 713 | 713 | |
| 714 | 714 | When you see an error: |
| 715 | -1. Press `Ctrl+Shift+D` to see all errors | |
| 715 | +1. Press `F8` to see all errors | |
| 716 | 716 | 2. Navigate to each error |
| 717 | 717 | 3. Press `Ctrl+.` to see quick fixes |
| 718 | 718 | 4. Apply fixes with `Enter` |
@@ -737,15 +737,15 @@ Many language servers support format-on-save: | ||
| 737 | 737 | |
| 738 | 738 | | Feature | Keybinding | What It Does | |
| 739 | 739 | |---------|-----------|--------------| |
| 740 | -| **Diagnostics Panel** | `Ctrl+Shift+D` | Show all errors/warnings | | |
| 741 | -| **Go to Definition** | `F12` | Jump to where symbol is defined | | |
| 742 | -| **Find References** | `Shift+F12` | Find all usages of symbol | | |
| 740 | +| **Diagnostics Panel** | `F8` or `Ctrl+Shift+D` | Show all errors/warnings | | |
| 741 | +| **Go to Definition** | `F12` or `Ctrl+\` | Jump to where symbol is defined | | |
| 742 | +| **Find References** | `Shift+F12` or `Ctrl+Shift+R` | Find all usages of symbol | | |
| 743 | 743 | | **Code Actions** | `Ctrl+.` | Quick fixes and refactorings | |
| 744 | 744 | | **Rename Symbol** | `F2` | Rename across entire project | |
| 745 | -| **Document Symbols** | `Ctrl+Shift+O` | Outline of current file | | |
| 746 | -| **Workspace Symbols** | `Ctrl+Shift+T` | Search symbols across project | | |
| 745 | +| **Document Symbols** | `F4` or `Ctrl+Shift+O` | Outline of current file | | |
| 746 | +| **Workspace Symbols** | `F6` or `Ctrl+Shift+T` | Search symbols across project | | |
| 747 | 747 | | **Format Document** | `Shift+Alt+F` | Auto-format code | |
| 748 | -| **Command Palette** | `Ctrl+Shift+P` | Access all commands | | |
| 748 | +| **Command Palette** | `Ctrl+P` | Access all commands | | |
| 749 | 749 | | **Jump Back** | `Alt+,` | Return to previous location | |
| 750 | 750 | |
| 751 | 751 | --- |
@@ -779,7 +779,7 @@ Now that you understand LSP in `fac`: | ||
| 779 | 779 | |
| 780 | 780 | 1. **Install language servers** for your languages |
| 781 | 781 | 2. **Practice the keybindings** - they'll become second nature |
| 782 | -3. **Explore your codebase** with `Ctrl+Shift+T` and `F12` | |
| 782 | +3. **Explore your codebase** with `F6` and `F12` | |
| 783 | 783 | 4. **Let LSP catch errors** before you run your code |
| 784 | 784 | 5. **Refactor confidently** with `F2` and `Ctrl+.` |
| 785 | 785 | |
src/commands/command_handler_module.f90modified@@ -119,6 +119,14 @@ contains | ||
| 119 | 119 | line_count = buffer_get_line_count(buffer) |
| 120 | 120 | is_edit_action = .false. |
| 121 | 121 | |
| 122 | + ! DEBUG: Log ALL keys to file only (not screen to avoid overwriting status messages) | |
| 123 | + block | |
| 124 | + integer :: debug_unit | |
| 125 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 126 | + write(debug_unit, '(A)') '[KEY] "' // trim(key_str) // '"' | |
| 127 | + close(debug_unit) | |
| 128 | + end block | |
| 129 | + | |
| 122 | 130 | ! Ignore empty key strings (from terminal position reports, etc) |
| 123 | 131 | if (len_trim(key_str) == 0 .and. key_str(1:1) /= ' ') then |
| 124 | 132 | return |
@@ -132,10 +140,16 @@ contains | ||
| 132 | 140 | match_case_sensitive = .true. ! Reset to default |
| 133 | 141 | end if |
| 134 | 142 | |
| 135 | - ! Route input when in fuss mode (except ctrl-b/ctrl-shift-b/F2/F3 and ctrl-q which work in both modes) | |
| 143 | + ! Route input when in fuss mode (except ctrl-b/ctrl-shift-b/F-keys/Alt-keys/ctrl-q which work in both modes) | |
| 136 | 144 | if (editor%fuss_mode_active .and. trim(key_str) /= 'ctrl-b' .and. & |
| 137 | 145 | trim(key_str) /= 'ctrl-shift-b' .and. trim(key_str) /= 'f2' .and. & |
| 138 | - trim(key_str) /= 'f3' .and. trim(key_str) /= 'ctrl-q') then | |
| 146 | + trim(key_str) /= 'f3' .and. trim(key_str) /= 'f4' .and. & | |
| 147 | + trim(key_str) /= 'f6' .and. trim(key_str) /= 'f8' .and. & | |
| 148 | + trim(key_str) /= 'f12' .and. trim(key_str) /= 'shift-f12' .and. & | |
| 149 | + trim(key_str) /= 'alt-g' .and. trim(key_str) /= 'alt-o' .and. & | |
| 150 | + trim(key_str) /= 'alt-p' .and. trim(key_str) /= 'alt-e' .and. & | |
| 151 | + trim(key_str) /= 'alt-r' .and. trim(key_str) /= 'ctrl-\\' .and. & | |
| 152 | + trim(key_str) /= 'ctrl-q') then | |
| 139 | 153 | call handle_fuss_input(key_str, editor, buffer) |
| 140 | 154 | return |
| 141 | 155 | end if |
@@ -1193,8 +1207,20 @@ contains | ||
| 1193 | 1207 | end if |
| 1194 | 1208 | end if |
| 1195 | 1209 | |
| 1196 | - case('f12') | |
| 1197 | - ! Go to definition | |
| 1210 | + case('f12', 'ctrl-\\', 'alt-g') | |
| 1211 | + ! Go to definition (F12, Ctrl+\, or Alt+G) | |
| 1212 | + block | |
| 1213 | + integer :: debug_unit | |
| 1214 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 1215 | + write(debug_unit, '(A)') '>>> INSIDE F12/ALT-G HANDLER <<<' | |
| 1216 | + write(debug_unit, '(A,I0)') 'active_tab_index = ', editor%active_tab_index | |
| 1217 | + write(debug_unit, '(A,I0)') 'size(tabs) = ', size(editor%tabs) | |
| 1218 | + if (editor%active_tab_index > 0 .and. editor%active_tab_index <= size(editor%tabs)) then | |
| 1219 | + write(debug_unit, '(A,I0)') 'lsp_server_index = ', editor%tabs(editor%active_tab_index)%lsp_server_index | |
| 1220 | + end if | |
| 1221 | + close(debug_unit) | |
| 1222 | + end block | |
| 1223 | + call terminal_move_cursor(editor%screen_rows, 1) | |
| 1198 | 1224 | if (editor%active_tab_index > 0 .and. editor%active_tab_index <= size(editor%tabs)) then |
| 1199 | 1225 | if (editor%tabs(editor%active_tab_index)%lsp_server_index > 0) then |
| 1200 | 1226 | ! Save current location to jump stack |
@@ -1211,22 +1237,40 @@ contains | ||
| 1211 | 1237 | lsp_line = editor%cursors(editor%active_cursor)%line - 1 |
| 1212 | 1238 | lsp_char = editor%cursors(editor%active_cursor)%column - 1 |
| 1213 | 1239 | |
| 1240 | + ! Save editor state for callback | |
| 1241 | + if (.not. allocated(saved_editor_for_callback)) then | |
| 1242 | + allocate(saved_editor_for_callback) | |
| 1243 | + end if | |
| 1244 | + saved_editor_for_callback = editor | |
| 1245 | + | |
| 1214 | 1246 | request_id = request_definition(editor%lsp_manager, & |
| 1215 | 1247 | editor%tabs(editor%active_tab_index)%lsp_server_index, & |
| 1216 | 1248 | editor%tabs(editor%active_tab_index)%filename, & |
| 1217 | - lsp_line, lsp_char) | |
| 1249 | + lsp_line, lsp_char, handle_definition_response_wrapper) | |
| 1250 | + | |
| 1251 | + block | |
| 1252 | + integer :: debug_unit | |
| 1253 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 1254 | + write(debug_unit, '(A,I0)') 'request_definition returned request_id = ', request_id | |
| 1255 | + close(debug_unit) | |
| 1256 | + end block | |
| 1218 | 1257 | |
| 1219 | 1258 | if (request_id > 0) then |
| 1220 | 1259 | ! Response will be handled by callback |
| 1221 | - call terminal_move_cursor(editor%screen_rows, 1) | |
| 1222 | 1260 | call terminal_write('Searching for definition... ') |
| 1261 | + else | |
| 1262 | + call terminal_write('[F12] LSP request failed ') | |
| 1223 | 1263 | end if |
| 1224 | 1264 | end block |
| 1265 | + else | |
| 1266 | + call terminal_write('[F12] No LSP server for this file ') | |
| 1225 | 1267 | end if |
| 1268 | + else | |
| 1269 | + call terminal_write('[F12] No active tab ') | |
| 1226 | 1270 | end if |
| 1227 | 1271 | |
| 1228 | - case('shift-f12') | |
| 1229 | - ! Find all references | |
| 1272 | + case('shift-f12', 'alt-r') | |
| 1273 | + ! Find all references (Shift+F12 or Alt+R) | |
| 1230 | 1274 | if (editor%active_tab_index > 0 .and. editor%active_tab_index <= size(editor%tabs)) then |
| 1231 | 1275 | if (editor%tabs(editor%active_tab_index)%lsp_server_index > 0) then |
| 1232 | 1276 | ! Request references at current cursor position |
@@ -1339,8 +1383,14 @@ contains | ||
| 1339 | 1383 | end if |
| 1340 | 1384 | end if |
| 1341 | 1385 | |
| 1342 | - case('ctrl-shift-o') | |
| 1343 | - ! Document symbols outline | |
| 1386 | + case('f4', 'alt-o') | |
| 1387 | + ! Document symbols outline (F4 or Alt+O) | |
| 1388 | + block | |
| 1389 | + integer :: debug_unit | |
| 1390 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 1391 | + write(debug_unit, '(A)') '>>> INSIDE F4/ALT-O HANDLER <<<' | |
| 1392 | + close(debug_unit) | |
| 1393 | + end block | |
| 1344 | 1394 | if (editor%active_tab_index > 0 .and. editor%active_tab_index <= size(editor%tabs)) then |
| 1345 | 1395 | if (editor%tabs(editor%active_tab_index)%lsp_server_index > 0) then |
| 1346 | 1396 | ! Request document symbols |
@@ -1369,8 +1419,9 @@ contains | ||
| 1369 | 1419 | end if |
| 1370 | 1420 | end if |
| 1371 | 1421 | |
| 1372 | - case('ctrl-shift-p') | |
| 1373 | - ! Command palette | |
| 1422 | + case('ctrl-p') | |
| 1423 | + ! Command palette (Ctrl+P - VSCode standard) | |
| 1424 | + ! Note: ctrl-shift-p doesn't work - terminals can't distinguish ctrl-p from ctrl-shift-p | |
| 1374 | 1425 | block |
| 1375 | 1426 | use command_palette_module, only: show_command_palette_interactive |
| 1376 | 1427 | character(len=:), allocatable :: cmd_id |
@@ -1386,7 +1437,8 @@ contains | ||
| 1386 | 1437 | call render_screen(buffer, editor) |
| 1387 | 1438 | end block |
| 1388 | 1439 | |
| 1389 | - case('ctrl-shift-t') | |
| 1440 | + case('f6', 'alt-p') | |
| 1441 | + ! Workspace symbols (F6 or Alt+P for project) | |
| 1390 | 1442 | ! Workspace symbols (fuzzy search across project) |
| 1391 | 1443 | if (size(editor%tabs) > 0 .and. editor%active_tab_index > 0) then |
| 1392 | 1444 | block |
@@ -1525,9 +1577,22 @@ contains | ||
| 1525 | 1577 | call sync_editor_to_pane(editor) |
| 1526 | 1578 | call update_viewport(editor) |
| 1527 | 1579 | |
| 1528 | - case('ctrl-shift-d') | |
| 1529 | - ! Toggle diagnostics panel | |
| 1580 | + case('f8', 'alt-e') | |
| 1581 | + ! Toggle diagnostics panel (F8 or Alt+E for errors) | |
| 1582 | + block | |
| 1583 | + integer :: debug_unit | |
| 1584 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 1585 | + write(debug_unit, '(A)') '>>> INSIDE F8/ALT-E HANDLER <<<' | |
| 1586 | + write(debug_unit, '(A)') 'Calling toggle_diagnostics_panel...' | |
| 1587 | + close(debug_unit) | |
| 1588 | + end block | |
| 1530 | 1589 | call toggle_diagnostics_panel(editor) |
| 1590 | + block | |
| 1591 | + integer :: debug_unit | |
| 1592 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 1593 | + write(debug_unit, '(A)') 'toggle_diagnostics_panel returned' | |
| 1594 | + close(debug_unit) | |
| 1595 | + end block | |
| 1531 | 1596 | |
| 1532 | 1597 | case('alt-c') |
| 1533 | 1598 | ! Toggle case sensitivity for match mode (ctrl-d) |
@@ -1604,6 +1669,12 @@ contains | ||
| 1604 | 1669 | end if |
| 1605 | 1670 | |
| 1606 | 1671 | case default |
| 1672 | + ! DEBUG: Show unhandled function keys | |
| 1673 | + if (index(key_str, 'f') == 1 .or. index(key_str, 'shift-f') == 1) then | |
| 1674 | + call terminal_move_cursor(editor%screen_rows, 1) | |
| 1675 | + call terminal_write('[DEBUG] Unhandled key: ' // trim(key_str) // ' ') | |
| 1676 | + end if | |
| 1677 | + | |
| 1607 | 1678 | ! Check for mouse events |
| 1608 | 1679 | if (index(key_str, 'mouse-') == 1) then |
| 1609 | 1680 | call handle_mouse_event_action(key_str, editor, buffer) |
@@ -6395,4 +6466,124 @@ contains | ||
| 6395 | 6466 | end block |
| 6396 | 6467 | end subroutine navigate_to_workspace_symbol |
| 6397 | 6468 | |
| 6469 | + ! ================================================== | |
| 6470 | + ! LSP Definition Response Handler | |
| 6471 | + ! ================================================== | |
| 6472 | + | |
| 6473 | + ! Wrapper callback for go to definition | |
| 6474 | + subroutine handle_definition_response_wrapper(request_id, response) | |
| 6475 | + use lsp_protocol_module, only: lsp_message_t | |
| 6476 | + integer, intent(in) :: request_id | |
| 6477 | + type(lsp_message_t), intent(in) :: response | |
| 6478 | + | |
| 6479 | + ! Call actual handler with saved editor state | |
| 6480 | + if (allocated(saved_editor_for_callback)) then | |
| 6481 | + call handle_definition_response_impl(saved_editor_for_callback, response) | |
| 6482 | + end if | |
| 6483 | + end subroutine handle_definition_response_wrapper | |
| 6484 | + | |
| 6485 | + ! Handle LSP textDocument/definition response | |
| 6486 | + subroutine handle_definition_response_impl(editor, response) | |
| 6487 | + use lsp_protocol_module, only: lsp_message_t | |
| 6488 | + use json_module, only: json_value_t, json_get_object, json_get_string, & | |
| 6489 | + json_get_number, json_array_size, json_get_array_element, & | |
| 6490 | + json_has_key | |
| 6491 | + type(editor_state_t), intent(inout) :: editor | |
| 6492 | + type(lsp_message_t), intent(in) :: response | |
| 6493 | + type(json_value_t) :: location_obj, range_obj, start_obj | |
| 6494 | + character(len=:), allocatable :: uri, filepath | |
| 6495 | + real(8) :: line_real, col_real | |
| 6496 | + integer :: target_line, target_col, i, num_locations | |
| 6497 | + logical :: found_file | |
| 6498 | + | |
| 6499 | + ! Log response for debugging | |
| 6500 | + block | |
| 6501 | + integer :: debug_unit | |
| 6502 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 6503 | + write(debug_unit, '(A)') '>>> DEFINITION RESPONSE RECEIVED <<<' | |
| 6504 | + close(debug_unit) | |
| 6505 | + end block | |
| 6506 | + | |
| 6507 | + ! Try to treat result as array first | |
| 6508 | + num_locations = json_array_size(response%result) | |
| 6509 | + | |
| 6510 | + if (num_locations > 0) then | |
| 6511 | + ! Array of locations - take first one | |
| 6512 | + location_obj = json_get_array_element(response%result, 0) | |
| 6513 | + else if (json_has_key(response%result, "uri")) then | |
| 6514 | + ! Single location object | |
| 6515 | + location_obj = response%result | |
| 6516 | + else | |
| 6517 | + ! No definition found | |
| 6518 | + call terminal_move_cursor(editor%screen_rows, 1) | |
| 6519 | + call terminal_write('No definition found ') | |
| 6520 | + return | |
| 6521 | + end if | |
| 6522 | + | |
| 6523 | + ! Extract URI | |
| 6524 | + uri = json_get_string(location_obj, 'uri', '') | |
| 6525 | + if (len(uri) == 0) then | |
| 6526 | + call terminal_move_cursor(editor%screen_rows, 1) | |
| 6527 | + call terminal_write('Invalid definition response ') | |
| 6528 | + return | |
| 6529 | + end if | |
| 6530 | + | |
| 6531 | + ! Convert URI to filepath (remove file:// prefix) | |
| 6532 | + if (len(uri) > 7 .and. uri(1:7) == 'file://') then | |
| 6533 | + filepath = uri(8:) | |
| 6534 | + else | |
| 6535 | + filepath = uri | |
| 6536 | + end if | |
| 6537 | + | |
| 6538 | + ! Get range | |
| 6539 | + range_obj = json_get_object(location_obj, 'range') | |
| 6540 | + start_obj = json_get_object(range_obj, 'start') | |
| 6541 | + | |
| 6542 | + line_real = json_get_number(start_obj, 'line', 0.0d0) | |
| 6543 | + col_real = json_get_number(start_obj, 'character', 0.0d0) | |
| 6544 | + | |
| 6545 | + ! Convert from 0-based LSP to 1-based editor coordinates | |
| 6546 | + target_line = int(line_real) + 1 | |
| 6547 | + target_col = int(col_real) + 1 | |
| 6548 | + | |
| 6549 | + ! Log details | |
| 6550 | + block | |
| 6551 | + integer :: debug_unit | |
| 6552 | + open(newunit=debug_unit, file='/tmp/fac_keys.log', position='append', action='write') | |
| 6553 | + write(debug_unit, '(A)') 'File: ' // trim(filepath) | |
| 6554 | + write(debug_unit, '(A,I0,A,I0)') 'Position: line=', target_line, ', col=', target_col | |
| 6555 | + close(debug_unit) | |
| 6556 | + end block | |
| 6557 | + | |
| 6558 | + ! Check if the file is already open in a tab | |
| 6559 | + found_file = .false. | |
| 6560 | + do i = 1, size(editor%tabs) | |
| 6561 | + if (allocated(editor%tabs(i)%filename)) then | |
| 6562 | + if (trim(editor%tabs(i)%filename) == trim(filepath)) then | |
| 6563 | + ! Switch to this tab | |
| 6564 | + editor%active_tab_index = i | |
| 6565 | + found_file = .true. | |
| 6566 | + exit | |
| 6567 | + end if | |
| 6568 | + end if | |
| 6569 | + end do | |
| 6570 | + | |
| 6571 | + ! If file not found in tabs, report location | |
| 6572 | + if (.not. found_file) then | |
| 6573 | + call terminal_move_cursor(editor%screen_rows, 1) | |
| 6574 | + call terminal_write('Found in: ' // trim(filepath) // ' ') | |
| 6575 | + return | |
| 6576 | + end if | |
| 6577 | + | |
| 6578 | + ! Jump to the line and column in current tab | |
| 6579 | + editor%cursors(editor%active_cursor)%line = target_line | |
| 6580 | + editor%cursors(editor%active_cursor)%column = target_col | |
| 6581 | + | |
| 6582 | + ! Center viewport on target | |
| 6583 | + editor%viewport_line = max(1, target_line - editor%screen_rows / 2) | |
| 6584 | + | |
| 6585 | + call terminal_move_cursor(editor%screen_rows, 1) | |
| 6586 | + call terminal_write('Jumped to definition ') | |
| 6587 | + end subroutine handle_definition_response_impl | |
| 6588 | + | |
| 6398 | 6589 | end module command_handler_module |
src/lsp/lsp_server_manager_module.f90modified@@ -325,21 +325,24 @@ contains | ||
| 325 | 325 | server%process_id = -1 |
| 326 | 326 | end subroutine stop_server |
| 327 | 327 | |
| 328 | - subroutine send_request(server, msg, callback) | |
| 329 | - type(lsp_server_t), intent(inout) :: server | |
| 328 | + subroutine send_request(manager, server_index, msg, callback) | |
| 329 | + type(lsp_manager_t), intent(inout) :: manager | |
| 330 | + integer, intent(in) :: server_index | |
| 330 | 331 | type(lsp_message_t), intent(in) :: msg |
| 331 | 332 | procedure(response_callback), optional :: callback |
| 332 | 333 | character(len=:), allocatable :: json_msg |
| 333 | 334 | |
| 334 | - if (.not. server%initialized .and. .not. server%initializing) return | |
| 335 | + if (server_index < 1 .or. server_index > manager%num_servers) return | |
| 336 | + if (.not. manager%servers(server_index)%initialized .and. & | |
| 337 | + .not. manager%servers(server_index)%initializing) return | |
| 335 | 338 | |
| 336 | 339 | json_msg = format_json_rpc(msg) |
| 337 | - call send_raw_message(server, json_msg) | |
| 340 | + call send_raw_message(manager%servers(server_index), json_msg) | |
| 338 | 341 | |
| 339 | 342 | ! Track request and register callback |
| 340 | - call track_request(server, msg%id) | |
| 343 | + call track_request(manager%servers(server_index), msg%id) | |
| 341 | 344 | if (present(callback)) then |
| 342 | - ! TODO: Register callback in manager | |
| 345 | + call register_callback(manager, msg%id, callback) | |
| 343 | 346 | end if |
| 344 | 347 | end subroutine send_request |
| 345 | 348 | |
@@ -825,7 +828,7 @@ contains | ||
| 825 | 828 | msg = create_completion_request(trim(uri), line, character) |
| 826 | 829 | request_id = msg%id |
| 827 | 830 | |
| 828 | - call send_request(manager%servers(server_index), msg, callback) | |
| 831 | + call send_request(manager, server_index, msg, callback) | |
| 829 | 832 | end function request_completion |
| 830 | 833 | |
| 831 | 834 | ! Request hover information at cursor position |
@@ -850,7 +853,7 @@ contains | ||
| 850 | 853 | msg = create_hover_request(trim(uri), line, character) |
| 851 | 854 | request_id = msg%id |
| 852 | 855 | |
| 853 | - call send_request(manager%servers(server_index), msg, callback) | |
| 856 | + call send_request(manager, server_index, msg, callback) | |
| 854 | 857 | end function request_hover |
| 855 | 858 | |
| 856 | 859 | ! Request definition location at cursor position |
@@ -875,7 +878,7 @@ contains | ||
| 875 | 878 | msg = create_definition_request(uri, line, character) |
| 876 | 879 | request_id = msg%id |
| 877 | 880 | |
| 878 | - call send_request(manager%servers(server_index), msg, callback) | |
| 881 | + call send_request(manager, server_index, msg, callback) | |
| 879 | 882 | end function request_definition |
| 880 | 883 | |
| 881 | 884 | ! Request references at cursor position |
@@ -901,7 +904,7 @@ contains | ||
| 901 | 904 | msg = create_references_request(uri, line, character, .true.) |
| 902 | 905 | request_id = msg%id |
| 903 | 906 | |
| 904 | - call send_request(manager%servers(server_index), msg, callback) | |
| 907 | + call send_request(manager, server_index, msg, callback) | |
| 905 | 908 | end function request_references |
| 906 | 909 | |
| 907 | 910 | ! Request code actions for a range |
@@ -929,7 +932,7 @@ contains | ||
| 929 | 932 | msg = create_code_action_request(uri, start_line, start_char, end_line, end_char) |
| 930 | 933 | request_id = msg%id |
| 931 | 934 | |
| 932 | - call send_request(manager%servers(server_index), msg, callback) | |
| 935 | + call send_request(manager, server_index, msg, callback) | |
| 933 | 936 | end function request_code_actions |
| 934 | 937 | |
| 935 | 938 | ! Request document symbols |
@@ -954,7 +957,7 @@ contains | ||
| 954 | 957 | msg = create_document_symbols_request(uri) |
| 955 | 958 | request_id = msg%id |
| 956 | 959 | |
| 957 | - call send_request(manager%servers(server_index), msg, callback) | |
| 960 | + call send_request(manager, server_index, msg, callback) | |
| 958 | 961 | end function request_document_symbols |
| 959 | 962 | |
| 960 | 963 | ! Request signature help |
@@ -980,7 +983,7 @@ contains | ||
| 980 | 983 | msg = create_signature_help_request(uri, line, character) |
| 981 | 984 | request_id = msg%id |
| 982 | 985 | |
| 983 | - call send_request(manager%servers(server_index), msg, callback) | |
| 986 | + call send_request(manager, server_index, msg, callback) | |
| 984 | 987 | end function request_signature_help |
| 985 | 988 | |
| 986 | 989 | ! Request formatting |
@@ -1007,7 +1010,7 @@ contains | ||
| 1007 | 1010 | msg = create_formatting_request(uri, tab_size, insert_spaces) |
| 1008 | 1011 | request_id = msg%id |
| 1009 | 1012 | |
| 1010 | - call send_request(manager%servers(server_index), msg, callback) | |
| 1013 | + call send_request(manager, server_index, msg, callback) | |
| 1011 | 1014 | end function request_formatting |
| 1012 | 1015 | |
| 1013 | 1016 | ! Request rename |
@@ -1034,7 +1037,7 @@ contains | ||
| 1034 | 1037 | msg = create_rename_request(uri, line, character, new_name) |
| 1035 | 1038 | request_id = msg%id |
| 1036 | 1039 | |
| 1037 | - call send_request(manager%servers(server_index), msg, callback) | |
| 1040 | + call send_request(manager, server_index, msg, callback) | |
| 1038 | 1041 | end function request_rename |
| 1039 | 1042 | |
| 1040 | 1043 | ! Request workspace symbols |
@@ -1056,7 +1059,7 @@ contains | ||
| 1056 | 1059 | msg = create_workspace_symbols_request(query) |
| 1057 | 1060 | request_id = msg%id |
| 1058 | 1061 | |
| 1059 | - call send_request(manager%servers(server_index), msg, callback) | |
| 1062 | + call send_request(manager, server_index, msg, callback) | |
| 1060 | 1063 | end function request_workspace_symbols |
| 1061 | 1064 | |
| 1062 | 1065 | ! Set the diagnostics notification handler |
src/syntax/syntax_highlighter_module.f90modified@@ -54,15 +54,15 @@ module syntax_highlighter_module | ||
| 54 | 54 | end type syntax_highlighter_t |
| 55 | 55 | |
| 56 | 56 | ! Color mapping (ANSI escape codes) |
| 57 | - character(len=16), parameter :: COLOR_KEYWORD = char(27) // '[1;34m' ! Bold Blue | |
| 58 | - character(len=16), parameter :: COLOR_STRING = char(27) // '[32m' ! Green | |
| 59 | - character(len=16), parameter :: COLOR_NUMBER = char(27) // '[35m' ! Magenta | |
| 60 | - character(len=16), parameter :: COLOR_COMMENT = char(27) // '[90m' ! Gray | |
| 61 | - character(len=16), parameter :: COLOR_OPERATOR = char(27) // '[33m' ! Yellow | |
| 62 | - character(len=16), parameter :: COLOR_TYPE = char(27) // '[36m' ! Cyan | |
| 63 | - character(len=16), parameter :: COLOR_FUNCTION = char(27) // '[1;36m' ! Bold Cyan | |
| 64 | - character(len=16), parameter :: COLOR_PREPROC = char(27) // '[95m' ! Light Magenta | |
| 65 | - character(len=16), parameter :: COLOR_RESET = char(27) // '[0m' | |
| 57 | + character(len=*), parameter :: COLOR_KEYWORD = char(27) // '[1;34m' ! Bold Blue | |
| 58 | + character(len=*), parameter :: COLOR_STRING = char(27) // '[32m' ! Green | |
| 59 | + character(len=*), parameter :: COLOR_NUMBER = char(27) // '[35m' ! Magenta | |
| 60 | + character(len=*), parameter :: COLOR_COMMENT = char(27) // '[90m' ! Gray | |
| 61 | + character(len=*), parameter :: COLOR_OPERATOR = char(27) // '[33m' ! Yellow | |
| 62 | + character(len=*), parameter :: COLOR_TYPE = char(27) // '[36m' ! Cyan | |
| 63 | + character(len=*), parameter :: COLOR_FUNCTION = char(27) // '[1;36m' ! Bold Cyan | |
| 64 | + character(len=*), parameter :: COLOR_PREPROC = char(27) // '[95m' ! Light Magenta | |
| 65 | + character(len=*), parameter :: COLOR_RESET = char(27) // '[0m' | |
| 66 | 66 | |
| 67 | 67 | contains |
| 68 | 68 | |
src/terminal/input_handler_module.f90modified@@ -194,27 +194,34 @@ contains | ||
| 194 | 194 | end if |
| 195 | 195 | else if (ch3 == '1' .or. ch3 == '2' .or. ch3 == '3' .or. ch3 == '4' .or. & |
| 196 | 196 | ch3 == '5' .or. ch3 == '7' .or. ch3 == '8' .or. ch3 == '9') then |
| 197 | - ! Function keys F1-F9: ESC [ 1 X ~ | |
| 197 | + ! Function keys F1-F8: ESC [ 1 X ~ or ESC [ 1 X ; modifier ~ | |
| 198 | 198 | char_code = terminal_read_char() |
| 199 | - if (char_code >= 0 .and. achar(char_code) == '~') then | |
| 200 | - select case(ch3) | |
| 201 | - case('1') | |
| 202 | - key_str = 'f1' | |
| 203 | - case('2') | |
| 204 | - key_str = 'f2' | |
| 205 | - case('3') | |
| 206 | - key_str = 'f3' | |
| 207 | - case('4') | |
| 208 | - key_str = 'f4' | |
| 209 | - case('5') | |
| 210 | - key_str = 'f5' | |
| 211 | - case('7') | |
| 212 | - key_str = 'f6' | |
| 213 | - case('8') | |
| 214 | - key_str = 'f7' | |
| 215 | - case('9') | |
| 216 | - key_str = 'f8' | |
| 217 | - end select | |
| 199 | + if (char_code >= 0) then | |
| 200 | + ch = achar(char_code) | |
| 201 | + if (ch == '~') then | |
| 202 | + ! Unmodified F1-F8 | |
| 203 | + select case(ch3) | |
| 204 | + case('1') | |
| 205 | + key_str = 'f1' | |
| 206 | + case('2') | |
| 207 | + key_str = 'f2' | |
| 208 | + case('3') | |
| 209 | + key_str = 'f3' | |
| 210 | + case('4') | |
| 211 | + key_str = 'f4' | |
| 212 | + case('5') | |
| 213 | + key_str = 'f5' | |
| 214 | + case('7') | |
| 215 | + key_str = 'f6' | |
| 216 | + case('8') | |
| 217 | + key_str = 'f7' | |
| 218 | + case('9') | |
| 219 | + key_str = 'f8' | |
| 220 | + end select | |
| 221 | + else if (ch == ';') then | |
| 222 | + ! Modified F1-F8: ESC [ 1 X ; modifier ~ | |
| 223 | + call handle_modified_function_key(key_str, '1', ch3) | |
| 224 | + end if | |
| 218 | 225 | end if |
| 219 | 226 | else if (ch3 == ';') then |
| 220 | 227 | ! Modified arrow key or home/end: ESC [ 1 ; 2 A format |
@@ -227,19 +234,26 @@ contains | ||
| 227 | 234 | if (char_code >= 0) then |
| 228 | 235 | ch3 = achar(char_code) |
| 229 | 236 | if (ch3 == '0' .or. ch3 == '1' .or. ch3 == '3' .or. ch3 == '4') then |
| 230 | - ! Function keys F9-F12: ESC [ 2 X ~ | |
| 237 | + ! Function keys F9-F12: ESC [ 2 X ~ or ESC [ 2 X ; modifier ~ | |
| 231 | 238 | char_code = terminal_read_char() |
| 232 | - if (char_code >= 0 .and. achar(char_code) == '~') then | |
| 233 | - select case(ch3) | |
| 234 | - case('0') | |
| 235 | - key_str = 'f9' | |
| 236 | - case('1') | |
| 237 | - key_str = 'f10' | |
| 238 | - case('3') | |
| 239 | - key_str = 'f11' | |
| 240 | - case('4') | |
| 241 | - key_str = 'f12' | |
| 242 | - end select | |
| 239 | + if (char_code >= 0) then | |
| 240 | + ch = achar(char_code) | |
| 241 | + if (ch == '~') then | |
| 242 | + ! Unmodified F9-F12 | |
| 243 | + select case(ch3) | |
| 244 | + case('0') | |
| 245 | + key_str = 'f9' | |
| 246 | + case('1') | |
| 247 | + key_str = 'f10' | |
| 248 | + case('3') | |
| 249 | + key_str = 'f11' | |
| 250 | + case('4') | |
| 251 | + key_str = 'f12' | |
| 252 | + end select | |
| 253 | + else if (ch == ';') then | |
| 254 | + ! Modified F9-F12: ESC [ 2 X ; modifier ~ | |
| 255 | + call handle_modified_function_key(key_str, '2', ch3) | |
| 256 | + end if | |
| 243 | 257 | end if |
| 244 | 258 | else if (ch3 == ';') then |
| 245 | 259 | ! ESC [ 2 ; A format (shift+arrow) |
@@ -662,6 +676,90 @@ contains | ||
| 662 | 676 | end if |
| 663 | 677 | end subroutine handle_modified_special_key |
| 664 | 678 | |
| 679 | + subroutine handle_modified_function_key(key_str, series, fkey_code) | |
| 680 | + character(len=*), intent(out) :: key_str | |
| 681 | + character, intent(in) :: series ! '1' for ESC[1X~, '2' for ESC[2X~ | |
| 682 | + character, intent(in) :: fkey_code ! The X in ESC[1X~ or ESC[2X~ | |
| 683 | + character :: ch, modifier_ch | |
| 684 | + integer :: char_code, modifier | |
| 685 | + character(len=10) :: base_key | |
| 686 | + | |
| 687 | + key_str = '' | |
| 688 | + | |
| 689 | + ! Determine base function key from series and code | |
| 690 | + if (series == '1') then | |
| 691 | + ! ESC[1X~ format: 1=F1, 2=F2, 3=F3, 4=F4, 5=F5, 7=F6, 8=F7, 9=F8 | |
| 692 | + select case(fkey_code) | |
| 693 | + case('1') | |
| 694 | + base_key = 'f1' | |
| 695 | + case('2') | |
| 696 | + base_key = 'f2' | |
| 697 | + case('3') | |
| 698 | + base_key = 'f3' | |
| 699 | + case('4') | |
| 700 | + base_key = 'f4' | |
| 701 | + case('5') | |
| 702 | + base_key = 'f5' | |
| 703 | + case('7') | |
| 704 | + base_key = 'f6' | |
| 705 | + case('8') | |
| 706 | + base_key = 'f7' | |
| 707 | + case('9') | |
| 708 | + base_key = 'f8' | |
| 709 | + case default | |
| 710 | + return | |
| 711 | + end select | |
| 712 | + else if (series == '2') then | |
| 713 | + ! ESC[2X~ format: 0=F9, 1=F10, 3=F11, 4=F12 | |
| 714 | + select case(fkey_code) | |
| 715 | + case('0') | |
| 716 | + base_key = 'f9' | |
| 717 | + case('1') | |
| 718 | + base_key = 'f10' | |
| 719 | + case('3') | |
| 720 | + base_key = 'f11' | |
| 721 | + case('4') | |
| 722 | + base_key = 'f12' | |
| 723 | + case default | |
| 724 | + return | |
| 725 | + end select | |
| 726 | + else | |
| 727 | + return | |
| 728 | + end if | |
| 729 | + | |
| 730 | + ! Read modifier (should be a digit 2-8) | |
| 731 | + char_code = terminal_read_char() | |
| 732 | + if (char_code < 0) return | |
| 733 | + modifier_ch = achar(char_code) | |
| 734 | + | |
| 735 | + ! Read terminating ~ | |
| 736 | + char_code = terminal_read_char() | |
| 737 | + if (char_code < 0 .or. achar(char_code) /= '~') return | |
| 738 | + | |
| 739 | + ! Parse modifier: 2=Shift, 3=Alt, 4=Alt+Shift, 5=Ctrl, 6=Ctrl+Shift, 7=Alt+Ctrl, 8=Alt+Shift | |
| 740 | + read(modifier_ch, '(i1)') modifier | |
| 741 | + | |
| 742 | + select case(modifier) | |
| 743 | + case(2) ! Shift | |
| 744 | + key_str = 'shift-' // trim(base_key) | |
| 745 | + case(3) ! Alt | |
| 746 | + key_str = 'alt-' // trim(base_key) | |
| 747 | + case(4) ! Alt+Shift | |
| 748 | + key_str = 'alt-shift-' // trim(base_key) | |
| 749 | + case(5) ! Ctrl | |
| 750 | + key_str = 'ctrl-' // trim(base_key) | |
| 751 | + case(6) ! Ctrl+Shift | |
| 752 | + key_str = 'ctrl-shift-' // trim(base_key) | |
| 753 | + case(7) ! Alt+Ctrl | |
| 754 | + key_str = 'alt-ctrl-' // trim(base_key) | |
| 755 | + case(8) ! Alt+Ctrl+Shift | |
| 756 | + key_str = 'alt-ctrl-shift-' // trim(base_key) | |
| 757 | + case default | |
| 758 | + ! Unknown modifier, return unmodified key | |
| 759 | + key_str = trim(base_key) | |
| 760 | + end select | |
| 761 | + end subroutine handle_modified_function_key | |
| 762 | + | |
| 665 | 763 | subroutine handle_mouse_event(key_str) |
| 666 | 764 | character(len=*), intent(out) :: key_str |
| 667 | 765 | character :: ch |
src/ui/help_display_module.f90modified@@ -248,7 +248,11 @@ contains | ||
| 248 | 248 | lines(i) = " F12 go to definition"; i = i + 1 |
| 249 | 249 | lines(i) = " shift-F12 find all references"; i = i + 1 |
| 250 | 250 | lines(i) = " alt-, (alt-comma) jump back (navigation history)"; i = i + 1 |
| 251 | - lines(i) = " ctrl-shift-d toggle diagnostics panel"; i = i + 1 | |
| 251 | + lines(i) = " F2 rename symbol"; i = i + 1 | |
| 252 | + lines(i) = " F4 document symbols (outline)"; i = i + 1 | |
| 253 | + lines(i) = " F6 workspace symbols (search project)"; i = i + 1 | |
| 254 | + lines(i) = " F8 toggle diagnostics panel (errors)"; i = i + 1 | |
| 255 | + lines(i) = " ctrl-p command palette"; i = i + 1 | |
| 252 | 256 | lines(i) = ""; i = i + 1 |
| 253 | 257 | |
| 254 | 258 | n_lines = i - 1 |
test_keys.shadded@@ -0,0 +1,25 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# Test what escape sequences the terminal sends for function keys | |
| 3 | + | |
| 4 | +echo "Press keys to see their escape sequences (Ctrl+C to quit):" | |
| 5 | +echo "Try: F12, Shift-F12, F8, F4, F6, F2" | |
| 6 | +echo "" | |
| 7 | + | |
| 8 | +# Read raw input and show escape sequences | |
| 9 | +while true; do | |
| 10 | + read -rsn1 char | |
| 11 | + if [[ $char == $'\e' ]]; then | |
| 12 | + # Start of escape sequence | |
| 13 | + seq="$char" | |
| 14 | + # Read the rest quickly | |
| 15 | + while read -rsn1 -t 0.001 char; do | |
| 16 | + seq="$seq$char" | |
| 17 | + done | |
| 18 | + # Show it in a readable format | |
| 19 | + echo -n "Escape sequence: " | |
| 20 | + echo -n "$seq" | od -An -tx1 | tr -d ' \n' | |
| 21 | + echo " (raw: $(echo -n "$seq" | cat -v))" | |
| 22 | + else | |
| 23 | + echo "Character: '$char' (ASCII: $(printf '%d' "'$char"))" | |
| 24 | + fi | |
| 25 | +done | |
test_raw_keys.shadded@@ -0,0 +1,32 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# Test what escape sequences WezTerm actually sends for F-keys | |
| 3 | +# Press F8, F12, Shift-F12, etc. and see what comes through | |
| 4 | + | |
| 5 | +echo "=== Raw Key Sequence Tester ===" | |
| 6 | +echo "Press F8, F12, or Shift-F12, then Ctrl+C to exit" | |
| 7 | +echo "This will show the EXACT escape sequences received" | |
| 8 | +echo "" | |
| 9 | + | |
| 10 | +# Use od to show the raw bytes of each key press | |
| 11 | +while true; do | |
| 12 | + echo -n "Press a key: " | |
| 13 | + # Read one key sequence with timeout | |
| 14 | + IFS= read -rsn1 char | |
| 15 | + | |
| 16 | + # If it's ESC, read the rest of the sequence | |
| 17 | + if [ "$char" = $'\x1b' ]; then | |
| 18 | + # Read the next character(s) quickly | |
| 19 | + rest="" | |
| 20 | + while IFS= read -rsn1 -t 0.01 c; do | |
| 21 | + rest="$rest$c" | |
| 22 | + done | |
| 23 | + full="$char$rest" | |
| 24 | + | |
| 25 | + # Show as hex and as escaped string | |
| 26 | + echo -n "$full" | od -An -tx1 | tr -d ' \n' | |
| 27 | + echo -n " = ESC" | |
| 28 | + echo "$rest" | sed 's/./[&]/g' | |
| 29 | + else | |
| 30 | + echo "$char" | |
| 31 | + fi | |
| 32 | +done | |
tests/scratch_files/README.mdadded@@ -0,0 +1,268 @@ | ||
| 1 | +# LSP Feature Test Files | |
| 2 | + | |
| 3 | +These files are designed to test all LSP features in `fac`. Each file contains intentional errors and cross-references for comprehensive testing. | |
| 4 | + | |
| 5 | +## Files Overview | |
| 6 | + | |
| 7 | +### Python Files | |
| 8 | +- **calculator.py** - Math functions with intentional errors | |
| 9 | + - Undefined variable errors (`reslt` instead of `result`) | |
| 10 | + - Missing import errors (`math` module) | |
| 11 | + - Multiple functions for testing navigation | |
| 12 | + | |
| 13 | +- **main.py** - Main program importing calculator | |
| 14 | + - Cross-file references for testing F12 | |
| 15 | + - Class structure for Document Symbols | |
| 16 | + - Missing import error (`divide` function) | |
| 17 | + | |
| 18 | +### Fortran Files | |
| 19 | +- **math_utils.f90** - Math utilities module | |
| 20 | + - Vector operations (add, dot product, magnitude) | |
| 21 | + - Matrix multiplication | |
| 22 | + - Intentional errors (uninitialized variables) | |
| 23 | + | |
| 24 | +- **test_program.f90** - Program using math_utils | |
| 25 | + - Cross-file references to test F12 | |
| 26 | + - Undefined variable error | |
| 27 | + | |
| 28 | +### C Files | |
| 29 | +- **utils.c** - Utility functions | |
| 30 | + - Factorial, fibonacci, array operations | |
| 31 | + - Intentional errors (undefined variables, type mismatches) | |
| 32 | + | |
| 33 | +- **main.c** - Main program using utils | |
| 34 | + - Cross-file function calls | |
| 35 | + - Structure definition | |
| 36 | + - Multiple intentional errors | |
| 37 | + | |
| 38 | +## Testing LSP Features | |
| 39 | + | |
| 40 | +### 1. Diagnostics (F8) | |
| 41 | + | |
| 42 | +**Test:** Open any file and check for error markers in the gutter. | |
| 43 | + | |
| 44 | +Expected errors: | |
| 45 | +- **calculator.py**: `reslt` undefined, `math` not imported | |
| 46 | +- **main.py**: `divide` not imported | |
| 47 | +- **math_utils.f90**: `undefined_var` in `broken_routine` | |
| 48 | +- **test_program.f90**: `undefined_result` not declared | |
| 49 | +- **utils.c**: `undefined_var`, type mismatch, missing return | |
| 50 | +- **main.c**: `undefined_function`, type mismatch, `undeclared_var` | |
| 51 | + | |
| 52 | +**How to test:** | |
| 53 | +```bash | |
| 54 | +fac calculator.py | |
| 55 | +# Press F8 to open diagnostics panel | |
| 56 | +# Navigate with j/k or ↑/↓ | |
| 57 | +# Press Enter to jump to error | |
| 58 | +``` | |
| 59 | + | |
| 60 | +### 2. Go to Definition (F12) | |
| 61 | + | |
| 62 | +**Test:** Cross-file navigation between importing files. | |
| 63 | + | |
| 64 | +**In main.py:** | |
| 65 | +1. Put cursor on `add` on line ~15 | |
| 66 | +2. Press `F12` | |
| 67 | +3. Should jump to `def add()` in calculator.py | |
| 68 | + | |
| 69 | +**In test_program.f90:** | |
| 70 | +1. Put cursor on `vector_add` on line ~23 | |
| 71 | +2. Press `F12` | |
| 72 | +3. Should jump to `subroutine vector_add` in math_utils.f90 | |
| 73 | + | |
| 74 | +**In main.c:** | |
| 75 | +1. Put cursor on `factorial` on line ~28 | |
| 76 | +2. Press `F12` | |
| 77 | +3. Should jump to `int factorial()` in utils.c | |
| 78 | + | |
| 79 | +**Return:** Press `Alt+,` to jump back! | |
| 80 | + | |
| 81 | +### 3. Find References (Shift+F12) | |
| 82 | + | |
| 83 | +**Test:** Find all usages of a function. | |
| 84 | + | |
| 85 | +**In calculator.py:** | |
| 86 | +1. Put cursor on `calculate_total` function name | |
| 87 | +2. Press `Shift+F12` | |
| 88 | +3. Should show: | |
| 89 | + - Definition in calculator.py | |
| 90 | + - Usage in main.py (imported and called) | |
| 91 | + | |
| 92 | +**In math_utils.f90:** | |
| 93 | +1. Put cursor on `vector_dot` | |
| 94 | +2. Press `Shift+F12` | |
| 95 | +3. Should show usage in test_program.f90 | |
| 96 | + | |
| 97 | +### 4. Code Actions (Ctrl+.) | |
| 98 | + | |
| 99 | +**Test:** Quick fixes for errors. | |
| 100 | + | |
| 101 | +**In calculator.py line ~48:** | |
| 102 | +1. Put cursor on `reslt` (undefined error) | |
| 103 | +2. Press `Ctrl+.` | |
| 104 | +3. Should offer to fix the typo or define `reslt` | |
| 105 | + | |
| 106 | +**In main.py line ~45:** | |
| 107 | +1. Put cursor on `divide` (not imported error) | |
| 108 | +2. Press `Ctrl+.` | |
| 109 | +3. Should offer to add `divide` to imports | |
| 110 | + | |
| 111 | +### 5. Rename Symbol (F2) | |
| 112 | + | |
| 113 | +**Test:** Rename across files. | |
| 114 | + | |
| 115 | +**In calculator.py:** | |
| 116 | +1. Put cursor on `add` function name | |
| 117 | +2. Press `F2` | |
| 118 | +3. Type new name (e.g., `add_numbers`) | |
| 119 | +4. Press Enter | |
| 120 | +5. Check: | |
| 121 | + - Function renamed in calculator.py | |
| 122 | + - Import statement updated in main.py | |
| 123 | + - Function call updated in main.py | |
| 124 | + | |
| 125 | +**Undo:** Press `Ctrl+Z` repeatedly to revert | |
| 126 | + | |
| 127 | +### 6. Document Symbols (F4) | |
| 128 | + | |
| 129 | +**Test:** See file outline. | |
| 130 | + | |
| 131 | +**In calculator.py:** | |
| 132 | +1. Press `F4` | |
| 133 | +2. Should see: | |
| 134 | + - `add` function | |
| 135 | + - `subtract` function | |
| 136 | + - `multiply` function | |
| 137 | + - `divide` function | |
| 138 | + - etc. | |
| 139 | +3. Type "broken" to filter | |
| 140 | +4. Press Enter to jump to function | |
| 141 | + | |
| 142 | +**In main.py:** | |
| 143 | +1. Press `F4` | |
| 144 | +2. Should see: | |
| 145 | + - `DataProcessor` class | |
| 146 | + - `process_data` function | |
| 147 | + - `main` function | |
| 148 | + | |
| 149 | +### 7. Workspace Symbols (F6) | |
| 150 | + | |
| 151 | +**Test:** Search symbols across ALL files. | |
| 152 | + | |
| 153 | +1. Press `F6` | |
| 154 | +2. Type "add" | |
| 155 | +3. Should see: | |
| 156 | + - `add` function in calculator.py | |
| 157 | + - `vector_add` in math_utils.f90 | |
| 158 | + - Maybe `sum_array` in utils.c (fuzzy match) | |
| 159 | +4. Navigate with j/k | |
| 160 | +5. Press Enter to jump (opens file if needed) | |
| 161 | + | |
| 162 | +**Try different searches:** | |
| 163 | +- "calc" → finds `calculate_total`, `calculator`, etc. | |
| 164 | +- "vector" → finds all vector functions | |
| 165 | +- "fact" → finds `factorial` | |
| 166 | + | |
| 167 | +### 8. Signature Help | |
| 168 | + | |
| 169 | +**Test:** Parameter hints while typing. | |
| 170 | + | |
| 171 | +**In any Python file:** | |
| 172 | +1. Type: `calculate_total(` | |
| 173 | +2. Should see tooltip: `calculate_total(items)` | |
| 174 | +3. As you type, current parameter is highlighted | |
| 175 | + | |
| 176 | +**In Fortran:** | |
| 177 | +1. Type: `call vector_add(` | |
| 178 | +2. Should see signature with parameters | |
| 179 | + | |
| 180 | +### 9. Document Formatting (Shift+Alt+F) | |
| 181 | + | |
| 182 | +**Test:** Auto-format code. | |
| 183 | + | |
| 184 | +**In calculator.py:** | |
| 185 | +1. Mess up the formatting (add extra spaces, wrong indentation) | |
| 186 | +2. Press `Shift+Alt+F` | |
| 187 | +3. Code should be auto-formatted to PEP 8 style | |
| 188 | + | |
| 189 | +**In utils.c:** | |
| 190 | +1. Mess up braces and indentation | |
| 191 | +2. Press `Shift+Alt+F` | |
| 192 | +3. Code should be formatted with clang-format | |
| 193 | + | |
| 194 | +### 10. Command Palette (Ctrl+P) | |
| 195 | + | |
| 196 | +**Test:** Access commands without keybindings. | |
| 197 | + | |
| 198 | +1. Press `Ctrl+P` | |
| 199 | +2. Type "format" | |
| 200 | +3. Select "Format Document" | |
| 201 | +4. Same as Shift+Alt+F | |
| 202 | + | |
| 203 | +Or: | |
| 204 | +1. Press `Ctrl+P` | |
| 205 | +2. Type "def" | |
| 206 | +3. Select "Go to Definition" | |
| 207 | +4. Same as F12 | |
| 208 | + | |
| 209 | +## Language Server Setup | |
| 210 | + | |
| 211 | +To test these files, you need language servers installed: | |
| 212 | + | |
| 213 | +**Python:** | |
| 214 | +```bash | |
| 215 | +pip install python-lsp-server | |
| 216 | +``` | |
| 217 | + | |
| 218 | +**Fortran:** | |
| 219 | +```bash | |
| 220 | +pip install fortls | |
| 221 | +``` | |
| 222 | + | |
| 223 | +**C/C++:** | |
| 224 | +```bash | |
| 225 | +# macOS | |
| 226 | +brew install llvm | |
| 227 | + | |
| 228 | +# Linux | |
| 229 | +sudo apt install clangd | |
| 230 | +``` | |
| 231 | + | |
| 232 | +## Expected Behavior | |
| 233 | + | |
| 234 | +### With Language Server Installed: | |
| 235 | +- ✅ Red/yellow error markers in gutter | |
| 236 | +- ✅ F12 jumps to definitions (even across files) | |
| 237 | +- ✅ Shift+F12 shows all references | |
| 238 | +- ✅ Ctrl+. offers quick fixes | |
| 239 | +- ✅ F2 renames across files | |
| 240 | +- ✅ F4 shows file outline | |
| 241 | +- ✅ F6 searches all symbols | |
| 242 | +- ✅ Shift+Alt+F formats code | |
| 243 | + | |
| 244 | +### Without Language Server: | |
| 245 | +- ❌ No error markers | |
| 246 | +- ❌ LSP features won't work | |
| 247 | +- ✅ Basic editing still works | |
| 248 | +- ✅ Syntax highlighting may work (if implemented separately) | |
| 249 | + | |
| 250 | +## Known Issues to Test | |
| 251 | + | |
| 252 | +1. **Syntax highlighting for Fortran** - Check if keywords are colored | |
| 253 | +2. **F8 vs Ctrl+D** - Check if modifier keys work correctly | |
| 254 | +3. **Cross-file tabs** - Check if F12 opens new tabs correctly | |
| 255 | +4. **Jump stack** - Check if Alt+, returns to previous location | |
| 256 | + | |
| 257 | +## Quick Test Checklist | |
| 258 | + | |
| 259 | +- [ ] Open calculator.py and see diagnostics (red/yellow markers) | |
| 260 | +- [ ] Press F8 and see error list | |
| 261 | +- [ ] F12 on `add` in main.py → jumps to calculator.py | |
| 262 | +- [ ] Alt+, → jumps back | |
| 263 | +- [ ] Shift+F12 on `calculate_total` → shows references | |
| 264 | +- [ ] F2 on `multiply` → rename and see it update in both files | |
| 265 | +- [ ] F4 → see file outline | |
| 266 | +- [ ] F6, type "vector" → find Fortran functions | |
| 267 | +- [ ] Ctrl+. on an error → see quick fixes | |
| 268 | +- [ ] Shift+Alt+F → format code | |
tests/scratch_files/calculator.pyadded@@ -0,0 +1,68 @@ | ||
| 1 | +""" | |
| 2 | +Simple calculator module for testing LSP features. | |
| 3 | + | |
| 4 | +Test these features: | |
| 5 | +- Diagnostics: See the intentional errors below | |
| 6 | +- Go to Definition: F12 on function calls | |
| 7 | +- Find References: Shift+F12 on function names | |
| 8 | +- Rename: F2 on any function/variable | |
| 9 | +- Code Actions: Ctrl+. on errors | |
| 10 | +- Document Symbols: Ctrl+Shift+O to see outline | |
| 11 | +- Formatting: Shift+Alt+F to format | |
| 12 | +""" | |
| 13 | + | |
| 14 | +def add(x, y): | |
| 15 | + """Add two numbers together.""" | |
| 16 | + return x + y | |
| 17 | + | |
| 18 | + | |
| 19 | +def subtract(x, y): | |
| 20 | + """Subtract y from x.""" | |
| 21 | + return x - y | |
| 22 | + | |
| 23 | + | |
| 24 | +def multiply(x, y): | |
| 25 | + """Multiply two numbers.""" | |
| 26 | + return x * y | |
| 27 | + | |
| 28 | + | |
| 29 | +def divide(x, y): | |
| 30 | + """Divide x by y.""" | |
| 31 | + if y == 0: | |
| 32 | + raise ValueError("Cannot divide by zero") | |
| 33 | + return x / y | |
| 34 | + | |
| 35 | + | |
| 36 | +def calculate_total(items): | |
| 37 | + """Calculate total from a list of numbers.""" | |
| 38 | + total = 0 | |
| 39 | + for item in items: | |
| 40 | + total = total + item | |
| 41 | + return total | |
| 42 | + | |
| 43 | + | |
| 44 | +# Intentional error for diagnostics testing | |
| 45 | +def broken_function(): | |
| 46 | + """This function has an error - undefined variable.""" | |
| 47 | + result = add(5, 3) | |
| 48 | + print(f"Result: {reslt}") # ERROR: typo - should be 'result' | |
| 49 | + return reslt | |
| 50 | + | |
| 51 | + | |
| 52 | +# Another intentional error - missing import | |
| 53 | +def use_math(): | |
| 54 | + """Uses math module without importing it.""" | |
| 55 | + return math.sqrt(16) # ERROR: 'math' is not defined | |
| 56 | + | |
| 57 | + | |
| 58 | +# Test code | |
| 59 | +if __name__ == "__main__": | |
| 60 | + # These work fine | |
| 61 | + print(add(10, 5)) | |
| 62 | + print(subtract(10, 5)) | |
| 63 | + print(multiply(10, 5)) | |
| 64 | + print(divide(10, 5)) | |
| 65 | + | |
| 66 | + # This will show diagnostic errors | |
| 67 | + broken_function() | |
| 68 | + use_math() | |
tests/scratch_files/main.cadded@@ -0,0 +1,79 @@ | ||
| 1 | +/* | |
| 2 | + * Main program using utility functions | |
| 3 | + * | |
| 4 | + * Test cross-file navigation: | |
| 5 | + * - F12 on 'factorial' should jump to utils.c | |
| 6 | + * - Shift+F12 on 'fibonacci' should show all usages | |
| 7 | + * - Ctrl+Shift+T and search "sum" to find sum_array | |
| 8 | + */ | |
| 9 | + | |
| 10 | +#include <stdio.h> | |
| 11 | + | |
| 12 | +// Forward declarations from utils.c | |
| 13 | +int factorial(int n); | |
| 14 | +int fibonacci(int n); | |
| 15 | +int sum_array(int *arr, int size); | |
| 16 | +int find_max(int *arr, int size); | |
| 17 | + | |
| 18 | +// Structure for testing Document Symbols (Ctrl+Shift+O) | |
| 19 | +typedef struct { | |
| 20 | + int id; | |
| 21 | + char name[50]; | |
| 22 | + double value; | |
| 23 | +} Record; | |
| 24 | + | |
| 25 | +// Test: F12 on this function name to jump to definition | |
| 26 | +void process_records(Record *records, int count) { | |
| 27 | + printf("Processing %d records\n", count); | |
| 28 | + for (int i = 0; i < count; i++) { | |
| 29 | + printf("Record %d: %s = %.2f\n", | |
| 30 | + records[i].id, | |
| 31 | + records[i].name, | |
| 32 | + records[i].value); | |
| 33 | + } | |
| 34 | +} | |
| 35 | + | |
| 36 | +int main() { | |
| 37 | + printf("Testing LSP features in C\n\n"); | |
| 38 | + | |
| 39 | + // Test factorial | |
| 40 | + // F12 on 'factorial' should jump to utils.c | |
| 41 | + int fact = factorial(5); | |
| 42 | + printf("factorial(5) = %d\n", fact); | |
| 43 | + | |
| 44 | + // Test fibonacci | |
| 45 | + // Shift+F12 on 'fibonacci' should show all usages | |
| 46 | + int fib = fibonacci(7); | |
| 47 | + printf("fibonacci(7) = %d\n", fib); | |
| 48 | + | |
| 49 | + // Test array functions | |
| 50 | + int numbers[] = {5, 2, 8, 1, 9, 3}; | |
| 51 | + int size = sizeof(numbers) / sizeof(numbers[0]); | |
| 52 | + | |
| 53 | + // F12 on 'sum_array' should jump to definition | |
| 54 | + int total = sum_array(numbers, size); | |
| 55 | + printf("sum_array() = %d\n", total); | |
| 56 | + | |
| 57 | + // F12 on 'find_max' should jump to definition | |
| 58 | + int max = find_max(numbers, size); | |
| 59 | + printf("find_max() = %d\n", max); | |
| 60 | + | |
| 61 | + // Test with records | |
| 62 | + Record records[2] = { | |
| 63 | + {1, "Alpha", 3.14}, | |
| 64 | + {2, "Beta", 2.71} | |
| 65 | + }; | |
| 66 | + process_records(records, 2); | |
| 67 | + | |
| 68 | + // Intentional errors for diagnostics | |
| 69 | + // ERROR: undefined function | |
| 70 | + int result = undefined_function(10); | |
| 71 | + | |
| 72 | + // ERROR: type mismatch | |
| 73 | + char *str = fact; | |
| 74 | + | |
| 75 | + // ERROR: undeclared variable | |
| 76 | + printf("Value: %d\n", undeclared_var); | |
| 77 | + | |
| 78 | + return 0; | |
| 79 | +} | |
tests/scratch_files/main.pyadded@@ -0,0 +1,71 @@ | ||
| 1 | +""" | |
| 2 | +Main module that uses calculator. | |
| 3 | + | |
| 4 | +Test cross-file navigation: | |
| 5 | +- Put cursor on 'add' below and press F12 - should jump to calculator.py | |
| 6 | +- Put cursor on 'calculate_total' and press Shift+F12 - find all usages | |
| 7 | +- Use Ctrl+Shift+T and search for "multiply" - should find it in calculator.py | |
| 8 | +""" | |
| 9 | + | |
| 10 | +from calculator import add, subtract, multiply, calculate_total | |
| 11 | + | |
| 12 | + | |
| 13 | +def process_data(numbers): | |
| 14 | + """Process a list of numbers using calculator functions.""" | |
| 15 | + # Test: F12 on 'add' should jump to calculator.py | |
| 16 | + sum_result = add(numbers[0], numbers[1]) | |
| 17 | + | |
| 18 | + # Test: F12 on 'multiply' should jump to calculator.py | |
| 19 | + product = multiply(sum_result, 2) | |
| 20 | + | |
| 21 | + # Test: Shift+F12 on 'calculate_total' should show usages | |
| 22 | + total = calculate_total(numbers) | |
| 23 | + | |
| 24 | + return { | |
| 25 | + 'sum': sum_result, | |
| 26 | + 'product': product, | |
| 27 | + 'total': total | |
| 28 | + } | |
| 29 | + | |
| 30 | + | |
| 31 | +class DataProcessor: | |
| 32 | + """A class for processing numerical data. | |
| 33 | + | |
| 34 | + Test Document Symbols (Ctrl+Shift+O) to see this class structure. | |
| 35 | + """ | |
| 36 | + | |
| 37 | + def __init__(self, data): | |
| 38 | + self.data = data | |
| 39 | + self.processed = False | |
| 40 | + | |
| 41 | + def calculate_sum(self): | |
| 42 | + """Calculate sum using calculator module.""" | |
| 43 | + return calculate_total(self.data) | |
| 44 | + | |
| 45 | + def calculate_average(self): | |
| 46 | + """Calculate average of the data.""" | |
| 47 | + total = self.calculate_sum() | |
| 48 | + return divide(total, len(self.data)) # ERROR: 'divide' not imported | |
| 49 | + | |
| 50 | + def process(self): | |
| 51 | + """Process the data.""" | |
| 52 | + self.processed = True | |
| 53 | + return self.calculate_average() | |
| 54 | + | |
| 55 | + | |
| 56 | +# Test with intentional error | |
| 57 | +def main(): | |
| 58 | + numbers = [1, 2, 3, 4, 5] | |
| 59 | + | |
| 60 | + # This works | |
| 61 | + result = process_data(numbers) | |
| 62 | + print(f"Results: {result}") | |
| 63 | + | |
| 64 | + # This has errors | |
| 65 | + processor = DataProcessor(numbers) | |
| 66 | + avg = processor.process() # Will error due to missing 'divide' import | |
| 67 | + print(f"Average: {avg}") | |
| 68 | + | |
| 69 | + | |
| 70 | +if __name__ == "__main__": | |
| 71 | + main() | |
tests/scratch_files/math_utils.f90added@@ -0,0 +1,89 @@ | ||
| 1 | +module math_utils | |
| 2 | + ! Simple math utilities for testing LSP features | |
| 3 | + ! | |
| 4 | + ! Test: | |
| 5 | + ! - Syntax highlighting (keywords, comments, strings) | |
| 6 | + ! - Go to Definition (F12 on subroutine calls) | |
| 7 | + ! - Find References (Shift+F12 on subroutine names) | |
| 8 | + ! - Document Symbols (Ctrl+Shift+O to see module structure) | |
| 9 | + ! - Formatting (Shift+Alt+F) | |
| 10 | + | |
| 11 | + implicit none | |
| 12 | + private | |
| 13 | + | |
| 14 | + public :: vector_add, vector_dot, vector_magnitude | |
| 15 | + public :: matrix_multiply | |
| 16 | + | |
| 17 | +contains | |
| 18 | + | |
| 19 | + subroutine vector_add(a, b, result, n) | |
| 20 | + ! Add two vectors element-wise | |
| 21 | + integer, intent(in) :: n | |
| 22 | + real, intent(in) :: a(n), b(n) | |
| 23 | + real, intent(out) :: result(n) | |
| 24 | + integer :: i | |
| 25 | + | |
| 26 | + do i = 1, n | |
| 27 | + result(i) = a(i) + b(i) | |
| 28 | + end do | |
| 29 | + end subroutine vector_add | |
| 30 | + | |
| 31 | + function vector_dot(a, b, n) result(dot_product) | |
| 32 | + ! Calculate dot product of two vectors | |
| 33 | + integer, intent(in) :: n | |
| 34 | + real, intent(in) :: a(n), b(n) | |
| 35 | + real :: dot_product | |
| 36 | + integer :: i | |
| 37 | + | |
| 38 | + dot_product = 0.0 | |
| 39 | + do i = 1, n | |
| 40 | + dot_product = dot_product + a(i) * b(i) | |
| 41 | + end do | |
| 42 | + end function vector_dot | |
| 43 | + | |
| 44 | + function vector_magnitude(vec, n) result(magnitude) | |
| 45 | + ! Calculate magnitude of a vector | |
| 46 | + integer, intent(in) :: n | |
| 47 | + real, intent(in) :: vec(n) | |
| 48 | + real :: magnitude | |
| 49 | + integer :: i | |
| 50 | + real :: sum_squares | |
| 51 | + | |
| 52 | + sum_squares = 0.0 | |
| 53 | + do i = 1, n | |
| 54 | + sum_squares = sum_squares + vec(i)**2 | |
| 55 | + end do | |
| 56 | + magnitude = sqrt(sum_squares) | |
| 57 | + end function vector_magnitude | |
| 58 | + | |
| 59 | + subroutine matrix_multiply(A, B, C, m, n, p) | |
| 60 | + ! Multiply two matrices: C = A * B | |
| 61 | + ! A is m x n, B is n x p, C is m x p | |
| 62 | + integer, intent(in) :: m, n, p | |
| 63 | + real, intent(in) :: A(m, n), B(n, p) | |
| 64 | + real, intent(out) :: C(m, p) | |
| 65 | + integer :: i, j, k | |
| 66 | + | |
| 67 | + ! Intentional error: missing initialization | |
| 68 | + do i = 1, m | |
| 69 | + do j = 1, p | |
| 70 | + ! ERROR: C(i,j) not initialized before use | |
| 71 | + do k = 1, n | |
| 72 | + C(i, j) = C(i, j) + A(i, k) * B(k, j) | |
| 73 | + end do | |
| 74 | + end do | |
| 75 | + end do | |
| 76 | + end subroutine matrix_multiply | |
| 77 | + | |
| 78 | + ! Intentional error function for diagnostics | |
| 79 | + subroutine broken_routine(x, y) | |
| 80 | + real, intent(in) :: x | |
| 81 | + real, intent(out) :: y | |
| 82 | + real :: temp | |
| 83 | + | |
| 84 | + temp = x * 2.0 | |
| 85 | + ! ERROR: using undefined variable | |
| 86 | + y = temp + undefined_var | |
| 87 | + end subroutine broken_routine | |
| 88 | + | |
| 89 | +end module math_utils | |
tests/scratch_files/test_program.f90added@@ -0,0 +1,43 @@ | ||
| 1 | +program test_program | |
| 2 | + ! Test program using math_utils module | |
| 3 | + ! | |
| 4 | + ! Test cross-file navigation: | |
| 5 | + ! - F12 on 'vector_add' should jump to math_utils.f90 | |
| 6 | + ! - Shift+F12 on 'vector_dot' should show all usages | |
| 7 | + ! - Ctrl+Shift+T and search "vector" to find all vector functions | |
| 8 | + | |
| 9 | + use math_utils | |
| 10 | + implicit none | |
| 11 | + | |
| 12 | + integer, parameter :: n = 3 | |
| 13 | + real :: a(n), b(n), result(n) | |
| 14 | + real :: dot_prod, mag | |
| 15 | + integer :: i | |
| 16 | + | |
| 17 | + ! Initialize vectors | |
| 18 | + a = [1.0, 2.0, 3.0] | |
| 19 | + b = [4.0, 5.0, 6.0] | |
| 20 | + | |
| 21 | + ! Test vector addition | |
| 22 | + ! F12 on vector_add should jump to math_utils.f90 | |
| 23 | + call vector_add(a, b, result, n) | |
| 24 | + | |
| 25 | + print *, 'Vector A:', a | |
| 26 | + print *, 'Vector B:', b | |
| 27 | + print *, 'A + B :', result | |
| 28 | + | |
| 29 | + ! Test dot product | |
| 30 | + ! F12 on vector_dot should jump to definition | |
| 31 | + dot_prod = vector_dot(a, b, n) | |
| 32 | + print *, 'A · B :', dot_prod | |
| 33 | + | |
| 34 | + ! Test magnitude | |
| 35 | + mag = vector_magnitude(a, n) | |
| 36 | + print *, '|A| :', mag | |
| 37 | + | |
| 38 | + ! Intentional error - missing variable declaration | |
| 39 | + ! ERROR: 'undefined_result' is not declared | |
| 40 | + undefined_result = dot_prod * 2.0 | |
| 41 | + print *, 'Result :', undefined_result | |
| 42 | + | |
| 43 | +end program test_program | |
tests/scratch_files/utils.cadded@@ -0,0 +1,76 @@ | ||
| 1 | +/* | |
| 2 | + * Simple utility functions for testing LSP features | |
| 3 | + * | |
| 4 | + * Test: | |
| 5 | + * - Diagnostics: Intentional errors marked below | |
| 6 | + * - Go to Definition: F12 on function calls | |
| 7 | + * - Find References: Shift+F12 on function names | |
| 8 | + * - Code Actions: Ctrl+. on errors | |
| 9 | + * - Formatting: Shift+Alt+F | |
| 10 | + */ | |
| 11 | + | |
| 12 | +#include <stdio.h> | |
| 13 | +#include <stdlib.h> | |
| 14 | + | |
| 15 | +// Calculate factorial | |
| 16 | +int factorial(int n) { | |
| 17 | + if (n <= 1) { | |
| 18 | + return 1; | |
| 19 | + } | |
| 20 | + return n * factorial(n - 1); | |
| 21 | +} | |
| 22 | + | |
| 23 | +// Calculate fibonacci number | |
| 24 | +int fibonacci(int n) { | |
| 25 | + if (n <= 1) { | |
| 26 | + return n; | |
| 27 | + } | |
| 28 | + return fibonacci(n - 1) + fibonacci(n - 2); | |
| 29 | +} | |
| 30 | + | |
| 31 | +// Sum array elements | |
| 32 | +int sum_array(int *arr, int size) { | |
| 33 | + int total = 0; | |
| 34 | + for (int i = 0; i < size; i++) { | |
| 35 | + total += arr[i]; | |
| 36 | + } | |
| 37 | + return total; | |
| 38 | +} | |
| 39 | + | |
| 40 | +// Find maximum in array | |
| 41 | +int find_max(int *arr, int size) { | |
| 42 | + if (size <= 0) { | |
| 43 | + return 0; | |
| 44 | + } | |
| 45 | + | |
| 46 | + int max = arr[0]; | |
| 47 | + for (int i = 1; i < size; i++) { | |
| 48 | + if (arr[i] > max) { | |
| 49 | + max = arr[i]; | |
| 50 | + } | |
| 51 | + } | |
| 52 | + return max; | |
| 53 | +} | |
| 54 | + | |
| 55 | +// Intentional error function for diagnostics | |
| 56 | +int broken_function() { | |
| 57 | + int x = 10; | |
| 58 | + int y = 20; | |
| 59 | + | |
| 60 | + // ERROR: undefined variable | |
| 61 | + int result = x + y + undefined_var; | |
| 62 | + | |
| 63 | + // ERROR: incompatible pointer type | |
| 64 | + char *str = result; | |
| 65 | + | |
| 66 | + printf("Result: %d\n", result); | |
| 67 | + return result; | |
| 68 | +} | |
| 69 | + | |
| 70 | +// Another error - missing return | |
| 71 | +int missing_return(int x) { | |
| 72 | + if (x > 0) { | |
| 73 | + return x * 2; | |
| 74 | + } | |
| 75 | + // ERROR: missing return statement for x <= 0 | |
| 76 | +} | |