mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 22:16:23 +00:00
multilspy 43209c8521e93e8c41bdd2060800474fd0b9f8b1
commit 6c2565baababc9c978b7c1ab40a56fadef16c816
Author: Developer <trane@trane-VirtualBox>
Date: Fri Apr 4 16:08:10 2025 +0530
address review comments for clangd support
src/multilspy/language_servers/clangd_language_server/clangd_language_server.py
commit 78f1c2b43ee16bb29b222a1c1af77956d6fd906d
Author: IDontHaveBrain <jonamjun.dev@gmail.com>
Date: Thu Apr 3 09:00:26 2025 +0900
chore: Remove win-arm64 support from kotlin runtime dependencies
src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json
commit b907cc227a01f4a500d3445bfa17d7237f91c5ec
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Tue Apr 1 22:39:18 2025 +0900
chore: use vscode-java runtime dependencies
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json
commit 3d19a282fe3d68d1a0b589c879775cac7ec23b8e
Author: moz-mdauer <mdauer@mozilla.com>
Date: Tue Apr 1 13:56:45 2025 +0200
feat: add support for workspace/symbol request
src/multilspy/language_server.py
src/multilspy/lsp_protocol_handler/lsp_constants.py
commit e60081cbcac5b938ee8936f715478321c2be9b5c
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 29 14:55:16 2025 +0900
refactor: improve process termination handling
src/multilspy/lsp_protocol_handler/server.py
commit 88345e14e2172390b5f0d48d7877b2ee8620c187
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 29 14:12:28 2025 +0900
feat: add JDK runtime dependency
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json
commit ef9ad63083ebbbf929fd1cc4d0cc084e5cb69ad4
Author: Andrej730 <azhilenkov@gmail.com>
Date: Sun Mar 23 19:26:08 2025 +0500
Location.relativePath to use `None` value if relative path is not available #85
src/multilspy/language_server.py
src/multilspy/multilspy_types.py
src/multilspy/multilspy_utils.py
commit 85b1fe84140ec7bb84db843d618ea4bd8f4a096c
Author: Huang YunKun <htynkn@gmail.com>
Date: Fri Mar 28 11:40:34 2025 +0800
Update runtime_dependencies.json
src/multilspy/language_servers/eclipse_jdtls/runtime_dependencies.json
commit f0a42717cd95f9d7a845603cee60ac2fd22f16ac
Author: Andrej730 <azhilenkov@gmail.com>
Date: Sun Mar 23 19:15:21 2025 +0500
SyncLanguageServer.create - small documentation note
To clarify that relative paths for repositories are not supported.
Currently if it's using relative path, `create_server` fails with traceback similar to the one below:
```python
Traceback (most recent call last):
File "third_test.py", line 11, in <module>
with lsp.start_server():
File "\Python311\Lib\contextlib.py", line 137, in __enter__
return next(self.gen)
^^^^^^^^^^^^^^
File "\Python311\Lib\site-packages\multilspy\language_server.py", line 744, in start_server
asyncio.run_coroutine_threadsafe(ctx.__aenter__(), loop=self.loop).result()
File "\Python311\Lib\concurrent\futures\_base.py", line 456, in result
return self.__get_result()
^^^^^^^^^^^^^^^^^^^
File "\Python311\Lib\concurrent\futures\_base.py", line 401, in __get_result
raise self._exception
File "\Python311\Lib\contextlib.py", line 210, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "\Python311\Lib\site-packages\multilspy\language_servers\jedi_language_server\jedi_server.py", line 101, in start_server
initialize_params = self._get_initialize_params(self.repository_root_path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "\Python311\Lib\site-packages\multilspy\language_servers\jedi_language_server\jedi_server.py", line 50, in _get_initialize_params
d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "\Python311\Lib\pathlib.py", line 566, in as_uri
raise ValueError("relative path can't be expressed as a file URI")
ValueError: relative path can't be expressed as a file URI
```
src/multilspy/language_server.py
commit 9f69f75b1c9a460d63dbcc9d1b2b50596ee8a49b
Author: Developer <trane@trane-VirtualBox>
Date: Tue Mar 18 18:54:18 2025 +0530
clangd language server commit
src/multilspy/language_server.py
src/multilspy/language_servers/clangd_language_server/clangd_language_server.py
src/multilspy/language_servers/clangd_language_server/initialize_params.json
src/multilspy/language_servers/clangd_language_server/runtime_dependencies.json
src/multilspy/multilspy_config.py
commit edeb1771b8cd6064e6eca28a1126988780fc4608
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sun Mar 9 01:26:59 2025 +0900
test: update Kotlin executable permissions and adjust Java version in workflow
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
commit 79a8f6902e91c0a1301c63b9f8737efbcbcccf24
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sun Mar 9 00:47:38 2025 +0900
fix: improve process termination handling
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
src/multilspy/lsp_protocol_handler/server.py
commit 90622f2788df412021565008dd83c27ac4d0b467
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sun Mar 9 00:10:28 2025 +0900
fix: improve process termination handling
src/multilspy/lsp_protocol_handler/server.py
commit 907e6f340dec6b6e50c5712059bf3563fd7f064c
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 8 23:52:07 2025 +0900
fix: improve process termination handling
src/multilspy/lsp_protocol_handler/server.py
commit 694b30108ff3025b7f6f2e7923c6077f6b0dee23
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 8 23:45:48 2025 +0900
chore: cleanup
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
commit 635a6a83617e02a24c141a85b8dd8cabbf3a4601
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 8 21:14:00 2025 +0900
fix: platform check for Kotlin LSP from architecture-specific to OS-only
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
commit 2b053ee8b1f8b8d2ff391110baf27bb21163b715
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 8 20:25:05 2025 +0900
feat: Update Kotlin Language Server configuration and runtime dependencies
src/multilspy/language_servers/kotlin_language_server/initialize_params.json
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json
commit 59f84082b9cd64e64bfc9a7644a644fdb8cc881e
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 8 17:51:18 2025 +0900
feat: Add Kotlin language server support
src/multilspy/language_server.py
src/multilspy/language_servers/kotlin_language_server/initialize_params.json
src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py
src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json
src/multilspy/multilspy_config.py
commit c85e2825aaa85d64611e7bfb392daa6eb7aa54ac
Author: Jet Zhou <jet@jetzhou.com>
Date: Thu Mar 6 16:10:28 2025 -0800
Add `start_independent_lsp_process` configuration option to `MultilspyConfig`
Default value is `True`, which means language server process will be in
its own process group. SIGINT and SIGTERM will only be received by the
`multilspy` python process itself, not propagated. We rely on the
existing `shutdown` and `stop` code to clean up the process.
If set to `False`, the language server process will be under the same
process group as the `multilspy` python process. This means SIGINT and
SIGTERM will be received by both processes. This may cause issues if the
python process has clean up code that runs after receiving
SIGINT/SIGTERM because the `start_server` context manager will begin
running its cleanup code, part of which invokes the `shutdown` and
`stop` in `language_protocol_handler.server`. This will hang forever
while awaiting an response from the language server process, since it
would have already terminated itself after receiving SIGINT/SIGTERM.
src/multilspy/language_server.py
src/multilspy/lsp_protocol_handler/server.py
src/multilspy/multilspy_config.py
commit ff17f9365f4862627f00c43f8f88cd7a76f10fc6
Author: Jet Zhou <jet@jetzhou.com>
Date: Tue Mar 4 22:27:55 2025 -0800
Let language server subprocess be its own process group
In situations where `multilspy` is launched from another process, a
SIGINT or SIGTERM signal will be received by both the launching process
as well as the process running the language server because
`asyncio.create_subprocess_shell` defaults to `start_new_session=False`.
In such a situation, `lsp_protocol_handler.server.shutdown` will be
called but will hang since the underlying process is already terminated,
`lsp_protocol_handler.server` won't ever receive an answer back for
`shutdown`.
This change makes it so that SIGINT and SIGTERM won't propagate to the
language server process. We rely on the clean up code already in place
to shut down the process as desired.
See
[python documentation](https://docs.python.org/3.13/library/subprocess.html#subprocess.Popen)
for more details on `start_new_session`
src/multilspy/lsp_protocol_handler/server.py
commit ee104b062d1e0fda4a063937f5ca6488ac42683e
Author: Jet Zhou <jet@jetzhou.com>
Date: Mon Mar 3 18:08:27 2025 -0800
Add assertion string to `references` and `document_symbol` requests to language server
These endpoints could return `None` in case the symbol is one that's not
found. Adding the assertion string can help caller catch these
exceptions and handle the errors as appropriate.
src/multilspy/language_server.py
commit 2edff10b31585fbcd5829afc07d1258b3b83b7c8
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sun Mar 2 12:49:34 2025 +0900
refactor: Change to use `PlatformUtils` instead of `sys.platform`
src/multilspy/language_servers/typescript_language_server/typescript_language_server.py
commit e2cc4dd5f54adf89a2e4e8140387fc71fd5ebc56
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Sat Mar 1 20:25:38 2025 -0300
feat: add initialization options for Dart Language Server
src/multilspy/language_servers/dart_language_server/initialize_params.json
commit 807ec41b1098a2aecccbbe074f80d8c2dbd15d5d
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Sat Mar 1 20:15:38 2025 -0300
fix: update description for Dart Language Server runtime dependencies
src/multilspy/language_servers/dart_language_server/runtime_dependencies.json
commit ca5e3e91e7093838f3cc33ef5fb5561fdf9cf7ee
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Sat Mar 1 20:12:53 2025 -0300
feat: implement Dart Language Server runtime dependency setup
src/multilspy/language_servers/dart_language_server/dart_language_server.py
src/multilspy/language_servers/dart_language_server/runtime_dependencies.json
commit 6295dd19db279033e2507dd665bd5c7df79e2623
Author: nj.jo <jonamjun.dev@gmail.com>
Date: Sat Mar 1 15:18:41 2025 +0900
fix: resolve 'No module named pwd' error on Windows
src/multilspy/language_servers/typescript_language_server/typescript_language_server.py
commit 2390a21479553e2b42e06eb10049d5c09ab9ef0a
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Fri Feb 28 09:54:58 2025 -0300
fix race codnition in process termination
src/multilspy/lsp_protocol_handler/server.py
commit e193501fb605ba86d52513f9fe044c2576bfc4cd
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Thu Feb 27 22:05:03 2025 -0300
feat: add Dart language support and corresponding tests
src/multilspy/language_server.py
src/multilspy/language_servers/dart_language_server/dart_language_server.py
src/multilspy/language_servers/dart_language_server/initialize_params.json
src/multilspy/multilspy_config.py
commit 11c0da5450d0ce032b5ea832561e87ead6a0ca5b
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Thu Feb 27 21:43:32 2025 -0300
Add psutil dependency and enhance process termination handling
src/multilspy/lsp_protocol_handler/server.py
commit 7b85f39636e1971b0e9e0b9e69f4f8d7be51f931
Author: v4rgas <66626747+v4rgas@users.noreply.github.com>
Date: Thu Feb 27 09:25:02 2025 -0300
chore: undo styling changes
src/multilspy/language_server.py
commit 0d567618dda795c0789b374667c0325c3df4c87e
Author: Jet Zhou <jet@jetzhou.com>
Date: Tue Feb 11 17:55:35 2025 -0800
Force `npm i` to use current directory, in case some parent directory already contains a node_modules folder
src/multilspy/language_servers/typescript_language_server/runtime_dependencies.json
This commit is contained in:
1 parent
87134b6ba0
commit
90c42923c8
19 files changed
+1608
-201
No files matched your search
@@ -1 +1 @@
|
||||
07ffa48166beb0c7f20ac76dbf7724d0bca4bbb4
|
||||
43209c8521e93e8c41bdd2060800474fd0b9f8b1
|
||||
@@ -1 +0,0 @@
|
||||
e766d55fd568f5313fb46796b8463097bdb9a293
|
||||
+226
-163
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
Provides C/C++ specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C/C++.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import pathlib
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncIterator
|
||||
|
||||
from multilspy.multilspy_logger import MultilspyLogger
|
||||
from multilspy.language_server import LanguageServer
|
||||
from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from multilspy.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from multilspy.multilspy_config import MultilspyConfig
|
||||
from multilspy.multilspy_utils import FileUtils
|
||||
from multilspy.multilspy_utils import PlatformUtils
|
||||
|
||||
|
||||
class ClangdLanguageServer(LanguageServer):
|
||||
"""
|
||||
Provides C/C++ specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C/C++.
|
||||
As the project gets bigger in size, building index will take time. Try running clangd multiple times to ensure index is built properly.
|
||||
Also make sure compile_commands.json is created at root of the source directory. Check clangd test case for example.
|
||||
"""
|
||||
|
||||
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
|
||||
"""
|
||||
Creates a ClangdLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
clangd_executable_path = self.setup_runtime_dependencies(logger, config)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=clangd_executable_path, cwd=repository_root_path),
|
||||
"cpp",
|
||||
)
|
||||
self.server_ready = asyncio.Event()
|
||||
|
||||
def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for ClangdLanguageServer.
|
||||
"""
|
||||
platform_id = PlatformUtils.get_platform_id()
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f:
|
||||
d = json.load(f)
|
||||
del d["_description"]
|
||||
|
||||
assert platform_id.value in [
|
||||
"linux-x64"
|
||||
], "Only linux-x64 is supported for in multilspy at the moment"
|
||||
|
||||
runtime_dependencies = d["runtimeDependencies"]
|
||||
runtime_dependencies = [
|
||||
dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value
|
||||
]
|
||||
assert len(runtime_dependencies) == 1
|
||||
dependency = runtime_dependencies[0]
|
||||
|
||||
clangd_ls_dir = os.path.join(os.path.dirname(__file__), "static/clangd")
|
||||
clangd_executable_path = os.path.join(clangd_ls_dir, "clangd_19.1.2", "bin", dependency["binaryName"])
|
||||
if not os.path.exists(clangd_ls_dir):
|
||||
os.makedirs(clangd_ls_dir)
|
||||
if dependency["archiveType"] == "zip":
|
||||
FileUtils.download_and_extract_archive(
|
||||
logger, dependency["url"], clangd_ls_dir, dependency["archiveType"]
|
||||
)
|
||||
assert os.path.exists(clangd_executable_path)
|
||||
os.chmod(clangd_executable_path, stat.S_IEXEC)
|
||||
|
||||
return clangd_executable_path
|
||||
|
||||
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
|
||||
"""
|
||||
Returns the initialize params for the clangd Language Server.
|
||||
"""
|
||||
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r") as f:
|
||||
d = json.load(f)
|
||||
|
||||
del d["_description"]
|
||||
|
||||
d["processId"] = os.getpid()
|
||||
assert d["rootPath"] == "$rootPath"
|
||||
d["rootPath"] = repository_absolute_path
|
||||
|
||||
assert d["rootUri"] == "$rootUri"
|
||||
d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri()
|
||||
|
||||
assert d["workspaceFolders"][0]["uri"] == "$uri"
|
||||
d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri()
|
||||
|
||||
assert d["workspaceFolders"][0]["name"] == "$name"
|
||||
d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path)
|
||||
|
||||
return d
|
||||
|
||||
@asynccontextmanager
|
||||
async def start_server(self) -> AsyncIterator["ClangdLanguageServer"]:
|
||||
"""
|
||||
Starts the Clangd Language Server, waits for the server to be ready and yields the LanguageServer instance.
|
||||
|
||||
Usage:
|
||||
```
|
||||
async with lsp.start_server():
|
||||
# LanguageServer has been initialized and ready to serve requests
|
||||
await lsp.request_definition(...)
|
||||
await lsp.request_references(...)
|
||||
# Shutdown the LanguageServer on exit from scope
|
||||
# LanguageServer has been shutdown
|
||||
"""
|
||||
async def register_capability_handler(params):
|
||||
assert "registrations" in params
|
||||
for registration in params["registrations"]:
|
||||
if registration["method"] == "workspace/executeCommand":
|
||||
self.initialize_searcher_command_available.set()
|
||||
self.resolve_main_method_available.set()
|
||||
return
|
||||
|
||||
async def lang_status_handler(params):
|
||||
# TODO: Should we wait for
|
||||
# server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}}
|
||||
# Before proceeding?
|
||||
if params["type"] == "ServiceReady" and params["message"] == "ServiceReady":
|
||||
self.service_ready_event.set()
|
||||
|
||||
async def execute_client_command_handler(params):
|
||||
return []
|
||||
|
||||
async def do_nothing(params):
|
||||
return
|
||||
|
||||
async def check_experimental_status(params):
|
||||
if params["quiescent"] == True:
|
||||
self.server_ready.set()
|
||||
|
||||
async def window_log_message(msg):
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_notification("language/status", lang_status_handler)
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_request("workspace/executeClientCommand", execute_client_command_handler)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
async with super().start_server():
|
||||
self.logger.log("Starting Clangd server process", logging.INFO)
|
||||
await self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
init_response = await self.server.send.initialize(initialize_params)
|
||||
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
assert init_response["capabilities"]["completionProvider"] == {
|
||||
"triggerCharacters": ['.', '<', '>', ':', '"', '/', '*'],
|
||||
"resolveProvider": False,
|
||||
}
|
||||
|
||||
self.server.notify.initialized({})
|
||||
|
||||
self.completions_available.set()
|
||||
# set ready flag
|
||||
self.server_ready.set()
|
||||
await self.server_ready.wait()
|
||||
|
||||
yield self
|
||||
|
||||
await self.server.shutdown()
|
||||
await self.server.stop()
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize",
|
||||
"processId": "os.getpid()",
|
||||
"locale": "en",
|
||||
"rootPath": "$rootPath",
|
||||
"rootUri": "$rootUri",
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
"synchronization": {
|
||||
"didSave": true,
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"completion": {
|
||||
"dynamicRegistration": true,
|
||||
"completionItem": {
|
||||
"snippetSupport": true
|
||||
}
|
||||
},
|
||||
"definition": {
|
||||
"dynamicRegistration": true
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"workspaceFolders": true,
|
||||
"didChangeConfiguration": {
|
||||
"dynamicRegistration": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaceFolders": [
|
||||
{
|
||||
"uri": "$uri",
|
||||
"name": "$name"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"_description": "Used to download the runtime dependencies for running Clangd.",
|
||||
"runtimeDependencies": [
|
||||
{
|
||||
"id": "Clangd",
|
||||
"description": "Clangd for Linux (x64)",
|
||||
"url": "https://github.com/clangd/clangd/releases/download/19.1.2/clangd-linux-19.1.2.zip",
|
||||
"platformId": "linux-x64",
|
||||
"archiveType": "zip",
|
||||
"binaryName": "clangd"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
from contextlib import asynccontextmanager
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import stat
|
||||
from typing import AsyncIterator
|
||||
from multilspy.language_server import LanguageServer
|
||||
from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
import json
|
||||
from multilspy.multilspy_utils import FileUtils, PlatformUtils
|
||||
|
||||
|
||||
class DartLanguageServer(LanguageServer):
|
||||
"""
|
||||
Provides Dart specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Dart.
|
||||
"""
|
||||
|
||||
def __init__(self, config, logger, repository_root_path):
|
||||
"""
|
||||
Creates a DartServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
|
||||
executable_path = self.setup_runtime_dependencies(logger)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=executable_path, cwd=repository_root_path),
|
||||
"dart",
|
||||
)
|
||||
|
||||
def setup_runtime_dependencies(self, logger: "MultilspyLogger") -> str:
|
||||
platform_id = PlatformUtils.get_platform_id()
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f:
|
||||
d = json.load(f)
|
||||
del d["_description"]
|
||||
|
||||
runtime_dependencies = d["runtimeDependencies"]
|
||||
runtime_dependencies = [
|
||||
dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value
|
||||
]
|
||||
|
||||
assert len(runtime_dependencies) == 1
|
||||
dependency = runtime_dependencies[0]
|
||||
|
||||
dart_ls_dir = os.path.join(os.path.dirname(__file__), "static", "dart-language-server")
|
||||
dart_executable_path = os.path.join(dart_ls_dir, dependency["binaryName"])
|
||||
|
||||
if not os.path.exists(dart_ls_dir):
|
||||
os.makedirs(dart_ls_dir)
|
||||
FileUtils.download_and_extract_archive(
|
||||
logger, dependency["url"], dart_ls_dir, dependency["archiveType"]
|
||||
)
|
||||
|
||||
|
||||
assert os.path.exists(dart_executable_path)
|
||||
os.chmod(dart_executable_path, stat.S_IEXEC)
|
||||
|
||||
return f"{dart_executable_path} language-server --client-id multilspy.dart --client-version 1.2"
|
||||
|
||||
|
||||
def _get_initialize_params(self, repository_absolute_path: str):
|
||||
"""
|
||||
Returns the initialize params for the Dart Language Server.
|
||||
"""
|
||||
with open(
|
||||
os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r"
|
||||
) as f:
|
||||
d = json.load(f)
|
||||
|
||||
del d["_description"]
|
||||
|
||||
d["processId"] = os.getpid()
|
||||
assert d["rootPath"] == "$rootPath"
|
||||
d["rootPath"] = repository_absolute_path
|
||||
|
||||
assert d["rootUri"] == "$rootUri"
|
||||
d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri()
|
||||
|
||||
assert d["workspaceFolders"][0]["uri"] == "$uri"
|
||||
d["workspaceFolders"][0]["uri"] = pathlib.Path(
|
||||
repository_absolute_path
|
||||
).as_uri()
|
||||
|
||||
assert d["workspaceFolders"][0]["name"] == "$name"
|
||||
d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path)
|
||||
|
||||
return d
|
||||
|
||||
@asynccontextmanager
|
||||
async def start_server(self) -> AsyncIterator["DartLanguageServer"]:
|
||||
"""
|
||||
Start the language server and yield when the server is ready.
|
||||
"""
|
||||
|
||||
async def execute_client_command_handler(params):
|
||||
return []
|
||||
|
||||
async def do_nothing(params):
|
||||
return
|
||||
|
||||
async def check_experimental_status(params):
|
||||
pass
|
||||
|
||||
async def window_log_message(msg):
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
|
||||
self.server.on_request("client/registerCapability", do_nothing)
|
||||
self.server.on_notification("language/status", do_nothing)
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_request(
|
||||
"workspace/executeClientCommand", execute_client_command_handler
|
||||
)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification(
|
||||
"experimental/serverStatus", check_experimental_status
|
||||
)
|
||||
|
||||
async with super().start_server():
|
||||
self.logger.log(
|
||||
"Starting dart-language-server server process", logging.INFO
|
||||
)
|
||||
await self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
self.logger.log(
|
||||
"Sending initialize request to dart-language-server",
|
||||
logging.DEBUG,
|
||||
)
|
||||
init_response = await self.server.send_request(
|
||||
"initialize", initialize_params
|
||||
)
|
||||
self.logger.log(
|
||||
f"Received initialize response from dart-language-server: {init_response}",
|
||||
logging.INFO,
|
||||
)
|
||||
|
||||
self.server.notify.initialized({})
|
||||
|
||||
yield self
|
||||
|
||||
await self.server.shutdown()
|
||||
await self.server.stop()
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"_description": "This file contains the initialization parameters for the Dart Language Server.",
|
||||
"processId": "$processId",
|
||||
"rootPath": "$rootPath",
|
||||
"rootUri": "$rootUri",
|
||||
"capabilities": {},
|
||||
"initializationOptions": {
|
||||
"onlyAnalyzeProjectsWithOpenFiles": false,
|
||||
"suggestFromUnimportedLibraries": true,
|
||||
"closingLabels": false,
|
||||
"outline": false,
|
||||
"flutterOutline": false,
|
||||
"allowOpenUri": false
|
||||
},
|
||||
"trace": "verbose",
|
||||
"workspaceFolders": [
|
||||
{
|
||||
"uri": "$uri",
|
||||
"name": "$name"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"_description": "Used to download the runtime dependencies for running Dart Language Server, downloaded from https://dart.dev/get-dart/archive",
|
||||
"runtimeDependencies": [
|
||||
{
|
||||
"id": "DartLanguageServer",
|
||||
"description": "Dart Language Server for Linux (x64)",
|
||||
"url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-linux-x64-release.zip",
|
||||
"platformId": "linux-x64",
|
||||
"archiveType": "zip",
|
||||
"binaryName": "dart-sdk/bin/dart"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,6 +23,16 @@
|
||||
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.6.500.v20230717-2134.jar",
|
||||
"jdtls_readonly_config_path": "extension/server/config_mac_arm"
|
||||
},
|
||||
"osx-x64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@darwin-x64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
"relative_extraction_path": "vscode-java",
|
||||
"jre_home_path": "extension/jre/17.0.8.1-macosx-x86_64",
|
||||
"jre_path": "extension/jre/17.0.8.1-macosx-x86_64/bin/java",
|
||||
"lombok_jar_path": "extension/lombok/lombok-1.18.30.jar",
|
||||
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.6.500.v20230717-2134.jar",
|
||||
"jdtls_readonly_config_path": "extension/server/config_mac"
|
||||
},
|
||||
"linux-arm64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@linux-arm64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
@@ -59,4 +69,4 @@
|
||||
"intellisense_members_path": "extension/dist/bundledModels/java_intellisense-members"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
{
|
||||
"_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize",
|
||||
"processId": "os.getpid()",
|
||||
"clientInfo": {
|
||||
"name": "Multilspy Kotlin Client",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"locale": "en",
|
||||
"rootPath": "repository_absolute_path",
|
||||
"rootUri": "pathlib.Path(repository_absolute_path).as_uri()",
|
||||
"capabilities": {
|
||||
"workspace": {
|
||||
"applyEdit": true,
|
||||
"workspaceEdit": {
|
||||
"documentChanges": true,
|
||||
"resourceOperations": [
|
||||
"create",
|
||||
"rename",
|
||||
"delete"
|
||||
],
|
||||
"failureHandling": "textOnlyTransactional",
|
||||
"normalizesLineEndings": true,
|
||||
"changeAnnotationSupport": {
|
||||
"groupsOnLabel": true
|
||||
}
|
||||
},
|
||||
"didChangeConfiguration": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"didChangeWatchedFiles": {
|
||||
"dynamicRegistration": true,
|
||||
"relativePatternSupport": true
|
||||
},
|
||||
"symbol": {
|
||||
"dynamicRegistration": true,
|
||||
"symbolKind": {
|
||||
"valueSet": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26
|
||||
]
|
||||
},
|
||||
"tagSupport": {
|
||||
"valueSet": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"resolveSupport": {
|
||||
"properties": [
|
||||
"location.range"
|
||||
]
|
||||
}
|
||||
},
|
||||
"codeLens": {
|
||||
"refreshSupport": true
|
||||
},
|
||||
"executeCommand": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"configuration": true,
|
||||
"workspaceFolders": true,
|
||||
"semanticTokens": {
|
||||
"refreshSupport": true
|
||||
},
|
||||
"fileOperations": {
|
||||
"dynamicRegistration": true,
|
||||
"didCreate": true,
|
||||
"didRename": true,
|
||||
"didDelete": true,
|
||||
"willCreate": true,
|
||||
"willRename": true,
|
||||
"willDelete": true
|
||||
},
|
||||
"inlineValue": {
|
||||
"refreshSupport": true
|
||||
},
|
||||
"inlayHint": {
|
||||
"refreshSupport": true
|
||||
},
|
||||
"diagnostics": {
|
||||
"refreshSupport": true
|
||||
}
|
||||
},
|
||||
"textDocument": {
|
||||
"publishDiagnostics": {
|
||||
"relatedInformation": true,
|
||||
"versionSupport": false,
|
||||
"tagSupport": {
|
||||
"valueSet": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
"codeDescriptionSupport": true,
|
||||
"dataSupport": true
|
||||
},
|
||||
"synchronization": {
|
||||
"dynamicRegistration": true,
|
||||
"willSave": true,
|
||||
"willSaveWaitUntil": true,
|
||||
"didSave": true
|
||||
},
|
||||
"completion": {
|
||||
"dynamicRegistration": true,
|
||||
"contextSupport": true,
|
||||
"completionItem": {
|
||||
"snippetSupport": false,
|
||||
"commitCharactersSupport": true,
|
||||
"documentationFormat": [
|
||||
"markdown",
|
||||
"plaintext"
|
||||
],
|
||||
"deprecatedSupport": true,
|
||||
"preselectSupport": true,
|
||||
"tagSupport": {
|
||||
"valueSet": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"insertReplaceSupport": false,
|
||||
"resolveSupport": {
|
||||
"properties": [
|
||||
"documentation",
|
||||
"detail",
|
||||
"additionalTextEdits"
|
||||
]
|
||||
},
|
||||
"insertTextModeSupport": {
|
||||
"valueSet": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
"labelDetailsSupport": true
|
||||
},
|
||||
"insertTextMode": 2,
|
||||
"completionItemKind": {
|
||||
"valueSet": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25
|
||||
]
|
||||
},
|
||||
"completionList": {
|
||||
"itemDefaults": [
|
||||
"commitCharacters",
|
||||
"editRange",
|
||||
"insertTextFormat",
|
||||
"insertTextMode"
|
||||
]
|
||||
}
|
||||
},
|
||||
"hover": {
|
||||
"dynamicRegistration": true,
|
||||
"contentFormat": [
|
||||
"markdown",
|
||||
"plaintext"
|
||||
]
|
||||
},
|
||||
"signatureHelp": {
|
||||
"dynamicRegistration": true,
|
||||
"signatureInformation": {
|
||||
"documentationFormat": [
|
||||
"markdown",
|
||||
"plaintext"
|
||||
],
|
||||
"parameterInformation": {
|
||||
"labelOffsetSupport": true
|
||||
},
|
||||
"activeParameterSupport": true
|
||||
},
|
||||
"contextSupport": true
|
||||
},
|
||||
"definition": {
|
||||
"dynamicRegistration": true,
|
||||
"linkSupport": true
|
||||
},
|
||||
"references": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"documentHighlight": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"documentSymbol": {
|
||||
"dynamicRegistration": true,
|
||||
"symbolKind": {
|
||||
"valueSet": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26
|
||||
]
|
||||
},
|
||||
"hierarchicalDocumentSymbolSupport": true,
|
||||
"tagSupport": {
|
||||
"valueSet": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"labelSupport": true
|
||||
},
|
||||
"codeAction": {
|
||||
"dynamicRegistration": true,
|
||||
"isPreferredSupport": true,
|
||||
"disabledSupport": true,
|
||||
"dataSupport": true,
|
||||
"resolveSupport": {
|
||||
"properties": [
|
||||
"edit"
|
||||
]
|
||||
},
|
||||
"codeActionLiteralSupport": {
|
||||
"codeActionKind": {
|
||||
"valueSet": [
|
||||
"",
|
||||
"quickfix",
|
||||
"refactor",
|
||||
"refactor.extract",
|
||||
"refactor.inline",
|
||||
"refactor.rewrite",
|
||||
"source",
|
||||
"source.organizeImports"
|
||||
]
|
||||
}
|
||||
},
|
||||
"honorsChangeAnnotations": false
|
||||
},
|
||||
"codeLens": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"formatting": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"rangeFormatting": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"onTypeFormatting": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"rename": {
|
||||
"dynamicRegistration": true,
|
||||
"prepareSupport": true,
|
||||
"prepareSupportDefaultBehavior": 1,
|
||||
"honorsChangeAnnotations": true
|
||||
},
|
||||
"documentLink": {
|
||||
"dynamicRegistration": true,
|
||||
"tooltipSupport": true
|
||||
},
|
||||
"typeDefinition": {
|
||||
"dynamicRegistration": true,
|
||||
"linkSupport": true
|
||||
},
|
||||
"implementation": {
|
||||
"dynamicRegistration": true,
|
||||
"linkSupport": true
|
||||
},
|
||||
"colorProvider": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"foldingRange": {
|
||||
"dynamicRegistration": true,
|
||||
"rangeLimit": 5000,
|
||||
"lineFoldingOnly": true,
|
||||
"foldingRangeKind": {
|
||||
"valueSet": [
|
||||
"comment",
|
||||
"imports",
|
||||
"region"
|
||||
]
|
||||
},
|
||||
"foldingRange": {
|
||||
"collapsedText": false
|
||||
}
|
||||
},
|
||||
"declaration": {
|
||||
"dynamicRegistration": true,
|
||||
"linkSupport": true
|
||||
},
|
||||
"selectionRange": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"callHierarchy": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"semanticTokens": {
|
||||
"dynamicRegistration": true,
|
||||
"tokenTypes": [
|
||||
"namespace",
|
||||
"type",
|
||||
"class",
|
||||
"enum",
|
||||
"interface",
|
||||
"struct",
|
||||
"typeParameter",
|
||||
"parameter",
|
||||
"variable",
|
||||
"property",
|
||||
"enumMember",
|
||||
"event",
|
||||
"function",
|
||||
"method",
|
||||
"macro",
|
||||
"keyword",
|
||||
"modifier",
|
||||
"comment",
|
||||
"string",
|
||||
"number",
|
||||
"regexp",
|
||||
"operator",
|
||||
"decorator"
|
||||
],
|
||||
"tokenModifiers": [
|
||||
"declaration",
|
||||
"definition",
|
||||
"readonly",
|
||||
"static",
|
||||
"deprecated",
|
||||
"abstract",
|
||||
"async",
|
||||
"modification",
|
||||
"documentation",
|
||||
"defaultLibrary"
|
||||
],
|
||||
"formats": [
|
||||
"relative"
|
||||
],
|
||||
"requests": {
|
||||
"range": true,
|
||||
"full": {
|
||||
"delta": true
|
||||
}
|
||||
},
|
||||
"multilineTokenSupport": false,
|
||||
"overlappingTokenSupport": false,
|
||||
"serverCancelSupport": true,
|
||||
"augmentsSyntaxTokens": true
|
||||
},
|
||||
"linkedEditingRange": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"typeHierarchy": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"inlineValue": {
|
||||
"dynamicRegistration": true
|
||||
},
|
||||
"inlayHint": {
|
||||
"dynamicRegistration": true,
|
||||
"resolveSupport": {
|
||||
"properties": [
|
||||
"tooltip",
|
||||
"textEdits",
|
||||
"label.tooltip",
|
||||
"label.location",
|
||||
"label.command"
|
||||
]
|
||||
}
|
||||
},
|
||||
"diagnostic": {
|
||||
"dynamicRegistration": true,
|
||||
"relatedDocumentSupport": false
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"showMessage": {
|
||||
"messageActionItem": {
|
||||
"additionalPropertiesSupport": true
|
||||
}
|
||||
},
|
||||
"showDocument": {
|
||||
"support": true
|
||||
},
|
||||
"workDoneProgress": true
|
||||
},
|
||||
"general": {
|
||||
"staleRequestSupport": {
|
||||
"cancel": true,
|
||||
"retryOnContentModified": [
|
||||
"textDocument/semanticTokens/full",
|
||||
"textDocument/semanticTokens/range",
|
||||
"textDocument/semanticTokens/full/delta"
|
||||
]
|
||||
},
|
||||
"regularExpressions": {
|
||||
"engine": "ECMAScript",
|
||||
"version": "ES2020"
|
||||
},
|
||||
"markdown": {
|
||||
"parser": "marked",
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"positionEncodings": [
|
||||
"utf-16"
|
||||
]
|
||||
},
|
||||
"notebookDocument": {
|
||||
"synchronization": {
|
||||
"dynamicRegistration": true,
|
||||
"executionSummarySupport": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"initializationOptions": {
|
||||
"workspaceFolders": "[pathlib.Path(repository_absolute_path).as_uri()]",
|
||||
"storagePath": null,
|
||||
"codegen": {
|
||||
"enabled": false
|
||||
},
|
||||
"compiler": {
|
||||
"jvm": {
|
||||
"target": "default"
|
||||
}
|
||||
},
|
||||
"completion": {
|
||||
"snippets": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"diagnostics": {
|
||||
"enabled": true,
|
||||
"level": 4,
|
||||
"debounceTime": 250
|
||||
},
|
||||
"scripts": {
|
||||
"enabled": true,
|
||||
"buildScriptsEnabled": true
|
||||
},
|
||||
"indexing": {
|
||||
"enabled": true
|
||||
},
|
||||
"externalSources": {
|
||||
"useKlsScheme": false,
|
||||
"autoConvertToKotlin": false
|
||||
},
|
||||
"inlayHints": {
|
||||
"typeHints": false,
|
||||
"parameterHints": false,
|
||||
"chainedHints": false
|
||||
},
|
||||
"formatting": {
|
||||
"formatter": "ktfmt",
|
||||
"ktfmt": {
|
||||
"style": "google",
|
||||
"indent": 4,
|
||||
"maxWidth": 100,
|
||||
"continuationIndent": 8,
|
||||
"removeUnusedImports": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"trace": "verbose",
|
||||
"workspaceFolders": "[\n {\n \"uri\": pathlib.Path(repository_absolute_path).as_uri(),\n \"name\": os.path.basename(repository_absolute_path),\n }\n ]"
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
"""
|
||||
Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import dataclasses
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import pathlib
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncIterator
|
||||
|
||||
from multilspy.multilspy_logger import MultilspyLogger
|
||||
from multilspy.language_server import LanguageServer
|
||||
from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from multilspy.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from multilspy.multilspy_config import MultilspyConfig
|
||||
from multilspy.multilspy_utils import FileUtils
|
||||
from multilspy.multilspy_utils import PlatformUtils
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class KotlinRuntimeDependencyPaths:
|
||||
"""
|
||||
Stores the paths to the runtime dependencies of Kotlin Language Server
|
||||
"""
|
||||
java_path: str
|
||||
java_home_path: str
|
||||
kotlin_executable_path: str
|
||||
|
||||
|
||||
class KotlinLanguageServer(LanguageServer):
|
||||
"""
|
||||
Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin.
|
||||
"""
|
||||
|
||||
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
|
||||
"""
|
||||
Creates a Kotlin Language Server instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
runtime_dependency_paths = self.setup_runtime_dependencies(logger, config)
|
||||
self.runtime_dependency_paths = runtime_dependency_paths
|
||||
|
||||
# Create command to execute the Kotlin Language Server script
|
||||
cmd = f'"{self.runtime_dependency_paths.kotlin_executable_path}"'
|
||||
|
||||
# Set environment variables including JAVA_HOME
|
||||
proc_env = {"JAVA_HOME": self.runtime_dependency_paths.java_home_path}
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=cmd, env=proc_env, cwd=repository_root_path),
|
||||
"kotlin",
|
||||
)
|
||||
|
||||
def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> KotlinRuntimeDependencyPaths:
|
||||
"""
|
||||
Setup runtime dependencies for Kotlin Language Server.
|
||||
"""
|
||||
platform_id = PlatformUtils.get_platform_id()
|
||||
|
||||
# Verify platform support
|
||||
assert platform_id.value.startswith("win-") or platform_id.value.startswith("linux-") or platform_id.value.startswith("osx-"), "Only Windows, Linux and macOS platforms are supported for Kotlin in multilspy at the moment"
|
||||
|
||||
# Load dependency information
|
||||
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f:
|
||||
d = json.load(f)
|
||||
del d["_description"]
|
||||
|
||||
kotlin_dependency = d["runtimeDependency"]
|
||||
java_dependency = d["java"][platform_id.value]
|
||||
|
||||
# Setup paths for dependencies
|
||||
static_dir = os.path.join(os.path.dirname(__file__), "static")
|
||||
os.makedirs(static_dir, exist_ok=True)
|
||||
|
||||
# Setup Java paths
|
||||
java_dir = os.path.join(static_dir, "java")
|
||||
os.makedirs(java_dir, exist_ok=True)
|
||||
|
||||
java_home_path = os.path.join(java_dir, java_dependency["java_home_path"])
|
||||
java_path = os.path.join(java_dir, java_dependency["java_path"])
|
||||
|
||||
# Download and extract Java if not exists
|
||||
if not os.path.exists(java_path):
|
||||
logger.log(f"Downloading Java for {platform_id.value}...", logging.INFO)
|
||||
FileUtils.download_and_extract_archive(
|
||||
logger, java_dependency["url"], java_dir, java_dependency["archiveType"]
|
||||
)
|
||||
# Make Java executable
|
||||
if not platform_id.value.startswith("win-"):
|
||||
os.chmod(java_path, 0o755)
|
||||
|
||||
assert os.path.exists(java_path), f"Java executable not found at {java_path}"
|
||||
|
||||
# Setup Kotlin Language Server paths
|
||||
kotlin_ls_dir = os.path.join(static_dir, "server")
|
||||
|
||||
# Get platform-specific executable script path
|
||||
if platform_id.value.startswith("win-"):
|
||||
kotlin_script = os.path.join(kotlin_ls_dir, "bin", "kotlin-language-server.bat")
|
||||
else:
|
||||
kotlin_script = os.path.join(kotlin_ls_dir, "bin", "kotlin-language-server")
|
||||
|
||||
# Download and extract Kotlin Language Server if script doesn't exist
|
||||
if not os.path.exists(kotlin_script):
|
||||
logger.log("Downloading Kotlin Language Server...", logging.INFO)
|
||||
FileUtils.download_and_extract_archive(
|
||||
logger, kotlin_dependency["url"], static_dir, kotlin_dependency["archiveType"]
|
||||
)
|
||||
|
||||
# Make script executable on Unix platforms
|
||||
if os.path.exists(kotlin_script) and not platform_id.value.startswith("win-"):
|
||||
os.chmod(kotlin_script, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
||||
|
||||
# Use script file
|
||||
if os.path.exists(kotlin_script):
|
||||
kotlin_executable_path = kotlin_script
|
||||
logger.log(f"Using Kotlin Language Server script at {kotlin_script}", logging.INFO)
|
||||
else:
|
||||
raise FileNotFoundError(f"Kotlin Language Server script not found at {kotlin_script}")
|
||||
|
||||
return KotlinRuntimeDependencyPaths(
|
||||
java_path=java_path,
|
||||
java_home_path=java_home_path,
|
||||
kotlin_executable_path=kotlin_executable_path
|
||||
)
|
||||
|
||||
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
|
||||
"""
|
||||
Returns the initialize params for the Kotlin Language Server.
|
||||
"""
|
||||
with open(str(pathlib.PurePath(os.path.dirname(__file__), "initialize_params.json")), "r") as f:
|
||||
d: InitializeParams = json.load(f)
|
||||
|
||||
del d["_description"]
|
||||
|
||||
if not os.path.isabs(repository_absolute_path):
|
||||
repository_absolute_path = os.path.abspath(repository_absolute_path)
|
||||
|
||||
assert d["processId"] == "os.getpid()"
|
||||
d["processId"] = os.getpid()
|
||||
|
||||
assert d["rootPath"] == "repository_absolute_path"
|
||||
d["rootPath"] = repository_absolute_path
|
||||
|
||||
assert d["rootUri"] == "pathlib.Path(repository_absolute_path).as_uri()"
|
||||
d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri()
|
||||
|
||||
assert d["initializationOptions"]["workspaceFolders"] == "[pathlib.Path(repository_absolute_path).as_uri()]"
|
||||
d["initializationOptions"]["workspaceFolders"] = [pathlib.Path(repository_absolute_path).as_uri()]
|
||||
|
||||
assert (
|
||||
d["workspaceFolders"]
|
||||
== '[\n {\n "uri": pathlib.Path(repository_absolute_path).as_uri(),\n "name": os.path.basename(repository_absolute_path),\n }\n ]'
|
||||
)
|
||||
d["workspaceFolders"] = [
|
||||
{
|
||||
"uri": pathlib.Path(repository_absolute_path).as_uri(),
|
||||
"name": os.path.basename(repository_absolute_path),
|
||||
}
|
||||
]
|
||||
|
||||
return d
|
||||
|
||||
@asynccontextmanager
|
||||
async def start_server(self) -> AsyncIterator["KotlinLanguageServer"]:
|
||||
"""
|
||||
Starts the Kotlin Language Server, waits for the server to be ready and yields the LanguageServer instance.
|
||||
|
||||
Usage:
|
||||
```
|
||||
async with lsp.start_server():
|
||||
# LanguageServer has been initialized and ready to serve requests
|
||||
await lsp.request_definition(...)
|
||||
await lsp.request_references(...)
|
||||
# Shutdown the LanguageServer on exit from scope
|
||||
# LanguageServer has been shutdown
|
||||
```
|
||||
"""
|
||||
async def execute_client_command_handler(params):
|
||||
return []
|
||||
|
||||
async def do_nothing(params):
|
||||
return
|
||||
|
||||
async def window_log_message(msg):
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
|
||||
self.server.on_request("client/registerCapability", do_nothing)
|
||||
self.server.on_notification("language/status", do_nothing)
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_request("workspace/executeClientCommand", execute_client_command_handler)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
|
||||
async with super().start_server():
|
||||
self.logger.log("Starting Kotlin server process", logging.INFO)
|
||||
await self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
init_response = await self.server.send.initialize(initialize_params)
|
||||
|
||||
capabilities = init_response["capabilities"]
|
||||
assert "textDocumentSync" in capabilities, "Server must support textDocumentSync"
|
||||
assert "hoverProvider" in capabilities, "Server must support hover"
|
||||
assert "completionProvider" in capabilities, "Server must support code completion"
|
||||
assert "signatureHelpProvider" in capabilities, "Server must support signature help"
|
||||
assert "definitionProvider" in capabilities, "Server must support go to definition"
|
||||
assert "referencesProvider" in capabilities, "Server must support find references"
|
||||
assert "documentSymbolProvider" in capabilities, "Server must support document symbols"
|
||||
assert "workspaceSymbolProvider" in capabilities, "Server must support workspace symbols"
|
||||
assert "semanticTokensProvider" in capabilities, "Server must support semantic tokens"
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
|
||||
yield self
|
||||
|
||||
try:
|
||||
await self.server.shutdown()
|
||||
except Exception as e:
|
||||
self.logger.log(f"Error during Kotlin server shutdown: {str(e)}", logging.WARNING)
|
||||
finally:
|
||||
await self.server.stop()
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"_description": "Used to download the runtime dependencies for Kotlin Language Server from https://github.com/fwcd/kotlin-language-server",
|
||||
"runtimeDependency": {
|
||||
"id": "KotlinLsp",
|
||||
"description": "Kotlin Language Server",
|
||||
"url": "https://github.com/fwcd/kotlin-language-server/releases/download/1.3.13/server.zip",
|
||||
"archiveType": "zip"
|
||||
},
|
||||
"java": {
|
||||
"win-x64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@win32-x64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
"java_home_path": "extension/jre/17.0.8.1-win32-x86_64",
|
||||
"java_path": "extension/jre/17.0.8.1-win32-x86_64/bin/java.exe"
|
||||
},
|
||||
"linux-x64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@linux-x64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
"java_home_path": "extension/jre/17.0.8.1-linux-x86_64",
|
||||
"java_path": "extension/jre/17.0.8.1-linux-x86_64/bin/java"
|
||||
},
|
||||
"linux-arm64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@linux-arm64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
"java_home_path": "extension/jre/17.0.8.1-linux-aarch64",
|
||||
"java_path": "extension/jre/17.0.8.1-linux-aarch64/bin/java"
|
||||
},
|
||||
"osx-x64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@darwin-x64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
"java_home_path": "extension/jre/17.0.8.1-macosx-x86_64",
|
||||
"java_path": "extension/jre/17.0.8.1-macosx-x86_64/bin/java"
|
||||
},
|
||||
"osx-arm64": {
|
||||
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.23.0/java@darwin-arm64-1.23.0.vsix",
|
||||
"archiveType": "zip",
|
||||
"java_home_path": "extension/jre/17.0.8.1-macosx-aarch64",
|
||||
"java_path": "extension/jre/17.0.8.1-macosx-aarch64/bin/java"
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-17
@@ -32,6 +32,11 @@ else:
|
||||
return type('obj', (), {'pw_name': os.environ.get('USERNAME', 'unknown')})()
|
||||
|
||||
|
||||
# Conditionally import pwd module (Unix-only)
|
||||
if not PlatformUtils.get_platform_id().value.startswith("win"):
|
||||
import pwd
|
||||
|
||||
|
||||
class TypeScriptLanguageServer(LanguageServer):
|
||||
"""
|
||||
Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript.
|
||||
@@ -50,7 +55,7 @@ class TypeScriptLanguageServer(LanguageServer):
|
||||
"typescript",
|
||||
)
|
||||
self.server_ready = asyncio.Event()
|
||||
|
||||
|
||||
@override
|
||||
def should_always_ignore(self, dirname: str) -> bool:
|
||||
return super().should_always_ignore(dirname) or dirname in [
|
||||
@@ -95,22 +100,29 @@ class TypeScriptLanguageServer(LanguageServer):
|
||||
if not os.path.exists(tsserver_ls_dir):
|
||||
os.makedirs(tsserver_ls_dir, exist_ok=True)
|
||||
for dependency in runtime_dependencies:
|
||||
# Handle platform-specific user settings
|
||||
subprocess_kwargs = {
|
||||
'shell': True,
|
||||
'check': True,
|
||||
'cwd': tsserver_ls_dir,
|
||||
'stdout': subprocess.DEVNULL,
|
||||
'stderr': subprocess.DEVNULL
|
||||
}
|
||||
|
||||
# Only add user parameter on Unix-like systems
|
||||
if os.name != 'nt': # Not Windows
|
||||
# Windows doesn't support the 'user' parameter and doesn't have pwd module
|
||||
if PlatformUtils.get_platform_id().value.startswith("win"):
|
||||
subprocess.run(
|
||||
dependency["command"],
|
||||
shell=True,
|
||||
check=True,
|
||||
cwd=tsserver_ls_dir,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
else:
|
||||
# On Unix-like systems, run as non-root user
|
||||
user = pwd.getpwuid(os.getuid()).pw_name
|
||||
subprocess_kwargs['user'] = user
|
||||
|
||||
subprocess.run(dependency["command"], **subprocess_kwargs)
|
||||
|
||||
subprocess.run(
|
||||
dependency["command"],
|
||||
shell=True,
|
||||
check=True,
|
||||
user=user,
|
||||
cwd=tsserver_ls_dir,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
|
||||
tsserver_executable_path = os.path.join(tsserver_ls_dir, "node_modules", ".bin", "typescript-language-server")
|
||||
|
||||
assert os.path.exists(tsserver_executable_path), "typescript-language-server executable not found. Please install typescript-language-server and try again."
|
||||
@@ -209,4 +221,4 @@ class TypeScriptLanguageServer(LanguageServer):
|
||||
yield self
|
||||
|
||||
await self.server.shutdown()
|
||||
await self.server.stop()
|
||||
await self.server.stop()
|
||||
@@ -57,3 +57,6 @@ class LSPConstants:
|
||||
|
||||
# key used to represent children in document symbols
|
||||
CHILDREN = "children"
|
||||
|
||||
# key used to represent the location in symbols
|
||||
LOCATION = "location"
|
||||
@@ -33,6 +33,7 @@ import dataclasses
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import psutil
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from .lsp_requests import LspNotification, LspRequest
|
||||
@@ -173,9 +174,18 @@ class LanguageServerHandler:
|
||||
the asynchronous tasks created by the handler.
|
||||
task_counter: An integer that represents the next available task id for the handler.
|
||||
loop: An asyncio.AbstractEventLoop object that represents the event loop used by the handler.
|
||||
start_independent_lsp_process: An optional boolean flag that indicates whether to start the
|
||||
language server process in an independent process group. Default is `True`. Setting it to
|
||||
`False` means that the language server process will be in the same process group as the
|
||||
the current process, and any SIGINT and SIGTERM signals will be sent to both processes.
|
||||
"""
|
||||
|
||||
def __init__(self, process_launch_info: ProcessLaunchInfo, logger=None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
process_launch_info: ProcessLaunchInfo,
|
||||
logger=None,
|
||||
start_independent_lsp_process=True,
|
||||
) -> None:
|
||||
"""
|
||||
Params:
|
||||
cmd: A string that represents the command to launch the language server process.
|
||||
@@ -197,6 +207,7 @@ class LanguageServerHandler:
|
||||
self.tasks = {}
|
||||
self.task_counter = 0
|
||||
self.loop = None
|
||||
self.start_independent_lsp_process = start_independent_lsp_process
|
||||
|
||||
async def start(self) -> None:
|
||||
"""
|
||||
@@ -214,6 +225,7 @@ class LanguageServerHandler:
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
env=child_proc_env,
|
||||
cwd=self.process_launch_info.cwd,
|
||||
start_new_session=self.start_independent_lsp_process,
|
||||
)
|
||||
|
||||
# Check if process terminated immediately
|
||||
@@ -234,24 +246,111 @@ class LanguageServerHandler:
|
||||
"""
|
||||
Sends the terminate signal to the language server process and waits for it to exit, with a timeout, killing it if necessary
|
||||
"""
|
||||
for task in self.tasks.values():
|
||||
task.cancel()
|
||||
|
||||
self.tasks = {}
|
||||
|
||||
# First cancel all tasks
|
||||
await self._cancel_pending_tasks()
|
||||
|
||||
process = self.process
|
||||
self.process = None
|
||||
|
||||
if not process:
|
||||
return
|
||||
|
||||
# Clean up the process
|
||||
await self._cleanup_process(process)
|
||||
|
||||
if process:
|
||||
# TODO: Ideally, we should terminate the process here,
|
||||
# However, there's an issue with asyncio terminating processes documented at
|
||||
# https://bugs.python.org/issue35539 and https://bugs.python.org/issue41320
|
||||
# process.terminate()
|
||||
wait_for_end = process.wait()
|
||||
async def _cancel_pending_tasks(self):
|
||||
"""Cancel all pending tasks and wait for them to complete or timeout."""
|
||||
pending_tasks = []
|
||||
for task in self.tasks.values():
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
pending_tasks.append(task)
|
||||
|
||||
if pending_tasks:
|
||||
try:
|
||||
await asyncio.wait_for(wait_for_end, timeout=60)
|
||||
except asyncio.TimeoutError:
|
||||
process.kill()
|
||||
await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5.0)
|
||||
except (asyncio.TimeoutError, Exception):
|
||||
pass
|
||||
|
||||
self.tasks = {}
|
||||
|
||||
async def _cleanup_process(self, process):
|
||||
"""Clean up a process: close stdin, terminate/kill process, close stdout/stderr."""
|
||||
# Close stdin first to prevent deadlocks
|
||||
# See: https://bugs.python.org/issue35539
|
||||
self._safely_close_pipe(process.stdin)
|
||||
|
||||
# Terminate/kill the process if it's still running
|
||||
if process.returncode is None:
|
||||
await self._terminate_or_kill_process(process)
|
||||
|
||||
# Close stdout and stderr pipes after process has exited
|
||||
# This is essential to prevent "I/O operation on closed pipe" errors and
|
||||
# "Event loop is closed" errors during garbage collection
|
||||
# See: https://bugs.python.org/issue41320 and https://github.com/python/cpython/issues/88050
|
||||
self._safely_close_pipe(process.stdout)
|
||||
self._safely_close_pipe(process.stderr)
|
||||
|
||||
# Small delay to ensure OS has released file handles
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
def _safely_close_pipe(self, pipe):
|
||||
"""Safely close a pipe, ignoring any exceptions."""
|
||||
if pipe:
|
||||
try:
|
||||
pipe.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def _terminate_or_kill_process(self, process):
|
||||
"""Try to terminate the process gracefully, then forcefully if necessary."""
|
||||
# First try to terminate the process tree gracefully
|
||||
self._signal_process_tree(process, terminate=True)
|
||||
|
||||
# Wait for the process to exit (with timeout)
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=10)
|
||||
except (asyncio.TimeoutError, Exception):
|
||||
# If termination failed, forcefully kill the process tree
|
||||
self._signal_process_tree(process, terminate=False)
|
||||
try:
|
||||
# Give it one more chance to exit
|
||||
await asyncio.wait_for(process.wait(), timeout=2)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _signal_process_tree(self, process, terminate=True):
|
||||
"""Send signal (terminate or kill) to the process and all its children."""
|
||||
signal_method = "terminate" if terminate else "kill"
|
||||
|
||||
# Try to get the parent process
|
||||
parent = None
|
||||
try:
|
||||
parent = psutil.Process(process.pid)
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception):
|
||||
pass
|
||||
|
||||
# If we have the parent process and it's running, signal the entire tree
|
||||
if parent and parent.is_running():
|
||||
# Signal children first
|
||||
for child in parent.children(recursive=True):
|
||||
try:
|
||||
getattr(child, signal_method)()
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception):
|
||||
pass
|
||||
|
||||
# Then signal the parent
|
||||
try:
|
||||
getattr(parent, signal_method)()
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception):
|
||||
pass
|
||||
else:
|
||||
# Fall back to direct process signaling
|
||||
try:
|
||||
getattr(process, signal_method)()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""
|
||||
@@ -310,7 +409,7 @@ class LanguageServerHandler:
|
||||
line = await self.process.stderr.readline()
|
||||
if not line:
|
||||
continue
|
||||
self._log("LSP stderr: " + line.decode(ENCODING))
|
||||
self._log("LSP stderr: " + line.decode(ENCODING, errors='replace'))
|
||||
except (BrokenPipeError, ConnectionResetError, StopLoopException):
|
||||
pass
|
||||
|
||||
|
||||
@@ -30,10 +30,13 @@ class Language(str, Enum):
|
||||
PYTHON = "python"
|
||||
RUST = "rust"
|
||||
JAVA = "java"
|
||||
KOTLIN = "kotlin"
|
||||
TYPESCRIPT = "typescript"
|
||||
JAVASCRIPT = "javascript"
|
||||
GO = "go"
|
||||
RUBY = "ruby"
|
||||
DART = "dart"
|
||||
CPP = "cpp"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
@@ -67,6 +70,7 @@ class MultilspyConfig:
|
||||
"""
|
||||
code_language: Language
|
||||
trace_lsp_communication: bool = False
|
||||
start_independent_lsp_process: bool = True
|
||||
ignored_paths: list[str] = field(default_factory=list)
|
||||
"""Paths, dirs or glob-like patterns. The matching will follow the same logic as for .gitignore entries"""
|
||||
gitignore_file_content: str | None = None
|
||||
|
||||
@@ -82,7 +82,7 @@ class Location(TypedDict):
|
||||
uri: DocumentUri
|
||||
range: Range
|
||||
absolutePath: str
|
||||
relativePath: str
|
||||
relativePath: Union[str, None]
|
||||
|
||||
class CompletionItemKind(IntEnum):
|
||||
"""The kind of a completion entry."""
|
||||
|
||||
@@ -5,7 +5,7 @@ This file contains various utility functions like I/O operations, handling paths
|
||||
import gzip
|
||||
import logging
|
||||
import os
|
||||
from typing import Tuple
|
||||
from typing import Tuple, Union
|
||||
import requests
|
||||
import shutil
|
||||
import uuid
|
||||
@@ -94,6 +94,16 @@ class PathUtils:
|
||||
"""Check if a pattern contains glob-specific characters."""
|
||||
return any(c in pattern for c in '*?[]!')
|
||||
|
||||
@staticmethod
|
||||
def get_relative_path(path: str, base_path: str) -> Union[str, None]:
|
||||
"""
|
||||
Gets relative path if it's possible (paths should be on the same drive),
|
||||
returns `None` otherwise.
|
||||
"""
|
||||
if PurePath(path).drive == PurePath(base_path).drive:
|
||||
return str(PurePath(os.path.relpath(path, base_path)))
|
||||
return None
|
||||
|
||||
class FileUtils:
|
||||
"""
|
||||
Utility functions for file operations.
|
||||
|
||||
Reference in new issue
Block a user