├── .github ├── FUNDING.yml └── workflows │ ├── push.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── app ├── build.gradle.kts ├── src │ ├── main │ │ ├── kotlin │ │ │ └── org │ │ │ │ └── kotlinlsp │ │ │ │ ├── Main.kt │ │ │ │ ├── actions │ │ │ │ ├── GoToDefinition.kt │ │ │ │ ├── Hover.kt │ │ │ │ └── autocomplete │ │ │ │ │ ├── Autocomplete.kt │ │ │ │ │ ├── DeclarationExtensions.kt │ │ │ │ │ ├── DotExpressionCompletion.kt │ │ │ │ │ └── GenericCompletion.kt │ │ │ │ ├── analysis │ │ │ │ ├── AnalysisSession.kt │ │ │ │ ├── modules │ │ │ │ │ ├── LibraryModule.kt │ │ │ │ │ ├── Module.kt │ │ │ │ │ ├── NotUnderContentRootModule.kt │ │ │ │ │ ├── Serialization.kt │ │ │ │ │ └── SourceModule.kt │ │ │ │ ├── registration │ │ │ │ │ ├── AnalysisApiFir.kt │ │ │ │ │ ├── AnalysisApiImplBase.kt │ │ │ │ │ ├── LSPPlatform.kt │ │ │ │ │ ├── LowLevelApiFir.kt │ │ │ │ │ ├── Registrar.kt │ │ │ │ │ └── SymbolLightClasses.kt │ │ │ │ └── services │ │ │ │ │ ├── AnalysisPermissionOptions.kt │ │ │ │ │ ├── AnnotationResolver.kt │ │ │ │ │ ├── DeclarationProviderFactory.kt │ │ │ │ │ ├── DirectInheritorsProvider.kt │ │ │ │ │ ├── JavaModuleAccessibilityChecker.kt │ │ │ │ │ ├── JavaModuleAnnotationsProvider.kt │ │ │ │ │ ├── LanguageSettings.kt │ │ │ │ │ ├── ModificationTrackerFactory.kt │ │ │ │ │ ├── ModuleDependentsProvider.kt │ │ │ │ │ ├── PackagePartProviderFactory.kt │ │ │ │ │ ├── PackageProvider.kt │ │ │ │ │ ├── PlatformSettings.kt │ │ │ │ │ ├── ProjectStructureProvider.kt │ │ │ │ │ └── WriteAccessGuard.kt │ │ │ │ ├── buildsystem │ │ │ │ ├── BuildSystem.kt │ │ │ │ ├── FileBased.kt │ │ │ │ ├── Gradle.kt │ │ │ │ └── Resolver.kt │ │ │ │ ├── common │ │ │ │ ├── CacheFolder.kt │ │ │ │ ├── Log.kt │ │ │ │ ├── LspMappers.kt │ │ │ │ ├── ReadWriteLock.kt │ │ │ │ ├── Text.kt │ │ │ │ └── Version.kt │ │ │ │ ├── index │ │ │ │ ├── Command.kt │ │ │ │ ├── Index.kt │ │ │ │ ├── ScanFilesThread.kt │ │ │ │ ├── db │ │ │ │ │ ├── Database.kt │ │ │ │ │ ├── Declaration.kt │ │ │ │ │ ├── File.kt │ │ │ │ │ └── adapters │ │ │ │ │ │ ├── DatabaseAdapter.kt │ │ │ │ │ │ └── RocksDBAdapter.kt │ │ │ │ ├── queries │ │ │ │ │ ├── Completions.kt │ │ │ │ │ ├── FilesForPackage.kt │ │ │ │ │ ├── PackageExistsInSourceFiles.kt │ │ │ │ │ └── SubpackageNames.kt │ │ │ │ └── worker │ │ │ │ │ ├── IndexClassFile.kt │ │ │ │ │ ├── IndexKtFile.kt │ │ │ │ │ ├── ScanKtFile.kt │ │ │ │ │ ├── WorkQueue.kt │ │ │ │ │ └── WorkerThread.kt │ │ │ │ └── lsp │ │ │ │ └── Server.kt │ │ └── resources │ │ │ └── android.init.gradle │ └── test │ │ └── kotlin │ │ └── org │ │ └── kotlinlsp │ │ ├── Gradle.kt │ │ ├── RealTimeDiagnostics.kt │ │ ├── index │ │ └── FileTests.kt │ │ └── setup │ │ └── Scenario.kt └── test-projects │ ├── android │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── myapplication │ │ │ └── MainActivity.kt │ ├── build.gradle.kts │ ├── gradle.properties │ └── settings.gradle.kts │ ├── kmp │ ├── build.gradle.kts │ ├── composeApp │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── androidMain │ │ │ ├── AndroidManifest.xml │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── project │ │ │ │ └── MainActivity.kt │ │ │ ├── commonMain │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── project │ │ │ │ └── App.kt │ │ │ ├── commonTest │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── project │ │ │ │ └── ComposeAppCommonTest.kt │ │ │ └── desktopMain │ │ │ └── kotlin │ │ │ └── org │ │ │ └── example │ │ │ └── project │ │ │ └── main.kt │ ├── gradle.properties │ ├── gradle │ │ └── libs.versions.toml │ ├── settings.gradle.kts │ └── shared │ │ ├── build.gradle.kts │ │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── org │ │ │ └── example │ │ │ └── project │ │ │ └── Platform.android.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── org │ │ │ └── example │ │ │ └── project │ │ │ ├── Greeting.kt │ │ │ └── Platform.kt │ │ ├── commonTest │ │ └── kotlin │ │ │ └── org │ │ │ └── example │ │ │ └── project │ │ │ └── SharedCommonTest.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── org │ │ │ └── example │ │ │ └── project │ │ │ └── Platform.ios.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── org │ │ └── example │ │ └── project │ │ └── Platform.jvm.kt │ ├── multi-module │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── org │ │ │ └── app │ │ │ └── App.kt │ ├── gradle.properties │ ├── settings.gradle.kts │ └── submodule │ │ ├── build.gradle.kts │ │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── submodule │ │ └── Submodule.kt │ ├── playground │ ├── Errors.kt │ └── Main.kt │ └── single-module │ ├── app │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── example │ │ └── App.kt │ ├── gradle.properties │ └── settings.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── licenses ├── com.jetbrains.intellij.platform │ ├── LICENSE │ ├── NOTICE │ └── thirdparty │ │ ├── javahelp_license.txt │ │ ├── javolution_license.txt │ │ ├── saxon-conditions.html │ │ └── yourkit-license-dist.txt ├── org.eclipse.lsp4j │ ├── LICENSE │ └── NOTICE ├── org.gradle.gradle-tooling-api │ └── LICENSE ├── org.jetbrains.java.decompiler │ ├── LICENSE │ └── NOTICE ├── org.jetbrains.kotlin │ ├── COPYRIGHT.txt │ ├── COPYRIGHT_HEADER.txt │ ├── LICENSE.txt │ ├── NOTICE.txt │ ├── README.md │ └── third_party │ │ ├── aalto_xml_licence.txt │ │ ├── aether_license.txt │ │ ├── aosp_license.txt │ │ ├── args4j_LICENSE.txt │ │ ├── asm_license.txt │ │ ├── asmble_license.txt │ │ ├── assemblyscript_license.txt │ │ ├── boost_LICENSE.txt │ │ ├── caffeine_LICENSE.txt │ │ ├── closure-compiler_LICENSE.txt │ │ ├── compose_license.txt │ │ ├── dart_LICENSE.txt │ │ ├── fastutil_licence │ │ ├── gradle-node-plugin_LICENSE.txt │ │ ├── gradle_license.txt │ │ ├── guava_license.txt │ │ ├── gwt_license.txt │ │ ├── jgit_license.txt │ │ ├── jquery_license.txt │ │ ├── jshashtable_license.txt │ │ ├── karma-teamcity-reporter_LICENSE.txt │ │ ├── karma_LICENSE.txt │ │ ├── lodash_LICENSE.txt │ │ ├── lombok_LICENSE.txt │ │ ├── maven_LICENSE.txt │ │ ├── mocha-teamcity-reporter_LICENSE.txt │ │ ├── okhttp_license.txt │ │ ├── opentelemetry_license.txt │ │ ├── power_assert_license.txt │ │ ├── prototype_license.txt │ │ ├── rhino_LICENSE.txt │ │ ├── scala_license.txt │ │ ├── sl4f_license.txt │ │ ├── stax2-api.txt │ │ ├── sun_license.txt │ │ ├── teamcity-service-messages_LICENSE.txt │ │ ├── testdata │ │ ├── dagger_license.txt │ │ ├── eclipse_distribution_license.txt │ │ ├── eclipse_license.txt │ │ ├── findbugs_license.txt │ │ ├── jspecify_license.txt │ │ ├── lombok_license.txt │ │ ├── rxjava_license.txt │ │ └── spring_license.txt │ │ └── threetenbp_license.txt ├── org.jetbrains.kotlinx.kotlinx-serialization │ ├── LICENSE │ └── NOTICE └── org.rocksdb.rocksdbjni │ └── LICENSE ├── scripts └── build.sh └── settings.gradle.kts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: amgdev9 2 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Set up JDK 17 | uses: actions/setup-java@v4 18 | with: 19 | distribution: 'temurin' 20 | java-version: '21' 21 | 22 | - name: Add Android SDK to PATH 23 | run: | 24 | echo "ANDROID_SDK_ROOT=/usr/local/lib/android/sdk" >> $GITHUB_ENV 25 | echo "ANDROID_HOME=/usr/local/lib/android/sdk" >> $GITHUB_ENV 26 | echo "/usr/local/lib/android/sdk/cmdline-tools/latest/bin" >> $GITHUB_PATH 27 | 28 | - name: Accept licenses 29 | run: yes | sdkmanager --licenses 30 | 31 | - name: Setup Gradle 32 | uses: gradle/actions/setup-gradle@v4 33 | 34 | - name: Run tests 35 | run: ./gradlew test -PisCI=true 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up JDK 20 | uses: actions/setup-java@v4 21 | with: 22 | distribution: 'temurin' 23 | java-version: '21' 24 | 25 | - name: Setup Gradle 26 | uses: gradle/actions/setup-gradle@v4 27 | 28 | - name: Build distribution 29 | run: ./gradlew clean distZip 30 | 31 | - name: Create release 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | tag: ${{ github.ref_name }} 35 | run: | 36 | gh release create "$tag" \ 37 | --repo="$GITHUB_REPOSITORY" \ 38 | --title="$tag" \ 39 | --generate-notes 40 | - name: Upload distribution 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | tag: ${{ github.ref_name }} 44 | run: | 45 | gh release upload "$tag" \ 46 | --repo="$GITHUB_REPOSITORY" \ 47 | --clobber \ 48 | "app/build/distributions/*.zip" 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | .gradle 3 | .idea 4 | .kotlin 5 | build 6 | 7 | # LSP 8 | lsp-dist 9 | .kotlin-lsp 10 | .kotlinlsp-modules.json 11 | log.txt 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 AMG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This project is licensed under the MIT License. 2 | 3 | It includes third-party components, listed below. Their full license texts, notices and required attributions can be found in the licenses/ directory. 4 | 5 | The following notices are included in accordance with the respective licenses: 6 | 7 | --- 8 | 9 | JetBrains IntelliJ Platform v241.19416.19 10 | Licensed under the Apache License, Version 2.0 11 | Modules used: core, core-impl, util 12 | 13 | This software includes code from IntelliJ IDEA Community Edition 14 | Copyright (C) JetBrains s.r.o. 15 | https://www.jetbrains.com/idea/ 16 | 17 | --- 18 | 19 | JetBrains Java Decompiler FernFlower v243.22562.218 20 | Licensed under the Apache License, Version 2.0 21 | 22 | Copyright © 2014–2025 JetBrains s.r.o. 23 | https://www.jetbrains.com/ 24 | 25 | --- 26 | 27 | Kotlin Compiler & Analysis API v2.2.20-dev-2432 28 | Licensed under the Apache License, Version 2.0 29 | Modules used: kotlin-compiler, analysis-api-k2-for-ide, analysis-api-for-ide, low-level-api-fir-for-ide, analysis-api-platform-interface-for-ide, symbol-light-classes-for-ide, analysis-api-impl-base-for-ide, kotlin-compiler-common-for-ide, kotlin-compiler-fir-for-ide 30 | 31 | Copyright 2010-2024 JetBrains s.r.o and respective authors and developers 32 | 33 | --- 34 | 35 | Eclipse LSP4J v0.24.0 36 | 37 | This program and the accompanying materials are made available under the terms 38 | of the Eclipse Public License v. 2.0 which is available at 39 | https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License v1.0 40 | which is available at https://www.eclipse.org/org/documents/edl-v10.php. 41 | 42 | SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause 43 | 44 | Eclipse LSP4J is a trademark of the Eclipse Foundation. 45 | 46 | --- 47 | 48 | Kotlinx Serialization v1.8.0 49 | Licensed under the Apache License, Version 2.0 50 | Modules used: core, protobuf 51 | 52 | Copyright 2017-2019 JetBrains s.r.o and respective authors and developers 53 | 54 | --- 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin Language Server 2 | 3 | [![Status](https://github.com/amgdev9/kotlin-lsp/actions/workflows/push.yml/badge.svg)](https://github.com/amgdev9/kotlin-lsp/actions/workflows/push.yml) 4 | [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da)](https://discord.gg/mSYevKDnA5) 5 | 6 | This is an implementation of the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specification) for the [Kotlin](https://kotlinlang.org) programming language, leveraging the Kotlin [Analysis API](https://github.com/JetBrains/kotlin/blob/master/docs/analysis/analysis-api/analysis-api.md) to provide real time diagnostics, syntax and semantic analysis of Kotlin source files and libraries. 7 | 8 | ## Current status 9 | 10 | Right now, this language server is at its infancy and thus not ready to use for production environments (yet). As of now, this language server is being prepared to analyse its own codebase, with upcoming support for other projects. Here are the most important steps to make in order to improve its usability: 11 | 12 | - Integration with build systems: right now we have 2 integrations available: 13 | - Gradle 14 | - File-based: for other build systems, you can write a `.kotlinlsp-modules.json` file at the root of your project with the modules and dependencies it contains. You have an example at `org.kotlinlsp.setup.Scenario.Kt` 15 | 16 | - Indexing solution: to provide features like autocomplete and search references, as well as caching to improve analysis performance, we need to create an index where we store all the references used in the project. For this we are using multiple key value stores using [RocksDB](https://rocksdb.org) on disk and perform a background indexing of the whole project, and incrementally update it as the user modifies the source files. One of the goals of this LS is to provide a fast startup time, so diagnostics are reported as quick as possible. 17 | 18 | ### Implemented features 19 | - ✅ Real time diagnostics: working for this codebase 20 | - ✅ Hover: fully working 21 | - 🚧 Go to definition: working (with decompilation support), needs polish when resolving to kotlin libraries and JDK classes 22 | - 🚧 Build system integration: there is support for 23 | * Gradle projects (single and multi module) 24 | * Single module Android projects (uses debug variant and does not handle source set merging yet) 25 | * Needs work on: 26 | * Multimodule Android projects 27 | * KMP projects (targeting JVM, native target needs investigation on how to do it) 28 | 29 | ## Installing 30 | We provide a distribution zip file, which you can download from [GitHub Releases](https://github.com/amgdev9/kotlin-lsp/releases/latest). Alternatively, there are unofficial methods to install it, provided by the community: 31 | - Nix: https://tangled.sh/@weethet.bsky.social/nix-packages, accessible via `packages.${system}.kotlin-lsp` or in an overlay 32 | 33 | ## Building and running 34 | 35 | To build the language server, just run the `./scripts/build.sh` script at the root directory, which compiles the project using gradle, packs it as a distribution zip and decompresses it in the `./lsp-dist` folder. Once built, you need to integrate it in a code editor to test its functionality. For example, in neovim the following config can be used: 36 | 37 | ```lua 38 | local root_dir = vim.fs.root(0, {"settings.gradle.kts", "settings.gradle"}) 39 | if not root_dir then 40 | root_dir = vim.fs.root(0, {"build.gradle.kts", "build.gradle"}) 41 | end 42 | local lsp_folder = "... path to lsp-dist folder ..." 43 | vim.lsp.config['kotlinlsp'] = { 44 | cmd = { '' .. lsp_folder .. '/kotlin-lsp-0.1a/bin/kotlin-lsp' }, 45 | filetypes = { 'kotlin' }, 46 | root_dir = root_dir 47 | } 48 | vim.lsp.enable('kotlinlsp') 49 | ``` 50 | 51 | After that, run the code editor in a kotlin file from this project and you should see diagnostics being reported. In the `Log.kt` file you can configure the verbosity of the logs. 52 | 53 | ## Running tests 54 | 55 | To run the tests, just run the `./gradlew test` command. The tests are made around the LSP interface so we test against real user interactions, providing a good safety net in case of refactoring or updating dependencies. 56 | 57 | ## Contributions 58 | 59 | Contributions are welcome! I try to improve this language server in my spare time but progress will be slow if I do it all by myself, so the more contributors this project has, the faster the development will be. Feel free to contact me if you want to contribute, have any doubts about how to start or if you need some more context about the Analysis API (which I'm not an expert, but I can provide my own research to help the development of the project). 60 | 61 | Feel free to create issues and submit pull requests, I'll answer them as soon as I can. 62 | 63 | ## Resources 64 | 65 | To help in the development of this project, these resources are extremely valuable: 66 | - [Kotlin Analysis API](https://github.com/JetBrains/kotlin/tree/master/analysis): especially the standalone platform, which is a static read only platform implementation we can use as a baseline 67 | - [IntelliJ IDEA Kotlin plugin](https://github.com/JetBrains/intellij-community/tree/master/plugins/kotlin): the kotlin plugin implements the Analysis API as well implementing the platform interface, so we have it as a base 68 | 69 | ## Sponsor this project 70 | 71 | If you want to economically support this project, I accept donations via 72 | 73 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/amgdev9) 74 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.time.Duration 2 | 3 | plugins { 4 | val kotlinVersion = "2.1.20" 5 | kotlin("jvm") version kotlinVersion 6 | kotlin("plugin.serialization") version kotlinVersion 7 | application 8 | } 9 | 10 | application { 11 | applicationName = "kotlin-lsp" 12 | mainClass = "org.kotlinlsp.MainKt" 13 | version = "0.1a" 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | 19 | maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") 20 | maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide-plugin-dependencies") 21 | maven(url = "https://www.jetbrains.com/intellij-repository/releases") 22 | maven(url = "https://cache-redirector.jetbrains.com/intellij-third-party-dependencies") 23 | maven(url = "https://repo.gradle.org/gradle/libs-releases") 24 | } 25 | 26 | dependencies { 27 | val intellijVersion = "241.19416.19" 28 | implementation("com.jetbrains.intellij.platform:core:$intellijVersion") 29 | implementation("com.jetbrains.intellij.platform:core-impl:$intellijVersion") 30 | implementation("com.jetbrains.intellij.platform:util:$intellijVersion") 31 | implementation("com.jetbrains.intellij.java:java-decompiler-engine:243.22562.218") 32 | 33 | val analysisApiKotlinVersion = "2.2.20-dev-2432" // 13-May-2025 (version that KSP uses) 34 | implementation("org.jetbrains.kotlin:kotlin-compiler:$analysisApiKotlinVersion") 35 | implementation("com.github.ben-manes.caffeine:caffeine:2.9.3") // Needed by kotlin analysis api 36 | listOf( 37 | "org.jetbrains.kotlin:analysis-api-k2-for-ide", 38 | "org.jetbrains.kotlin:analysis-api-for-ide", 39 | "org.jetbrains.kotlin:low-level-api-fir-for-ide", 40 | "org.jetbrains.kotlin:analysis-api-platform-interface-for-ide", 41 | "org.jetbrains.kotlin:symbol-light-classes-for-ide", 42 | "org.jetbrains.kotlin:analysis-api-impl-base-for-ide", 43 | "org.jetbrains.kotlin:kotlin-compiler-common-for-ide", 44 | "org.jetbrains.kotlin:kotlin-compiler-fir-for-ide" 45 | ).forEach { 46 | implementation("$it:$analysisApiKotlinVersion") { isTransitive = false } 47 | } 48 | 49 | implementation("org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0") 50 | implementation("org.rocksdb:rocksdbjni:10.0.1") 51 | implementation("org.gradle:gradle-tooling-api:8.14") 52 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.8.0") 53 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.8.0") 54 | 55 | testImplementation(platform("org.junit:junit-bom:5.12.2")) 56 | testImplementation("org.junit.jupiter:junit-jupiter") 57 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 58 | testImplementation("org.mockito:mockito-core:5.17.0") 59 | } 60 | 61 | java { 62 | toolchain { 63 | languageVersion = JavaLanguageVersion.of(21) 64 | } 65 | } 66 | 67 | tasks.test { 68 | val isCI: String? by project 69 | if (!isCI.isNullOrBlank()) { 70 | useJUnitPlatform { 71 | includeTags("CI") 72 | } 73 | } else { 74 | useJUnitPlatform() 75 | } 76 | 77 | forkEvery = 1 78 | maxParallelForks = 1 79 | timeout = Duration.ofMinutes(8) 80 | jvmArgs("-XX:+EnableDynamicAgentLoading") 81 | 82 | testLogging { 83 | exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL 84 | showExceptions = true 85 | showCauses = true 86 | showStackTraces = true 87 | showStandardStreams = true 88 | 89 | events("passed", "skipped", "failed") 90 | } 91 | } 92 | 93 | tasks.jar { 94 | manifest { 95 | attributes( 96 | "Implementation-Version" to project.version 97 | ) 98 | } 99 | } 100 | 101 | distributions { 102 | main { 103 | contents { 104 | into("license") { 105 | from("../LICENSE") 106 | from("../NOTICE") 107 | } 108 | into("license/thirdparty") { 109 | from("../licenses") 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/Main.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp 2 | 3 | import org.eclipse.lsp4j.launch.LSPLauncher 4 | import org.kotlinlsp.lsp.KotlinLanguageServer 5 | import org.kotlinlsp.common.getLspVersion 6 | import org.kotlinlsp.common.profileJvmStartup 7 | import org.kotlinlsp.lsp.KotlinLanguageServerNotifier 8 | import java.util.concurrent.Executors 9 | import kotlin.system.exitProcess 10 | 11 | fun main(args: Array) { 12 | profileJvmStartup() 13 | if ("-v" in args || "--version" in args) { 14 | println(getLspVersion()) 15 | return 16 | } 17 | 18 | val notifier = object : KotlinLanguageServerNotifier { 19 | override fun onExit() { 20 | exitProcess(0) 21 | } 22 | } 23 | val executor = Executors.newSingleThreadExecutor { 24 | Thread(it, "KotlinLSP-LSP") 25 | } 26 | val server = KotlinLanguageServer(notifier) 27 | val launcher = LSPLauncher.createServerLauncher(server, System.`in`, System.out, executor) { it } 28 | 29 | server.connect(launcher.remoteProxy) 30 | launcher.startListening() 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/actions/Hover.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.actions 2 | 3 | import com.intellij.psi.util.parentOfType 4 | import org.eclipse.lsp4j.Position 5 | import org.eclipse.lsp4j.Range 6 | import org.jetbrains.kotlin.analysis.api.KaExperimentalApi 7 | import org.jetbrains.kotlin.analysis.api.analyze 8 | import org.jetbrains.kotlin.analysis.api.renderer.declarations.impl.KaDeclarationRendererForSource 9 | import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol 10 | import org.jetbrains.kotlin.idea.references.mainReference 11 | import org.jetbrains.kotlin.psi.KtDeclaration 12 | import org.jetbrains.kotlin.psi.KtElement 13 | import org.jetbrains.kotlin.psi.KtFile 14 | import org.kotlinlsp.common.getElementRange 15 | import org.kotlinlsp.common.toOffset 16 | 17 | @OptIn(KaExperimentalApi::class) 18 | private val renderer = KaDeclarationRendererForSource.WITH_SHORT_NAMES 19 | 20 | @OptIn(KaExperimentalApi::class) 21 | fun hoverAction(ktFile: KtFile, position: Position): Pair? { 22 | val offset = position.toOffset(ktFile) 23 | val ktElement = ktFile.findElementAt(offset)?.parentOfType() ?: return null 24 | val range = getElementRange(ktFile, ktElement) 25 | val text = analyze(ktElement) { 26 | val symbol = 27 | if (ktElement is KtDeclaration) ktElement.symbol 28 | else ktElement.mainReference?.resolveToSymbol() as? KaDeclarationSymbol ?: return null 29 | symbol.render(renderer) 30 | } 31 | return Pair(text, range) 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/actions/autocomplete/Autocomplete.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.actions.autocomplete 2 | 3 | import com.intellij.psi.util.leavesAroundOffset 4 | import com.intellij.psi.util.parentOfType 5 | import org.eclipse.lsp4j.CompletionItem 6 | import org.jetbrains.kotlin.psi.* 7 | import org.kotlinlsp.index.Index 8 | 9 | fun autocompleteAction(ktFile: KtFile, offset: Int, index: Index): Sequence { 10 | val leaf = ktFile.leavesAroundOffset(offset).last().first 11 | 12 | val prefix = leaf.text.substring(0, offset - leaf.textRange.startOffset) 13 | val completingElement = leaf.parentOfType() ?: ktFile 14 | 15 | if (completingElement is KtNameReferenceExpression) { 16 | return if (completingElement.parent is KtDotQualifiedExpression) { 17 | autoCompletionDotExpression(ktFile, offset, index, completingElement.parent as KtDotQualifiedExpression, prefix) 18 | } else { 19 | autoCompletionGeneric(ktFile, offset, index, completingElement, prefix) 20 | } 21 | } 22 | 23 | if (completingElement is KtValueArgumentList) { 24 | return emptySequence() // TODO: function call arguments 25 | } 26 | 27 | return autoCompletionGeneric(ktFile, offset, index, completingElement, prefix) 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/actions/autocomplete/DeclarationExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.actions.autocomplete 2 | 3 | import org.eclipse.lsp4j.CompletionItemKind 4 | import org.eclipse.lsp4j.CompletionItemLabelDetails 5 | import org.eclipse.lsp4j.InsertTextFormat 6 | import org.kotlinlsp.index.db.Declaration 7 | 8 | fun Declaration.completionKind(): CompletionItemKind = 9 | when (this) { 10 | is Declaration.EnumEntry -> CompletionItemKind.EnumMember 11 | is Declaration.Class -> when (type) { 12 | Declaration.Class.Type.CLASS -> CompletionItemKind.Class 13 | Declaration.Class.Type.ABSTRACT_CLASS -> CompletionItemKind.Class 14 | Declaration.Class.Type.INTERFACE -> CompletionItemKind.Interface 15 | Declaration.Class.Type.ENUM_CLASS -> CompletionItemKind.Enum 16 | Declaration.Class.Type.OBJECT -> CompletionItemKind.Module 17 | Declaration.Class.Type.ANNOTATION_CLASS -> CompletionItemKind.Interface 18 | } 19 | is Declaration.Function -> CompletionItemKind.Function 20 | is Declaration.Field -> CompletionItemKind.Field 21 | } 22 | 23 | fun Declaration.details(): CompletionItemLabelDetails = when (this) { 24 | is Declaration.Class -> CompletionItemLabelDetails().apply { 25 | detail = " (${fqName})" 26 | } 27 | is Declaration.EnumEntry -> CompletionItemLabelDetails().apply { 28 | detail = ": $enumFqName" 29 | } 30 | is Declaration.Function -> CompletionItemLabelDetails().apply { 31 | detail = "(${parameters.joinToString(", ") { param -> "${param.name}: ${param.type}" }}): $returnType (${fqName})" 32 | } 33 | is Declaration.Field -> CompletionItemLabelDetails().apply { 34 | detail = ": $type (${fqName})" 35 | } 36 | } 37 | 38 | fun Declaration.insertInfo(): Pair = when (this) { 39 | is Declaration.Class -> name to InsertTextFormat.PlainText 40 | is Declaration.EnumEntry -> "${enumFqName.substringAfterLast('.')}.${name}" to InsertTextFormat.PlainText 41 | is Declaration.Function -> "${name}(${ 42 | parameters.mapIndexed { index, param -> "\${${index + 1}:${param.name}}" }.joinToString(", ") 43 | })" to InsertTextFormat.Snippet 44 | 45 | is Declaration.Field -> name to InsertTextFormat.PlainText 46 | } 47 | 48 | fun Sequence.filterMatchesReceiver(receiver: String): Sequence = 49 | filter { 50 | when (it) { 51 | is Declaration.Function -> it.receiverFqName == receiver || it.receiverFqName.isEmpty() 52 | is Declaration.Class -> true 53 | is Declaration.EnumEntry -> true 54 | is Declaration.Field -> it.parentFqName == receiver || it.parentFqName.isEmpty() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/actions/autocomplete/DotExpressionCompletion.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.actions.autocomplete 2 | 3 | import org.eclipse.lsp4j.CompletionItem 4 | import org.jetbrains.kotlin.analysis.api.analyze 5 | import org.jetbrains.kotlin.psi.KtDotQualifiedExpression 6 | import org.jetbrains.kotlin.psi.KtFile 7 | import org.kotlinlsp.index.Index 8 | import org.kotlinlsp.index.queries.getCompletions 9 | 10 | fun autoCompletionDotExpression( 11 | ktFile: KtFile, 12 | offset: Int, 13 | index: Index, 14 | completingElement: KtDotQualifiedExpression, 15 | prefix: String 16 | ): Sequence { 17 | val receiverType = analyze(completingElement) { 18 | completingElement.receiverExpression.expressionType.toString() 19 | } 20 | 21 | return index 22 | .getCompletions(prefix) // TODO: ThisRef 23 | .filterMatchesReceiver(receiverType) 24 | .map { decl -> 25 | val (inserted, insertionType) = decl.insertInfo() 26 | CompletionItem().apply { 27 | label = decl.name 28 | labelDetails = decl.details() 29 | kind = decl.completionKind() 30 | insertText = inserted 31 | insertTextFormat = insertionType 32 | additionalTextEdits = emptyList() 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/actions/autocomplete/GenericCompletion.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.actions.autocomplete 2 | 3 | import com.intellij.openapi.util.text.StringUtil 4 | import com.intellij.psi.util.parentOfType 5 | import org.eclipse.lsp4j.CompletionItem 6 | import org.eclipse.lsp4j.CompletionItemKind 7 | import org.eclipse.lsp4j.CompletionItemLabelDetails 8 | import org.eclipse.lsp4j.InsertTextFormat 9 | import org.eclipse.lsp4j.MarkupContent 10 | import org.eclipse.lsp4j.Position 11 | import org.eclipse.lsp4j.Range 12 | import org.eclipse.lsp4j.TextEdit 13 | import org.eclipse.lsp4j.jsonrpc.messages.Either 14 | import org.jetbrains.kotlin.analysis.api.KaExperimentalApi 15 | import org.jetbrains.kotlin.analysis.api.analyze 16 | import org.jetbrains.kotlin.analysis.api.components.KaScopeKind 17 | import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource 18 | import org.jetbrains.kotlin.analysis.api.symbols.KaVariableSymbol 19 | import org.jetbrains.kotlin.analysis.api.symbols.name 20 | import org.jetbrains.kotlin.psi.KtElement 21 | import org.jetbrains.kotlin.psi.KtFile 22 | import org.jetbrains.kotlin.psi.KtFunctionLiteral 23 | import org.jetbrains.kotlin.psi.KtImportDirective 24 | import org.jetbrains.kotlin.psi.KtLoopExpression 25 | import org.jetbrains.kotlin.psi.KtParameter 26 | import org.jetbrains.kotlin.psi.KtProperty 27 | import org.jetbrains.kotlin.types.Variance 28 | import org.kotlinlsp.index.Index 29 | import org.kotlinlsp.index.db.Declaration 30 | import org.kotlinlsp.index.queries.getCompletions 31 | 32 | private val newlines = arrayOf("", "\n", "\n\n") 33 | 34 | fun autoCompletionGeneric(ktFile: KtFile, offset: Int, index: Index, completingElement: KtElement, prefix: String): Sequence { 35 | // Get import list and where to add new imports 36 | val existingImports = ktFile.importList?.children?.filterIsInstance() ?: emptyList() 37 | val (importInsertionOffset, newlineCount) = if (existingImports.isEmpty()) { 38 | ktFile.packageDirective?.textRange?.let { it.endOffset to 2 } ?: (ktFile.textRange.startOffset to 0) 39 | } else { 40 | existingImports.last().textRange.endOffset to 1 41 | } 42 | val importInsertionPosition = 43 | StringUtil.offsetToLineColumn(ktFile.text, importInsertionOffset).let { Position(it.line, it.column) } 44 | 45 | val externalCompletions = index 46 | .getCompletions(prefix) // TODO: ThisRef 47 | .map { decl -> 48 | val additionalEdits = mutableListOf() 49 | 50 | // Add the import if not there yet 51 | if (decl is Declaration.Class) { 52 | val exists = existingImports.any { 53 | it.importedFqName?.asString() == decl.fqName 54 | } 55 | if (!exists) { 56 | val importText = "import ${decl.fqName}" 57 | val edit = TextEdit().apply { 58 | range = Range(importInsertionPosition, importInsertionPosition) 59 | newText = "${newlines[newlineCount]}$importText" 60 | } 61 | additionalEdits.add(edit) 62 | } 63 | } 64 | 65 | val (inserted, insertionType) = decl.insertInfo() 66 | 67 | CompletionItem().apply { 68 | label = decl.name 69 | labelDetails = decl.details() 70 | kind = decl.completionKind() 71 | insertText = inserted 72 | insertTextFormat = insertionType 73 | additionalTextEdits = additionalEdits 74 | } 75 | } 76 | 77 | val localCompletions = fetchLocalCompletions(ktFile, offset, completingElement, prefix) 78 | 79 | return localCompletions.plus(externalCompletions).asSequence() 80 | } 81 | 82 | @OptIn(KaExperimentalApi::class) 83 | private fun fetchLocalCompletions( 84 | ktFile: KtFile, 85 | offset: Int, 86 | completingElement: KtElement, 87 | prefix: String 88 | ): List = analyze(ktFile) { 89 | ktFile 90 | .scopeContext(completingElement) 91 | .scopes 92 | .asSequence() 93 | .filter { it.kind is KaScopeKind.LocalScope } 94 | .flatMap { it.scope.declarations } 95 | .filter { it.name.toString().startsWith(prefix) } 96 | .mapNotNull { if (it.psi != null) Pair(it, it.psi!!) else null } 97 | .filter { (_, psi) -> 98 | // TODO: This is a hack to get the correct offset for function literals, can analysis tell us if a declaration is accessible? 99 | val declOffset = 100 | if (psi is KtFunctionLiteral) psi.textRange.startOffset else psi.textRange.endOffset 101 | declOffset < offset 102 | } 103 | .map { (decl, psi) -> 104 | val detail = when (decl) { 105 | is KaVariableSymbol -> decl.returnType.render( 106 | KaTypeRendererForSource.WITH_SHORT_NAMES, 107 | Variance.INVARIANT 108 | ) 109 | 110 | else -> "Missing ${decl.javaClass.simpleName}" 111 | } 112 | 113 | val preview = when (psi) { 114 | is KtProperty -> psi.text 115 | is KtParameter -> { 116 | if (psi.isLoopParameter) { 117 | val loop = psi.parentOfType()!! 118 | loop.text.replace(loop.body!!.text, "") 119 | } else psi.text 120 | } 121 | 122 | is KtFunctionLiteral -> decl.name // TODO: Show the function call containing the lambda? 123 | else -> "TODO: Preview for ${psi.javaClass.simpleName}" 124 | } 125 | 126 | CompletionItem().apply { 127 | label = decl.name.toString() 128 | labelDetails = CompletionItemLabelDetails().apply { 129 | this.detail = " $detail" 130 | description = "" 131 | } 132 | documentation = Either.forRight( 133 | MarkupContent("markdown", "```kotlin\n${preview}\n```") 134 | ) 135 | kind = CompletionItemKind.Variable 136 | insertText = decl.name.toString() 137 | insertTextFormat = InsertTextFormat.PlainText 138 | } 139 | } 140 | .toList() 141 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/modules/LibraryModule.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.modules 2 | 3 | import com.intellij.core.CoreApplicationEnvironment 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.openapi.vfs.StandardFileSystems.JAR_PROTOCOL 6 | import com.intellij.openapi.vfs.VirtualFile 7 | import com.intellij.openapi.vfs.VirtualFileManager 8 | import com.intellij.psi.search.GlobalSearchScope 9 | import org.jetbrains.kotlin.analysis.api.KaExperimentalApi 10 | import org.jetbrains.kotlin.analysis.api.KaImplementationDetail 11 | import org.jetbrains.kotlin.analysis.api.KaPlatformInterface 12 | import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils 13 | import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaModuleBase 14 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaLibraryModule 15 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaLibrarySourceModule 16 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule 17 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment 18 | import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem 19 | import org.jetbrains.kotlin.config.JvmTarget 20 | import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION 21 | import org.jetbrains.kotlin.platform.TargetPlatform 22 | import org.jetbrains.kotlin.platform.jvm.JvmPlatforms 23 | import org.kotlinlsp.common.read 24 | import java.nio.file.Path 25 | import kotlin.io.path.absolutePathString 26 | 27 | class LibraryModule( 28 | override val id: String, 29 | val appEnvironment: KotlinCoreApplicationEnvironment, 30 | override val contentRoots: List, 31 | val javaVersion: JvmTarget, 32 | override val dependencies: List = listOf(), 33 | val isJdk: Boolean = false, 34 | private val project: Project, 35 | private val sourceModule: KaLibrarySourceModule? = null, 36 | ): Module { 37 | override val isSourceModule: Boolean 38 | get() = false 39 | 40 | @OptIn(KaImplementationDetail::class) 41 | override fun computeFiles(extended: Boolean): Sequence { 42 | val roots = if (isJdk) { 43 | // This returns urls to the JMOD files in the jdk 44 | project.read { LibraryUtils.findClassesFromJdkHome(contentRoots.first(), isJre = false) } 45 | } else { 46 | // These are JAR/class files 47 | contentRoots 48 | } 49 | 50 | val notExtendedFiles = roots 51 | .asSequence() 52 | .mapNotNull { 53 | getVirtualFileForLibraryRoot(it, appEnvironment, project) 54 | } 55 | 56 | if (!extended) return notExtendedFiles 57 | 58 | return notExtendedFiles 59 | .map { 60 | project.read { LibraryUtils.getAllVirtualFilesFromRoot(it, includeRoot = true) } 61 | } 62 | .flatten() 63 | } 64 | 65 | override val kaModule: KaModule by lazy { 66 | object : KaLibraryModule, KaModuleBase() { 67 | @KaPlatformInterface 68 | override val baseContentScope: GlobalSearchScope by lazy { 69 | val virtualFileUrls = computeFiles(extended = true).map { it.url }.toSet() 70 | 71 | object : GlobalSearchScope(project) { 72 | override fun contains(file: VirtualFile): Boolean = file.url in virtualFileUrls 73 | 74 | override fun isSearchInModuleContent(p0: com.intellij.openapi.module.Module): Boolean = false 75 | 76 | override fun isSearchInLibraries(): Boolean = true 77 | 78 | override fun toString(): String = virtualFileUrls.joinToString("\n") { 79 | it 80 | } 81 | } 82 | } 83 | 84 | override val binaryRoots: Collection 85 | get() = contentRoots 86 | 87 | @KaExperimentalApi 88 | override val binaryVirtualFiles: Collection 89 | get() = emptyList() // Not supporting in-memory libraries 90 | override val directDependsOnDependencies: List 91 | get() = emptyList() // Not supporting KMP right now 92 | override val directFriendDependencies: List 93 | get() = emptyList() // No support for this right now 94 | override val directRegularDependencies: List 95 | get() = dependencies.map { it.kaModule } 96 | 97 | @KaPlatformInterface 98 | override val isSdk: Boolean 99 | get() = isJdk 100 | override val libraryName: String 101 | get() = id 102 | override val librarySources: KaLibrarySourceModule? 103 | get() = sourceModule 104 | override val project: Project 105 | get() = this@LibraryModule.project 106 | override val targetPlatform: TargetPlatform 107 | get() = JvmPlatforms.jvmPlatformByTargetVersion(javaVersion) 108 | } 109 | } 110 | } 111 | 112 | private const val JAR_SEPARATOR = "!/" 113 | 114 | private fun getVirtualFileForLibraryRoot( 115 | root: Path, 116 | environment: CoreApplicationEnvironment, 117 | project: Project 118 | ): VirtualFile? { 119 | val pathString = root.absolutePathString() 120 | 121 | // .jar or .klib files 122 | if (pathString.endsWith(JAR_PROTOCOL) || pathString.endsWith(KLIB_FILE_EXTENSION)) { 123 | return project.read { environment.jarFileSystem.findFileByPath(pathString + JAR_SEPARATOR) } 124 | } 125 | 126 | // JDK classes 127 | if (pathString.contains(JAR_SEPARATOR)) { 128 | val (libHomePath, pathInImage) = CoreJrtFileSystem.splitPath(pathString) 129 | val adjustedPath = libHomePath + JAR_SEPARATOR + "modules/$pathInImage" 130 | return project.read { environment.jrtFileSystem?.findFileByPath(adjustedPath) } 131 | } 132 | 133 | // Regular .class file 134 | return project.read { VirtualFileManager.getInstance().findFileByNioPath(root) } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/modules/Module.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.modules 2 | 3 | import com.intellij.openapi.vfs.VirtualFile 4 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule 5 | import java.nio.file.Path 6 | 7 | interface Module { 8 | val id: String 9 | val dependencies: List 10 | val isSourceModule: Boolean 11 | val contentRoots: List 12 | val kaModule: KaModule 13 | 14 | // Extended = true includes each file from .jar file 15 | fun computeFiles(extended: Boolean): Sequence 16 | } 17 | 18 | fun List.asFlatSequence(): Sequence { 19 | val processedModules = mutableSetOf() 20 | 21 | return this 22 | .asSequence() 23 | .map { 24 | getModuleFlatSequence(it, processedModules) 25 | } 26 | .flatten() 27 | } 28 | 29 | private fun getModuleFlatSequence(module: Module, processedModules: MutableSet): Sequence = sequence { 30 | if(processedModules.contains(module.id)) return@sequence 31 | 32 | yield(module) 33 | 34 | module.dependencies.forEach { 35 | yieldAll(getModuleFlatSequence(it, processedModules)) 36 | } 37 | 38 | processedModules.add(module.id) 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/modules/NotUnderContentRootModule.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.modules 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.psi.PsiFile 5 | import com.intellij.psi.search.GlobalSearchScope 6 | import org.jetbrains.kotlin.analysis.api.KaExperimentalApi 7 | import org.jetbrains.kotlin.analysis.api.KaPlatformInterface 8 | import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaModuleBase 9 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule 10 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaNotUnderContentRootModule 11 | import org.jetbrains.kotlin.platform.TargetPlatform 12 | import org.jetbrains.kotlin.platform.jvm.JvmPlatforms 13 | 14 | @OptIn(KaExperimentalApi::class, KaPlatformInterface::class) 15 | internal class NotUnderContentRootModule( 16 | override val name: String, 17 | override val directRegularDependencies: List = emptyList(), 18 | override val directDependsOnDependencies: List = emptyList(), 19 | override val directFriendDependencies: List = emptyList(), 20 | override val targetPlatform: TargetPlatform = JvmPlatforms.defaultJvmPlatform, 21 | override val file: PsiFile? = null, 22 | override val moduleDescription: String, 23 | override val project: Project, 24 | ) : KaNotUnderContentRootModule, KaModuleBase() { 25 | override val baseContentScope: GlobalSearchScope = 26 | if (file != null) GlobalSearchScope.fileScope(file) else GlobalSearchScope.EMPTY_SCOPE 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/modules/Serialization.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.modules 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.GsonBuilder 5 | import com.intellij.openapi.project.Project 6 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment 7 | import org.jetbrains.kotlin.config.JvmTarget 8 | import org.jetbrains.kotlin.config.LanguageVersion 9 | import kotlin.io.path.Path 10 | import kotlin.io.path.absolutePathString 11 | 12 | data class SerializedModule( 13 | val id: String, 14 | val dependencies: List, 15 | val contentRoots: List, 16 | val javaVersion: String, 17 | val isSource: Boolean, 18 | // SourceModule 19 | val kotlinVersion: String? = null, 20 | // LibraryModule 21 | val isJdk: Boolean? = null, 22 | ) 23 | 24 | fun serializeModules(modules: List): String { 25 | val visited = LinkedHashMap() 26 | val stack = ArrayDeque() 27 | stack.addAll(modules) 28 | 29 | while (stack.isNotEmpty()) { 30 | val current = stack.removeLast() 31 | val id = current.id 32 | if(visited.containsKey(id)) continue 33 | 34 | visited[id] = when(current) { 35 | is SourceModule -> SerializedModule( 36 | id = id, 37 | contentRoots = current.contentRoots.map { it.absolutePathString() }, 38 | kotlinVersion = current.kotlinVersion.versionString, 39 | javaVersion = current.javaVersion.toString(), 40 | dependencies = current.dependencies.map { it.id }, 41 | isSource = current.isSourceModule 42 | ) 43 | is LibraryModule -> SerializedModule( 44 | id = id, 45 | contentRoots = current.contentRoots.map { it.absolutePathString() }, 46 | isJdk = current.isJdk, 47 | javaVersion = current.javaVersion.toString(), 48 | dependencies = current.dependencies.map { it.id }, 49 | isSource = current.isSourceModule 50 | ) 51 | else -> throw Exception("Unsupported KaModule!") 52 | } 53 | stack.addAll(current.dependencies) 54 | } 55 | 56 | return GsonBuilder().setPrettyPrinting().create().toJson(visited.values) 57 | } 58 | 59 | fun deserializeModules( 60 | data: String, 61 | appEnvironment: KotlinCoreApplicationEnvironment, 62 | project: Project 63 | ): List { 64 | val gson = Gson() 65 | val modules: List = gson.fromJson(data, Array::class.java).toList() 66 | val moduleMap = modules.associateBy { it.id } 67 | return buildModulesGraph(modules, moduleMap, appEnvironment, project) 68 | } 69 | 70 | fun buildModulesGraph( 71 | modules: List, 72 | moduleMap: Map, 73 | appEnvironment: KotlinCoreApplicationEnvironment, 74 | project: Project 75 | ): List { 76 | val builtModules = mutableMapOf() 77 | 78 | fun build(id: String): Module { 79 | if (builtModules.containsKey(id)) return builtModules[id]!! 80 | val serialized = moduleMap[id]!! 81 | val deps = serialized.dependencies.map { build(it) } 82 | val module = buildModule(serialized, deps, project, appEnvironment) 83 | builtModules[id] = module 84 | return module 85 | } 86 | 87 | return modules 88 | .asSequence() 89 | .filter { it.isSource } 90 | .map { build(it.id) } 91 | .toList() 92 | } 93 | 94 | private fun buildModule( 95 | it: SerializedModule, 96 | deps: List, 97 | project: Project, 98 | appEnvironment: KotlinCoreApplicationEnvironment 99 | ): Module = 100 | if(it.isSource) { 101 | SourceModule( 102 | id = it.id, 103 | kotlinVersion = LanguageVersion.fromVersionString(it.kotlinVersion!!)!!, 104 | javaVersion = JvmTarget.fromString(it.javaVersion)!!, 105 | contentRoots = it.contentRoots.map { Path(it) }, 106 | dependencies = deps, 107 | project = project 108 | ) 109 | } else { 110 | LibraryModule( 111 | id = it.id, 112 | javaVersion = JvmTarget.fromString(it.javaVersion)!!, 113 | isJdk = it.isJdk!!, 114 | contentRoots = it.contentRoots.map { Path(it) }, 115 | dependencies = deps, 116 | project = project, 117 | appEnvironment = appEnvironment, 118 | ) 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/modules/SourceModule.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.modules 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.vfs.VirtualFile 5 | import com.intellij.openapi.vfs.VirtualFileManager 6 | import com.intellij.psi.search.GlobalSearchScope 7 | import org.jetbrains.kotlin.analysis.api.KaExperimentalApi 8 | import org.jetbrains.kotlin.analysis.api.KaPlatformInterface 9 | import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaModuleBase 10 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule 11 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaSourceModule 12 | import org.jetbrains.kotlin.config.* 13 | import org.jetbrains.kotlin.platform.TargetPlatform 14 | import org.jetbrains.kotlin.platform.jvm.JvmPlatforms 15 | import org.kotlinlsp.common.read 16 | import java.io.File 17 | import java.nio.file.Path 18 | import kotlin.io.path.absolutePathString 19 | 20 | class SourceModule( 21 | override val id: String, 22 | override val contentRoots: List, 23 | override val dependencies: List, 24 | val javaVersion: JvmTarget, 25 | val kotlinVersion: LanguageVersion, 26 | private val project: Project, 27 | ) : Module { 28 | override val isSourceModule: Boolean 29 | get() = true 30 | 31 | override fun computeFiles(extended: Boolean): Sequence = 32 | contentRoots 33 | .asSequence() 34 | .map { File(it.absolutePathString()).walk() } 35 | .flatten() 36 | .filter { it.isFile && (it.extension == "kt" || it.extension == "java") } 37 | .map { "file://${it.absolutePath}" } 38 | .mapNotNull { project.read { VirtualFileManager.getInstance().findFileByUrl(it) } } 39 | 40 | override val kaModule: KaModule by lazy { 41 | object : KaSourceModule, KaModuleBase() { 42 | private val scope: GlobalSearchScope by lazy { 43 | val files = computeFiles(extended = true).toList() 44 | 45 | GlobalSearchScope.filesScope(this@SourceModule.project, files) 46 | } 47 | 48 | @KaPlatformInterface 49 | override val baseContentScope: GlobalSearchScope 50 | get() = scope 51 | override val directDependsOnDependencies: List 52 | get() = emptyList() // Not supporting KMP right now 53 | override val directFriendDependencies: List 54 | get() = emptyList() // No support for this right now 55 | override val directRegularDependencies: List 56 | get() = dependencies.map { it.kaModule } 57 | override val languageVersionSettings: LanguageVersionSettings 58 | get() = LanguageVersionSettingsImpl(kotlinVersion, ApiVersion.createByLanguageVersion(kotlinVersion)) 59 | 60 | @KaExperimentalApi 61 | override val moduleDescription: String 62 | get() = "Source module: $name" 63 | override val name: String 64 | get() = id 65 | override val project: Project 66 | get() = this@SourceModule.project 67 | override val targetPlatform: TargetPlatform 68 | get() = JvmPlatforms.jvmPlatformByTargetVersion(javaVersion) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/registration/AnalysisApiFir.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.registration 2 | 3 | import com.intellij.openapi.util.registry.Registry 4 | import org.jetbrains.kotlin.analysis.api.KaImplementationDetail 5 | import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationTopics 6 | 7 | @OptIn(KaImplementationDetail::class) 8 | fun Registrar.analysisApiFir() { 9 | analysisApiImplBase() 10 | lowLevelApiFir() 11 | symbolLightClasses() 12 | 13 | projectService( 14 | "org.jetbrains.kotlin.analysis.api.session.KaSessionProvider", 15 | "org.jetbrains.kotlin.analysis.api.fir.KaFirSessionProvider", 16 | ) 17 | projectService( 18 | "org.jetbrains.kotlin.analysis.api.platform.modification.KaSourceModificationService", 19 | "org.jetbrains.kotlin.analysis.api.fir.modification.KaFirSourceModificationService" 20 | ) 21 | projectService( 22 | "org.jetbrains.kotlin.idea.references.KotlinReferenceProviderContributor", 23 | "org.jetbrains.kotlin.analysis.api.fir.references.KotlinFirReferenceContributor" 24 | ) 25 | projectService( 26 | "org.jetbrains.kotlin.idea.references.ReadWriteAccessChecker", 27 | "org.jetbrains.kotlin.analysis.api.fir.references.ReadWriteAccessCheckerFirImpl" 28 | ) 29 | projectService( 30 | "org.jetbrains.kotlin.analysis.api.imports.KaDefaultImportsProvider", 31 | "org.jetbrains.kotlin.analysis.api.fir.KaFirDefaultImportsProvider" 32 | ) 33 | projectService( 34 | "org.jetbrains.kotlin.analysis.api.platform.statistics.KaStatisticsService", 35 | "org.jetbrains.kotlin.analysis.api.fir.statistics.KaFirStatisticsService" 36 | ) 37 | Registry.get( 38 | "kotlin.analysis.lowMemoryCacheCleanup" 39 | ).setValue(true) 40 | Registry.get( 41 | "kotlin.analysis.postComputeEnhancedJavaFunctionsCache" 42 | ).setValue(false) 43 | Registry.get( 44 | "kotlin.analysis.compilerFacility.useStdlibBuildOutput" 45 | ).setValue(true) 46 | projectListener( 47 | "org.jetbrains.kotlin.analysis.api.fir.KaFirSessionProvider\$SessionInvalidationListener", 48 | LLFirSessionInvalidationTopics.SESSION_INVALIDATION 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/registration/AnalysisApiImplBase.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.registration 2 | 3 | import com.intellij.openapi.util.registry.Registry 4 | import org.jetbrains.kotlin.analysis.api.platform.lifetime.KaLifetimeTracker 5 | import org.jetbrains.kotlin.asJava.finder.JavaElementFinder 6 | 7 | fun Registrar.analysisApiImplBase() { 8 | Registry.get( 9 | "kotlin.analysis.statistics" 10 | ).setValue(false) 11 | projectService( 12 | "org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade", 13 | "org.jetbrains.kotlin.analysis.api.impl.base.java.KaBaseKotlinJavaPsiFacade" 14 | ) 15 | projectService( 16 | "org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver", 17 | "org.jetbrains.kotlin.analysis.api.impl.base.java.KaBaseJavaModuleResolver" 18 | ) 19 | projectService( 20 | "org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory", 21 | "org.jetbrains.kotlin.analysis.api.impl.base.java.source.JavaElementSourceWithSmartPointerFactory" 22 | ) 23 | projectService( 24 | "org.jetbrains.kotlin.psi.KotlinReferenceProvidersService", 25 | "org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService" 26 | ) 27 | projectService( 28 | "org.jetbrains.kotlin.analysis.api.projectStructure.KaModuleProvider", 29 | "org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaBaseModuleProvider" 30 | ) 31 | projectService( 32 | "org.jetbrains.kotlin.analysis.api.platform.KotlinMessageBusProvider", 33 | "org.jetbrains.kotlin.analysis.api.platform.KotlinProjectMessageBusProvider" 34 | ) 35 | appService( 36 | "org.jetbrains.kotlin.analysis.api.permissions.KaAnalysisPermissionRegistry", 37 | "org.jetbrains.kotlin.analysis.api.impl.base.permissions.KaBaseAnalysisPermissionRegistry" 38 | ) 39 | projectService( 40 | "org.jetbrains.kotlin.analysis.api.platform.permissions.KaAnalysisPermissionChecker", 41 | "org.jetbrains.kotlin.analysis.api.impl.base.permissions.KaBaseAnalysisPermissionChecker" 42 | ) 43 | projectService( 44 | "org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaResolutionScopeProvider", 45 | "org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaBaseResolutionScopeProvider" 46 | ) 47 | projectServiceSingleton( 48 | "org.jetbrains.kotlin.analysis.api.platform.lifetime.KaLifetimeTracker", 49 | "org.jetbrains.kotlin.analysis.api.impl.base.lifetime.KaBaseLifetimeTracker" 50 | ) 51 | projectService( 52 | "org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaContentScopeProvider", 53 | "org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaBaseContentScopeProvider" 54 | ) 55 | projectService( 56 | "org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaGlobalSearchScopeMerger", 57 | "org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KotlinOptimizingGlobalSearchScopeMerger" 58 | ) 59 | appServiceClass( 60 | "org.jetbrains.kotlin.analysis.decompiled.light.classes.origin.KotlinDeclarationInCompiledFileSearcher" 61 | ) 62 | // Here we don't register java.elementFinder as it will conflict 63 | // with our platform 64 | Registry.get( 65 | "kotlin.decompiled.light.classes.check.inconsistency" 66 | ).setValue(false) 67 | Registry.get( 68 | "kotlin.analysis.unrelatedSymbolCreation.allowed" 69 | ).setValue(false) 70 | projectExtensionPoint( 71 | "org.jetbrains.kotlin.kotlinContentScopeRefiner", 72 | "org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaResolveExtensionToContentScopeRefinerBridge" 73 | ) 74 | projectExtensionPoint( 75 | "org.jetbrains.kotlin.kotlinGlobalSearchScopeMergeStrategy", 76 | "org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KotlinResolveExtensionGeneratedFileScopeMergeStrategy" 77 | ) 78 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/registration/LowLevelApiFir.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.registration 2 | 3 | import com.intellij.openapi.util.registry.Registry 4 | import com.intellij.psi.util.PsiModificationTracker 5 | import com.intellij.util.messages.Topic 6 | import org.jetbrains.kotlin.analysis.api.KaImplementationDetail 7 | import org.jetbrains.kotlin.analysis.api.platform.modification.KotlinModificationEvent 8 | import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.LLFirInBlockModificationListener 9 | import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator 10 | 11 | @OptIn(KaImplementationDetail::class) 12 | fun Registrar.lowLevelApiFir() { 13 | projectExtensionPoint( 14 | "org.jetbrains.kotlin.llFirSessionConfigurator", 15 | LLFirSessionConfigurator::class.java 16 | ) 17 | Registry.get( 18 | "kotlin.parallel.resolve.under.global.lock" 19 | ).setValue(false) 20 | Registry.get( 21 | "kotlin.analysis.ll.locking.interval" 22 | ).setValue("100") 23 | Registry.get( 24 | "kotlin.analysis.jvmBuiltinActualizationForStdlibSources" 25 | ).setValue(true) 26 | projectService( 27 | "org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLFirElementByPsiElementChooser", 28 | "org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLRealFirElementByPsiElementChooser" 29 | ) 30 | projectServiceClass( 31 | "org.jetbrains.kotlin.analysis.low.level.api.fir.projectStructure.LLFirBuiltinsSessionFactory", 32 | ) 33 | projectServiceClass( 34 | "org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache", 35 | ) 36 | projectServiceClass( 37 | "org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationService" 38 | ) 39 | projectServiceClass( 40 | "org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationEventPublisher" 41 | ) 42 | projectServiceClass( 43 | "org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirGlobalResolveComponents" 44 | ) 45 | projectServiceClass( 46 | "org.jetbrains.kotlin.analysis.low.level.api.fir.LLResolutionFacadeService", 47 | ) 48 | projectServiceClass( 49 | "org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.LLFirDeclarationModificationService" 50 | ) 51 | projectServiceClass( 52 | "org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.LLFirInBlockModificationTracker" 53 | ) 54 | projectServiceClass( 55 | "org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.LLStatisticsService" 56 | ) 57 | appService( 58 | "org.jetbrains.kotlin.analysis.api.platform.resolution.KaResolutionActivityTracker", 59 | "org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.LLFirResolutionActivityTracker" 60 | ) 61 | projectListener( 62 | "org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationService\$LLKotlinModificationEventListener", 63 | KotlinModificationEvent.TOPIC 64 | ) 65 | projectListener( 66 | "org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationService\$LLPsiModificationTrackerListener", 67 | PsiModificationTracker.TOPIC 68 | ) 69 | val llFirInBlockModificationListenerTopic = Topic( 70 | LLFirInBlockModificationListener::class.java, 71 | Topic.BroadcastDirection.TO_CHILDREN, 72 | true, 73 | ) 74 | projectListener( 75 | "org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.LLFirInBlockModificationListenerForCodeFragments", 76 | llFirInBlockModificationListenerTopic 77 | ) 78 | projectListener( 79 | "org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.LLFirInBlockModificationTracker\$Listener", 80 | llFirInBlockModificationListenerTopic 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/registration/Registrar.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.registration 2 | 3 | import com.intellij.core.CoreApplicationEnvironment 4 | import com.intellij.mock.MockApplication 5 | import com.intellij.mock.MockComponentManager 6 | import com.intellij.mock.MockProject 7 | import com.intellij.openapi.Disposable 8 | import com.intellij.openapi.extensions.DefaultPluginDescriptor 9 | import com.intellij.util.messages.Topic 10 | import org.jetbrains.kotlin.load.kotlin.JvmType 11 | import kotlin.reflect.full.primaryConstructor 12 | 13 | class Registrar(val project: MockProject, val app: MockApplication, val disposable: Disposable) { 14 | private val pluginDescriptor = DefaultPluginDescriptor("analysis-api-plugin-descriptor") 15 | 16 | fun projectListener(className: String, topic: Topic) { 17 | val listenerClass = loadClass(project, className, pluginDescriptor) as Class 18 | project.messageBus.connect().subscribe( 19 | topic, 20 | listenerClass.kotlin.primaryConstructor!!.call(project) 21 | ) 22 | } 23 | 24 | fun appServiceClass(className: String) { 25 | val implClass = app.loadClass(className, pluginDescriptor) 26 | app.registerService(implClass) 27 | } 28 | 29 | fun projectServiceClass(className: String) { 30 | val implClass = project.loadClass(className, pluginDescriptor) 31 | project.registerService(implClass) 32 | } 33 | 34 | fun projectService(interfaceName: String, className: String) { 35 | val interfaceClass = project.loadClass(interfaceName, pluginDescriptor) 36 | val implClass = project.loadClass(className, pluginDescriptor) 37 | project.registerService(interfaceClass, implClass) 38 | } 39 | 40 | fun projectServiceSingleton(interfaceName: String, className: String) { 41 | val interfaceClass = project.loadClass(interfaceName, pluginDescriptor) 42 | val implClass = project.loadClass(className, pluginDescriptor) 43 | val implClassInstance = implClass.getDeclaredConstructor().newInstance() 44 | project.registerService(interfaceClass, implClassInstance) 45 | } 46 | 47 | fun appService(interfaceName: String, className: String) { 48 | val interfaceClass = app.loadClass(interfaceName, pluginDescriptor) 49 | val implClass = app.loadClass(className, pluginDescriptor) 50 | app.registerService(interfaceClass, implClass) 51 | } 52 | 53 | fun projectExtensionPoint(interfaceName: String, className: String) { 54 | val loadedClass = loadClass( 55 | app, 56 | className, 57 | pluginDescriptor 58 | ) 59 | this.projectExtensionPoint(interfaceName, loadedClass) 60 | } 61 | 62 | fun projectExtensionPoint(interfaceName: String, classObject: Class) { 63 | CoreApplicationEnvironment.registerExtensionPoint( 64 | project.extensionArea, 65 | interfaceName, 66 | classObject 67 | ) 68 | } 69 | 70 | fun appExtensionPoint(interfaceName: String, classObject: Class) { 71 | CoreApplicationEnvironment.registerExtensionPoint( 72 | app.extensionArea, 73 | interfaceName, 74 | classObject 75 | ) 76 | } 77 | } 78 | 79 | fun loadClass(componentManager: MockComponentManager, className: String, pluginDescriptor: DefaultPluginDescriptor): Class { 80 | return componentManager.loadClass(className, pluginDescriptor) 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/registration/SymbolLightClasses.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.registration 2 | 3 | fun Registrar.symbolLightClasses() { 4 | projectService( 5 | "org.jetbrains.kotlin.asJava.KotlinAsJavaSupport", 6 | "org.jetbrains.kotlin.light.classes.symbol.SymbolKotlinAsJavaSupport" 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/AnalysisPermissionOptions.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import org.jetbrains.kotlin.analysis.api.platform.permissions.KotlinAnalysisPermissionOptions 4 | 5 | class AnalysisPermissionOptions : KotlinAnalysisPermissionOptions { 6 | override val defaultIsAnalysisAllowedOnEdt: Boolean get() = false 7 | override val defaultIsAnalysisAllowedInWriteAction: Boolean get() = true 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/JavaModuleAccessibilityChecker.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.openapi.vfs.VirtualFile 4 | import org.jetbrains.kotlin.analysis.api.platform.java.KotlinJavaModuleAccessibilityChecker 5 | import org.jetbrains.kotlin.analysis.api.platform.java.KotlinJavaModuleAccessibilityError 6 | import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver 7 | import org.jetbrains.kotlin.name.FqName 8 | import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver 9 | import org.kotlinlsp.common.trace 10 | 11 | class JavaModuleAccessibilityChecker( 12 | private val javaModuleResolver: CliJavaModuleResolver, 13 | ): KotlinJavaModuleAccessibilityChecker { 14 | override fun checkAccessibility( 15 | useSiteFile: VirtualFile?, 16 | referencedFile: VirtualFile, 17 | referencedPackage: FqName? 18 | ): KotlinJavaModuleAccessibilityError? { 19 | trace("KotlinJavaModuleAccessibilityChecker.checkAccessibility") 20 | val accessError = javaModuleResolver.checkAccessibility(useSiteFile, referencedFile, referencedPackage) 21 | return accessError?.let(::convertAccessError) 22 | } 23 | 24 | private fun convertAccessError(accessError: JavaModuleResolver.AccessError): KotlinJavaModuleAccessibilityError = 25 | when (accessError) { 26 | is JavaModuleResolver.AccessError.ModuleDoesNotReadUnnamedModule -> 27 | KotlinJavaModuleAccessibilityError.ModuleDoesNotReadUnnamedModule 28 | 29 | is JavaModuleResolver.AccessError.ModuleDoesNotReadModule -> 30 | KotlinJavaModuleAccessibilityError.ModuleDoesNotReadModule(accessError.dependencyModuleName) 31 | 32 | is JavaModuleResolver.AccessError.ModuleDoesNotExportPackage -> 33 | KotlinJavaModuleAccessibilityError.ModuleDoesNotExportPackage(accessError.dependencyModuleName) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/JavaModuleAnnotationsProvider.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import org.jetbrains.kotlin.analysis.api.KaNonPublicApi 4 | import org.jetbrains.kotlin.analysis.api.platform.java.KotlinJavaModuleJavaAnnotationsProvider 5 | import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver 6 | import org.jetbrains.kotlin.load.java.structure.JavaAnnotation 7 | import org.jetbrains.kotlin.name.ClassId 8 | import org.kotlinlsp.common.trace 9 | 10 | @OptIn(KaNonPublicApi::class) 11 | class JavaModuleAnnotationsProvider( 12 | private val javaModuleResolver: CliJavaModuleResolver, 13 | ): KotlinJavaModuleJavaAnnotationsProvider { 14 | override fun getAnnotationsForModuleOwnerOfClass(classId: ClassId): List? { 15 | trace("getAnnotationsForModuleOwnerOfClass") 16 | return javaModuleResolver.getAnnotationsForModuleOwnerOfClass(classId) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/LanguageSettings.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import org.jetbrains.kotlin.config.ApiVersion 4 | import org.jetbrains.kotlin.config.LanguageVersion 5 | import org.jetbrains.kotlin.config.LanguageVersionSettings 6 | import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl 7 | 8 | val latestLanguageVersionSettings: LanguageVersionSettings = 9 | LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST) 10 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/ModificationTrackerFactory.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.openapi.project.Project 4 | import org.jetbrains.kotlin.analysis.api.platform.modification.KotlinModificationTrackerByEventFactoryBase 5 | 6 | // TODO May need implementation for module changes (e.g. adding a file) 7 | class ModificationTrackerFactory(project: Project) : KotlinModificationTrackerByEventFactoryBase(project) -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/ModuleDependentsProvider.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.util.containers.ContainerUtil.createConcurrentSoftMap 4 | import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinModuleDependentsProviderBase 5 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule 6 | import org.jetbrains.kotlin.analysis.api.projectStructure.allDirectDependencies 7 | import org.kotlinlsp.analysis.modules.Module 8 | import org.kotlinlsp.common.profile 9 | 10 | class ModuleDependentsProvider: KotlinModuleDependentsProviderBase() { 11 | private lateinit var modules: List 12 | 13 | fun setup(modules: List) { 14 | this.modules = modules 15 | } 16 | 17 | private val directDependentsByKtModule: Map> by lazy { 18 | modules 19 | .asSequence() 20 | .map { 21 | buildDependentsMap(it.kaModule) { it.allDirectDependencies() } 22 | } 23 | .reduce { ac, it -> ac + it } 24 | } 25 | 26 | private val transitiveDependentsByKtModule = createConcurrentSoftMap>() 27 | 28 | private val refinementDependentsByKtModule: Map> by lazy { 29 | modules 30 | .asSequence() 31 | .map { 32 | buildDependentsMap(it.kaModule) { it.transitiveDependsOnDependencies.asSequence() } 33 | } 34 | .reduce { ac, it -> ac + it } 35 | } 36 | 37 | override fun getDirectDependents(module: KaModule): Set = profile("getDirectDependents", "$module") { 38 | directDependentsByKtModule[module].orEmpty() 39 | } 40 | 41 | override fun getRefinementDependents(module: KaModule): Set = 42 | profile("getRefinementDependents", "$module") { 43 | refinementDependentsByKtModule[module].orEmpty() 44 | } 45 | 46 | override fun getTransitiveDependents(module: KaModule): Set = 47 | profile("getTransitiveDependents", "$module") { 48 | transitiveDependentsByKtModule.computeIfAbsent(module) { computeTransitiveDependents(it) } 49 | } 50 | } 51 | 52 | private inline fun buildDependentsMap( 53 | module: KaModule, 54 | getDependencies: (KaModule) -> Sequence, 55 | ): Map> = buildMap { 56 | for (dependency in getDependencies(module)) { 57 | if (dependency == module) continue 58 | 59 | val dependents = computeIfAbsent(dependency) { mutableSetOf() } 60 | dependents.add(module) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/PackagePartProviderFactory.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.psi.search.GlobalSearchScope 4 | import org.jetbrains.kotlin.analysis.api.platform.packages.KotlinPackagePartProviderFactory 5 | import org.jetbrains.kotlin.cli.common.messages.MessageCollector 6 | import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider 7 | import org.jetbrains.kotlin.cli.jvm.index.JavaRoot 8 | import org.jetbrains.kotlin.load.kotlin.PackagePartProvider 9 | import org.kotlinlsp.common.trace 10 | 11 | class PackagePartProviderFactory: KotlinPackagePartProviderFactory { 12 | private lateinit var allLibraryRoots: List 13 | 14 | fun setup(allLibraryRoots: List) { 15 | this.allLibraryRoots = allLibraryRoots 16 | } 17 | 18 | override fun createPackagePartProvider(scope: GlobalSearchScope): PackagePartProvider { 19 | trace("createPackagePartProvider $scope") 20 | 21 | return JvmPackagePartProvider(latestLanguageVersionSettings, scope).apply { 22 | addRoots(allLibraryRoots, MessageCollector.NONE) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/PackageProvider.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.mock.MockProject 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.psi.search.GlobalSearchScope 6 | import org.jetbrains.kotlin.analysis.api.platform.mergeSpecificProviders 7 | import org.jetbrains.kotlin.analysis.api.platform.packages.* 8 | import org.jetbrains.kotlin.name.FqName 9 | import org.jetbrains.kotlin.name.Name 10 | import org.kotlinlsp.common.profile 11 | import org.kotlinlsp.index.Index 12 | import org.kotlinlsp.index.queries.packageExistsInSourceFiles 13 | import org.kotlinlsp.index.queries.subpackageNames 14 | 15 | class PackageProviderFactory: KotlinPackageProviderFactory { 16 | private lateinit var project: MockProject 17 | private lateinit var index: Index 18 | 19 | fun setup(project: MockProject, index: Index) { 20 | this.project = project 21 | this.index = index 22 | } 23 | 24 | override fun createPackageProvider(searchScope: GlobalSearchScope): KotlinPackageProvider = PackageProvider(project, searchScope, index) 25 | } 26 | 27 | private class PackageProvider( 28 | project: Project, 29 | searchScope: GlobalSearchScope, 30 | private val index: Index 31 | ): KotlinPackageProviderBase(project, searchScope) { 32 | override fun doesKotlinOnlyPackageExist(packageFqName: FqName): Boolean = profile("doesKotlinOnlyPackageExist", "$packageFqName") { 33 | packageFqName.isRoot || index.packageExistsInSourceFiles(packageFqName) 34 | } 35 | 36 | override fun getKotlinOnlySubpackageNames(packageFqName: FqName): Set = profile("getKotlinOnlySubpackageNames", "$packageFqName") { 37 | index.subpackageNames(packageFqName.asString()).map { Name.identifier(it) }.toSet() 38 | } 39 | } 40 | 41 | class PackageProviderMerger(private val project: Project) : KotlinPackageProviderMerger { 42 | override fun merge(providers: List): KotlinPackageProvider = 43 | providers.mergeSpecificProviders<_, PackageProvider>(KotlinCompositePackageProvider.factory) { targetProviders -> 44 | val combinedScope = GlobalSearchScope.union(targetProviders.map { it.searchScope }) 45 | project.createPackageProvider(combinedScope).apply { 46 | check(this is PackageProvider) { 47 | "`${PackageProvider::class.simpleName}` can only be merged into a combined package provider of the same type." 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/PlatformSettings.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import org.jetbrains.kotlin.analysis.api.platform.KotlinDeserializedDeclarationsOrigin 4 | import org.jetbrains.kotlin.analysis.api.platform.KotlinPlatformSettings 5 | 6 | class PlatformSettings : KotlinPlatformSettings { 7 | override val deserializedDeclarationsOrigin: KotlinDeserializedDeclarationsOrigin 8 | get() = KotlinDeserializedDeclarationsOrigin.BINARIES 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/ProjectStructureProvider.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.vfs.VirtualFile 5 | import com.intellij.psi.PsiElement 6 | import org.jetbrains.kotlin.analysis.api.KaPlatformInterface 7 | import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinProjectStructureProviderBase 8 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule 9 | import org.jetbrains.kotlin.analysis.api.projectStructure.KaNotUnderContentRootModule 10 | import org.kotlinlsp.analysis.modules.Module 11 | import org.kotlinlsp.analysis.modules.NotUnderContentRootModule 12 | import org.kotlinlsp.common.profile 13 | import org.kotlinlsp.common.trace 14 | 15 | class ProjectStructureProvider: KotlinProjectStructureProviderBase() { 16 | private lateinit var modules: List 17 | private lateinit var project: Project 18 | 19 | private val notUnderContentRootModuleWithoutPsiFile by lazy { 20 | NotUnderContentRootModule( 21 | name = "unnamed-outside-content-root", 22 | moduleDescription = "not-under-content-root module without a PSI file.", 23 | project = project, 24 | ) 25 | } 26 | 27 | fun setup(modules: List, project: Project) { 28 | this.modules = modules 29 | this.project = project 30 | } 31 | 32 | override fun getImplementingModules(module: KaModule): List = profile("getImplementingModules", "$module") { 33 | emptyList() // TODO Implement for KMP support 34 | } 35 | 36 | override fun getModule(element: PsiElement, useSiteModule: KaModule?): KaModule = profile("getModule", "$element, useSiteModule: $useSiteModule") { 37 | val virtualFile = element.containingFile.virtualFile 38 | val visited = mutableSetOf() 39 | 40 | modules.forEach { 41 | val moduleFound = searchVirtualFileInModule(virtualFile, useSiteModule ?: it.kaModule, visited) 42 | if(moduleFound != null) return@profile moduleFound 43 | } 44 | 45 | return@profile NotUnderContentRootModule( 46 | file = element.containingFile, 47 | name = "unnamed-outside-content-root", 48 | moduleDescription = "Standalone not-under-content-root module with a PSI file.", 49 | project = project, 50 | ) 51 | } 52 | 53 | private fun searchVirtualFileInModule(virtualFile: VirtualFile, module: KaModule, visited: MutableSet): KaModule? { 54 | if(visited.contains(module)) return null 55 | if(module.contentScope.contains(virtualFile)) return module 56 | 57 | for(it in module.directRegularDependencies) { 58 | val submodule = searchVirtualFileInModule(virtualFile, it, visited) 59 | if(submodule != null) return submodule 60 | } 61 | 62 | visited.add(module) 63 | return null 64 | } 65 | 66 | @OptIn(KaPlatformInterface::class) 67 | override fun getNotUnderContentRootModule(project: Project): KaNotUnderContentRootModule { 68 | trace("getNotUnderContentRootModule") 69 | return notUnderContentRootModuleWithoutPsiFile 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/analysis/services/WriteAccessGuard.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.analysis.services 2 | 3 | import com.intellij.openapi.editor.impl.DocumentWriteAccessGuard 4 | import com.intellij.openapi.editor.Document 5 | 6 | class WriteAccessGuard: DocumentWriteAccessGuard() { 7 | override fun isWritable(p0: Document): Result { 8 | return success() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/buildsystem/BuildSystem.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.buildsystem 2 | 3 | import org.kotlinlsp.analysis.modules.Module 4 | 5 | interface BuildSystem { 6 | // List of files used to trigger this build system 7 | val markerFiles: List 8 | 9 | data class Result(val modules: List, val metadata: String?) 10 | 11 | // Resolves the modules if the cached metadata is not up to date 12 | // Returns null if cached metadata is up to date, otherwise 13 | // it returns the modules along with the current new metadata 14 | // If the returned metadata is null it means caching is disabled 15 | fun resolveModulesIfNeeded(cachedMetadata: String?): Result? 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/buildsystem/FileBased.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.buildsystem 2 | 3 | import com.intellij.openapi.project.Project 4 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment 5 | import org.kotlinlsp.analysis.modules.deserializeModules 6 | import java.io.File 7 | 8 | // This build system is used to integrate projects are not supported by the LSP 9 | // Also used for testing purposes 10 | class FileBasedBuildSystem( 11 | private val project: Project, 12 | private val appEnvironment: KotlinCoreApplicationEnvironment, 13 | private val rootFolder: String 14 | ): BuildSystem { 15 | override val markerFiles: List 16 | get() = listOf("$rootFolder/.kotlinlsp-modules.json") 17 | 18 | override fun resolveModulesIfNeeded(cachedMetadata: String?): BuildSystem.Result? { 19 | val file = File("$rootFolder/.kotlinlsp-modules.json") 20 | val currentVersion = file.lastModified() 21 | if(cachedMetadata != null) { 22 | val cachedVersionLong = cachedMetadata.toLong() 23 | if(currentVersion <= cachedVersionLong) return null 24 | } 25 | 26 | val contents = file.readText() 27 | val rootModule = deserializeModules( 28 | contents, 29 | project = project, 30 | appEnvironment = appEnvironment 31 | ) 32 | return BuildSystem.Result(rootModule, currentVersion.toString()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/buildsystem/Resolver.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.buildsystem 2 | 3 | import com.google.gson.Gson 4 | import com.intellij.openapi.project.Project 5 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment 6 | import org.kotlinlsp.analysis.ProgressNotifier 7 | import org.kotlinlsp.analysis.modules.Module 8 | import org.kotlinlsp.analysis.modules.deserializeModules 9 | import org.kotlinlsp.analysis.modules.serializeModules 10 | import org.kotlinlsp.common.getCachePath 11 | import org.kotlinlsp.common.info 12 | import org.kotlinlsp.common.profile 13 | import java.io.File 14 | import java.nio.file.Path 15 | import kotlin.io.path.deleteIfExists 16 | 17 | data class BuildSystemVersion(val version: String, val buildSystemName: String) 18 | 19 | class BuildSystemResolver( 20 | private val project: Project, 21 | private val appEnvironment: KotlinCoreApplicationEnvironment, 22 | progressNotifier: ProgressNotifier, 23 | rootFolder: String 24 | ) { 25 | private val cachePath = getCachePath(rootFolder) 26 | private val versionFile = cachePath.resolve("buildsystem-version.json") 27 | private val cachedModulesFile = cachePath.resolve("buildsystem.json") 28 | 29 | private val BUILD_SYSTEMS: List = listOf( 30 | FileBasedBuildSystem(project, appEnvironment, rootFolder), 31 | GradleBuildSystem(project, appEnvironment, rootFolder, progressNotifier) 32 | ) 33 | 34 | fun resolveModules(): List = profile("BuildSystemResolver", "") { 35 | val version = readVersionFile(versionFile) 36 | 37 | BUILD_SYSTEMS.forEach { 38 | if(it.markerFiles.any { File(it).exists() }) { 39 | val cachedVersion = getCachedVersionForBuildSystem(it::class.java.simpleName, version) 40 | val cachedModules = getCachedModules() 41 | val result = it.resolveModulesIfNeeded(if(cachedModules != null) { cachedVersion } else { null }) 42 | 43 | if(result == null) { 44 | if(cachedModules != null) { 45 | info("Retrieved cached modules for ${it::class.java.simpleName} buildsystem") 46 | return@profile cachedModules 47 | } 48 | } else { 49 | val newMetadata = result.metadata 50 | if(newMetadata != null) { 51 | cachedModulesFile.deleteIfExists() 52 | versionFile.deleteIfExists() 53 | val serializedCachedModules = serializeModules(result.modules) 54 | val serializedVersionData = Gson().toJson(BuildSystemVersion( 55 | version = newMetadata, 56 | buildSystemName = it::class.java.simpleName 57 | )) 58 | File(cachedModulesFile.toUri()).writeText(serializedCachedModules) 59 | File(versionFile.toUri()).writeText(serializedVersionData) 60 | } 61 | return@profile result.modules 62 | } 63 | } 64 | } 65 | throw Exception("Not suitable build system found!") 66 | } 67 | 68 | private fun readVersionFile(path: Path): BuildSystemVersion? { 69 | val file = File(path.toUri()) 70 | if (!file.exists()) return null 71 | 72 | try { 73 | val content = file.readText() 74 | return Gson().fromJson(content, BuildSystemVersion::class.java) 75 | } catch(_: Exception) { 76 | return null 77 | } 78 | } 79 | 80 | private fun getCachedVersionForBuildSystem(name: String, version: BuildSystemVersion?): String? { 81 | if(version == null) return null 82 | if(version.buildSystemName != name) return null 83 | return version.version 84 | } 85 | 86 | private fun getCachedModules(): List? { 87 | val file = File(cachedModulesFile.toUri()) 88 | if(!file.exists()) return null 89 | try { 90 | return deserializeModules(file.readText(), appEnvironment, project) 91 | } catch(_: Exception) { 92 | return null 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/common/CacheFolder.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.common 2 | 3 | import java.io.File 4 | import java.nio.file.Files 5 | import java.nio.file.Path 6 | import java.nio.file.Paths 7 | import kotlin.io.path.absolutePathString 8 | 9 | fun getCachePath(rootPath: String): Path { 10 | val cachePath = Paths.get(rootPath).resolve(".kotlin-lsp") 11 | 12 | if (!Files.exists(cachePath)) Files.createDirectories(cachePath) 13 | 14 | return cachePath 15 | } 16 | 17 | fun removeCacheFolder(rootPath: String) { 18 | val cachePath = getCachePath(rootPath).absolutePathString() 19 | File(cachePath).deleteRecursively() 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/common/Log.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.common 2 | 3 | import org.eclipse.lsp4j.MessageParams 4 | import org.eclipse.lsp4j.MessageType 5 | import org.eclipse.lsp4j.services.LanguageClient 6 | import java.io.PrintWriter 7 | import java.io.StringWriter 8 | import java.lang.management.ManagementFactory 9 | import java.util.logging.Handler 10 | import java.util.logging.Level 11 | import java.util.logging.LogRecord 12 | import java.util.logging.Logger 13 | import kotlin.time.Duration 14 | import kotlin.time.Duration.Companion.milliseconds 15 | import kotlin.time.measureTime 16 | 17 | private enum class LogLevel(level: Int) { 18 | Trace(0), 19 | Debug(1), 20 | Info(2), 21 | Warning(3), 22 | Error(4), 23 | Off(5) 24 | } 25 | 26 | private fun LogLevel.asMessageType(): MessageType { 27 | return when (this) { 28 | LogLevel.Trace -> MessageType.Log 29 | LogLevel.Debug -> MessageType.Log 30 | LogLevel.Info -> MessageType.Info 31 | LogLevel.Warning -> MessageType.Warning 32 | LogLevel.Error -> MessageType.Error 33 | LogLevel.Off -> throw IllegalArgumentException("LogLevel Off cannot be converted to MessageType") 34 | } 35 | } 36 | 37 | // Configure as needed 38 | private val logLevel = LogLevel.Debug 39 | private const val profileEnabled = true 40 | 41 | private lateinit var logger: LSPLogger 42 | private val profileInfo = mutableMapOf>() 43 | 44 | private class LSPLogger(val client: LanguageClient) { 45 | fun log(level: LogLevel, message: String) { 46 | if (level < logLevel) return 47 | 48 | client.logMessage(MessageParams(level.asMessageType(), message)) 49 | } 50 | } 51 | 52 | fun setupLogger(client: LanguageClient) { 53 | logger = LSPLogger(client) 54 | 55 | // This is to log the exceptions from JUL (java.util.log) 56 | Logger.getLogger("").addHandler(JULRedirector()) 57 | } 58 | 59 | fun profile(tag: String, message: String, fn: () -> T): T { 60 | trace("$tag $message") 61 | 62 | if(profileEnabled) { 63 | var result: T 64 | val time = measureTime { 65 | result = fn() 66 | } 67 | if(!profileInfo.containsKey(tag)) { 68 | profileInfo[tag] = Pair(1, time) 69 | } else { 70 | val value = profileInfo[tag]!! 71 | profileInfo[tag] = Pair(value.first + 1, value.second.plus(time)) 72 | } 73 | return result 74 | } else { 75 | return fn() 76 | } 77 | } 78 | 79 | fun profileJvmStartup() { 80 | val runtimeMXBean = ManagementFactory.getRuntimeMXBean() 81 | val jvmStartTimeMillis = runtimeMXBean.startTime 82 | val deltaMillis = System.currentTimeMillis() - jvmStartTimeMillis 83 | profileInfo["JVM Startup"] = Pair(1, deltaMillis.milliseconds) 84 | } 85 | 86 | fun logProfileInfo() { 87 | if (!profileEnabled) return 88 | 89 | val sb = StringBuilder() 90 | sb.appendLine("------------") 91 | sb.appendLine("PROFILE INFO") 92 | var totalDuration = Duration.ZERO 93 | profileInfo.entries.sortedByDescending { it.value.second }.forEach { 94 | val header = "${it.key} (x${it.value.first}):".padEnd(65) 95 | val formattedDuration = formatDuration(it.value.second) 96 | totalDuration += it.value.second 97 | sb.appendLine("$header $formattedDuration") 98 | } 99 | sb.appendLine("------------") 100 | sb.appendLine("TOTAL: ${formatDuration(totalDuration)}") 101 | sb.appendLine("------------") 102 | profileInfo.clear() 103 | 104 | debug(sb.toString()) 105 | } 106 | 107 | private fun formatDuration(duration: Duration): String { 108 | return "%.3f ms".format(duration.inWholeMicroseconds.toDouble() / 1000) 109 | } 110 | 111 | fun debug(message: String) = logger.log(LogLevel.Debug, message) 112 | fun info(message: String) = logger.log(LogLevel.Info, message) 113 | fun error(message: String) = logger.log(LogLevel.Error, message) 114 | fun trace(message: String) = logger.log(LogLevel.Trace, message) 115 | fun warn(message: String) = logger.log(LogLevel.Warning, message) 116 | 117 | private class JULRedirector: Handler() { 118 | override fun publish(record: LogRecord) { 119 | when (record.level) { 120 | Level.SEVERE -> { 121 | error(record.message) 122 | val stackTrace = StringWriter().also { PrintWriter(it).use { pw -> record.thrown?.printStackTrace(pw) } }.toString() 123 | if(stackTrace.isNotEmpty()) error(stackTrace) 124 | } 125 | Level.WARNING -> warn(record.message) 126 | Level.INFO -> info(record.message) 127 | Level.CONFIG -> debug(record.message) 128 | Level.FINE -> trace(record.message) 129 | else -> trace(record.message) 130 | } 131 | } 132 | 133 | override fun flush() {} 134 | override fun close() {} 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/common/LspMappers.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.common 2 | 3 | import org.eclipse.lsp4j.DiagnosticSeverity 4 | import org.jetbrains.kotlin.analysis.api.diagnostics.KaSeverity 5 | 6 | fun KaSeverity.toLspSeverity(): DiagnosticSeverity = 7 | when(this) { 8 | KaSeverity.ERROR -> DiagnosticSeverity.Error 9 | KaSeverity.WARNING -> DiagnosticSeverity.Warning 10 | KaSeverity.INFO -> DiagnosticSeverity.Information 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/common/ReadWriteLock.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.common 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.util.Key 5 | import java.util.concurrent.locks.ReentrantReadWriteLock 6 | import kotlin.concurrent.read 7 | import kotlin.concurrent.write 8 | 9 | private val key = Key.create("org.kotlinlsp.rwlock") 10 | private val lock = ReentrantReadWriteLock() 11 | 12 | fun Project.registerRWLock() { 13 | putUserData(key, lock) 14 | } 15 | 16 | fun Project.read(fn: () -> T): T { 17 | val lock = getUserData(key)!! 18 | return lock.read(fn) 19 | } 20 | 21 | fun Project.write(fn: () -> T): T { 22 | val lock = getUserData(key)!! 23 | return lock.write(fn) 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/common/Text.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.common 2 | 3 | import com.intellij.openapi.util.TextRange 4 | import com.intellij.openapi.util.text.StringUtil 5 | import com.intellij.psi.PsiFile 6 | import org.eclipse.lsp4j.Position 7 | import org.eclipse.lsp4j.Range 8 | import org.jetbrains.kotlin.psi.KtElement 9 | import org.jetbrains.kotlin.psi.KtFile 10 | 11 | fun Position.toOffset(ktFile: KtFile): Int = StringUtil.lineColToOffset(ktFile.text, line, character) 12 | 13 | fun TextRange.toLspRange(ktFile: PsiFile): Range { 14 | val text = ktFile.text 15 | val lineColumnStart = StringUtil.offsetToLineColumn(text, startOffset) 16 | val lineColumnEnd = StringUtil.offsetToLineColumn(text, endOffset) 17 | 18 | return Range( 19 | Position(lineColumnStart.line, lineColumnStart.column), 20 | Position(lineColumnEnd.line, lineColumnEnd.column) 21 | ) 22 | } 23 | 24 | fun getElementRange(ktFile: KtFile, element: KtElement): Range { 25 | val document = ktFile.viewProvider.document 26 | val textRange = element.textRange 27 | val startOffset = textRange.startOffset 28 | val endOffset = textRange.endOffset 29 | val start = document.getLineNumber(startOffset).let { line -> 30 | Position(line, startOffset - document.getLineStartOffset(line)) 31 | } 32 | val end = document.getLineNumber(endOffset).let { line -> 33 | Position(line, endOffset - document.getLineStartOffset(line)) 34 | } 35 | return Range(start, end) 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/common/Version.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.common 2 | 3 | fun getLspVersion(): String { 4 | val pkg = object {}.javaClass.`package` 5 | return pkg.implementationVersion ?: "unknown" 6 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/Command.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index 2 | 3 | import com.intellij.openapi.vfs.VirtualFile 4 | import org.jetbrains.kotlin.psi.KtFile 5 | 6 | sealed class Command { 7 | data object Stop : Command() 8 | data object SourceScanningFinished: Command() 9 | data object IndexingFinished: Command() 10 | data class ScanSourceFile(val virtualFile: VirtualFile) : Command() 11 | data class IndexModifiedFile(val ktFile: KtFile) : Command() 12 | data class IndexFile(val virtualFile: VirtualFile) : Command() 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/Index.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index 2 | 3 | import com.github.benmanes.caffeine.cache.Caffeine 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.openapi.vfs.VirtualFile 6 | import com.intellij.psi.PsiManager 7 | import org.jetbrains.kotlin.psi.KtFile 8 | import org.kotlinlsp.analysis.modules.Module 9 | import org.kotlinlsp.common.read 10 | import org.kotlinlsp.index.db.Database 11 | import org.kotlinlsp.index.worker.WorkerThread 12 | import org.kotlinlsp.index.worker.WorkerThreadNotifier 13 | import java.util.concurrent.ConcurrentHashMap 14 | import java.util.concurrent.CountDownLatch 15 | 16 | interface IndexNotifier { 17 | fun onBackgroundIndexFinished() 18 | } 19 | 20 | class Index( 21 | modules: List, 22 | private val project: Project, 23 | rootFolder: String, 24 | notifier: IndexNotifier 25 | ) { 26 | private val sourceFileScanningFinishedSignal = CountDownLatch(1) 27 | private val workerThreadNotifier = object : WorkerThreadNotifier { 28 | override fun onSourceFileScanningFinished() { 29 | sourceFileScanningFinishedSignal.countDown() 30 | } 31 | 32 | override fun onBackgroundIndexFinished() = notifier.onBackgroundIndexFinished() 33 | } 34 | private val db = Database(rootFolder) 35 | private val workerThreadRunner = WorkerThread(db, project, workerThreadNotifier) 36 | private val workerThread = Thread(workerThreadRunner, "KotlinLSP-Worker") 37 | private val scanFilesThreadRunner = ScanFilesThread(workerThreadRunner, modules) 38 | private val scanFilesThread = Thread(scanFilesThreadRunner, "KotlinLSP-ScanFiles") 39 | private val openedFiles: MutableMap = ConcurrentHashMap() 40 | 41 | // This cache prevents parsing KtFiles over and over 42 | private val ktFileCache = Caffeine.newBuilder() 43 | .maximumSize(100) 44 | .build() 45 | 46 | fun syncIndexInBackground() { 47 | // We have 2 threads here 48 | // Scan files -> It scans files to index, loads them as a KtFile and submits them to a work queue 49 | // Worker -> Takes each KtFile, fetches its symbols and writes them to the index database 50 | // The scan files thread will stop when the background indexing is done, while the worker is kept alive 51 | // for the duration of the server. It will keep receiving work whenever a KtFile is edited by the user so the 52 | // index is always up to date 53 | workerThread.start() 54 | scanFilesThread.start() 55 | } 56 | 57 | fun queueOnFileChanged(ktFile: KtFile) { 58 | workerThreadRunner.submitCommand(Command.IndexModifiedFile(ktFile)) 59 | } 60 | 61 | fun close() { 62 | scanFilesThreadRunner.signalToStop() 63 | workerThreadRunner.submitCommand(Command.Stop) 64 | scanFilesThread.join() 65 | workerThread.join() 66 | 67 | db.close() 68 | } 69 | 70 | fun query(block: (connection: Database) -> T): T { 71 | sourceFileScanningFinishedSignal.await() 72 | return block(db) 73 | } 74 | 75 | fun openKtFile(path: String, ktFile: KtFile) { 76 | openedFiles[path] = ktFile 77 | } 78 | 79 | fun closeKtFile(path: String) { 80 | openedFiles.remove(path) 81 | } 82 | 83 | fun getOpenedKtFile(path: String): KtFile? = openedFiles[path] 84 | 85 | val openedKtFiles: Sequence> 86 | get() = openedFiles.asSequence() 87 | 88 | fun getKtFile(virtualFile: VirtualFile): KtFile? { 89 | // First check opened files 90 | val openedFile = openedFiles.get(virtualFile.url) 91 | if(openedFile != null) return openedFile 92 | 93 | // Then check the cache 94 | val cachedKtFile = ktFileCache.getIfPresent(virtualFile.url) 95 | if(cachedKtFile != null) return cachedKtFile 96 | 97 | // If not, load from disk and store in cache 98 | val ktFile = project.read { PsiManager.getInstance(project).findFile(virtualFile) as? KtFile } ?: return null 99 | ktFileCache.put(virtualFile.url, ktFile) 100 | return ktFile 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/ScanFilesThread.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index 2 | 3 | import org.kotlinlsp.analysis.modules.Module 4 | import org.kotlinlsp.analysis.modules.asFlatSequence 5 | import org.kotlinlsp.index.worker.WorkerThread 6 | import java.util.concurrent.atomic.AtomicBoolean 7 | 8 | class ScanFilesThread( 9 | private val worker: WorkerThread, 10 | private val modules: List 11 | ) : Runnable { 12 | private val shouldStop = AtomicBoolean(false) 13 | 14 | override fun run() { 15 | 16 | // Scan phase 17 | modules.asFlatSequence() 18 | .filter { it.isSourceModule } 19 | .map { it.computeFiles(extended = true) } 20 | .flatten() 21 | .takeWhile { !shouldStop.get() } 22 | .forEach { 23 | val command = Command.ScanSourceFile(it) 24 | worker.submitCommand(command) 25 | } 26 | 27 | worker.submitCommand(Command.SourceScanningFinished) 28 | 29 | // Once scanning is done and the analysis API is available, index all files (Index phase) 30 | modules.asFlatSequence() 31 | // Scan source files first as they will be more frequently accessed by the user 32 | .sortedByDescending { it.isSourceModule } 33 | .map { it.computeFiles(extended = true) } 34 | .flatten() 35 | .takeWhile { !shouldStop.get() } 36 | .forEach { 37 | worker.submitCommand(Command.IndexFile(it)) 38 | } 39 | 40 | worker.submitCommand(Command.IndexingFinished) 41 | } 42 | 43 | fun signalToStop() { 44 | shouldStop.set(true) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/db/Database.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.db 2 | 3 | import org.kotlinlsp.common.getCachePath 4 | import org.kotlinlsp.common.info 5 | import org.kotlinlsp.index.db.adapters.DatabaseAdapter 6 | import org.kotlinlsp.index.db.adapters.RocksDBAdapter 7 | import org.kotlinlsp.index.db.adapters.get 8 | import org.kotlinlsp.index.db.adapters.put 9 | import java.io.File 10 | import kotlin.io.path.absolutePathString 11 | 12 | const val CURRENT_SCHEMA_VERSION = 5 // Increment on schema changes 13 | const val VERSION_KEY = "__version" 14 | 15 | class Database(rootFolder: String) { 16 | private val cachePath = getCachePath(rootFolder) 17 | val filesDb: DatabaseAdapter 18 | val packagesDb: DatabaseAdapter 19 | val declarationsDb: DatabaseAdapter 20 | 21 | init { 22 | var projectDb = RocksDBAdapter(cachePath.resolve("project")) 23 | val schemaVersion = projectDb.get(VERSION_KEY) 24 | 25 | if(schemaVersion == null || schemaVersion != CURRENT_SCHEMA_VERSION) { 26 | 27 | // Schema version mismatch, wipe the db 28 | info("Index DB schema version mismatch, recreating!") 29 | projectDb.close() 30 | deleteAll() 31 | 32 | projectDb = RocksDBAdapter(cachePath.resolve("project")) 33 | projectDb.put(VERSION_KEY, CURRENT_SCHEMA_VERSION) 34 | } 35 | 36 | filesDb = RocksDBAdapter(cachePath.resolve("files")) 37 | packagesDb = RocksDBAdapter(cachePath.resolve("packages")) 38 | declarationsDb = RocksDBAdapter(cachePath.resolve("declarations")) 39 | projectDb.close() 40 | } 41 | 42 | fun close() { 43 | filesDb.close() 44 | packagesDb.close() 45 | declarationsDb.close() 46 | } 47 | 48 | private fun deleteAll() { 49 | File(cachePath.resolve("project").absolutePathString()).deleteRecursively() 50 | File(cachePath.resolve("files").absolutePathString()).deleteRecursively() 51 | File(cachePath.resolve("packages").absolutePathString()).deleteRecursively() 52 | File(cachePath.resolve("declarations").absolutePathString()).deleteRecursively() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/db/Declaration.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.db 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import org.kotlinlsp.index.db.adapters.put 6 | 7 | @Serializable 8 | sealed class Declaration() { 9 | abstract val name: String 10 | abstract val file: String 11 | abstract val startOffset: Int 12 | abstract val endOffset: Int 13 | 14 | @Serializable 15 | @SerialName("function") 16 | data class Function( 17 | override val name: String, 18 | val fqName: String, 19 | override val file: String, 20 | override val startOffset: Int, 21 | override val endOffset: Int, 22 | val parameters: List, 23 | val returnType: String, 24 | val parentFqName: String, 25 | val receiverFqName: String, 26 | ) : Declaration() { 27 | @Serializable 28 | data class Parameter( 29 | val name: String, 30 | val type: String, 31 | ) 32 | } 33 | 34 | @Serializable 35 | @SerialName("class") 36 | data class Class( 37 | override val name: String, 38 | val type: Type, 39 | val fqName: String, 40 | override val file: String, 41 | override val startOffset: Int, 42 | override val endOffset: Int, 43 | ) : Declaration() { 44 | enum class Type { 45 | CLASS, 46 | ABSTRACT_CLASS, 47 | INTERFACE, 48 | ENUM_CLASS, 49 | OBJECT, 50 | ANNOTATION_CLASS, 51 | } 52 | } 53 | 54 | @Serializable 55 | @SerialName("enumEntry") 56 | data class EnumEntry( 57 | override val name: String, 58 | val fqName: String, 59 | override val file: String, 60 | override val startOffset: Int, 61 | override val endOffset: Int, 62 | val enumFqName: String, 63 | ) : Declaration() 64 | 65 | @Serializable 66 | @SerialName("field") 67 | data class Field( 68 | override val name: String, 69 | val fqName: String, 70 | override val file: String, 71 | override val startOffset: Int, 72 | override val endOffset: Int, 73 | val type: String, 74 | val parentFqName: String, 75 | ) : Declaration() 76 | } 77 | 78 | fun Declaration.id() = "${name}:${file}:${startOffset}:${endOffset}" 79 | 80 | fun Database.putDeclarations(declarations: Iterable) { 81 | declarationsDb.put(declarations.map { Pair(it.id(), it) }) 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/db/File.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.db 2 | 3 | import com.intellij.openapi.project.Project 4 | import kotlinx.serialization.Serializable 5 | import org.jetbrains.kotlin.psi.KtFile 6 | import org.kotlinlsp.common.read 7 | import org.kotlinlsp.index.db.adapters.get 8 | import org.kotlinlsp.index.db.adapters.put 9 | import java.time.Instant 10 | 11 | data class File( 12 | val path: String, 13 | val packageFqName: String, 14 | val lastModified: Instant, 15 | val modificationStamp: Long, 16 | val indexed: Boolean, 17 | val declarationKeys: MutableList = mutableListOf() 18 | ) { 19 | companion object { 20 | fun fromKtFile(ktFile: KtFile, project: Project, indexed: Boolean): File = project.read { 21 | val packageFqName = ktFile.packageFqName.asString() 22 | val file = File( 23 | packageFqName = packageFqName, 24 | path = ktFile.virtualFile.url, 25 | lastModified = Instant.ofEpochMilli(ktFile.virtualFile.timeStamp), 26 | modificationStamp = ktFile.modificationStamp, 27 | indexed = indexed, 28 | ) 29 | file 30 | } 31 | 32 | // Check if the file record has been modified since last time 33 | // I think the case of overflowing modificationStamp is not worth to be considered as it is 64bit int 34 | // (a trillion modifications on the same file in the same coding session) 35 | fun shouldBeSkipped(existingFile: File?, newFile: File) = existingFile != null && 36 | !existingFile.lastModified.isBefore(newFile.lastModified) && 37 | existingFile.modificationStamp >= newFile.modificationStamp && 38 | (newFile.modificationStamp != 0L || existingFile.modificationStamp == 0L) 39 | } 40 | } 41 | 42 | fun File.toDto(): FileDto = FileDto( 43 | packageFqName = packageFqName, 44 | lastModified = lastModified.toEpochMilli(), 45 | modificationStamp = modificationStamp, 46 | indexed = indexed, 47 | declarationKeys = declarationKeys 48 | ) 49 | 50 | @Serializable 51 | data class FileDto( 52 | val packageFqName: String, 53 | val lastModified: Long, 54 | val modificationStamp: Long, 55 | val indexed: Boolean, 56 | val declarationKeys: List 57 | ) 58 | 59 | fun Database.file(path: String): File? { 60 | return filesDb.get(path)?.let { 61 | File( 62 | path = path, 63 | packageFqName = it.packageFqName, 64 | lastModified = Instant.ofEpochMilli(it.lastModified), 65 | modificationStamp = it.modificationStamp, 66 | indexed = it.indexed, 67 | ) 68 | } 69 | } 70 | 71 | fun Database.setFile(file: File) { 72 | val dto = file.toDto() 73 | val previousPackageFqName = filesDb.get(file.path)?.packageFqName 74 | 75 | filesDb.put(file.path, dto) 76 | 77 | if(previousPackageFqName != file.packageFqName) { 78 | // Remove previous package name 79 | if(previousPackageFqName != null) { 80 | val files = packagesDb.get>(previousPackageFqName)?.toMutableList() ?: mutableListOf() 81 | files.remove(file.path) 82 | if(files.size == 0) { 83 | packagesDb.remove(previousPackageFqName) 84 | } else { 85 | packagesDb.put(previousPackageFqName, files) 86 | } 87 | } 88 | 89 | // Add new one 90 | val files = packagesDb.get>(file.packageFqName)?.toMutableList() ?: mutableListOf() 91 | files.add(file.path) 92 | packagesDb.put(file.packageFqName, files) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/db/adapters/DatabaseAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.db.adapters 2 | 3 | import kotlinx.serialization.ExperimentalSerializationApi 4 | import kotlinx.serialization.decodeFromByteArray 5 | import kotlinx.serialization.encodeToByteArray 6 | import kotlinx.serialization.protobuf.ProtoBuf 7 | 8 | interface DatabaseAdapter { 9 | fun putRawData(key: String, value: ByteArray) 10 | fun putRawData(values: Iterable>) 11 | fun getRawData(key: String): ByteArray? 12 | fun prefixSearchRaw(prefix: String): Sequence> 13 | fun remove(key: String) 14 | fun remove(keys: Iterable) 15 | fun close() 16 | fun deleteDb() 17 | } 18 | 19 | @OptIn(ExperimentalSerializationApi::class) 20 | inline fun DatabaseAdapter.prefixSearch(key: String): Sequence> { 21 | return prefixSearchRaw(key) 22 | .map { (key, value) -> Pair(key, ProtoBuf.decodeFromByteArray(value)) } 23 | } 24 | 25 | @OptIn(ExperimentalSerializationApi::class) 26 | inline fun DatabaseAdapter.put(key: String, value: T) { 27 | putRawData(key, ProtoBuf.encodeToByteArray(value)) 28 | } 29 | 30 | @OptIn(ExperimentalSerializationApi::class) 31 | inline fun DatabaseAdapter.put(values: Iterable>) { 32 | putRawData(values.map { Pair(it.first, ProtoBuf.encodeToByteArray(it.second)) }) 33 | } 34 | 35 | @OptIn(ExperimentalSerializationApi::class) 36 | inline fun DatabaseAdapter.get(key: String): T? { 37 | val data = getRawData(key) ?: return null 38 | return ProtoBuf.decodeFromByteArray(data) 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/db/adapters/RocksDBAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.db.adapters 2 | 3 | import org.rocksdb.* 4 | import java.io.File 5 | import java.nio.charset.Charset 6 | import java.nio.file.Path 7 | import kotlin.io.path.absolutePathString 8 | 9 | class RocksDBAdapter(private val path: Path): DatabaseAdapter { 10 | companion object { 11 | init { 12 | RocksDB.loadLibrary() 13 | } 14 | 15 | val options = Options().apply { 16 | setCreateIfMissing(true) 17 | setKeepLogFileNum(1) 18 | setInfoLogLevel(InfoLogLevel.FATAL_LEVEL) 19 | } 20 | } 21 | 22 | private val db = RocksDB.open(options, path.absolutePathString()) 23 | 24 | override fun putRawData(key: String, value: ByteArray) { 25 | db.put(key.toByteArray(), value) 26 | } 27 | 28 | override fun putRawData(values: Iterable>) { 29 | val batch = WriteBatch() 30 | values.forEach { (key, value) -> 31 | batch.put(key.toByteArray(), value) 32 | } 33 | db.write(WriteOptions(), batch) 34 | batch.close() 35 | } 36 | 37 | override fun getRawData(key: String): ByteArray? { 38 | val data = db.get(key.toByteArray()) ?: return null 39 | return data 40 | } 41 | 42 | override fun prefixSearchRaw(prefix: String): Sequence> = sequence { 43 | val readOptions = ReadOptions().setPrefixSameAsStart(true) 44 | 45 | readOptions.use { 46 | val iterator = db.newIterator(readOptions) 47 | iterator.seek(prefix.toByteArray()) 48 | 49 | while (iterator.isValid) { 50 | val key = iterator.key() 51 | val keyString = key.toString(Charset.defaultCharset()) 52 | if (!keyString.startsWith(prefix)) break 53 | 54 | yield(Pair(keyString, iterator.value())) 55 | iterator.next() 56 | } 57 | } 58 | } 59 | 60 | override fun remove(key: String) { 61 | db.delete(key.toByteArray()) 62 | } 63 | 64 | override fun remove(keys: Iterable) { 65 | val batch = WriteBatch() 66 | keys.forEach { 67 | batch.delete(it.toByteArray()) 68 | } 69 | db.write(WriteOptions(), batch) 70 | batch.close() 71 | } 72 | 73 | override fun close() { 74 | db.close() 75 | } 76 | 77 | override fun deleteDb() { 78 | if(!db.isClosed) db.close() 79 | File(path.absolutePathString()).deleteRecursively() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/queries/Completions.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.queries 2 | 3 | import org.kotlinlsp.index.Index 4 | import org.kotlinlsp.index.db.Declaration 5 | import org.kotlinlsp.index.db.adapters.prefixSearch 6 | 7 | fun Index.getCompletions(prefix: String): Sequence = query { 8 | it.declarationsDb.prefixSearch(prefix) 9 | .map { (_, value) -> value } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/queries/FilesForPackage.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.queries 2 | 3 | import org.jetbrains.kotlin.name.FqName 4 | import org.kotlinlsp.index.Index 5 | import org.kotlinlsp.index.db.adapters.get 6 | 7 | fun Index.filesForPackage(fqName: FqName): List = query { db -> 8 | db.packagesDb.get>(fqName.asString()) ?: emptyList() 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/queries/PackageExistsInSourceFiles.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.queries 2 | 3 | import org.jetbrains.kotlin.name.FqName 4 | import org.kotlinlsp.index.Index 5 | 6 | fun Index.packageExistsInSourceFiles(fqName: FqName): Boolean = query { db -> 7 | db.packagesDb.prefixSearchRaw(fqName.asString()).iterator().hasNext() 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/queries/SubpackageNames.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.queries 2 | 3 | import org.kotlinlsp.index.Index 4 | 5 | fun Index.subpackageNames(fqName: String): Set = query { db -> 6 | db.packagesDb.prefixSearchRaw(fqName) 7 | .filter { (key, _) -> key != fqName } 8 | .map { (key, _) -> key.removePrefix("$fqName.").split(".") } 9 | .filter { it.isNotEmpty() } 10 | .map { it.first() } 11 | .toSet() 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/worker/IndexClassFile.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.worker 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.vfs.VirtualFile 5 | import org.kotlinlsp.index.db.Database 6 | 7 | fun indexClassFile(project: Project, virtualFile: VirtualFile, db: Database) { 8 | // TODO 9 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/worker/IndexKtFile.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.worker 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.psi.util.parentOfType 5 | import org.jetbrains.kotlin.analysis.api.KaSession 6 | import org.jetbrains.kotlin.analysis.api.analyze 7 | import org.jetbrains.kotlin.psi.* 8 | import org.jetbrains.kotlin.psi.psiUtil.isAbstract 9 | import org.kotlinlsp.common.read 10 | import org.kotlinlsp.common.warn 11 | import org.kotlinlsp.index.db.* 12 | 13 | fun indexKtFile(project: Project, ktFile: KtFile, db: Database) { 14 | val newFile = File.fromKtFile(ktFile, project, indexed = true) 15 | 16 | // Check if skipping the indexing of that file 17 | val existingFile = db.file(newFile.path) 18 | if ( 19 | File.shouldBeSkipped(existingFile = existingFile, newFile = newFile) && 20 | existingFile?.indexed == true // Already indexed 21 | ) return 22 | 23 | // Remove declarations for this file first 24 | existingFile?.declarationKeys?.let { 25 | db.declarationsDb.remove(it) 26 | } 27 | 28 | // Get declarations metadata 29 | val declarations = project.read { 30 | val list = mutableListOf() 31 | ktFile.accept(object : KtTreeVisitorVoid() { 32 | override fun visitDeclaration(dcl: KtDeclaration) { 33 | val record = analyze(dcl) { 34 | analyzeDeclaration(newFile.path, dcl) 35 | } ?: return 36 | list.add(record) 37 | super.visitDeclaration(dcl) 38 | } 39 | }) 40 | return@read list 41 | } 42 | 43 | // Save declarations 44 | db.putDeclarations(declarations) 45 | 46 | // Update the file timestamp, package and declaration names 47 | newFile.declarationKeys.addAll(declarations.map { it.id() }) 48 | db.setFile(newFile) 49 | } 50 | 51 | private fun KaSession.analyzeDeclaration(path: String, dcl: KtDeclaration): Declaration? { 52 | val name = dcl.name ?: return null 53 | val startOffset = dcl.textOffset 54 | val endOffset = dcl.textOffset + name.length 55 | 56 | return when (dcl) { 57 | is KtNamedFunction -> { 58 | val parentFqName = if (dcl.parent is KtClassBody) { 59 | (dcl.parent.parent as? KtClassOrObject)?.fqName?.asString() ?: "" 60 | } else "" 61 | 62 | Declaration.Function( 63 | name, 64 | dcl.fqName?.asString() ?: "", 65 | path, 66 | startOffset, 67 | endOffset, 68 | dcl.valueParameters.map { 69 | Declaration.Function.Parameter( 70 | it.nameAsSafeName.asString(), 71 | it.returnType.toString() 72 | ) 73 | }, 74 | dcl.returnType.toString(), 75 | parentFqName, 76 | dcl.receiverTypeReference?.type?.toString() ?: "" 77 | ) 78 | } 79 | 80 | is KtClass -> { 81 | if (dcl is KtEnumEntry) { 82 | return Declaration.EnumEntry( 83 | name, 84 | dcl.fqName?.asString() ?: "", 85 | path, 86 | startOffset, 87 | endOffset, 88 | dcl.parentOfType()?.fqName?.asString() ?: "" 89 | ) 90 | } 91 | 92 | val type = if (dcl.isEnum()) { 93 | Declaration.Class.Type.ENUM_CLASS 94 | } else if (dcl.isAnnotation()) { 95 | Declaration.Class.Type.ANNOTATION_CLASS 96 | } else if (dcl.isInterface()) { 97 | Declaration.Class.Type.INTERFACE 98 | } else if (dcl.isAbstract()) { 99 | Declaration.Class.Type.ABSTRACT_CLASS 100 | } else { 101 | Declaration.Class.Type.CLASS 102 | } 103 | 104 | Declaration.Class( 105 | name, 106 | type, 107 | dcl.fqName?.asString() ?: "", 108 | path, 109 | startOffset, 110 | endOffset 111 | ) 112 | } 113 | 114 | is KtParameter -> { 115 | if (!dcl.hasValOrVar()) return null 116 | val constructor = dcl.parentOfType() ?: return null 117 | val clazz = constructor.parentOfType() ?: return null 118 | 119 | Declaration.Field( 120 | name, 121 | dcl.fqName?.asString() ?: "", 122 | path, 123 | startOffset, 124 | endOffset, 125 | dcl.returnType.toString(), 126 | clazz.fqName?.asString() ?: "" 127 | ) 128 | } 129 | 130 | is KtProperty -> { 131 | if (dcl.isLocal) return null 132 | val clazz = dcl.parentOfType() ?: return Declaration.Field( 133 | name, 134 | dcl.fqName?.asString() ?: "", 135 | path, 136 | startOffset, 137 | endOffset, 138 | dcl.returnType.toString(), 139 | "" 140 | ) 141 | 142 | Declaration.Field( 143 | name, 144 | dcl.fqName?.asString() ?: "", 145 | path, 146 | startOffset, 147 | endOffset, 148 | dcl.returnType.toString(), 149 | clazz.fqName?.asString() ?: "" 150 | ) 151 | } 152 | 153 | else -> { 154 | // TODO Handle other declarations 155 | warn("Declaration type not handled: ${dcl::class.simpleName}") 156 | null 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/worker/ScanKtFile.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.worker 2 | 3 | import com.intellij.openapi.project.Project 4 | import org.jetbrains.kotlin.psi.KtFile 5 | import org.kotlinlsp.index.db.Database 6 | import org.kotlinlsp.index.db.File 7 | import org.kotlinlsp.index.db.file 8 | import org.kotlinlsp.index.db.setFile 9 | 10 | fun scanKtFile(project: Project, ktFile: KtFile, db: Database) { 11 | val newFile = File.fromKtFile(ktFile, project, indexed = false) 12 | 13 | val existingFile = db.file(newFile.path) 14 | if ( 15 | File.shouldBeSkipped(existingFile = existingFile, newFile = newFile) 16 | ) return 17 | 18 | // Update the file timestamp and package 19 | db.setFile(newFile) 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/worker/WorkQueue.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.worker 2 | 3 | import java.util.* 4 | import java.util.concurrent.locks.ReentrantLock 5 | import kotlin.concurrent.withLock 6 | 7 | class WorkQueue { 8 | private val scanQueue: Queue = LinkedList() 9 | private val editQueue: Stack = Stack() 10 | private val indexQueue: Queue = LinkedList() 11 | private val mutex = ReentrantLock() 12 | private val notEmpty = mutex.newCondition() 13 | private val notFullEdit = mutex.newCondition() 14 | private val notFullIndex = mutex.newCondition() 15 | private val notFullScan = mutex.newCondition() 16 | 17 | companion object { 18 | const val MAX_EDIT_SIZE = 20 19 | const val MAX_INDEX_SIZE = 100 20 | const val MAX_SCAN_SIZE = 100 21 | } 22 | 23 | fun putEditQueue(item: T) { 24 | mutex.withLock { 25 | while (editQueue.size >= MAX_EDIT_SIZE) { 26 | notFullEdit.await() 27 | } 28 | editQueue.push(item) 29 | notEmpty.signal() 30 | } 31 | } 32 | 33 | fun putIndexQueue(item: T) { 34 | mutex.withLock { 35 | while (indexQueue.size >= MAX_INDEX_SIZE) { 36 | notFullIndex.await() 37 | } 38 | indexQueue.offer(item) 39 | notEmpty.signal() 40 | } 41 | } 42 | 43 | fun putScanQueue(item: T) { 44 | mutex.withLock { 45 | while (scanQueue.size >= MAX_SCAN_SIZE) { 46 | notFullScan.await() 47 | } 48 | scanQueue.offer(item) 49 | notEmpty.signal() 50 | } 51 | } 52 | 53 | fun take(): T = mutex.withLock { 54 | while (editQueue.isEmpty() && indexQueue.isEmpty() && scanQueue.isEmpty()) { 55 | notEmpty.await() 56 | } 57 | 58 | // Priority is ScanQueue > EditQueue > IndexQueue 59 | if (scanQueue.isNotEmpty()) { 60 | val item = scanQueue.poll() 61 | notFullScan.signal() 62 | return@withLock item 63 | } 64 | 65 | if (editQueue.isNotEmpty()) { 66 | val item = editQueue.pop() 67 | notFullEdit.signal() 68 | return@withLock item 69 | } 70 | 71 | val item = indexQueue.poll() 72 | notFullIndex.signal() 73 | return@withLock item 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/index/worker/WorkerThread.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index.worker 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.psi.PsiManager 5 | import org.jetbrains.kotlin.psi.KtFile 6 | import org.kotlinlsp.common.info 7 | import org.kotlinlsp.common.read 8 | import org.kotlinlsp.index.Command 9 | import org.kotlinlsp.index.IndexNotifier 10 | import org.kotlinlsp.index.db.Database 11 | 12 | interface WorkerThreadNotifier: IndexNotifier { 13 | fun onSourceFileScanningFinished() 14 | } 15 | 16 | class WorkerThread( 17 | private val db: Database, 18 | private val project: Project, 19 | private val notifier: WorkerThreadNotifier 20 | ): Runnable { 21 | private val workQueue = WorkQueue() 22 | 23 | override fun run() { 24 | var scanCount = 0 25 | var indexCount = 0 26 | 27 | while(true) { 28 | when(val command = workQueue.take()) { 29 | is Command.Stop -> break 30 | is Command.ScanSourceFile -> { 31 | if(!command.virtualFile.url.startsWith("file://")) return 32 | 33 | val ktFile = project.read { PsiManager.getInstance(project).findFile(command.virtualFile) } as KtFile 34 | scanKtFile(project, ktFile, db) 35 | scanCount ++ 36 | } 37 | is Command.IndexFile -> { 38 | if(command.virtualFile.url.startsWith("file://")) { 39 | val ktFile = project.read { PsiManager.getInstance(project).findFile(command.virtualFile) } as KtFile 40 | indexKtFile(project, ktFile, db) 41 | } else { 42 | indexClassFile(project, command.virtualFile, db) 43 | } 44 | indexCount ++ 45 | } 46 | is Command.IndexModifiedFile -> { 47 | info("Indexing modified file: ${command.ktFile.virtualFile.name}") 48 | indexKtFile(project, command.ktFile, db) 49 | } 50 | is Command.IndexingFinished -> { 51 | // TODO Should remove in this point files which do not exist anymore 52 | info("Background indexing finished!, $indexCount files!") 53 | notifier.onBackgroundIndexFinished() 54 | } 55 | Command.SourceScanningFinished -> { 56 | // TODO Should remove in this point files which do not exist anymore 57 | info("Source file scanning finished!, $scanCount files!") 58 | notifier.onSourceFileScanningFinished() 59 | } 60 | } 61 | } 62 | } 63 | 64 | fun submitCommand(command: Command) { 65 | when(command) { 66 | is Command.ScanSourceFile, Command.SourceScanningFinished -> { 67 | workQueue.putScanQueue(command) 68 | } 69 | is Command.IndexModifiedFile -> { 70 | workQueue.putEditQueue(command) 71 | } 72 | else -> { 73 | workQueue.putIndexQueue(command) 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/kotlin/org/kotlinlsp/lsp/Server.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.lsp 2 | 3 | import org.eclipse.lsp4j.* 4 | import org.eclipse.lsp4j.jsonrpc.messages.Either 5 | import org.eclipse.lsp4j.services.* 6 | import org.jetbrains.kotlin.analysis.api.platform.lifetime.KotlinReadActionConfinementLifetimeToken 7 | import org.kotlinlsp.analysis.AnalysisSession 8 | import org.kotlinlsp.analysis.AnalysisSessionNotifier 9 | import org.kotlinlsp.common.getLspVersion 10 | import org.kotlinlsp.common.info 11 | import org.kotlinlsp.common.setupLogger 12 | import java.util.concurrent.CompletableFuture 13 | import java.util.concurrent.CompletableFuture.completedFuture 14 | import java.util.concurrent.Executors 15 | import java.util.concurrent.ScheduledFuture 16 | import java.util.concurrent.TimeUnit 17 | 18 | interface KotlinLanguageServerNotifier { 19 | fun onExit() {} 20 | fun onBackgroundIndexingFinished() {} 21 | } 22 | 23 | class KotlinLanguageServer( 24 | private val notifier: KotlinLanguageServerNotifier 25 | ) : LanguageServer, TextDocumentService, WorkspaceService, LanguageClientAware { 26 | private lateinit var client: LanguageClient 27 | private lateinit var analysisSession: AnalysisSession 28 | private lateinit var rootPath: String 29 | private val lintExecutor = Executors.newSingleThreadScheduledExecutor { 30 | Thread(it, "KotlinLSP-Lint") 31 | } 32 | private var lintFuture: ScheduledFuture<*>? = null 33 | 34 | private val analysisSessionNotifier = object : AnalysisSessionNotifier { 35 | override fun onBackgroundIndexFinished() { 36 | notifier.onBackgroundIndexingFinished() 37 | } 38 | 39 | override fun onDiagnostics(params: PublishDiagnosticsParams) { 40 | client.publishDiagnostics(params) 41 | } 42 | 43 | override fun onReportProgress(phase: WorkDoneProgressKind, progressToken: String, text: String) { 44 | val notification = when (phase) { 45 | WorkDoneProgressKind.begin -> WorkDoneProgressBegin().apply { title = text } 46 | WorkDoneProgressKind.report -> WorkDoneProgressReport().apply { message = text } 47 | WorkDoneProgressKind.end -> WorkDoneProgressEnd().apply { message = text } 48 | } 49 | val params = ProgressParams().apply { 50 | token = Either.forLeft(progressToken) 51 | value = Either.forLeft(notification) 52 | } 53 | client.notifyProgress(params) 54 | } 55 | } 56 | 57 | override fun initialize(params: InitializeParams): CompletableFuture { 58 | val capabilities = ServerCapabilities().apply { 59 | textDocumentSync = Either.forLeft(TextDocumentSyncKind.Incremental) 60 | hoverProvider = Either.forLeft(true) 61 | definitionProvider = Either.forLeft(true) 62 | completionProvider = CompletionOptions(false, listOf(".")) 63 | } 64 | val serverInfo = ServerInfo().apply { 65 | name = "kotlin-lsp" 66 | version = getLspVersion() 67 | } 68 | 69 | rootPath = params.workspaceFolders.first().uri.removePrefix("file://") 70 | 71 | return completedFuture(InitializeResult(capabilities, serverInfo)) 72 | } 73 | 74 | override fun initialized(params: InitializedParams) { 75 | setupLogger(client) 76 | info(rootPath) 77 | 78 | analysisSession = AnalysisSession(analysisSessionNotifier, rootPath) 79 | } 80 | 81 | override fun shutdown(): CompletableFuture { 82 | exit() // TODO Nvim does not call exit so the server is kept alive and reparented to the init process (?) 83 | return completedFuture(null) 84 | } 85 | 86 | override fun exit() { 87 | lintExecutor.close() 88 | analysisSession.dispose() 89 | notifier.onExit() 90 | } 91 | 92 | override fun getTextDocumentService(): TextDocumentService = this 93 | override fun getWorkspaceService(): WorkspaceService = this 94 | 95 | override fun didOpen(params: DidOpenTextDocumentParams) { 96 | analysisSession.onOpenFile(params.textDocument.uri) 97 | } 98 | 99 | override fun didChange(params: DidChangeTextDocumentParams) { 100 | val uri = params.textDocument.uri 101 | analysisSession.editFile(uri, params.textDocument.version, params.contentChanges) 102 | 103 | // Debounce the linting so it is not triggered on every keystroke 104 | lintFuture?.cancel(false) 105 | lintFuture = lintExecutor.schedule({ 106 | analysisSession.lintFile(uri) 107 | }, 250, TimeUnit.MILLISECONDS) 108 | } 109 | 110 | override fun didClose(params: DidCloseTextDocumentParams) { 111 | analysisSession.onCloseFile(params.textDocument.uri) 112 | } 113 | 114 | override fun didSave(params: DidSaveTextDocumentParams) { 115 | 116 | } 117 | 118 | override fun didChangeConfiguration(params: DidChangeConfigurationParams) { 119 | 120 | } 121 | 122 | override fun didChangeWatchedFiles(params: DidChangeWatchedFilesParams) { 123 | 124 | } 125 | 126 | override fun completion(params: CompletionParams): CompletableFuture, CompletionList>> { 127 | val completions = analysisSession.autocomplete(params.textDocument.uri, params.position) 128 | return completedFuture(Either.forRight(CompletionList(false, completions))) 129 | } 130 | 131 | override fun connect(params: LanguageClient) { 132 | client = params 133 | } 134 | 135 | override fun hover(params: HoverParams): CompletableFuture { 136 | // TODO Add javadoc 137 | val (text, range) = analysisSession.hover(params.textDocument.uri, params.position) ?: return completedFuture( 138 | null 139 | ) 140 | val content = MarkupContent().apply { 141 | kind = "markdown" 142 | value = "```kotlin\n${text}\n```" 143 | } 144 | 145 | val hover = Hover().apply { 146 | contents = Either.forRight(content) 147 | this.range = range 148 | } 149 | 150 | return completedFuture(hover) 151 | } 152 | 153 | override fun definition(params: DefinitionParams): CompletableFuture, MutableList>?> { 154 | val location = 155 | analysisSession.goToDefinition(params.textDocument.uri, params.position) ?: return completedFuture(null) 156 | return completedFuture(Either.forLeft(mutableListOf(location))) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /app/src/main/resources/android.init.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.plugins.ide.idea.model.IdeaModel 2 | import org.gradle.plugins.ide.idea.model.IdeaModule 3 | 4 | class AndroidPlugin implements Plugin { 5 | 6 | @Override 7 | void apply(Project project) { 8 | if (!project.hasProperty("android")) return 9 | 10 | def variant = System.getProperty("androidVariant") 11 | 12 | project.afterEvaluate { 13 | def android = project.android 14 | 15 | IdeaModel ideaModel = (IdeaModel)project.extensions.findByName("idea") 16 | IdeaModule module = ideaModel.module 17 | 18 | android.applicationVariants.all { appVariant -> 19 | if (appVariant.name != variant) return 20 | 21 | // TODO Handle merge conflicts (e.g. debug sourceset overrides main sourceset) 22 | appVariant.sourceSets.forEach { 23 | if(it.name == "test" || it.name == "androidTest") { 24 | it.kotlinDirectories.forEach { 25 | module.testSourceDirs.add(new File(it.toString())) 26 | } 27 | } else { 28 | it.kotlinDirectories.forEach { 29 | module.sourceDirs.add(new File(it.toString())) 30 | } 31 | } 32 | } 33 | 34 | // TODO Handle multiple module dependencies 35 | android.bootClasspath.each { 36 | module.sourceDirs.add(new File("jar:${it.path}")) 37 | } 38 | appVariant.getCompileClasspath().files.each { 39 | // This is a workaround as we cannot add new dependencies to IdeaModel because of project.afterEvaluate 40 | // But we need project.afterEvaluate so appVariant.getCompileClasspath() works 41 | module.sourceDirs.add(new File("jar:${it.path}")) 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | allprojects { 49 | afterEvaluate { 50 | it.getPlugins().apply("idea") 51 | it.getPlugins().apply(AndroidPlugin) 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/test/kotlin/org/kotlinlsp/Gradle.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.util.Disposer 5 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment 6 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironmentMode 7 | import org.jetbrains.kotlin.cli.jvm.compiler.setupIdeaStandaloneExecution 8 | import org.junit.jupiter.api.Assertions.assertEquals 9 | import org.junit.jupiter.api.Assertions.assertTrue 10 | import org.junit.jupiter.api.Test 11 | import org.kotlinlsp.analysis.ProgressNotifier 12 | import org.kotlinlsp.buildsystem.GradleBuildSystem 13 | import org.mockito.Mockito.mock 14 | import java.nio.file.Paths 15 | import org.kotlinlsp.analysis.modules.Module 16 | import kotlin.io.path.Path 17 | import kotlin.io.path.absolutePathString 18 | 19 | class Gradle { 20 | private val cwd = Paths.get("").toAbsolutePath().toString() 21 | 22 | private fun scenario(projectName: String, testCase: (GradleBuildSystem) -> Unit) { 23 | setupIdeaStandaloneExecution() 24 | val rootFolder = "$cwd/test-projects/$projectName" 25 | val project = mock(Project::class.java) 26 | val progressNotifier = mock(ProgressNotifier::class.java) 27 | val disposable = Disposer.newDisposable() 28 | val appEnvironment = KotlinCoreApplicationEnvironment.create(disposable, KotlinCoreApplicationEnvironmentMode.UnitTest) 29 | val buildSystem = GradleBuildSystem( 30 | project = project, 31 | progressNotifier = progressNotifier, 32 | rootFolder = rootFolder, 33 | appEnvironment = appEnvironment 34 | ) 35 | 36 | try { 37 | testCase(buildSystem) 38 | } finally { 39 | disposable.dispose() 40 | } 41 | } 42 | 43 | private fun Module.firstContentRootFilename() = contentRoots.first().fileName.toString() 44 | 45 | @Test 46 | fun `loads single module project successfully`() = scenario("single-module") { buildSystem -> 47 | // Act 48 | val (modules, _) = buildSystem.resolveModulesIfNeeded(cachedMetadata = null)!! 49 | 50 | // Assert 51 | assertEquals(modules.size, 2) 52 | assertEquals(modules[0].isSourceModule, true) 53 | assertEquals( 54 | modules[0].contentRoots, 55 | listOf( 56 | Path("$cwd/test-projects/single-module/app/src/main/java"), 57 | Path("$cwd/test-projects/single-module/app/src/main/kotlin"), 58 | ) 59 | ) 60 | assertEquals(modules[0].dependencies.size, 3) 61 | modules[0].dependencies.forEach { 62 | assertEquals(it.dependencies.size, 0) 63 | assertEquals(it.contentRoots.size, 1) 64 | } 65 | val depNames = modules[0].dependencies.map { it.firstContentRootFilename() }.toSet() 66 | assertTrue("annotations-13.0.jar" in depNames) 67 | assertTrue("kotlin-stdlib-2.1.20.jar" in depNames) 68 | 69 | assertEquals(modules[1].isSourceModule, true) 70 | assertEquals( 71 | modules[1].contentRoots, 72 | listOf( 73 | Path("$cwd/test-projects/single-module/app/src/test/java"), 74 | Path("$cwd/test-projects/single-module/app/src/test/kotlin"), 75 | ) 76 | ) 77 | assertEquals(modules[1].dependencies.size, 4) 78 | assertEquals(modules[1].dependencies.filter { it.isSourceModule }.size, 1) 79 | } 80 | 81 | @Test 82 | fun `loads multi module project successfully`() = scenario("multi-module") { buildSystem -> 83 | // Act 84 | val (modules, _) = buildSystem.resolveModulesIfNeeded(cachedMetadata = null)!! 85 | 86 | // Assert 87 | assertEquals(modules.size, 4) 88 | assertEquals(modules[0].isSourceModule, true) 89 | assertEquals( 90 | modules[0].contentRoots, 91 | listOf( 92 | Path("$cwd/test-projects/multi-module/app/src/main/java"), 93 | Path("$cwd/test-projects/multi-module/app/src/main/kotlin"), 94 | ) 95 | ) 96 | assertEquals(modules[0].dependencies.size, 4) 97 | val depNames = modules[0].dependencies.map { it.id }.toSet() 98 | assertTrue("submodule" in depNames) 99 | 100 | assertEquals(modules[1].isSourceModule, true) 101 | assertEquals( 102 | modules[1].contentRoots, 103 | listOf( 104 | Path("$cwd/test-projects/multi-module/app/src/test/java"), 105 | Path("$cwd/test-projects/multi-module/app/src/test/kotlin"), 106 | ) 107 | ) 108 | assertEquals(modules[1].dependencies.size, 5) 109 | assertEquals(modules[1].dependencies.filter { it.isSourceModule }.size, 2) 110 | 111 | assertEquals(modules[2].isSourceModule, true) 112 | assertEquals( 113 | modules[2].contentRoots, 114 | listOf( 115 | Path("$cwd/test-projects/multi-module/submodule/src/main/java"), 116 | Path("$cwd/test-projects/multi-module/submodule/src/main/kotlin"), 117 | ) 118 | ) 119 | assertEquals(modules[2].dependencies.size, 3) 120 | 121 | assertEquals(modules[3].isSourceModule, true) 122 | assertEquals( 123 | modules[3].contentRoots, 124 | listOf( 125 | Path("$cwd/test-projects/multi-module/submodule/src/test/java"), 126 | Path("$cwd/test-projects/multi-module/submodule/src/test/kotlin"), 127 | ) 128 | ) 129 | assertEquals(modules[3].dependencies.size, 4) 130 | assertEquals(modules[3].dependencies.filter { it.isSourceModule }.size, 1) 131 | } 132 | 133 | @Test 134 | fun `loads android project successfully`() = scenario("android") { buildSystem -> 135 | // Act 136 | val (modules, _) = buildSystem.resolveModulesIfNeeded(cachedMetadata = null)!! 137 | 138 | // Assert 139 | assertEquals(modules.size, 1) 140 | assertEquals(modules[0].isSourceModule, true) 141 | assertTrue(modules[0].dependencies.isNotEmpty()) 142 | assertEquals( 143 | modules[0].contentRoots, 144 | listOf( 145 | Path("$cwd/test-projects/android/app/src/main/kotlin"), 146 | Path("$cwd/test-projects/android/app/src/main/java"), 147 | Path("$cwd/test-projects/android/app/src/debug/kotlin"), 148 | Path("$cwd/test-projects/android/app/src/debug/java"), 149 | ) 150 | ) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/src/test/kotlin/org/kotlinlsp/RealTimeDiagnostics.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp 2 | 3 | import org.eclipse.lsp4j.DiagnosticSeverity 4 | import org.eclipse.lsp4j.DidOpenTextDocumentParams 5 | import org.eclipse.lsp4j.TextDocumentItem 6 | import org.junit.jupiter.api.Tag 7 | import org.junit.jupiter.api.Test 8 | import org.kotlinlsp.setup.scenario 9 | import org.mockito.ArgumentMatchers.argThat 10 | import org.mockito.Mockito.verify 11 | 12 | @Tag("CI") 13 | class RealTimeDiagnostics { 14 | @Test 15 | fun `analyzes basic codebase with no error diagnostics`() = scenario("playground") { server, client, projectUrl, _ -> 16 | // Act 17 | server.didOpen(DidOpenTextDocumentParams().apply { 18 | textDocument = TextDocumentItem().apply { 19 | uri = "$projectUrl/Main.kt" 20 | } 21 | }) 22 | 23 | // Assert 24 | verify(client).publishDiagnostics(argThat { !it.diagnostics.any { it.severity == DiagnosticSeverity.Error } }) 25 | } 26 | 27 | @Test 28 | fun `analyzes basic codebase and reports syntax error`() = scenario("playground") { server, client, projectUrl, _ -> 29 | // Act 30 | server.didOpen(DidOpenTextDocumentParams().apply { 31 | textDocument = TextDocumentItem().apply { 32 | uri = "$projectUrl/Errors.kt" 33 | } 34 | }) 35 | 36 | // Assert 37 | verify(client).publishDiagnostics(argThat { 38 | it.diagnostics.any { 39 | it.severity == DiagnosticSeverity.Error && it.message == "Expecting a top level declaration" 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/test/kotlin/org/kotlinlsp/index/FileTests.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.index 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.params.ParameterizedTest 5 | import org.junit.jupiter.params.provider.Arguments 6 | import org.junit.jupiter.params.provider.MethodSource 7 | import org.kotlinlsp.index.db.File 8 | import java.time.Instant 9 | import java.util.stream.Stream 10 | 11 | class FileTests { 12 | companion object { 13 | @JvmStatic 14 | fun provideData(): Stream = Stream.of( 15 | // Unmodified file 16 | Arguments.of( 17 | buildFile(modificationStamp = 0, lastModified = Instant.ofEpochMilli(100)), 18 | buildFile(modificationStamp = 0, lastModified = Instant.ofEpochMilli(100)), 19 | true 20 | ), 21 | // File modified on disk 22 | Arguments.of( 23 | buildFile(modificationStamp = 0, lastModified = Instant.ofEpochMilli(50)), 24 | buildFile(modificationStamp = 0, lastModified = Instant.ofEpochMilli(100)), 25 | false 26 | ), 27 | // File modified in memory 28 | Arguments.of( 29 | buildFile(modificationStamp = 0, lastModified = Instant.ofEpochMilli(50)), 30 | buildFile(modificationStamp = 1, lastModified = Instant.ofEpochMilli(100)), 31 | false 32 | ), 33 | Arguments.of( 34 | buildFile(modificationStamp = 1, lastModified = Instant.ofEpochMilli(50)), 35 | buildFile(modificationStamp = 2, lastModified = Instant.ofEpochMilli(100)), 36 | false 37 | ), 38 | // Reloading file from disk (after being edited in memory but not saved) 39 | Arguments.of( 40 | buildFile(modificationStamp = 10, lastModified = Instant.ofEpochMilli(100)), 41 | buildFile(modificationStamp = 0, lastModified = Instant.ofEpochMilli(100)), 42 | false 43 | ), 44 | // Old in memory file trying to be indexed 45 | Arguments.of( 46 | buildFile(modificationStamp = 10, lastModified = Instant.ofEpochMilli(50)), 47 | buildFile(modificationStamp = 9, lastModified = Instant.ofEpochMilli(50)), 48 | true 49 | ), 50 | // New file should always be indexed 51 | Arguments.of( 52 | null, 53 | buildFile(modificationStamp = 9, lastModified = Instant.ofEpochMilli(50)), 54 | false 55 | ), 56 | ) 57 | } 58 | 59 | @ParameterizedTest 60 | @MethodSource("provideData") 61 | fun `test file index skip logic`(existingFile: File?, newFile: File, result: Boolean) { 62 | assertEquals(File.shouldBeSkipped(existingFile = existingFile, newFile = newFile), result) 63 | } 64 | 65 | } 66 | 67 | private fun buildFile(lastModified: Instant, modificationStamp: Long, indexed: Boolean = false): File { 68 | val path = "/sample/path.kt" 69 | val packageFqName = "com.example" 70 | return File( 71 | path = path, 72 | modificationStamp = modificationStamp, 73 | packageFqName = packageFqName, 74 | lastModified = lastModified, 75 | indexed = indexed 76 | ) 77 | } -------------------------------------------------------------------------------- /app/src/test/kotlin/org/kotlinlsp/setup/Scenario.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.setup 2 | 3 | import org.eclipse.lsp4j.InitializeParams 4 | import org.eclipse.lsp4j.InitializedParams 5 | import org.eclipse.lsp4j.MessageParams 6 | import org.eclipse.lsp4j.WorkspaceFolder 7 | import org.eclipse.lsp4j.services.LanguageClient 8 | import org.kotlinlsp.lsp.KotlinLanguageServer 9 | import org.kotlinlsp.common.removeCacheFolder 10 | import org.kotlinlsp.lsp.KotlinLanguageServerNotifier 11 | import org.mockito.Mockito 12 | import org.mockito.Mockito.* 13 | import java.io.File 14 | import java.nio.file.Paths 15 | 16 | fun scenario( 17 | projectName: String, 18 | testCase: (server: KotlinLanguageServer, client: LanguageClient, projectUrl: String, notifier: KotlinLanguageServerNotifier) -> Unit 19 | ) { 20 | // Setup 21 | val cwd = Paths.get("").toAbsolutePath().toString() 22 | val jdkHome = System.getProperty("java.home") 23 | val moduleContents = """ 24 | [ 25 | { 26 | "id": "main", 27 | "dependencies": [ 28 | "JDK 21" 29 | ], 30 | "javaVersion": "21", 31 | "contentRoots": ["$cwd/test-projects/$projectName"], 32 | "isSource": true, 33 | "kotlinVersion": "2.1" 34 | }, 35 | { 36 | "id": "JDK 21", 37 | "dependencies": [], 38 | "javaVersion": "21", 39 | "isJdk": true, 40 | "isSource": false, 41 | "contentRoots": ["$jdkHome"] 42 | } 43 | ] 44 | """.trimIndent() 45 | val moduleFile = File("$cwd/test-projects/$projectName/.kotlinlsp-modules.json") 46 | moduleFile.delete() 47 | removeCacheFolder("$cwd/test-projects/$projectName") 48 | moduleFile.writeText(moduleContents) 49 | 50 | val notifier = mock(KotlinLanguageServerNotifier::class.java) 51 | val server = KotlinLanguageServer(notifier) 52 | val initParams = InitializeParams().apply { 53 | workspaceFolders = listOf( 54 | WorkspaceFolder().apply { 55 | uri = "file://$cwd/test-projects/$projectName" 56 | } 57 | ) 58 | } 59 | 60 | val client = mock(LanguageClient::class.java) 61 | `when`(client.logMessage(any())).thenAnswer { 62 | val params = it.getArgument(0) 63 | println("[${params.type.toString().uppercase()}]: ${params.message}") 64 | } 65 | server.connect(client) 66 | server.initialize(initParams).join() 67 | server.initialized(InitializedParams()) 68 | 69 | // Run test case 70 | try { 71 | testCase(server, client, "file://$cwd/test-projects/$projectName", notifier) 72 | } finally { 73 | // Cleanup 74 | server.shutdown().join() 75 | moduleFile.delete() 76 | removeCacheFolder("$cwd/test-projects/$projectName") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/test-projects/android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") version "8.7.3" 3 | id("org.jetbrains.kotlin.android") version "2.0.0" 4 | id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" 5 | } 6 | 7 | android { 8 | namespace = "com.example.myapplication" 9 | compileSdk = 34 10 | 11 | defaultConfig { 12 | applicationId = "com.example.myapplication" 13 | minSdk = 24 14 | targetSdk = 34 15 | versionCode = 1 16 | versionName = "1.0" 17 | 18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility = JavaVersion.VERSION_17 23 | targetCompatibility = JavaVersion.VERSION_17 24 | } 25 | 26 | kotlinOptions { 27 | jvmTarget = "17" 28 | } 29 | 30 | buildFeatures { 31 | compose = true 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation("androidx.core:core-ktx:1.10.1") 37 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") 38 | implementation("androidx.activity:activity-compose:1.8.0") 39 | implementation(platform("androidx.compose:compose-bom:2024.04.01")) 40 | implementation("androidx.compose.ui:ui") 41 | implementation("androidx.compose.ui:ui-graphics") 42 | implementation("androidx.compose.ui:ui-tooling-preview") 43 | implementation("androidx.compose.material3:material3") 44 | testImplementation("junit:junit:4.13.2") 45 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 46 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 47 | androidTestImplementation(platform("androidx.compose:compose-bom:2024.04.01")) 48 | androidTestImplementation("androidx.compose.ui:ui-test-junit4") 49 | debugImplementation("androidx.compose.ui:ui-tooling") 50 | debugImplementation("androidx.compose.ui:ui-test-manifest") 51 | } 52 | -------------------------------------------------------------------------------- /app/test-projects/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/test-projects/android/app/src/main/java/com/example/myapplication/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.enableEdgeToEdge 7 | import androidx.compose.material3.Text 8 | 9 | class MainActivity : ComponentActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | enableEdgeToEdge() 13 | setContent { Text("Hello World") } 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /app/test-projects/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") version "8.7.3" apply false 3 | id("org.jetbrains.kotlin.android") version "2.0.0" apply false 4 | id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" apply false 5 | } 6 | -------------------------------------------------------------------------------- /app/test-projects/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 2 | org.gradle.caching=true 3 | org.gradle.daemon=true 4 | org.gradle.incremental=true 5 | org.gradle.configuration-cache=true 6 | # When configured, Gradle will run in incubating parallel mode. 7 | # This option should only be used with decoupled projects. For more details, visit 8 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 9 | # org.gradle.parallel=true 10 | # AndroidX package structure to make it clearer which packages are bundled with the 11 | # Android operating system, and which are packaged with your app's APK 12 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 13 | android.useAndroidX=true 14 | # Kotlin code style for this project: "official" or "obsolete": 15 | kotlin.code.style=official 16 | # Enables namespacing of each library's R class so that its R class includes only the 17 | # resources declared in the library itself and none from the library's dependencies, 18 | # thereby reducing the size of the R class for that library 19 | android.nonTransitiveRClass=true 20 | 21 | -------------------------------------------------------------------------------- /app/test-projects/android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "My Application" 23 | include(":app") 24 | -------------------------------------------------------------------------------- /app/test-projects/kmp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.androidApplication) apply false 3 | alias(libs.plugins.androidLibrary) apply false 4 | alias(libs.plugins.composeMultiplatform) apply false 5 | alias(libs.plugins.composeCompiler) apply false 6 | alias(libs.plugins.kotlinMultiplatform) apply false 7 | } 8 | -------------------------------------------------------------------------------- /app/test-projects/kmp/composeApp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat 2 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 3 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 4 | 5 | plugins { 6 | alias(libs.plugins.kotlinMultiplatform) 7 | alias(libs.plugins.androidApplication) 8 | alias(libs.plugins.composeMultiplatform) 9 | alias(libs.plugins.composeCompiler) 10 | alias(libs.plugins.composeHotReload) 11 | } 12 | 13 | kotlin { 14 | androidTarget { 15 | @OptIn(ExperimentalKotlinGradlePluginApi::class) 16 | compilerOptions { 17 | jvmTarget.set(JvmTarget.JVM_11) 18 | } 19 | } 20 | 21 | jvm("desktop") 22 | 23 | sourceSets { 24 | val desktopMain by getting 25 | 26 | androidMain.dependencies { 27 | implementation(compose.preview) 28 | implementation(libs.androidx.activity.compose) 29 | } 30 | commonMain.dependencies { 31 | implementation(compose.runtime) 32 | implementation(compose.foundation) 33 | implementation(compose.material3) 34 | implementation(compose.ui) 35 | implementation(compose.components.resources) 36 | implementation(compose.components.uiToolingPreview) 37 | implementation(libs.androidx.lifecycle.viewmodel) 38 | implementation(libs.androidx.lifecycle.runtimeCompose) 39 | implementation(projects.shared) 40 | } 41 | commonTest.dependencies { 42 | implementation(libs.kotlin.test) 43 | } 44 | desktopMain.dependencies { 45 | implementation(compose.desktop.currentOs) 46 | implementation(libs.kotlinx.coroutinesSwing) 47 | } 48 | } 49 | } 50 | 51 | android { 52 | namespace = "org.example.project" 53 | compileSdk = libs.versions.android.compileSdk.get().toInt() 54 | 55 | defaultConfig { 56 | applicationId = "org.example.project" 57 | minSdk = libs.versions.android.minSdk.get().toInt() 58 | targetSdk = libs.versions.android.targetSdk.get().toInt() 59 | versionCode = 1 60 | versionName = "1.0" 61 | } 62 | packaging { 63 | resources { 64 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 65 | } 66 | } 67 | buildTypes { 68 | getByName("release") { 69 | isMinifyEnabled = false 70 | } 71 | } 72 | compileOptions { 73 | sourceCompatibility = JavaVersion.VERSION_11 74 | targetCompatibility = JavaVersion.VERSION_11 75 | } 76 | } 77 | 78 | dependencies { 79 | debugImplementation(compose.uiTooling) 80 | } 81 | 82 | compose.desktop { 83 | application { 84 | mainClass = "org.example.project.MainKt" 85 | 86 | nativeDistributions { 87 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) 88 | packageName = "org.example.project" 89 | packageVersion = "1.0.0" 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/test-projects/kmp/composeApp/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/test-projects/kmp/composeApp/src/androidMain/kotlin/org/example/project/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.enableEdgeToEdge 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.tooling.preview.Preview 9 | 10 | class MainActivity : ComponentActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | enableEdgeToEdge() 13 | super.onCreate(savedInstanceState) 14 | 15 | setContent { 16 | App() 17 | } 18 | } 19 | } 20 | 21 | @Preview 22 | @Composable 23 | fun AppAndroidPreview() { 24 | App() 25 | } -------------------------------------------------------------------------------- /app/test-projects/kmp/composeApp/src/commonMain/kotlin/org/example/project/App.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import androidx.compose.animation.AnimatedVisibility 4 | import androidx.compose.foundation.Image 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.safeContentPadding 9 | import androidx.compose.material3.Button 10 | import androidx.compose.material3.MaterialTheme 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.* 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import org.jetbrains.compose.resources.painterResource 16 | import org.jetbrains.compose.ui.tooling.preview.Preview 17 | 18 | import kmptest.composeapp.generated.resources.Res 19 | import kmptest.composeapp.generated.resources.compose_multiplatform 20 | 21 | @Composable 22 | @Preview 23 | fun App() { 24 | MaterialTheme { 25 | var showContent by remember { mutableStateOf(false) } 26 | Column( 27 | modifier = Modifier 28 | .safeContentPadding() 29 | .fillMaxSize(), 30 | horizontalAlignment = Alignment.CenterHorizontally, 31 | ) { 32 | Button(onClick = { showContent = !showContent }) { 33 | Text("Click me!") 34 | } 35 | AnimatedVisibility(showContent) { 36 | val greeting = remember { Greeting().greet() } 37 | Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { 38 | Text("Compose: $greeting") 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/test-projects/kmp/composeApp/src/commonTest/kotlin/org/example/project/ComposeAppCommonTest.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class ComposeAppCommonTest { 7 | 8 | @Test 9 | fun example() { 10 | assertEquals(3, 1 + 2) 11 | } 12 | } -------------------------------------------------------------------------------- /app/test-projects/kmp/composeApp/src/desktopMain/kotlin/org/example/project/main.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import androidx.compose.ui.window.Window 4 | import androidx.compose.ui.window.application 5 | 6 | fun main() = application { 7 | Window( 8 | onCloseRequest = ::exitApplication, 9 | title = "KMPTest", 10 | ) { 11 | App() 12 | } 13 | } -------------------------------------------------------------------------------- /app/test-projects/kmp/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | kotlin.daemon.jvmargs=-Xmx3072M 3 | 4 | org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 5 | org.gradle.configuration-cache=true 6 | org.gradle.caching=true 7 | 8 | android.nonTransitiveRClass=true 9 | android.useAndroidX=true 10 | -------------------------------------------------------------------------------- /app/test-projects/kmp/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.7.3" 3 | android-compileSdk = "35" 4 | android-minSdk = "24" 5 | android-targetSdk = "35" 6 | androidx-activity = "1.10.1" 7 | androidx-appcompat = "1.7.0" 8 | androidx-constraintlayout = "2.2.1" 9 | androidx-core = "1.16.0" 10 | androidx-espresso = "3.6.1" 11 | androidx-lifecycle = "2.8.4" 12 | androidx-testExt = "1.2.1" 13 | composeHotReload = "1.0.0-alpha09" 14 | composeMultiplatform = "1.8.0" 15 | junit = "4.13.2" 16 | kotlin = "2.1.21" 17 | kotlinx-coroutines = "1.10.2" 18 | 19 | [libraries] 20 | kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } 21 | kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } 22 | junit = { module = "junit:junit", version.ref = "junit" } 23 | androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" } 24 | androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" } 25 | androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } 26 | androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } 27 | androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" } 28 | androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } 29 | androidx-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-lifecycle" } 30 | androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } 31 | kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } 32 | 33 | [plugins] 34 | androidApplication = { id = "com.android.application", version.ref = "agp" } 35 | androidLibrary = { id = "com.android.library", version.ref = "agp" } 36 | composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "composeHotReload" } 37 | composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } 38 | composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 39 | kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -------------------------------------------------------------------------------- /app/test-projects/kmp/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "KMPTest" 2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 3 | 4 | pluginManagement { 5 | repositories { 6 | google { 7 | mavenContent { 8 | includeGroupAndSubgroups("androidx") 9 | includeGroupAndSubgroups("com.android") 10 | includeGroupAndSubgroups("com.google") 11 | } 12 | } 13 | mavenCentral() 14 | gradlePluginPortal() 15 | } 16 | } 17 | 18 | dependencyResolutionManagement { 19 | repositories { 20 | google { 21 | mavenContent { 22 | includeGroupAndSubgroups("androidx") 23 | includeGroupAndSubgroups("com.android") 24 | includeGroupAndSubgroups("com.google") 25 | } 26 | } 27 | mavenCentral() 28 | } 29 | } 30 | 31 | plugins { 32 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" 33 | } 34 | 35 | include(":composeApp") 36 | include(":shared") -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 3 | 4 | plugins { 5 | alias(libs.plugins.kotlinMultiplatform) 6 | alias(libs.plugins.androidLibrary) 7 | } 8 | 9 | kotlin { 10 | androidTarget { 11 | @OptIn(ExperimentalKotlinGradlePluginApi::class) 12 | compilerOptions { 13 | jvmTarget.set(JvmTarget.JVM_11) 14 | } 15 | } 16 | 17 | listOf( 18 | iosX64(), 19 | iosArm64(), 20 | iosSimulatorArm64() 21 | ).forEach { iosTarget -> 22 | iosTarget.binaries.framework { 23 | baseName = "Shared" 24 | isStatic = true 25 | } 26 | } 27 | 28 | jvm() 29 | 30 | sourceSets { 31 | commonMain.dependencies {} 32 | commonTest.dependencies { 33 | implementation(libs.kotlin.test) 34 | } 35 | } 36 | } 37 | 38 | android { 39 | namespace = "org.example.project.shared" 40 | compileSdk = libs.versions.android.compileSdk.get().toInt() 41 | compileOptions { 42 | sourceCompatibility = JavaVersion.VERSION_11 43 | targetCompatibility = JavaVersion.VERSION_11 44 | } 45 | defaultConfig { 46 | minSdk = libs.versions.android.minSdk.get().toInt() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/src/androidMain/kotlin/org/example/project/Platform.android.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import android.os.Build 4 | 5 | class AndroidPlatform : Platform { 6 | override val name: String = "Android ${Build.VERSION.SDK_INT}" 7 | } 8 | 9 | actual fun getPlatform(): Platform = AndroidPlatform() -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/src/commonMain/kotlin/org/example/project/Greeting.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | class Greeting { 4 | private val platform = getPlatform() 5 | 6 | fun greet(): String { 7 | return "Hello, ${platform.name}!" 8 | } 9 | } -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/src/commonMain/kotlin/org/example/project/Platform.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | interface Platform { 4 | val name: String 5 | } 6 | 7 | expect fun getPlatform(): Platform -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/src/commonTest/kotlin/org/example/project/SharedCommonTest.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class SharedCommonTest { 7 | 8 | @Test 9 | fun example() { 10 | assertEquals(3, 1 + 2) 11 | } 12 | } -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/src/iosMain/kotlin/org/example/project/Platform.ios.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | import platform.UIKit.UIDevice 4 | 5 | class IOSPlatform: Platform { 6 | override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion 7 | } 8 | 9 | actual fun getPlatform(): Platform = IOSPlatform() -------------------------------------------------------------------------------- /app/test-projects/kmp/shared/src/jvmMain/kotlin/org/example/project/Platform.jvm.kt: -------------------------------------------------------------------------------- 1 | package org.example.project 2 | 3 | class JVMPlatform: Platform { 4 | override val name: String = "Java ${System.getProperty("java.version")}" 5 | } 6 | 7 | actual fun getPlatform(): Platform = JVMPlatform() -------------------------------------------------------------------------------- /app/test-projects/multi-module/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.1.20" 3 | application 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation(project(":submodule")) 12 | } 13 | 14 | java { 15 | toolchain { 16 | languageVersion = JavaLanguageVersion.of(21) 17 | } 18 | } 19 | 20 | application { 21 | mainClass = "org.app.AppKt" 22 | } 23 | -------------------------------------------------------------------------------- /app/test-projects/multi-module/app/src/main/kotlin/org/app/App.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This source file was generated by the Gradle 'init' task 3 | */ 4 | package org.app 5 | 6 | import org.submodule.submodule 7 | 8 | fun main() { 9 | submodule() 10 | } 11 | -------------------------------------------------------------------------------- /app/test-projects/multi-module/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.configuration-cache=true 2 | -------------------------------------------------------------------------------- /app/test-projects/multi-module/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "single-module" 2 | include("app") 3 | include("submodule") -------------------------------------------------------------------------------- /app/test-projects/multi-module/submodule/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.1.20" 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies {} 10 | 11 | java { 12 | toolchain { 13 | languageVersion = JavaLanguageVersion.of(21) 14 | } 15 | } -------------------------------------------------------------------------------- /app/test-projects/multi-module/submodule/src/main/kotlin/org/submodule/Submodule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This source file was generated by the Gradle 'init' task 3 | */ 4 | package org.submodule 5 | 6 | fun submodule() {} 7 | -------------------------------------------------------------------------------- /app/test-projects/playground/Errors.kt: -------------------------------------------------------------------------------- 1 | aaa -------------------------------------------------------------------------------- /app/test-projects/playground/Main.kt: -------------------------------------------------------------------------------- 1 | package org.kotlinlsp.testprojects.basic 2 | 3 | fun a() {} 4 | 5 | fun main() { 6 | a() 7 | var b = 5 8 | b += 1 9 | var c = "" 10 | c += "a" 11 | } -------------------------------------------------------------------------------- /app/test-projects/single-module/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.1.20" 3 | application 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies {} 11 | 12 | java { 13 | toolchain { 14 | languageVersion = JavaLanguageVersion.of(21) 15 | } 16 | } 17 | 18 | application { 19 | mainClass = "org.example.AppKt" 20 | } 21 | -------------------------------------------------------------------------------- /app/test-projects/single-module/app/src/main/kotlin/org/example/App.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This source file was generated by the Gradle 'init' task 3 | */ 4 | package org.example 5 | 6 | class App { 7 | val greeting: String 8 | get() { 9 | return "Hello World!" 10 | } 11 | } 12 | 13 | fun main() { 14 | println(App().greeting) 15 | } 16 | -------------------------------------------------------------------------------- /app/test-projects/single-module/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.configuration-cache=true 2 | -------------------------------------------------------------------------------- /app/test-projects/single-module/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "single-module" 2 | include("app") 3 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.configuration-cache=true 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amgdev9/kotlin-lsp/588a6ebfc1c25160e7fc9b39df471a2e426278fa/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /licenses/com.jetbrains.intellij.platform/NOTICE: -------------------------------------------------------------------------------- 1 | This software includes code from IntelliJ IDEA Community Edition 2 | Copyright (C) JetBrains s.r.o. 3 | https://www.jetbrains.com/idea/ 4 | -------------------------------------------------------------------------------- /licenses/com.jetbrains.intellij.platform/thirdparty/javolution_license.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javolution - Java(tm) Solution for Real-Time and Embedded Systems 3 | * Copyright (c) 2012, Javolution (http://javolution.org/) 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | -------------------------------------------------------------------------------- /licenses/com.jetbrains.intellij.platform/thirdparty/yourkit-license-dist.txt: -------------------------------------------------------------------------------- 1 | The following files can be redistributed under the license below: 2 | 3 | yjpagent.dll 4 | libyjpagent.so 5 | libyjpagent.dylib 6 | yjp-controller-api-redist.jar 7 | yjp-probe-api-redist.jar 8 | 9 | ------------------------------------------------------------------- 10 | 11 | Copyright (c) 2003-2022, YourKit 12 | All rights reserved. 13 | 14 | Redistribution and use in source and binary forms, with or without 15 | modification, are permitted provided that the following conditions are met: 16 | 17 | * Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | * Redistributions in binary form must reproduce the above copyright 21 | notice, this list of conditions and the following disclaimer in the 22 | documentation and/or other materials provided with the distribution. 23 | 24 | * Neither the name of YourKit nor the 25 | names of its contributors may be used to endorse or promote products 26 | derived from this software without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY YOURKIT "AS IS" AND ANY 29 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | DISCLAIMED. IN NO EVENT SHALL YOURKIT BE LIABLE FOR ANY 32 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 35 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /licenses/org.eclipse.lsp4j/NOTICE: -------------------------------------------------------------------------------- 1 | # Notices for Eclipse LSP4J 2 | 3 | This content is produced and maintained by the Eclipse LSP4J project. 4 | 5 | * Project home: https://projects.eclipse.org/projects/technology.lsp4j 6 | 7 | ## Trademarks 8 | 9 | Eclipse LSP4J is a trademark of the Eclipse Foundation. 10 | 11 | ## Copyright 12 | 13 | All content is the property of the respective authors or their employers. For 14 | more information regarding authorship of content, please consult the listed 15 | source code repository logs. 16 | 17 | ## Declared Project Licenses 18 | 19 | This program and the accompanying materials are made available under the terms 20 | of the Eclipse Public License v. 2.0 which is available at 21 | https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License v1.0 22 | which is available at https://www.eclipse.org/org/documents/edl-v10.php. 23 | 24 | SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause 25 | 26 | ## Source Code 27 | 28 | The project maintains the following source code repositories: 29 | 30 | * https://github.com/eclipse-lsp4j/lsp4j 31 | 32 | ## Third-party Content 33 | 34 | This project leverages the following third party content. 35 | 36 | Google Gson (2.9.1) 37 | 38 | * License: Apache License, 2.0 39 | * Project: https://github.com/google/gson 40 | * Source: https://github.com/google/gson 41 | 42 | ## Cryptography 43 | 44 | Content may contain encryption software. The country in which you are currently 45 | may have restrictions on the import, possession, and use, and/or re-export to 46 | another country, of encryption software. BEFORE using any encryption software, 47 | please check the country's laws, regulations and policies concerning the import, 48 | possession, or use, and re-export of encryption software, to see if this is 49 | permitted. 50 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.java.decompiler/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright © 2014–2025 JetBrains s.r.o. 2 | https://www.jetbrains.com/ 3 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/COPYRIGHT_HEADER.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. 3 | * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. 4 | */ 5 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/NOTICE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | == NOTICE file corresponding to the section 4 d of == 3 | == the Apache License, Version 2.0, == 4 | == in this case for the Kotlin Compiler distribution. == 5 | ========================================================================= 6 | 7 | Kotlin Compiler 8 | Copyright 2010-2024 JetBrains s.r.o and respective authors and developers 9 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/args4j_LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2003, Kohsuke Kawaguchi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/asm_license.txt: -------------------------------------------------------------------------------- 1 | 2 | ASM: a very small and fast Java bytecode manipulation framework 3 | Copyright (c) 2000-2005 INRIA, France Telecom 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 3. Neither the name of the copyright holders nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/asmble_license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chad Retz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/boost_LICENSE.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/dart_LICENSE.txt: -------------------------------------------------------------------------------- 1 | This license applies to all parts of Dart that are not externally 2 | maintained libraries. The external maintained libraries used by 3 | Dart are: 4 | 5 | 7-Zip - in third_party/7zip 6 | JSCRE - in runtime/third_party/jscre 7 | Ant - in third_party/apache_ant 8 | args4j - in third_party/args4j 9 | bzip2 - in third_party/bzip2 10 | dromaeo - in samples/third_party/dromaeo 11 | Eclipse - in third_party/eclipse 12 | gsutil = in third_party/gsutil 13 | Guava - in third_party/guava 14 | hamcrest - in third_party/hamcrest 15 | Httplib2 - in samples/third_party/httplib2 16 | JSON - in third_party/json 17 | JUnit - in third_party/junit 18 | Oauth - in samples/third_party/oauth2client 19 | Rhino - in third_party/rhino 20 | weberknecht - in third_party/weberknecht 21 | 22 | The libraries may have their own licenses; we recommend you read them, 23 | as their terms may differ from the terms below. 24 | 25 | Copyright 2012, the Dart project authors. All rights reserved. 26 | Redistribution and use in source and binary forms, with or without 27 | modification, are permitted provided that the following conditions are 28 | met: 29 | * Redistributions of source code must retain the above copyright 30 | notice, this list of conditions and the following disclaimer. 31 | * Redistributions in binary form must reproduce the above 32 | copyright notice, this list of conditions and the following 33 | disclaimer in the documentation and/or other materials provided 34 | with the distribution. 35 | * Neither the name of Google Inc. nor the names of its 36 | contributors may be used to endorse or promote products derived 37 | from this software without specific prior written permission. 38 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 39 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 40 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 41 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 42 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 44 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 45 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 46 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 47 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 48 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 49 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/jgit_license.txt: -------------------------------------------------------------------------------- 1 | https://www.eclipse.org/jgit/ 2 | 3 | Eclipse Distribution License - v 1.0 4 | 5 | Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. 6 | 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 10 | 11 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 12 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/jquery_license.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/jshashtable_license.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 Tim Down. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/karma-teamcity-reporter_LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (C) 2011-2013 Vojta Jína and contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/karma_LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (C) 2011-2019 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/lodash_LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright JS Foundation and other contributors 4 | 5 | Based on Underscore.js, copyright Jeremy Ashkenas, 6 | DocumentCloud and Investigative Reporters & Editors 7 | 8 | This software consists of voluntary contributions made by many 9 | individuals. For exact contribution history, see the revision history 10 | available at https://github.com/lodash/lodash 11 | 12 | The following license applies to all parts of this software except as 13 | documented below: 14 | 15 | ==== 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining 18 | a copy of this software and associated documentation files (the 19 | "Software"), to deal in the Software without restriction, including 20 | without limitation the rights to use, copy, modify, merge, publish, 21 | distribute, sublicense, and/or sell copies of the Software, and to 22 | permit persons to whom the Software is furnished to do so, subject to 23 | the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be 26 | included in all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 29 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 31 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 32 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 33 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 34 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | ==== 37 | 38 | Copyright and related rights for sample code are waived via CC0. Sample 39 | code is defined as all source code displayed within the prose of the 40 | documentation. 41 | 42 | CC0: http://creativecommons.org/publicdomain/zero/1.0/ 43 | 44 | ==== 45 | 46 | Files located in the node_modules and vendor directories are externally 47 | maintained libraries used by this software which have their own 48 | licenses; we recommend you read them, as their terms may differ from the 49 | terms above. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/lombok_LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009-2021 The Project Lombok Authors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/mocha-teamcity-reporter_LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jamie Sherriff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/okhttp_license.txt: -------------------------------------------------------------------------------- 1 | https://github.com/square/okhttp/ 2 | 3 | Copyright 2019 Square, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/prototype_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2010 Sam Stephenson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 16 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/scala_license.txt: -------------------------------------------------------------------------------- 1 | SCALA LICENSE 2 | 3 | Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. 4 | All rights reserved. 5 | 6 | This software was developed by the Programming Methods Laboratory of the 7 | Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. 8 | 9 | Permission to use, copy, modify, and distribute this software in source 10 | or binary form for any purpose with or without fee is hereby granted, 11 | provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | 3. Neither the name of the EPFL nor the names of its contributors 21 | may be used to endorse or promote products derived from this 22 | software without specific prior written permission. 23 | 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | SUCH DAMAGE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/sl4f_license.txt: -------------------------------------------------------------------------------- 1 | http://www.slf4j.org 2 | 3 | MIT License 4 | 5 | Copyright (c) 2004-2017 QOS.ch 6 | All rights reserved. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/stax2-api.txt: -------------------------------------------------------------------------------- 1 | This copy of Stax2 API is licensed under the 2 | Simplified BSD License (also known as "2-clause BSD", or "FreeBSD License") 3 | See the License for details about distribution rights, and the 4 | specific rights regarding derivate works. 5 | 6 | You may obtain a copy of the License at: 7 | 8 | http://www.opensource.org/licenses/bsd-license.php 9 | 10 | with details of: 11 | 12 | = FasterXML.com 13 | = 2010- -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/sun_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 2 | 3 | Developed at SunSoft, a Sun Microsystems, Inc. business. 4 | Permission to use, copy, modify, and distribute this 5 | software is freely granted, provided that this notice 6 | is preserved. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/teamcity-service-messages_LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Aaron Forsander 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/testdata/eclipse_distribution_license.txt: -------------------------------------------------------------------------------- 1 | Eclipse Distribution License - v 1.0 2 | 3 | Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/testdata/lombok_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009-2015 The Project Lombok Authors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlin/third_party/threetenbp_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos. 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of JSR-310 nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /licenses/org.jetbrains.kotlinx.kotlinx-serialization/NOTICE: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | == NOTICE file corresponding to the section 4 d of == 3 | == the Apache License, Version 2.0, == 4 | == in this case for the kotlix.serialization library. == 5 | ========================================================================= 6 | 7 | kotlinx.serialization library. 8 | Copyright 2017-2019 JetBrains s.r.o and respective authors and developers 9 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | ./gradlew clean distZip 2 | rm -rf lsp-dist 3 | unzip app/build/distributions/kotlin-lsp-*.zip -d lsp-dist 4 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "kotlin-lsp" 2 | 3 | include("app") 4 | --------------------------------------------------------------------------------