microsoft / microsoft/vscode

editor.suggest.insertMode: "insert" not respected for completions using LSP InsertReplaceEdit (clangd 23.1.0+)

Open
#335,237 0 comments 0 reactions 1 assignee Claimed by @hediet View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Does this issue occur when all extensions are disabled?: N/A — reproducing this requires a language server that emits an LSP `InsertReplaceEdit` completion item (e.g. clangd 23.1.0+); no such completion is generated with all extensions disabled. However, the raw LSP response captured from the server (see "Evidence" below) shows a correctly-scoped `insert` range, which isolates the bug to how VS Code applies `editor.suggest.insertMode` rather than to the extension's logic.

- VS Code Version: 1.136.2 (user setup), commit 88e44fa0e00b08f7758b4f6d05632e4fd5e4df6f, 2026-09-04
- OS Version: Windows 10.0.19045 x64

Steps to Reproduce:

1. Install the `clangd` extension (llvm-vs-code-extensions) with clangd 23.1.0 or later configured for a C++ project.
2. In a `.cpp` file, given a class `Foo` with method `bar()` declared, place the caret immediately before an existing call/definition of `bar`, e.g. `|bar()`.
3. Trigger completion, type a prefix matching `Foo`, and accept the `Foo` suggestion from the popup.
4. Observe that `bar` is deleted and replaced, instead of `Foo` being inserted before the caret.

Expected: with `editor.suggest.insertMode` at its default value `"insert"`, only the typed prefix before the caret should be replaced; `bar` should remain untouched, producing `Foo|bar()`.

Actual: the entire identifier `bar` is deleted, producing `Foo()`.

Evidence (raw LSP response from clangd, captured via `--log=verbose`):
{
"textEdit": {
"insert": { "start": {"line":46,"character":4}, "end": {"line":46,"character":5} },
"replace": { "start": {"line":46,"character":4}, "end": {"line":46,"character":22} },
"newText": "GameplayInputAdapter"
}
}
The `insert` range is correctly scoped to only the typed prefix (width 1). The `replace` range spans the entire following identifier (width 18, matching `validatedEntityId` in the real repro). The observed deletion matches applying `replace`, contradicting the configured `insert` mode.

Regression note: not reproducible with clangd 22.1.18, which did not yet send `InsertReplaceEdit` (it returned a single narrow `TextEdit`, leaving no ambiguity for the client). The regression appeared once clangd 23.1.0 added `InsertReplaceEdit` support (fixing clangd/clangd#2190). A similar class of bug was previously reported for gopls: golang/go#61215.

`editor.suggest.insertMode`: left at default (`"insert"`), confirmed not overridden in settings.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.