Files
openidea/docs/kotlin-debugging.md
l0ng-ai 379a52d6e7 feat(debugger): Kotlin breakpoints through the Kotlin Debug Adapter (#434)
Kotlin line breakpoints never verified through java-debug because jdtls cannot resolve classes for .kt files. Kotlin Application configurations and JDWP attach for modules with Kotlin sources now use the official Kotlin Debug Adapter (installable from the debugger prompt, running on the language-server JDK); pure Java keeps java-debug.

The adapter resolves classes from source paths, so sources whose directories don't match their package are mirrored package-aligned outside the project (symlinks, copies where links fail) and paths are mapped back for breakpoints and stack frames; files that can't be mirrored are skipped rather than failing the session. Kotlin applications start with JDWP suspend=y and a session-owned JDWP relay holds the adapter's early resume until attach completes, so breakpoints are installed before the program runs.

Limitations (docs/kotlin-debugging.md): @file:JvmName facades are not resolved; package/source-root changes need a new session. Not verified on a real Windows machine.

Fixes #374
2026-10-30 18:15:42 +08:00

3.3 KiB

Kotlin debugging

Kotlin Application configurations use the official fwcd/kotlin-debug-adapter release, installed through Settings > Language Servers or the missing-debugger prompt. A module containing .kt files under src uses this adapter for both Application launch and Maven/Gradle JDWP attach, including mixed Java/Kotlin modules. Pure Java modules keep java-debug.

The adapter runs on the language-server JDK (11+). The application runs on its resolved project JDK, with the classpath, VM arguments, program arguments and environment from its run configuration.

Source paths

The adapter infers JVM class names from source paths. OpenIDEA creates a temporary, package-aligned source mirror outside the project and supplies it as the adapter's projectRoot. For example, src/main/kotlin/Main.kt declaring package org.example gets an external src/main/kotlin/org/example/Main.kt alias. Breakpoints go through that alias; stack frames and event paths map back to the original editor file. Source sets have separate mirror roots. Where file symlinks are unavailable, OpenIDEA copies sources and refreshes the copies on editor saves.

Restart debugging after changing a package or source root. The upstream adapter's path-based resolver does not support renamed Kotlin facades (@file:JvmName), or packages containing a literal java or kotlin component. Selecting the correct main class does not remove those breakpoint limitations.

The Kotlin Application launcher currently passes the classpath directly, without Java's large-command-line shortening, and decodes application output as UTF-8. Very large classpaths and non-UTF-8 application output remain limitations; Windows runtime behavior has not been verified.

Startup ordering

OpenIDEA starts Kotlin applications with JDWP suspend=y and attaches the adapter. It waits for each setBreakpoints response before sending configurationDone. Adapter 0.4.4 never completes the configurationDone response, so OpenIDEA sends that request without awaiting its response.

The official adapter can resume the VMStart event before installing its breakpoint hooks and DAP listeners. A session-owned loopback JDWP relay holds VM and thread resume commands until the successful DAP attach response, which follows listener installation. Other JDWP requests and replies continue during setup. Attach failure or session shutdown closes the relay and kills any JVM launched by OpenIDEA. No adapter patch or timed startup sleep is used. An already-running external JVM must be started with suspend=y to preserve its earliest breakpoints.

Verification

cargo test -p ide-dap --lib covers relay ordering, fragmented packets, cleanup, source mapping, and the adapter's missing configuration response. The ignored kotlin_official_release_ten_no_delay_runs test exercises the real DAP client ten times with an official adapter and a compiled reporter fixture. Set KOTLIN_PROBE_CONFIG to a LaunchConfig JSON file and KOTLIN_PROBE_ADAPTER to the release executable, then run:

cargo test -p ide-dap kotlin_official_release_ten_no_delay_runs -- --ignored --nocapture

The fixture is src/main/kotlin/Main.kt, with package org.example, a for (i in 0..3) loop at line 5 and println(i) at line 6, without any startup delay. Each run must hit line 6, expose i = 0, step to line 5, and stop its session.