Skip to content

migration to kotlin#116

Open
raleigh-g-thompson wants to merge 6 commits intomasterfrom
feature/kotlin-migration
Open

migration to kotlin#116
raleigh-g-thompson wants to merge 6 commits intomasterfrom
feature/kotlin-migration

Conversation

@raleigh-g-thompson
Copy link
Contributor

closes #114

Migrate cql-language-server from Java to Kotlin and remove Spring Boot

Summary

This PR converts the entire cql-language-server codebase from Java to Kotlin and removes Spring Boot. All 236 tests pass. The extension has been verified end-to-end in VS Code (hover, diagnostics, formatting, Execute CQL, View ELM).

This is a foundational change. The Kotlin conversion is the first step on the path to Kotlin Multiplatform, which is the prerequisite for our 2026 goal of supporting pure JavaScript VS Code plugin usage — eliminating the Java dependency for end users entirely.


What changed

161 files changed — ~7,605 lines of Java deleted, ~6,473 lines of Kotlin added. The net reduction reflects Kotlin's conciseness.

Every module was converted:

  • core/ContentService, Converters, Uris and their tests
  • ls/server/ — all ~35 files: events, utilities, content services, managers, providers, visitors, plugin interfaces, commands, repository, and services
  • ls/service/ — entry point replaced: Main.java + LanguageClientAppender.javamain.kt + LanguageClientAppender.kt; application.properties deleted
  • debug/server/, debug/service/, plugin/debug/ — DAP protocol files, debug service entry point, and debug plugin all converted
  • Buildspring-boot-maven-plugin replaced with maven-shade-plugin; kotlin-maven-plugin and ktlint via spotless (spotless-maven-plugin 2.44.0) added to root pom.xml; kapt added to ls/server and plugin/debug for annotation processing

Spring Boot removed entirely. ServerConfig.java and PluginConfig.java deleted. The ~12 beans are now wired manually in main.kt (114 lines). ServiceLoader handles plugin discovery as before.

Test infrastructure simplified. TestConfig.java, @SpringBootTest, and @Autowired removed. All tests use direct instantiation, following the pattern already established in HoverProviderTest.

Test coverage expanded. The migration added new test classes for previously-untested components: ActiveContentService, FileContentService, FederatedContentService, Diagnostics utilities, CompilerOptionsManager, FormattingProvider, ContentServiceSourceProvider, and LanguageServer integration. Test count grew from 163 to 236.

Visibility corrected for test access. Java's package-private visibility (default access modifier) has no direct Kotlin equivalent. Two methods that were package-private in Java were initially converted to protected (subclass-only in Kotlin), which broke same-package test access. They are now internal — the correct Kotlin equivalent, meaning module-visible: CompilerOptionsManager.clearOptions and ActiveContentService.patch / searchActiveContent.


Why we removed Spring Boot

Spring Boot was not being used for any of its typical benefits — no web server, no REST endpoints, no auto-configuration of business logic. It was present solely as a packaging and DI mechanism for ~12 beans. The cost was significant.

The classloader problem. Spring Boot's fat jar uses LaunchedURLClassLoader rather than the system classloader. This classloader is only attached to the main thread. Any work dispatched to a thread pool — ForkJoinPool, CompletableFuture.supplyAsync, coroutines — cannot load classes through it, including HAPI FHIR's model classes, parsers, and terminology resources. This is not a bug we can fix; it is a fundamental property of how Spring Boot packages JARs.

This single issue:

  • Blocked the DAP debugger. HAPI FHIR initialization in async threads threw ClassNotFoundException. The debugger has been paused for months because of this.
  • Prevented parallel CQL execution. Parallelizing the Execute CQL library loop was impossible for the same reason. Each library evaluation already constructs its own engine instance — the only thing blocking parallelism was the classloader.
  • Blocks any future async work. Any performance improvement that touches HAPI FHIR off the main thread hits this wall.

The replacement is simpler. The 12 beans form a linear dependency chain. Manual wiring in main.kt is clearer than Spring reflection magic and removes the framework entirely. maven-shade-plugin produces a standard fat jar with the system classloader — no special behavior, no surprises. Tests instantiate dependencies directly and are faster and easier to reason about.

This is also the foundation for our 2026 goals. Spring Boot is JVM-only. Removing it is a prerequisite for the Kotlin Multiplatform path that eventually targets JavaScript — enabling the extension to run without any Java dependency.


What this enables (roadmap)

Immediate follow-on PRs (branches already built and tested)

  • feature/cql-debugger — DAP debugger, UAT confirmed working. Stacked on this branch, ready to merge after this lands.
  • feature/cql-call-graph — CQL call graph command, pending UAT.
  • feature/find-all-referencestextDocument/references / Shift+F12.
  • feature/refine-hover-functionality — hover improvements.
  • feature/code-navigation — go-to-definition and related navigation features.

Near-term (new PRs)

  • Source directory renamesrc/main/javasrc/main/kotlin across all modules, once all .java files are gone. Single isolated commit.
  • Parallel Execute CQL — parallelizing the library loop in CqlCommand is now feasible. Each library already gets its own engine instance; the Spring Boot classloader was the only blocker.
  • Lifecycle management — add a Stoppable interface so services that hold resources (DiagnosticsService executor, EventBus registrations) can be torn down cleanly. Low urgency now (System.exit reclaims everything), mandatory before Kotlin/JS.
  • Configuration injection — make server behavior configurable via InitializeParams.initializationOptions (startup) and workspace/didChangeConfiguration (runtime). CqlWorkspaceService.didChangeConfiguration is already stubbed.

