name: vscode-lsp-configuration description: Best practices for reading and reacting to VS Code settings in a Language Server Protocol (LSP) server. Use when handling vscode configuration or LSP configuration from clients
VS Code LSP Configuration Skill
Goal
Correctly resolve VS Code settings for an LSP server, including:
- User vs Workspace vs Workspace Folder overrides
- Multi-root workspaces
- Language overrides (
"[lang]") - Cache + invalidation via
onDidChangeConfiguration
Quick SOP (Minimal Correct Implementation)
In extension
package.json, set appropriate settingscope:- Prefer
resource(orlanguage-overridable) for most LSP behavior settings.
- Prefer
In LSP server, always read settings with
scopeUri = document.uri:- Never rely on
getConfiguration()withoutscopeUriif you expect folder-level settings.
- Never rely on
Cache per-resource config:
- Cache key =
document.uri(not workspace root).
- Cache key =
Handle config changes:
onDidChangeConfiguration: clear cache and revalidate all open docs.
Configuration Scope (Extension package.json)
What scope controls
contributes.configuration.properties[*].scope declares where a setting is allowed to vary and how VS Code should resolve it.
Common scope values
resource- Setting can vary by resource URI (file) and therefore by workspace folder in multi-root.
- Recommended for diagnostics, lint rules, formatter settings, analysis toggles.
language-overridable- Allows language-specific overrides like:
"[beancount]": { "yourExtension.enable": true } - Still benefits from reading with
scopeUri.
- Allows language-specific overrides like:
window- Window-level only. Does not vary by file/folder.
- Use for UI/window toggles only.
Note:
machine/machine-overridableexist for Remote/machine-specific settings. Most LSPs don’t need them.
Reading Settings in the LSP Server
getConfiguration parameters
Typical Node server call:
connection.workspace.getConfiguration({ scopeUri, section })
scopeUri(string URI)The resource used to resolve configuration (e.g.,
document.uri).Required for correct behavior in:
- multi-root workspaces
- workspace folder overrides
section(string)- The prefix to fetch (e.g.,
"yourExtension"). - Returns an object representing
yourExtension.*settings.
- The prefix to fetch (e.g.,
Reference pattern (per-resource + cache)
const settingsCache = new Map<string, Thenable<any>>();
let hasConfigCapability = false;
connection.onInitialize((params) => {
hasConfigCapability = !!params.capabilities.workspace?.configuration;
return { capabilities: { /* ... */ } };
});
async function getSettings(resourceUri: string) {
if (!hasConfigCapability) return defaultSettings;
let v = settingsCache.get(resourceUri);
if (!v) {
v = connection.workspace.getConfiguration({
scopeUri: resourceUri,
section: "yourExtension",
});
settingsCache.set(resourceUri, v);
}
return v;
}
onDidChangeConfiguration (Server-side)
When it triggers
VS Code sends this notification when configuration may have changed, e.g.:
- User / Workspace / Workspace Folder settings edited (UI or JSON)
- Language overrides changed (
"[lang]") - External edits to
.vscode/settings.json(git checkout, scripts, sync tools)
Why it has no parameters
The LSP notification typically does not include which keys changed. Treat it as “something changed”.
Recommended handler
connection.onDidChangeConfiguration(() => {
settingsCache.clear();
for (const doc of documents.all()) {
// revalidate(doc)
}
});
Precedence Rules (Avoid False Bugs)
VS Code setting precedence: User < Workspace < Workspace Folder
If a key is set at Workspace or Folder level, it overrides User level.
Pitfalls Checklist (Common Failure Modes)
- No
scopeUripassed → Folder settings appear “ignored”, especially multi-root. - Setting declared as
windowbut you expect per-folder/per-file behavior → it won’t happen. - Expecting raw values by layer (user/workspace/folder) → LSP
workspace/configurationreturns merged final only. - Caching globally (one config for all docs) in multi-root → wrong folder settings applied.
- Not clearing cache on
onDidChangeConfiguration→ stale behavior after settings edits.
Decision Guide
- If a setting affects analysis/diagnostics/formatting for a specific file:
scope: resource+ read withscopeUri=document.uri - If a setting is language-specific:
scope: language-overridable+ read withscopeUri=document.uri - If a setting is purely UI/window behavior:
scope: window