fix(lsp): complete string union literals containing dots#34664
Merged
Conversation
When TSC returns completions for a string union literal type, each entry carries a `replacementSpan` covering the string contents but no `insertText`. The previous code only built a `text_edit` when both the replacement span and an `insertText` were present, so the edit was dropped and the editor fell back to its own word-based replacement. With a `.` in the string, that replaced only the text after the last dot, "forgetting" everything before it. Fall back to the entry name as the inserted text when `insertText` is absent (matching VSCode's TypeScript integration), so the whole string content is replaced. Co-Authored-By: Divy Srivastava <[email protected]>
littledivy
approved these changes
Jun 1, 2026
littledivy
enabled auto-merge (squash)
June 1, 2026 13:42
littledivy
added a commit
to crowlKats/deno
that referenced
this pull request
Jun 10, 2026
) ## Problem Autocompleting a string union literal type whose members contain `.` is broken in the Deno LSP. Given: ```ts type T = "foo.bar" | "foo.baz"; const x: T = "foo.b"; // completing here ``` Accepting the `foo.bar` suggestion produces a mangled result — everything typed before the last `.` is forgotten. Plain `tsc`/VSCode handle this correctly. Reported in denoland#28075. ## Cause For a string union (`Types`) completion, TSC sets a per-entry `replacementSpan` (covering the string contents) but does **not** set `insertText`. Deno's `CompletionEntry::as_completion_item` only constructed a `text_edit` when **both** the replacement span and `insertText` were `Some`: ```rust if let (Some(text_span), Some(new_text)) = (range, &insert_text) { ... } ``` So the edit was silently dropped, leaving the editor to apply its own word-based replacement. Because `.` is a word boundary, only the text after the last dot was replaced. ## Fix When a `replacementSpan` is present but `insertText` is not, fall back to the entry name as the inserted text — matching VSCode's TypeScript integration (`insertText ?? name`). This makes the full string content get replaced. ## Test Added `lsp_completions_string_union_with_dot`, which asserts the completion item carries a `text_edit` spanning the whole string content. Verified it fails before the change (`text_edit` is `None`) and passes after. The full existing `lsp_completions`/auto-import/registry completion suite (28 tests) still passes. Closes denoland/divybot#395 Fixes denoland#28075 Co-authored-by: divybot <[email protected]> Co-authored-by: Divy Srivastava <[email protected]>
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.
Problem
Autocompleting a string union literal type whose members contain
.is broken in the Deno LSP. Given:Accepting the
foo.barsuggestion produces a mangled result — everything typed before the last.is forgotten. Plaintsc/VSCode handle this correctly. Reported in #28075.Cause
For a string union (
Types) completion, TSC sets a per-entryreplacementSpan(covering the string contents) but does not setinsertText. Deno'sCompletionEntry::as_completion_itemonly constructed atext_editwhen both the replacement span andinsertTextwereSome:So the edit was silently dropped, leaving the editor to apply its own word-based replacement. Because
.is a word boundary, only the text after the last dot was replaced.Fix
When a
replacementSpanis present butinsertTextis not, fall back to the entry name as the inserted text — matching VSCode's TypeScript integration (insertText ?? name). This makes the full string content get replaced.Test
Added
lsp_completions_string_union_with_dot, which asserts the completion item carries atext_editspanning the whole string content. Verified it fails before the change (text_editisNone) and passes after. The full existinglsp_completions/auto-import/registry completion suite (28 tests) still passes.Closes denoland/divybot#395
Fixes #28075
Fixes #28083