Environment
- JDT LS Version: 1.58.0-SNAPSHOT (OSGi 1.58.0.202604151538)
- Git Commit:
bb27b6d
Summary
The LSP standard TypeHierarchy methods (textDocument/prepareTypeHierarchy, typeHierarchy/supertypes, typeHierarchy/subtypes) are fully implemented in TypeHierarchyHandler.java, and JDTLanguageServer.java correctly dispatches to them. However, the server never announces the typeHierarchyProvider capability to clients that declare support for dynamic registration of this feature. This makes Type Hierarchy undiscoverable for any client following the LSP 3.17 spec.
Steps to Reproduce
- Configure an LSP client to announce
typeHierarchy with dynamicRegistration: true in the initialize request.
- Check the
InitializeResult returned by JDT LS.
- Observe that
typeHierarchyProvider is absent from ServerCapabilities.
- Manually invoke
textDocument/prepareTypeHierarchy — it succeeds, confirming the feature works server-side but is simply not advertised.
Root Cause Analysis
The issue is a missing dynamic registration step:
1. Static registration is correctly skipped (InitHandler.java)
When the client supports dynamic registration, the server does not set the capability statically. This is the intended behavior.
if (!preferenceManager.getClientPreferences().isTypeHierarchyDynamicRegistrationSupported()) {
capabilities.setTypeHierarchyProvider(Boolean.TRUE);
}
The check isTypeHierarchyDynamicRegistrationSupported() in ClientPreferences.java correctly reads the client's dynamicRegistration flag.
2. Dynamic registration is never performed (JDTLanguageServer.java)
The server must now register the capability dynamically by sending a client/registerCapability request. This is done for other features (e.g., definition, references, inlayHint) in JDTLanguageServer.registerCapabilities(). However, the code to register typeHierarchy is missing.
private void registerCapabilities() {
// Registers: workspace/symbol, documentSymbol, definition,
// declaration, typeDefinition, hover, references,
// documentHighlight, implementation, inlayHint ...
// MISSING: typeHierarchy registration
}
3. Server-side handlers are ready and working
The server has a fully initialized TypeHierarchyHandler instance and the routing methods are implemented, proving the underlying functionality is complete.
private TypeHierarchyHandler typeHierarchyHandler = new TypeHierarchyHandler();
public CompletableFuture<List<TypeHierarchyItem>> prepareTypeHierarchy(...) { ... }
public CompletableFuture<List<TypeHierarchyItem>> typeHierarchySupertypes(...) { ... }
public CompletableFuture<List<TypeHierarchyItem>> typeHierarchySubtypes(...) { ... }
Comparison with callHierarchyProvider
The callHierarchyProvider capability is always announced statically, regardless of whether the client supports dynamic registration. This is the simplest and most robust approach.
capabilities.setCallHierarchyProvider(Boolean.TRUE);
Expected Behavior
When a client advertises typeHierarchy.dynamicRegistration: true, the server should either:
- Announce the capability statically (like
callHierarchyProvider), or
- Register it dynamically later via
client/registerCapability.
Currently, it does neither.
Workaround for Clients
To use Type Hierarchy with JDT LS, clients can set dynamicRegistration: false (or omit the property) when declaring the typeHierarchy capability. This forces the server to announce the provider statically.
Related Pull Requests
Suggested Fix
The simplest and most consistent fix is to always announce typeHierarchyProvider statically, just like callHierarchyProvider. This can be done by removing the condition in InitHandler.java:
// Replace the conditional block with the line below
capabilities.setTypeHierarchyProvider(Boolean.TRUE);
Alternatively, add the missing dynamic registration logic to JDTLanguageServer.registerCapabilities().
Environment
bb27b6dSummary
The LSP standard TypeHierarchy methods (
textDocument/prepareTypeHierarchy,typeHierarchy/supertypes,typeHierarchy/subtypes) are fully implemented inTypeHierarchyHandler.java, andJDTLanguageServer.javacorrectly dispatches to them. However, the server never announces thetypeHierarchyProvidercapability to clients that declare support for dynamic registration of this feature. This makes Type Hierarchy undiscoverable for any client following the LSP 3.17 spec.Steps to Reproduce
typeHierarchywithdynamicRegistration: truein theinitializerequest.InitializeResultreturned by JDT LS.typeHierarchyProvideris absent fromServerCapabilities.textDocument/prepareTypeHierarchy— it succeeds, confirming the feature works server-side but is simply not advertised.Root Cause Analysis
The issue is a missing dynamic registration step:
1. Static registration is correctly skipped (InitHandler.java)
When the client supports dynamic registration, the server does not set the capability statically. This is the intended behavior.
The check
isTypeHierarchyDynamicRegistrationSupported()inClientPreferences.javacorrectly reads the client'sdynamicRegistrationflag.2. Dynamic registration is never performed (JDTLanguageServer.java)
The server must now register the capability dynamically by sending a
client/registerCapabilityrequest. This is done for other features (e.g.,definition,references,inlayHint) inJDTLanguageServer.registerCapabilities(). However, the code to registertypeHierarchyis missing.3. Server-side handlers are ready and working
The server has a fully initialized
TypeHierarchyHandlerinstance and the routing methods are implemented, proving the underlying functionality is complete.Comparison with
callHierarchyProviderThe
callHierarchyProvidercapability is always announced statically, regardless of whether the client supports dynamic registration. This is the simplest and most robust approach.Expected Behavior
When a client advertises
typeHierarchy.dynamicRegistration: true, the server should either:callHierarchyProvider), orclient/registerCapability.Currently, it does neither.
Workaround for Clients
To use Type Hierarchy with JDT LS, clients can set
dynamicRegistration: false(or omit the property) when declaring thetypeHierarchycapability. This forces the server to announce the provider statically.Related Pull Requests
Suggested Fix
The simplest and most consistent fix is to always announce
typeHierarchyProviderstatically, just likecallHierarchyProvider. This can be done by removing the condition inInitHandler.java:Alternatively, add the missing dynamic registration logic to
JDTLanguageServer.registerCapabilities().