-
Notifications
You must be signed in to change notification settings - Fork 87
Description
CodeLensProvider Does Not Support Two-Phase Resolution (resolveProvider Hard-Coded to False)
Langium version: 4.1.0
Package name: langium
Steps To Reproduce
- Implement a
CodeLensProviderwith expensive operations (e.g., finding all references across the workspace) - Register the provider in your language services module
- Observe that the LSP initialization sets
codeLensProvider: { resolveProvider: false }in server capabilities - Notice that there is no way to implement a
resolveCodeLensmethod because the interface doesn't support it
Link to code example:
See the Langium source code itself:
packages/langium/src/lsp/language-server.tslines 177-179packages/langium/src/lsp/code-lens-provider.tslines 12-14
The current behavior
In language-server.ts (lines 177-179):
codeLensProvider: hasCodeLensProvider
? { resolveProvider: false }
: undefined,The CodeLensProvider interface (lines 12-14):
export interface CodeLensProvider {
provideCodeLens(document: LangiumDocument, params: CodeLensParams, cancelToken?:
CancellationToken): MaybePromise<CodeLens[] | undefined>
}The resolveProvider capability is hard-coded to false, and there is no resolveCodeLens method in the interface. This means:
- All code lens data must be computed immediately in provideCodeLens
- Expensive operations (like finding references) block the initial code lens request
- Users see delays when opening files with many code lenses
- There's no way to defer expensive computations until a lens is actually clicked/resolved
The expected behavior
Langium should support optional two-phase code lens resolution, exactly like it does for
WorkspaceSymbolProvider.
Example of how it SHOULD work (see WorkspaceSymbolProvider in lines 184-186):
workspaceSymbolProvider: workspaceSymbolProvider
? { resolveProvider: Boolean(workspaceSymbolProvider.resolveSymbol) }
: undefinedThe WorkspaceSymbolProvider interface includes an optional resolve method:
export interface WorkspaceSymbolProvider {
getSymbols(params: WorkspaceSymbolParams, cancelToken?: CancellationToken):
MaybePromise<WorkspaceSymbol[]>;
resolveSymbol?(symbol: WorkspaceSymbol, cancelToken?: CancellationToken):
MaybePromise<WorkspaceSymbol>; // OPTIONAL
}Proposed Solution
- Update the CodeLensProvider interface to include an optional resolveCodeLens method:
export interface CodeLensProvider {
provideCodeLens(document: LangiumDocument, params: CodeLensParams, cancelToken?:
CancellationToken): MaybePromise<CodeLens[] | undefined>;
resolveCodeLens?(codeLens: CodeLens, cancelToken?: CancellationToken): MaybePromise<CodeLens>;
// NEW
}- Update the capability registration in language-server.ts (lines 177-179):
codeLensProvider: hasCodeLensProvider
? { resolveProvider: Boolean(hasCodeLensProvider.resolveCodeLens) }
: undefined,- Add the resolve handler (similar to workspace symbol):
export function addCodeLensResolveHandler(connection: Connection, services: LangiumSharedServices):
void {
connection.onCodeLensResolve(createServerRequestHandler(
(services, document, codeLens, cancelToken) =>
services.lsp?.CodeLensProvider?.resolveCodeLens?.(codeLens, cancelToken) ?? codeLens,
services,
DocumentState.IndexedReferences
));
}Benefits
- Performance: Defer expensive operations (like reference counting) until the lens is actually needed
- Consistency: Matches the pattern already established by WorkspaceSymbolProvider,
DocumentLinkProvider, and InlayHintProvider - Developer Experience: Provides flexibility for implementers to choose immediate vs. deferred resolution based on their use case
- Backward Compatibility: The optional method means existing implementations continue to work unchanged
Why This Matters
Code lenses that display reference counts are a common use case, but finding all references across a large workspace can be expensive. The current limitation forces all computation to happen synchronously, causing poor user experience with visible delays. The framework already has the pattern and infrastructure to support two-phase resolution—it just hasn't been applied to CodeLensProvider.