docs/internal/lsp-feature-parity-plan.md
Several LSP features are missing or incomplete compared to VS Code. This document tracks the gaps and implementation plan, starting with the code action gaps that are causing real user-visible bugs, then covering broader feature parity.
Current bugs:
TextDocumentEdit.text_document.version is completely ignored. Stale edits can corrupt buffers.DocumentChanges::Operations filters for Edit only — CreateFile, RenameFile, DeleteFile are silently dropped.needsConfirmation) are unwrapped to plain TextEdit.Fix:
version is Some(v), compare against document_versions in the LSP manager. Reject on mismatch. If None, apply blind (spec says null = "version unknown").CreateFile (respect overwrite/ignoreIfExists), RenameFile (rename on disk, update open buffers), DeleteFile (respect recursive/ignoreIfNotExists). Apply in order alongside Edit operations.Files: crates/fresh-editor/src/app/lsp_requests.rs (~line 1579)
workspace/applyEdit (server→client request) — DONECurrently falls through to the default handler which returns null — edits are silently dropped.
Fix: Add case in server request dispatch (async_handler.rs ~line 3197). Parse ApplyWorkspaceEditParams, send AsyncMessage::LspApplyEdit to main loop, respond with { applied: true }.
Edge cases:
executeCommand await doesn't block applyEdit handling.apply_workspace_edit already handles this via open_file().applyEdit = one undo step. Use label field from params.Files: async_handler.rs, async_bridge.rs, mod.rs
workspace/executeCommand (client→server request) — DONECode actions with a command field (but no edit) currently log a warning and do nothing.
Fix: New LspCommand::ExecuteCommand variant, sequential handler that sends "workspace/executeCommand" request. Response is usually null — the real effect comes via workspace/applyEdit sent during processing (handled by step 1).
Edge cases:
applyEdits from a single command: arrive as separate server requests, serialized by reader task.Files: async_handler.rs, lsp_requests.rs
codeAction/resolve (client→server request) — DONEServers can return lightweight code actions without edit; the client must resolve before execution. Currently these actions silently do nothing.
Fix:
resolveProvider in ServerCapabilitySummary (manager.rs).LspCommand::CodeActionResolve variant, handler sends "codeAction/resolve", returns resolved CodeAction via AsyncMessage::LspCodeActionResolved.edit, no command, has data field, server supports resolve.Edge cases:
Files: async_handler.rs, async_bridge.rs, manager.rs
execute_code_action to use all three — DONERewrite the dispatch in lsp_requests.rs (~line 1301):
CodeAction with no edit/command but has data + server supports resolve → send codeAction/resolveCodeAction with edit → apply workspace editCodeAction with command → send workspace/executeCommandCodeAction with both edit and command → apply edit THEN execute commandCommand → send workspace/executeCommandHandle LspCodeActionResolved in mod.rs to execute the resolved action.
New file: tests/e2e/lsp_code_action_resolve_and_commands.rs
Custom fake LSP server with logging:
test_code_action_with_command_sends_execute_and_applies_edit — command-only action → executeCommand → server sends applyEdit → verify edit appliedtest_code_action_resolve_then_apply — resolve-needed action → codeAction/resolve → server fills edit → verify appliedtest_code_action_with_edit_and_command — both edit + command → verify edit applied AND executeCommand sentcompletionItem/resolveServers return minimal completion items; the client must resolve for full documentation, additional text edits, etc. Currently the editor shows whatever the server returns in the initial response.
Impact: Missing documentation in completion popups, missing auto-imports on completion accept.
textDocument/formatting / textDocument/rangeFormattingFormat document / format selection. Capabilities are advertised but no request handler exists.
Impact: Format-on-save, format selection — core editing features.
textDocument/prepareRenameValidates rename before showing the rename UI. Advertised via prepare_support: Some(true) but no handler.
Impact: Renaming may fail with unhelpful errors instead of pre-validating.
textDocument/documentSymbolGet all symbols in a document (for outline view, breadcrumbs, go-to-symbol).
workspace/symbolSearch for symbols across the workspace.
textDocument/documentHighlightHighlight all references to the symbol under cursor.
window/showMessageRequestServer asks client to show a message with action buttons. Currently falls through to plugin handler which returns null. Some servers use this for user confirmations during refactoring.
window/showDocumentServer asks client to open/reveal a document. Used by some servers to show generated files or documentation.
textDocument/onTypeFormattingFormat as user types (e.g., auto-indent after {).
textDocument/linkedEditingRangeLinked editing (e.g., rename HTML open/close tags simultaneously).
textDocument/selectionRangeSmart expand/shrink selection based on AST.
workspace/didChangeWatchedFilesNotify server when watched files change on disk.
workspace/will{Create,Rename,Delete}Files)Server participates in file operations (e.g., updating imports when files move).
TextDocumentEdit.version non-null → must match, null → apply blind. Legacy changes field has NO version info.TextDocumentEdit must not overlap, all ranges refer to original document.applied: false on version mismatch, invalid ranges, file not found.workspace/applyEdit BACK during command execution (nested request). Our architecture handles this — reader task processes independently.applyEdit.stdin_writer contention: tokio::Mutex prevents corruption. Sequential request model means no interleaving.process_async_messages drains all messages synchronously before user input — workspace edits from applyEdit don't race with typing within a single tick.