Skip to content

typeHierarchyProvider not announced when client supports dynamic registration #3769

@Hanatarou

Description

@Hanatarou

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

  1. Configure an LSP client to announce typeHierarchy with dynamicRegistration: true in the initialize request.
  2. Check the InitializeResult returned by JDT LS.
  3. Observe that typeHierarchyProvider is absent from ServerCapabilities.
  4. 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:

  1. Announce the capability statically (like callHierarchyProvider), or
  2. 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().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions