Repository navigation
feat(lsp): wire sourcekit-lsp completion into the editor (#1) - #21
Open
Yan123-tech wants to merge 2 commits into
Open
Yan123-tech wants to merge 2 commits into
Yan123-tech wants to merge 2 commits into
Conversation
… death Extract the Content-Length message splitter into a pure, testable `parseNextMessage(from:)` and the response-routing decision into a testable `routeResponse`, then exercise both. Round-trip tests (request -> framed response) and a server-crash test confirm that: - messages split across reads are reassembled, and malformed headers resync; - an `error:` envelope resumes the parked request throwing the server's ResponseError (previously it was silently turned into `null` and surfaced as an opaque decode failure); - a server exit fails in-flight requests with `notConnected` instead of hanging, and fires a new `setDisconnectionHandler` callback. LSPClient.connect now registers a disconnection handler that flips the client to not-ready on a dead sourcekit-lsp, so callers fail fast instead of waiting forever. Docs in the working tree are updated but intentionally not committed.
Nothing called the transport or LSPClient until now (#1). This connects them to SourceEditor: - AppState owns the LSPClient lifecycle (start on launch, (re)connect on first request, disconnect on quit) so a dead server recovers instead of wedging the UI. - LSPTextViewCoordinator is bound in via CodeEditorView's appState param and completionDelegate. It filters the trigger prefix, fuzzy-matches + sortTexts via CompletionMatching, applies the server textEdit as replaceCharacters, debounces didChange to a 100ms full-content sync, and turns a server crash or an undecodable response into a single inline error row rather than a hang. - ClientCapabilities.default advertises static completion. sourcekit-lsp's dynamically-registered completion path emits structurally-invalid ~212KB JSON for some Array members (bare-string textEdit values, dangling "markdown" tokens, sortText carrying a docstring); static capabilities return the same items as compact, strictly-valid JSON. - JSONRPCTransport.repairMalformedJSON is a defensive fallback for that shape; it only rewrites `:"<...>"}` -> `:null`, so well-formed messages pass untouched. Verified: swift build (clean, pre-existing EditorState warning only), swift test 108/108 (30 suites; the sourcekit case is local-only, gated on the toolchain binary — CI cannot build/run it, see MEMORY.md #1), swift-format lint --strict clean. Refs #1
Yan123-tech
marked this pull request as ready for review
October 10, 2026 17:55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1 —
sourcekit-lspcompletion is wired into the editor.Batch 1 — transport coverage (commit dbfbfbf)
Content-Lengthframe splitter and response routing.error:envelope now resumes the parked request throwing the server'sResponseError(previously silently turned into
nulland surfaced as an opaque decode failure).setDisconnectionHandler;LSPClient.connectflips tonot-ready so a dead sourcekit fails fast instead of hanging.
Batch 2 — editor wiring (commit 2bf4358)
AppStateowns theLSPClientlifecycle: lazily started per language id,(re)connected on first request with a 5s cooldown, disconnected on quit
(
AppDelegate).LSPTextViewCoordinator(TextViewCoordinator + TextViewDelegate + CodeSuggestionDelegate)bound via
CodeEditorView(appState:completionDelegate:)/MainWindowView.completionSuggestionsRequestedfilters on the trigger prefix, fuzzy-matches +sortTexts via
CompletionMatching, applies the servertextEditasreplaceCharacters.didChangeis debounced 100ms (full-content sync).A server crash or a response the decoder cannot parse yields a single inline error
row instead of hanging; requests are ACKed to string ids; a 10s request timeout
fails parked requests rather than parking forever.
ClientCapabilities.defaultadvertises static completion(
dynamicRegistration: false). With dynamic registration, sourcekit-lsp emits astructurally-invalid ~212KB completion response (a
textEditvalue that is a bare{<docstring>}, dangling"markdown"tokens,sortTextcarrying a docstring —verified by probing the toolchain binary). Static capabilities return the same items as
compact, strictly-valid JSON.
JSONRPCTransport.repairMalformedJSONis a defensive fallback for that shape; it onlyrewrites
:"<...>"}to:null, so well-formed messages pass untouched (unit-tested).Verification
swift build— clean (only the pre-existingEditorState.swiftwarning).swift test— 108/108 in 30 suites. IncludesLSPClientEndToEndTests(sourcekit-lspstarts, opens, returns
name/idcompletions), gated on the toolchain binary andlocal-only — CI cannot build or run sourcekit-lsp (see [MEMORY.md Wire LSPClient into SourceEditor via a TextViewCoordinator #1]).
sourcekit-lsp is flaky as a child of
swift test(stalls/OOMs on the ~200KBArray-member response), so the E2E uses a 3-member local struct (~1KB response) andretries flaky spawns — not a transport defect.
swift-format lint --strict— 0 violations.What's next
Diagnostics, hover and go-to-definition (#2, #3) reuse the transport and lifecycle this
batch proves.