Skip to content

Make the LSP more robust against invalid projects#16

Merged
hegyibalint merged 27 commits intomainfrom
bhegyi/robust-handling
Jul 29, 2025
Merged

Make the LSP more robust against invalid projects#16
hegyibalint merged 27 commits intomainfrom
bhegyi/robust-handling

Conversation

@hegyibalint
Copy link
Member

Currently, the LSP breaks when opened up with an invalid project (#14).
This can simply mean a typo in a settings.gradle[.kts] file.

This is quite bad, as it's completely expected that projects could (and will be) in a transitional state.
This PR solves this problem by:

  • Improving the state holder objects, and making the TAPI requests failure-free (i.e., no leaking exceptions).
  • Implement a simple trigger to initiate a resync (currently modifications to any **/settings.gradle[.kts] file)
    • This mechanism can be elaborated further in the future by implementing explicit commands, but this is deemed enough to get started with.

@hegyibalint hegyibalint requested a review from Copilot June 30, 2025 12:52
@hegyibalint hegyibalint self-assigned this Jun 30, 2025
@hegyibalint hegyibalint added the a:feature A new functionality label Jun 30, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the LSP’s resilience when opening invalid or broken Gradle projects by preventing unhandled exceptions in TAPI calls and enabling on-the-fly resynchronization when settings.gradle[.kts] files change.

  • Introduce DeclarativeModelStore to wrap all Tooling API interactions in a failure-safe store.
  • Refactor text/workspace services and tests to use the new model store and a simplified MutationRegistry.
  • Add a didSave handler that triggers updateModel() on settings file saves and cover with end-to-end tests.

Reviewed Changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
lsp/src/test/kotlin/.../VersionedDocumentStoreTest.kt Added VersionedDocumentStore import to exercise the relocated class.
lsp/src/test/kotlin/.../AbstractDeclarativeTextDocumentServiceTest.kt Switched test helpers to return DeclarativeModelStore and simplified MutationRegistry constructor.
lsp/src/main/kotlin/.../valueFactoryIndex.kt Removed obsolete import now that VersionedDocumentStore lives in org.gradle.declarative.lsp.
lsp/src/main/kotlin/.../mutation/MutationRegistry.kt Moved class to new package, dropped direct dependency on DeclarativeResourcesModel.
lsp/src/main/kotlin/.../extension/DeclarativeResourcesModelExtensions.kt Added helper extension to build a SimpleAnalysisEvaluator from a DeclarativeResourcesModel.
lsp/src/main/kotlin/org/gradle/declarative/lsp/VersionedDocumentStore.kt Changed package from .service to root org.gradle.declarative.lsp.
lsp/src/main/kotlin/org/gradle/declarative/lsp/ToolingApiConnector.kt Replaced TapiConnectionHandler with a failure-safe ToolingApiConnector.withToolingApi helper.
lsp/src/main/kotlin/org/gradle/declarative/lsp/Main.kt Extracted startDeclarativeLanguageServer for better I/O injection and testability.
lsp/src/main/kotlin/org/gradle/declarative/lsp/DeclarativeWorkspaceService.kt Updated to accept DeclarativeModelStore and removed legacy VersionedDocumentStore imports.
lsp/src/main/kotlin/org/gradle/declarative/lsp/DeclarativeTextDocumentService.kt Switched to use DeclarativeModelStore.ifAvailable, added settings-file didSave reload logic.
lsp/src/main/kotlin/org/gradle/declarative/lsp/DeclarativeModelStore.kt New class managing DeclarativeResourcesModel retrieval, caching, and safe reloads.
lsp/src/main/kotlin/org/gradle/declarative/lsp/DeclarativeLanguageServer.kt Refactored to wire up DeclarativeModelStore, defined MutationRegistry entries, and added isInitialized.
lsp/src/integTest/InvalidGradleProjectTests.kt Two new end-to-end tests verifying server doesn’t crash on invalid settings and recovers on fix.
lsp/src/integTest/AbstractEndToEndTest.kt New base for integration tests with project-dir init helper.
lsp/build.gradle.kts Added Kotlin serialization plugin, switched many dependencies to api, configured integration tests.
gradle/libs.versions.toml Bumped Kotlin and added kotlinx-serialization-json entries.
Comments suppressed due to low confidence (1)

lsp/src/main/kotlin/org/gradle/declarative/lsp/DeclarativeLanguageServer.kt:176

  • [nitpick] The method isInitialized reports only model availability, which might be confused with server initialization state. Consider renaming it to isModelAvailable or similar to clarify its intent.
    fun isInitialized(): Boolean {

Comment on lines +32 to +38
api(libs.lsp4j)
api(libs.gradle.tooling.api)
api(libs.gradle.declarative.dsl.api)
api(libs.gradle.declarative.dsl.core)
api(libs.gradle.declarative.dsl.evaluator)
api(libs.gradle.declarative.dsl.tooling.models)
api(libs.logback.classic)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ These were moved to the api configuration, as test-suites wouldn't inherit implementation dependencies.
I think it makes sense, as all of these are exposed in our API.

hegyibalint and others added 5 commits June 30, 2025 13:56
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@hegyibalint hegyibalint linked an issue Jul 1, 2025 that may be closed by this pull request
@hegyibalint hegyibalint marked this pull request as ready for review July 1, 2025 13:20
Copy link
Member

@eskatos eskatos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good to see some robustness!

I requested a few minor changes and asked some questions.

parentFile.mkdirs()
writeText(
"""
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💅 The Gradle version used in tests should probably be parameterized, eventually. I understand that this is out of scope for this PR but I think it would be nice to at least extract the version number as a prominent constant in order to make it more obvious.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT, what should be the parametrization source?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are options like:

  • a list of tested Gradle versions hardcoded in the base test
  • a list of tested Gradle versions passed from the build definition
  • a test task per tested Gradle version

But my point was that this can actually happen later, it doesn't have to be part of this PR.

I think it would be nice to at least extract the version number as a prominent constant in order to make it more obvious.

@eskatos eskatos changed the title Make the LSP more robust agains invalid projects Make the LSP more robust against invalid projects Jul 2, 2025
Comment on lines +170 to +173
message = "The build files have been changed. Declarative model might be out of sync."
actions = mutableListOf(
MessageActionItem("Resync"),
MessageActionItem("Ignore")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@hegyibalint hegyibalint merged commit 5d2531e into main Jul 29, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a:feature A new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make sync more resilient when the settings script is broken

4 participants