redhat-developer / redhat-developer/lsp4ij
Feature Request: Distinguish explicit vs implicit feature invocations
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 344
- Forks
- 113
- Avg merge
- 5h 22m
- Merged PRs (30d)
- 15
Description
Summary
Allow LSP client plugins to differentiate between explicit user-triggered actions ("Go To > LSP Reference(s)") and implicit framework-driven invocations (Ctrl+Click, Find Usages) when gating LSP features. Currently, both paths share the same gating mechanism, making it impossible to disable a feature for implicit integration while keeping the explicit "Go To" actions available.
Motivation
Use case
Consider a language server that supports textDocument/references for all languages, but the client plugin provides per-language user preferences (e.g., a "Navigation" toggle in settings). When a user disables Navigation for a specific language:
- Expected: The implicit integration (Ctrl+Click contributing to Find Usages,
GotoDeclarationHandler, etc.) should stop — the user doesn't want the LSP to participate in these automatic flows for this language. - Expected: The explicit "Go To > LSP Reference(s)" action should still work — the user deliberately chose this action from the context menu, signaling intent to use the language server.
- Actual: Both paths are blocked because they both flow through
getLanguageServers(file, isEnabled, isSupported), and theisEnabled()filter is applied uniformly.
Why this matters
Many language server clients support languages that also have first-class IDE plugins (e.g., Java in IntelliJ, Python in PyCharm). For these languages, users typically want:
- Implicit integration disabled (to avoid conflicts with the native plugin's Ctrl+Click, Find Usages, etc.)
- Explicit LSP actions available as a fallback (e.g., "Go To > LSP References" when the native plugin's results are insufficient)
Currently, there's no way to express this distinction.
Current Architecture
Two invocation paths
Implicit (framework-driven):
Ctrl+Click → LSPGotoDeclarationHandler → LSPDefinitionSupport.getDefinitions()
→ getLanguageServers(file,
f -> f.getDefinitionFeature().isEnabled(file),
f -> f.getDefinitionFeature().isSupported(file))
Explicit (user-triggered):
"Go To > LSP References" → LSPGoToReferenceAction.canSupportAction()
→ canSupportFeature() → isSupported(file) // visibility (OK)
→ LSPGoToReferenceAction.actionPerformed()
→ LSPReferenceSupport.getReferences()
→ getLanguageServers(file,
f -> f.getReferencesFeature().isEnabled(file),
f -> f.getReferencesFeature().isSupported(file))
The problem
Both paths call getLanguageServers() with the same isEnabled() predicate. When isEnabled() returns false (because the user disabled the feature for this language):
- The explicit "Go To > LSP References" menu item is visible (because
canSupportAction()checksisSupported(), notisEnabled()) - But clicking it produces no results (because
getLanguageServers()filters out the server viaisEnabled())
This is confusing UX — the action appears available but silently does nothing.
Affected features
This affects all features that have both explicit and implicit paths:
| Feature | Explicit Action | Implicit Integration |
|---|---|---|
| References | LSPGoToReferenceAction |
LSPUsageSearcher, LSPFindUsagesHandlerFactory |
| Declaration | LSPGoToDeclarationAction |
LSPGotoDeclarationHandler |
| Implementation | LSPGoToImplementationAction |
(via Find Usages) |
| Type Definition | LSPGoToTypeDefinitionAction |
(via Find Usages) |
Workaround
Until this is addressed, client plugins can work around the issue by always returning true from isEnabled() and moving the user-preference check into is*Supported(). However, this is suboptimal because isEnabled() serves as a pre-server-start optimization — returning true unconditionally may cause the server to be started for files where it shouldn't be.
Environment
- LSP4IJ version: latest (as of July 2025)
- Affected classes:
AbstractLSPDocumentFeature.isEnabled()AbstractLSPDocumentFeatureSupport.getLanguageServers()AbstractLSPGoToActionand all subclassesLSPGotoDeclarationHandlerLSPFindUsagesHandlerFactory/LSPUsageSearcher
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing AbstractLSPDocumentFeatureSupport.getLanguageServers() and the explicit paths through AbstractLSPGoToAction, then compare them with LSPGotoDeclarationHandler and LSPFindUsagesHandlerFactory/LSPUsageSearcher. Define how explicit actions bypass implicit gating without changing visibility checks, cover the affected feature actions, and verify that disabled implicit integrations stop while explicit actions still return results.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100