2023-07-27 19:52:58 +00:00
;;; lsp-kotlin.el --- description -*- lexical-binding: t; -*-
;; Copyright (C) 2020 emacs-lsp maintainers
;; Author: emacs-lsp maintainers
;; Keywords: lsp, kotlin
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; LSP Clients for the Kotlin Programming Language.
;;; Code:
( require 'lsp-mode )
( require 'cl-lib )
( require 'dash )
( defgroup lsp-kotlin nil
" LSP support for Kotlin, using KotlinLanguageServer. "
:group 'lsp-mode
:link ' ( url-link " https://github.com/fwcd/KotlinLanguageServer " ) )
( define-obsolete-variable-alias
'lsp-kotlin-language-server-path
'lsp-clients-kotlin-server-executable
" lsp-mode 6.4 " )
( defcustom lsp-clients-kotlin-server-executable
( if ( eq system-type 'windows-nt )
" kotlin-language-server.bat "
" kotlin-language-server " )
" The kotlin-language-server executable to use.
Leave as just the executable name to use the default behavior of finding the
executable with ` exec-path '. "
:type 'string
:group 'lsp-kotlin )
( defcustom lsp-kotlin-trace-server " off "
" Traces the communication between VSCode and the Kotlin language server. "
:type ' ( choice ( :tag " off " " messages " " verbose " ) )
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-compiler-jvm-target " 1.8 "
" Specifies the JVM target, e.g. \" 1.6 \" or \" 1.8 \" . "
:type 'string
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-linting-debounce-time 250
" [DEBUG] Specifies the debounce time limit.
Lower to increase responsiveness at the cost of possible stability issues. "
:type 'number
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-completion-snippets-enabled t
" Specifies whether code completion should provide snippets (true) or
plain-text items ( false ) . "
:type 'boolean
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-debug-adapter-enabled t
" [Recommended] Specifies whether the debug adapter should be used.
When enabled a debugger for Kotlin will be available. "
:type 'boolean )
( defcustom lsp-kotlin-debug-adapter-path " "
" Optionally a custom path to the debug adapter executable. "
:type 'string
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-external-sources-use-kls-scheme t
" [Recommended] Specifies whether URIs inside JARs should be represented
2024-07-28 16:03:37 +00:00
using the ` kls '-scheme. "
2023-07-27 19:52:58 +00:00
:type 'boolean
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-external-sources-auto-convert-to-kotlin t
" Specifies whether decompiled/external classes should be auto-converted
to Kotlin. "
:type 'boolean
:group 'lsp-kotlin
:package-version ' ( lsp-mode . " 6.1 " ) )
( defcustom lsp-kotlin-server-download-url
" https://github.com/fwcd/kotlin-language-server/releases/latest/download/server.zip "
" The URL for the language server download. "
:type 'string
:group 'lsp-kotlin
2024-07-28 16:03:37 +00:00
:package-version ' ( lsp-mode . " 9.0.0 " ) )
( defcustom lsp-kotlin-workspace-dir ( expand-file-name ( locate-user-emacs-file " workspace/ " ) )
" LSP kotlin workspace directory. "
:group 'lsp-kotlin
:risky t
:type 'directory )
( defcustom lsp-kotlin-workspace-cache-dir ( expand-file-name " .cache/ " lsp-kotlin-workspace-dir )
" LSP kotlin workspace cache directory. "
:group 'lsp-kotlin
:risky t
:type 'directory )
;; cache in this case is the dependency cache. Given as an initialization option.
( defcustom lsp-kotlin-ondisk-cache-path nil
" Path to the ondisk cache if used. If lsp-kotlin-ondisk-cache-enabled is t,
but path is nil , then the project root is used as a default. "
:type 'string
:group 'lsp-kotlin )
( defcustom lsp-kotlin-ondisk-cache-enabled nil
" Specifies whether to enable ondisk cache or not. If nil, in-memory cache
will be used. "
:type 'boolean
:group 'lsp-kotlin )
( defcustom lsp-kotlin-inlayhints-enable-typehints t
" Specifies whether to enable type hints or not.
Requires lsp-inlay-hints-mode. "
:type 'boolean
:group 'lsp-kotlin )
( defcustom lsp-kotlin-inlayhints-enable-parameterhints t
" Specifies whether to enable parameter hints or not.
Requires lsp-inlay-hints-mode. "
:type 'boolean
:group 'lsp-kotlin )
( defcustom lsp-kotlin-inlayhints-enable-chainedhints t
" Specifies whether to enable chained hints or not.
Requires lsp-inlay-hints-mode. "
:type 'boolean
:group 'lsp-kotlin )
2023-07-27 19:52:58 +00:00
( lsp-register-custom-settings
' ( ( " kotlin.externalSources.autoConvertToKotlin " lsp-kotlin-external-sources-auto-convert-to-kotlin t )
( " kotlin.externalSources.useKlsScheme " lsp-kotlin-external-sources-use-kls-scheme t )
( " kotlin.debugAdapter.path " lsp-kotlin-debug-adapter-path )
( " kotlin.debugAdapter.enabled " lsp-kotlin-debug-adapter-enabled t )
( " kotlin.completion.snippets.enabled " lsp-kotlin-completion-snippets-enabled t )
( " kotlin.linting.debounceTime " lsp-kotlin-linting-debounce-time )
( " kotlin.compiler.jvm.target " lsp-kotlin-compiler-jvm-target )
( " kotlin.trace.server " lsp-kotlin-trace-server )
2024-07-28 16:03:37 +00:00
( " kotlin.languageServer.path " lsp-clients-kotlin-server-executable )
( " kotlin.inlayHints.typeHints " lsp-kotlin-inlayhints-enable-typehints t )
( " kotlin.inlayHints.parameterHints " lsp-kotlin-inlayhints-enable-parameterhints t )
( " kotlin.inlayHints.chainedHints " lsp-kotlin-inlayhints-enable-chainedhints t ) ) )
2023-07-27 19:52:58 +00:00
( defvar lsp-kotlin--language-server-path
( f-join lsp-server-install-dir
" kotlin " " server " " bin " ( if ( eq system-type 'windows-nt )
" kotlin-language-server.bat "
" kotlin-language-server " ) )
" The path to store the language server at if necessary. " )
;; Debug and running
( declare-function dap-debug " ext:dap-mode " ( template ) t )
( defun lsp-kotlin-run-main ( main-class project-root debug? )
( require 'dap-kotlin )
( dap-debug ( list :type " kotlin "
:request " launch "
:mainClass main-class
:projectRoot project-root
:noDebug ( not debug? ) ) ) )
( defun lsp-kotlin-lens-backend ( _modified? callback )
( when lsp-kotlin-debug-adapter-enabled
( lsp-request-async
" kotlin/mainClass "
( list :uri ( lsp--buffer-uri ) )
( lambda ( mainInfo )
( let ( ( main-class ( lsp-get mainInfo :mainClass ) )
( project-root ( lsp-get mainInfo :projectRoot ) )
( range ( lsp-get mainInfo :range ) ) )
( funcall callback
( list ( lsp-make-code-lens :range range
:command
( lsp-make-command
:title " Run "
:command ( lambda ( )
( interactive )
( lsp-kotlin-run-main main-class project-root nil ) ) ) )
( lsp-make-code-lens :range range
:command
( lsp-make-command
:title " Debug "
:command ( lambda ( )
( interactive )
( lsp-kotlin-run-main main-class project-root t ) ) ) ) )
lsp--cur-version ) ) )
:mode 'tick ) ) )
( defvar lsp-lens-backends )
( declare-function lsp-lens-refresh " lsp-lens " ( buffer-modified? &optional buffer ) )
( define-minor-mode lsp-kotlin-lens-mode
" Toggle run/debug overlays. "
:group 'lsp-kotlin
:global nil
:init-value nil
:lighter nil
( cond
( lsp-kotlin-lens-mode
( require 'lsp-lens )
;; set lens backends so they are available is lsp-lens-mode is activated
;; backend does not support lenses, and block our other ones from showing. When backend support lenses again, we can use cl-pushnew to add it to lsp-lens-backends instead of overwriting
( setq-local lsp-lens-backends ( list #' lsp-kotlin-lens-backend ) )
( lsp-lens-refresh t ) )
( t ( setq-local lsp-lens-backends ( delete #' lsp-kotlin-lens-backend lsp-lens-backends ) ) ) ) )
;; Stolen from lsp-java:
;; https://github.com/emacs-lsp/lsp-java/blob/a1aff851bcf4f397f2a968557d213db1fede0c8a/lsp-java.el#L1065
( declare-function helm-make-source " ext:helm-source " )
( defvar lsp-kotlin--helm-result nil )
( defun lsp-kotlin--completing-read-multiple ( message items initial-selection )
( if ( functionp 'helm )
( progn
( require 'helm-source )
( helm :sources ( helm-make-source
message 'helm-source-sync :candidates items
:action ' ( ( " Identity " lambda ( _ )
( setq lsp-kotlin--helm-result ( helm-marked-candidates ) ) ) ) )
:buffer " *lsp-kotlin select* "
:prompt message )
lsp-kotlin--helm-result )
( if ( functionp 'ivy-read )
( let ( result )
( ivy-read message ( mapcar #' car items )
:action ( lambda ( c ) ( setq result ( list ( cdr ( assoc c items ) ) ) ) )
:multi-action
( lambda ( candidates )
( setq result ( mapcar ( lambda ( c ) ( cdr ( assoc c items ) ) ) candidates ) ) ) )
result )
( let ( ( deps initial-selection ) dep )
( while ( setq dep ( cl-rest ( lsp--completing-read
( if deps
( format " %s (selected %s): " message ( length deps ) )
( concat message " : " ) )
items
( -lambda ( ( name . id ) )
( if ( -contains? deps id )
( concat name " ✓ " )
name ) ) ) ) )
( if ( -contains? deps dep )
( setq deps ( remove dep deps ) )
( cl-pushnew dep deps ) ) )
deps ) ) ) )
( defun lsp-kotlin-implement-member ( )
( interactive )
( lsp-request-async
" kotlin/overrideMember "
( list :textDocument ( list :uri ( lsp--buffer-uri ) )
:position ( lsp--cur-position ) )
( lambda ( member-options )
( -if-let* ( ( option-items ( -map ( lambda ( x )
( list ( lsp-get x :title )
( lsp-get ( lsp-get ( lsp-get x :edit )
:changes )
( intern ( concat " : " ( lsp--buffer-uri ) ) ) ) ) )
member-options ) )
( selected-members ( lsp-kotlin--completing-read-multiple " Select overrides " option-items nil ) ) )
( dolist ( edit ( -flatten selected-members ) )
( lsp--apply-text-edits edit ) ) ) ) ) )
2024-07-28 16:03:37 +00:00
( defun lsp-kotlin--parse-uri ( uri )
" Get the path for where we'll store the file, calculating it based on URI. "
( or ( save-match-data
( when ( string-match " kls:file:/// \\ (.* \\ )!/ \\ (.* \. \\ (class \\ |java \\ |kt \\ ) \\ )?.* " uri )
( let* ( ( jar-path ( match-string 1 uri ) )
( file-path ( match-string 2 uri ) )
( lib-name ( string-join ( last ( split-string jar-path " / " ) 2 ) " . " ) )
( buffer-name ( replace-regexp-in-string " / " " . " file-path t t ) )
( file-location ( expand-file-name ( concat lsp-kotlin-workspace-cache-dir " / " lib-name " / " buffer-name ) ) ) )
file-location ) ) )
( error " Unable to match %s " uri ) ) )
( defun lsp-kotlin--uri-handler ( uri )
" Load a file corresponding to URI executing request to the kotlin server. "
( let ( ( file-location ( lsp-kotlin--parse-uri uri ) ) )
( unless ( file-readable-p file-location )
( lsp-kotlin--ensure-dir ( file-name-directory file-location ) )
( with-lsp-workspace ( lsp-find-workspace 'kotlin-ls nil )
( let ( ( content ( lsp-send-request ( lsp-make-request
" kotlin/jarClassContents "
( list :uri uri ) ) ) ) )
( with-temp-file file-location
( insert content ) ) ) ) )
file-location ) )
( defun lsp-kotlin--ensure-dir ( path )
" Ensure that directory PATH exists. "
( unless ( file-directory-p path )
( make-directory path t ) ) )
2023-07-27 19:52:58 +00:00
( lsp-dependency
'kotlin-language-server
` ( :system , lsp-clients-kotlin-server-executable )
` ( :download :url lsp-kotlin-server-download-url
:decompress :zip
:store-path , ( f-join lsp-server-install-dir " kotlin " " kotlin-language-server.zip " )
:binary-path lsp-clients-kotlin-server-executable
:set-executable? t ) )
( lsp-register-client
( make-lsp-client
:new-connection ( lsp-stdio-connection ( lambda ( )
` ( , ( or ( when ( f-exists? lsp-kotlin--language-server-path )
lsp-kotlin--language-server-path )
( or ( executable-find lsp-clients-kotlin-server-executable )
( lsp-package-path 'kotlin-language-server ) )
" kotlin-language-server " ) ) ) )
:major-modes ' ( kotlin-mode kotlin-ts-mode )
:priority -1
:server-id 'kotlin-ls
2024-07-28 16:03:37 +00:00
:uri-handlers ( lsp-ht ( " kls " #' lsp-kotlin--uri-handler ) )
2023-07-27 19:52:58 +00:00
:initialized-fn ( lambda ( workspace )
( with-lsp-workspace workspace
( lsp--set-configuration ( lsp-configuration-section " kotlin " ) ) ) )
2024-07-28 16:03:37 +00:00
:initialization-options ( lambda ( )
( when lsp-kotlin-ondisk-cache-enabled
( list :storagePath ( or lsp-kotlin-ondisk-cache-path
( lsp-workspace-root ) ) ) ) )
2023-07-27 19:52:58 +00:00
:download-server-fn ( lambda ( _client callback error-callback _update? )
( lsp-package-ensure 'kotlin-language-server callback error-callback ) ) ) )
( lsp-consistency-check lsp-kotlin )
( provide 'lsp-kotlin )
;;; lsp-kotlin.el ends here