Medium-term (KMP path)

  • Gradle migration — move from Maven to Gradle (Kotlin DSL). Prerequisite for KMP module introduction.
  • KMP module introduction — introduce a KMP module targeting JVM, consuming the clinical_quality_language KMP port when it lands from the other team.
  • JS-native FHIR layer — replace HAPI FHIR with a JS-native FHIR library behind an interface, enabling the full JS build including debugging support. Required for browser-based editor support (Codespaces, vscode.dev).

Test plan

  • mvn test from cql-language-server/ — 236 tests pass
  • End-to-end in VS Code: hover, diagnostics, formatting, Execute CQL, View ELM all verified
  • Update vscode-cql/package.json cql-language-server.version to 4.3.0-SNAPSHOT, press F5, verify

@raleigh-g-thompson raleigh-g-thompson added the enhancement New feature or request label Mar 16, 2026
@github-actions
Copy link

github-actions bot commented Mar 16, 2026

Formatting check succeeded!

@github-actions
Copy link

github-actions bot commented Mar 16, 2026

Coverage Report

Overall Project 63.17% -33.44%
Files changed 62.61%

Module Coverage
CQL Language Server Core 67.82% -28.05%
CQL Language Server - Server Component 62.87% -33.78%
Files
Module File Coverage
CQL Language Server Core Converters.kt 100% 🍏
Uris.kt 78.9% -21.1%
ContentService.kt 0% -80%
CQL Language Server - Server Component DidChangeTextDocumentEvent.kt 100% 🍏
DidChangeWatchedFilesEvent.kt 100% 🍏
BaseEvent.kt 100% 🍏
DidCloseTextDocumentEvent.kt 100% 🍏
DidOpenTextDocumentEvent.kt 100% 🍏
ContentServiceSourceProvider.kt 100% 🍏
IgStandardResourceCategory.kt 100% 🍏
IgStandardEncodingBehavior.kt 100% 🍏
Futures.kt 100% 🍏
Diagnostics.kt 100% 🍏
IgStandardCqlContent.kt 98.33% -1.67% 🍏
IgStandardConventions.kt 97.66% -0.58% 🍏
ExpressionTrackBackVisitor.kt 97.24% -2.76% 🍏
CqlCompilationManager.kt 96.3% -3.7% 🍏
FederatedContentService.kt 93.15% -6.85% 🍏
HoverProvider.kt 91.81% -8.19% 🍏
FileContentService.kt 90.48% -7.14% 🍏
IgStandardRepository.kt 86.96% -10.69% 🍏
ViewElmCommandContribution.kt 86.03% -13.97% 🍏
CompilerOptionsManager.kt 85.8% -14.2% 🍏
ActiveContentService.kt 84.83% -14.04% 🍏
FormattingProvider.kt 78.76% -21.24%
ContentServiceModelInfoProvider.kt 72.62% -27.38%
IgContextManager.kt 53.21% -46.79%
IgStandardRepositoryCompartment.kt 52.94% -45.38%
DiagnosticsService.kt 44.86% -53.01%
CqlLanguageServer.kt 33.62% -58.62%
CqlTextDocumentService.kt 32.76% -55.17%
CqlWorkspaceService.kt 14.49% -79.42%
CommandContribution.kt 0% -75%
DidSaveTextDocumentEvent.kt 0% -57.14%
CliCommand.kt 0% -75%
DebugCqlCommandContribution.kt 0% -96.27%
NoOpRepository.kt 0% -43.18%
CqlCommand.kt 0% -97.17%

@raleigh-g-thompson raleigh-g-thompson marked this pull request as draft March 16, 2026 13:04
@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 55.87983% with 514 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.06%. Comparing base (ce7ff9b) to head (0ed79ba).

Files with missing lines Patch % Lines
...rg/opencds/cqf/cql/ls/server/command/CqlCommand.kt 0.00% 142 Missing ⚠️
...s/cqf/cql/ls/server/service/CqlWorkspaceService.kt 12.28% 50 Missing ⚠️
...ds/cqf/cql/ls/server/service/DiagnosticsService.kt 43.58% 36 Missing and 8 partials ⚠️
...ver/repository/ig/standard/IgStandardRepository.kt 83.82% 24 Missing and 14 partials ⚠️
...l/ls/server/command/DebugCqlCommandContribution.kt 0.00% 27 Missing ⚠️
...ncds/cqf/cql/ls/server/manager/IgContextManager.kt 43.47% 22 Missing and 4 partials ⚠️
...qf/cql/ls/plugin/debug/DebugCommandContribution.kt 0.00% 19 Missing ⚠️
.../cqf/cql/ls/server/service/ActiveContentService.kt 73.77% 9 Missing and 7 partials ⚠️
...org/opencds/cqf/cql/ls/server/CqlLanguageServer.kt 31.81% 15 Missing ⚠️
...java/org/opencds/cqf/cql/ls/core/ContentService.kt 0.00% 13 Missing ⚠️
... and 20 more
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #116      +/-   ##
============================================
+ Coverage     55.21%   56.06%   +0.85%     
+ Complexity      299      278      -21     
============================================
  Files            46       44       -2     
  Lines          1543     1170     -373     
  Branches        189      159      -30     
============================================
- Hits            852      656     -196     
+ Misses          618      442     -176     
+ Partials         73       72       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@raleigh-g-thompson raleigh-g-thompson marked this pull request as ready for review March 16, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

port project to kotlin

1 participant