Skip to content

Java LSP : Hover definition works but definition doesn't #2105

@jdubois

Description

@jdubois

Describe the bug

goToDefinition via the LSP tool returns "No definition found" for all types defined in Maven/Gradle dependency JARs (e.g., Spring Framework, LangChain4j), even though hover at the exact same position correctly returns full type info and Javadoc from those same JARs.

Root cause

During JDTLS initialization, the Copilot CLI does not send extendedClientCapabilities.classFileContentsSupport: true in initializationOptions. Without this flag, JDTLS will not return jdt:// URIs in textDocument/definition responses for classes inside JARs — it returns null instead.

The relevant code in app.js constructs initializationOptions from this.config.lspInitializationOptions, but lsp-config.json has no such field, so it is undefined.

Steps to reproduce

  1. Configure JDTLS in ~/.copilot/lsp-config.json:
    {
      "lspServers": {
        "java": {
          "command": "jdtls",
          "args": ["-data", "/tmp/jdtls-workspace"],
          "fileExtensions": { ".java": "java" }
        }
      }
    }
  2. Open any Java project with Maven/Gradle dependencies
  3. Run hover on a dependency type (e.g., ChatModel from LangChain4j, or @Controller from Spring):
    > lsp hover ChatModel on line 43
    ✅ Returns: "dev.langchain4j.model.chat.ChatModel — Represents a language model..."
    Source: langchain4j-core-1.11.0.jar
    
  4. Run goToDefinition at the exact same position:
    > lsp goToDefinition ChatModel on line 43
    ❌ Returns: "No definition found at line 43, character 25."
    

This affects all dependency types — Spring (@Controller, @GetMapping), LangChain4j (ChatModel, UserMessage), etc. Navigation to project-local classes works fine.

Proposed fix

1. Send extendedClientCapabilities during JDTLS initialization:

Add classFileContentsSupport: true to the initialization options sent to JDTLS. This could be done via a new lspInitializationOptions field in lsp-config.json, or hardcoded for the Java/JDTLS server:

{
  "initializationOptions": {
    "extendedClientCapabilities": {
      "classFileContentsSupport": true
    }
  }
}

With this flag, JDTLS will return LocationLink objects with jdt://contents/... URIs for classes in JARs.

2. Handle jdt:// URIs in definition responses:

When the LSP tool receives a definition result with a jdt:// URI:

  • Send a java/classFileContents request to JDTLS with that URI
  • JDTLS responds with the decompiled (or source-attached) Java source code
  • Display the source at the indicated range, similar to how VS Code shows read-only decompiled class files

Additional context

The getClientCapabilities() method in app.js already declares definition: { linkSupport: true }, so the LSP protocol side is correct. The only missing piece is the JDTLS-specific extendedClientCapabilities.

This would also benefit goToImplementation for interfaces defined in JARs, and findReferences completeness for dependency types.

Related: #1349 (broader Java/JVM ecosystem awareness)

Additional context

Environment

  • Copilot CLI: 1.0.6
  • JDTLS: 1.57.0 (via Homebrew)
  • OS: macOS (Apple Silicon)
  • Java project: Spring Boot with Maven dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions