Add language server implementations from multilspy (unmodified)

This commit is contained in:
Dominik Jain
2025-06-21 13:20:06 +02:00
parent de466ef6a9
commit a43d0aa021
34 changed files with 8421 additions and 0 deletions
+1
View File
@@ -202,6 +202,7 @@ pylint.html
/agent-ui
# dynamic LS installations
/src/multilspy/language_servers/*/static
/src/solidlsp/language_servers/*/static
# Cache is in .multilspy
**/.multilspy/**
@@ -0,0 +1,190 @@
"""
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",
"win-x64",
"osx-arm64",
], "Unsupported platform: " + platform_id.value
runtime_dependencies = d["runtimeDependencies"]
runtime_dependencies = [
dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value
]
assert len(runtime_dependencies) == 1
# Select dependency matching the current platform
dependency = next((dep for dep in runtime_dependencies if dep["platformId"] == platform_id.value), None)
if dependency is None:
raise RuntimeError(f"No runtime dependency found for platform {platform_id.value}")
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_executable_path):
clangd_url = dependency["url"]
logger.log(f"Clangd executable not found at {clangd_executable_path}. Downloading from {clangd_url}", logging.INFO)
os.makedirs(clangd_ls_dir, exist_ok=True)
if dependency["archiveType"] == "zip":
FileUtils.download_and_extract_archive(
logger, clangd_url, clangd_ls_dir, dependency["archiveType"]
)
else:
raise RuntimeError(f"Unsupported archive type: {dependency['archiveType']}")
if not os.path.exists(clangd_executable_path):
raise FileNotFoundError(
f"Clangd executable not found at {clangd_executable_path}.\n"
"Make sure you have installed clangd. See https://clangd.llvm.org/installation"
)
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
@@ -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,29 @@
{
"_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"
},
{
"id": "Clangd",
"description": "Clangd for Windows (x64)",
"url": "https://github.com/clangd/clangd/releases/download/19.1.2/clangd-windows-19.1.2.zip",
"platformId": "windows-x64",
"archiveType": "zip",
"binaryName": "clangd.exe"
},
{
"id": "Clangd",
"description": "Clangd for macOS (Arm64)",
"url": "https://github.com/clangd/clangd/releases/download/19.1.2/clangd-mac-19.1.2.zip",
"platformId": "osx-arm64",
"archiveType": "zip",
"binaryName": "clangd"
}
]
}
@@ -0,0 +1,143 @@
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
@@ -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,45 @@
{
"_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"
},
{
"id": "DartLanguageServer",
"description": "Dart Language Server for Windows (x64)",
"url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-windows-x64-release.zip",
"platformId": "win-x64",
"archiveType": "zip",
"binaryName": "dart-sdk/bin/dart.exe"
},
{
"id": "DartLanguageServer",
"description": "Dart Language Server for Windows (arm64)",
"url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-windows-arm64-release.zip",
"platformId": "win-arm64",
"archiveType": "zip",
"binaryName": "dart-sdk/bin/dart.exe"
},
{
"id": "DartLanguageServer",
"description": "Dart Language Server for macOS (x64)",
"url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-macos-x64-release.zip",
"platformId": "osx-x64",
"archiveType": "zip",
"binaryName": "dart-sdk/bin/dart"
},
{
"id": "DartLanguageServer",
"description": "Dart Language Server for macOS (arm64)",
"url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-macos-arm64-release.zip",
"platformId": "osx-arm64",
"archiveType": "zip",
"binaryName": "dart-sdk/bin/dart"
}
]
}
@@ -0,0 +1,422 @@
"""
Provides Java specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Java.
"""
import asyncio
import dataclasses
import json
import logging
import os
import pathlib
import shutil
import stat
import uuid
from contextlib import asynccontextmanager
from typing import AsyncIterator
from overrides import override
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_settings import MultilspySettings
from multilspy.multilspy_utils import FileUtils
from multilspy.multilspy_utils import PlatformUtils
from pathlib import PurePath
@dataclasses.dataclass
class RuntimeDependencyPaths:
"""
Stores the paths to the runtime dependencies of EclipseJDTLS
"""
gradle_path: str
lombok_jar_path: str
jre_path: str
jre_home_path: str
jdtls_launcher_jar_path: str
jdtls_readonly_config_path: str
intellicode_jar_path: str
intellisense_members_path: str
class EclipseJDTLS(LanguageServer):
"""
The EclipseJDTLS class provides a Java specific implementation of the LanguageServer class
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates a new EclipseJDTLS instance initializing the language server settings appropriately.
This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
"""
runtime_dependency_paths = self.setupRuntimeDependencies(logger, config)
self.runtime_dependency_paths = runtime_dependency_paths
# ws_dir is the workspace directory for the EclipseJDTLS server
ws_dir = str(
PurePath(
MultilspySettings.get_language_server_directory(),
"EclipseJDTLS",
"workspaces",
uuid.uuid4().hex,
)
)
# shared_cache_location is the global cache used by Eclipse JDTLS across all workspaces
shared_cache_location = str(
PurePath(MultilspySettings.get_global_cache_directory(), "lsp", "EclipseJDTLS", "sharedIndex")
)
jre_path = self.runtime_dependency_paths.jre_path
lombok_jar_path = self.runtime_dependency_paths.lombok_jar_path
jdtls_launcher_jar = self.runtime_dependency_paths.jdtls_launcher_jar_path
os.makedirs(ws_dir, exist_ok=True)
data_dir = str(PurePath(ws_dir, "data_dir"))
jdtls_config_path = str(PurePath(ws_dir, "config_path"))
jdtls_readonly_config_path = self.runtime_dependency_paths.jdtls_readonly_config_path
if not os.path.exists(jdtls_config_path):
shutil.copytree(jdtls_readonly_config_path, jdtls_config_path)
for static_path in [
jre_path,
lombok_jar_path,
jdtls_launcher_jar,
jdtls_config_path,
jdtls_readonly_config_path,
]:
assert os.path.exists(static_path), static_path
# TODO: Add "self.runtime_dependency_paths.jre_home_path"/bin to $PATH as well
proc_env = {"syntaxserver": "false", "JAVA_HOME": self.runtime_dependency_paths.jre_home_path}
proc_cwd = repository_root_path
cmd = " ".join(
[
jre_path,
"--add-modules=ALL-SYSTEM",
"--add-opens",
"java.base/java.util=ALL-UNNAMED",
"--add-opens",
"java.base/java.lang=ALL-UNNAMED",
"--add-opens",
"java.base/sun.nio.fs=ALL-UNNAMED",
"-Declipse.application=org.eclipse.jdt.ls.core.id1",
"-Dosgi.bundles.defaultStartLevel=4",
"-Declipse.product=org.eclipse.jdt.ls.core.product",
"-Djava.import.generatesMetadataFilesAtProjectRoot=false",
"-Dfile.encoding=utf8",
"-noverify",
"-XX:+UseParallelGC",
"-XX:GCTimeRatio=4",
"-XX:AdaptiveSizePolicyWeight=90",
"-Dsun.zip.disableMemoryMapping=true",
"-Djava.lsp.joinOnCompletion=true",
"-Xmx3G",
"-Xms100m",
"-Xlog:disable",
"-Dlog.level=ALL",
f'"-javaagent:{lombok_jar_path}"',
f'"-Djdt.core.sharedIndexLocation={shared_cache_location}"',
"-jar",
f'"{jdtls_launcher_jar}"',
"-configuration",
f'"{jdtls_config_path}"',
"-data",
f'"{data_dir}"',
]
)
self.service_ready_event = asyncio.Event()
self.intellicode_enable_command_available = asyncio.Event()
self.initialize_searcher_command_available = asyncio.Event()
super().__init__(config, logger, repository_root_path, ProcessLaunchInfo(cmd, proc_env, proc_cwd), "java")
@override
def is_ignored_dirname(self, dirname: str) -> bool:
# Ignore common Java build directories from different build tools:
# - Maven: target
# - Gradle: build, .gradle
# - Eclipse: bin, .settings
# - IntelliJ IDEA: out, .idea
# - General: classes, dist, lib
return super().is_ignored_dirname(dirname) or dirname in [
"target", # Maven
"build", # Gradle
"bin", # Eclipse
"out", # IntelliJ IDEA
"classes", # General
"dist", # General
"lib" # General
]
def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> RuntimeDependencyPaths:
"""
Setup runtime dependencies for EclipseJDTLS.
"""
platformId = PlatformUtils.get_platform_id()
with open(str(PurePath(os.path.dirname(__file__), "runtime_dependencies.json")), "r", encoding="utf-8") as f:
runtimeDependencies = json.load(f)
del runtimeDependencies["_description"]
os.makedirs(str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static")), exist_ok=True)
# assert platformId.value in [
# "linux-x64",
# "win-x64",
# ], "Only linux-x64 platform is supported for in multilspy at the moment"
gradle_path = str(
PurePath(
os.path.abspath(os.path.dirname(__file__)),
"static/gradle-7.3.3",
)
)
if not os.path.exists(gradle_path):
FileUtils.download_and_extract_archive(
logger,
runtimeDependencies["gradle"]["platform-agnostic"]["url"],
str(PurePath(gradle_path).parent),
runtimeDependencies["gradle"]["platform-agnostic"]["archiveType"],
)
assert os.path.exists(gradle_path)
dependency = runtimeDependencies["vscode-java"][platformId.value]
vscode_java_path = str(
PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"])
)
os.makedirs(vscode_java_path, exist_ok=True)
jre_home_path = str(PurePath(vscode_java_path, dependency["jre_home_path"]))
jre_path = str(PurePath(vscode_java_path, dependency["jre_path"]))
lombok_jar_path = str(PurePath(vscode_java_path, dependency["lombok_jar_path"]))
jdtls_launcher_jar_path = str(PurePath(vscode_java_path, dependency["jdtls_launcher_jar_path"]))
jdtls_readonly_config_path = str(PurePath(vscode_java_path, dependency["jdtls_readonly_config_path"]))
if not all(
[
os.path.exists(vscode_java_path),
os.path.exists(jre_home_path),
os.path.exists(jre_path),
os.path.exists(lombok_jar_path),
os.path.exists(jdtls_launcher_jar_path),
os.path.exists(jdtls_readonly_config_path),
]
):
FileUtils.download_and_extract_archive(
logger, dependency["url"], vscode_java_path, dependency["archiveType"]
)
os.chmod(jre_path, stat.S_IEXEC)
assert os.path.exists(vscode_java_path)
assert os.path.exists(jre_home_path)
assert os.path.exists(jre_path)
assert os.path.exists(lombok_jar_path)
assert os.path.exists(jdtls_launcher_jar_path)
assert os.path.exists(jdtls_readonly_config_path)
dependency = runtimeDependencies["intellicode"]["platform-agnostic"]
intellicode_directory_path = str(
PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"])
)
os.makedirs(intellicode_directory_path, exist_ok=True)
intellicode_jar_path = str(PurePath(intellicode_directory_path, dependency["intellicode_jar_path"]))
intellisense_members_path = str(PurePath(intellicode_directory_path, dependency["intellisense_members_path"]))
if not all(
[
os.path.exists(intellicode_directory_path),
os.path.exists(intellicode_jar_path),
os.path.exists(intellisense_members_path),
]
):
FileUtils.download_and_extract_archive(
logger, dependency["url"], intellicode_directory_path, dependency["archiveType"]
)
assert os.path.exists(intellicode_directory_path)
assert os.path.exists(intellicode_jar_path)
assert os.path.exists(intellisense_members_path)
return RuntimeDependencyPaths(
gradle_path=gradle_path,
lombok_jar_path=lombok_jar_path,
jre_path=jre_path,
jre_home_path=jre_home_path,
jdtls_launcher_jar_path=jdtls_launcher_jar_path,
jdtls_readonly_config_path=jdtls_readonly_config_path,
intellicode_jar_path=intellicode_jar_path,
intellisense_members_path=intellisense_members_path,
)
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize parameters for the EclipseJDTLS server.
"""
# Look into https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java to understand all the options available
with open(str(PurePath(os.path.dirname(__file__), "initialize_params.json")), "r", encoding="utf-8") 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),
}
]
assert d["initializationOptions"]["bundles"] == ["intellicode-core.jar"]
bundles = [self.runtime_dependency_paths.intellicode_jar_path]
d["initializationOptions"]["bundles"] = bundles
assert d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] == [
{"name": "JavaSE-21", "path": "static/vscode-java/extension/jre/21.0.7-linux-x86_64", "default": True}
]
d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] = [
{"name": "JavaSE-21", "path": self.runtime_dependency_paths.jre_home_path, "default": True}
]
for runtime in d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"]:
assert "name" in runtime
assert "path" in runtime
assert os.path.exists(
runtime["path"]
), f"Runtime required for eclipse_jdtls at path {runtime['path']} does not exist"
assert d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["home"] == "abs(static/gradle-7.3.3)"
d["initializationOptions"]["settings"]["java"]["import"]["gradle"][
"home"
] = self.runtime_dependency_paths.gradle_path
d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["java"][
"home"
] = self.runtime_dependency_paths.jre_path
return d
@asynccontextmanager
async def start_server(self) -> AsyncIterator["EclipseJDTLS"]:
"""
Starts the Eclipse JDTLS 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"] == "textDocument/completion":
assert registration["registerOptions"]["resolveProvider"] == True
assert registration["registerOptions"]["triggerCharacters"] == [
".",
"@",
"#",
"*",
" ",
]
self.completions_available.set()
if registration["method"] == "workspace/executeCommand":
if "java.intellicode.enable" in registration["registerOptions"]["commands"]:
self.intellicode_enable_command_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):
assert params["command"] == "_java.reloadBundles.command"
assert params["arguments"] == []
return []
async def window_log_message(msg):
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
async def do_nothing(params):
return
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)
async with super().start_server():
self.logger.log("Starting EclipseJDTLS 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" not in init_response["capabilities"]
assert "executeCommandProvider" not in init_response["capabilities"]
self.server.notify.initialized({})
self.server.notify.workspace_did_change_configuration(
{"settings": initialize_params["initializationOptions"]["settings"]}
)
await self.intellicode_enable_command_available.wait()
java_intellisense_members_path = self.runtime_dependency_paths.intellisense_members_path
assert os.path.exists(java_intellisense_members_path)
intellicode_enable_result = await self.server.send.execute_command(
{
"command": "java.intellicode.enable",
"arguments": [True, java_intellisense_members_path],
}
)
assert intellicode_enable_result
# TODO: Add comments about why we wait here, and how this can be optimized
await self.service_ready_event.wait()
yield self
@@ -0,0 +1,849 @@
{
"_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": "Visual Studio Code - Insiders",
"version": "1.77.0-insider"
},
"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": {
"bundles": [
"intellicode-core.jar"
],
"workspaceFolders": "[pathlib.Path(repository_absolute_path).as_uri()]",
"settings": {
"java": {
"home": null,
"jdt": {
"ls": {
"java": {
"home": null
},
"vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -Xlog:disable",
"lombokSupport": {
"enabled": true
},
"protobufSupport": {
"enabled": true
},
"androidSupport": {
"enabled": true
}
}
},
"errors": {
"incompleteClasspath": {
"severity": "error"
}
},
"configuration": {
"checkProjectSettingsExclusions": false,
"updateBuildConfiguration": "interactive",
"maven": {
"userSettings": null,
"globalSettings": null,
"notCoveredPluginExecutionSeverity": "warning",
"defaultMojoExecutionAction": "ignore"
},
"workspaceCacheLimit": 90,
"runtimes": [
{
"name": "JavaSE-21",
"path": "static/vscode-java/extension/jre/21.0.7-linux-x86_64",
"default": true
}
]
},
"trace": {
"server": "verbose"
},
"import": {
"maven": {
"enabled": true,
"offline": {
"enabled": false
},
"disableTestClasspathFlag": false
},
"gradle": {
"enabled": true,
"wrapper": {
"enabled": true
},
"version": null,
"home": "abs(static/gradle-7.3.3)",
"java": {
"home": "abs(static/launch_jres/21.0.7-linux-x86_64)"
},
"offline": {
"enabled": false
},
"arguments": null,
"jvmArguments": null,
"user": {
"home": null
},
"annotationProcessing": {
"enabled": true
}
},
"exclusions": [
"**/node_modules/**",
"**/.metadata/**",
"**/archetype-resources/**",
"**/META-INF/maven/**"
],
"generatesMetadataFilesAtProjectRoot": false
},
"maven": {
"downloadSources": true,
"updateSnapshots": true
},
"eclipse": {
"downloadSources": true
},
"referencesCodeLens": {
"enabled": true
},
"signatureHelp": {
"enabled": true,
"description": {
"enabled": true
}
},
"implementationsCodeLens": {
"enabled": true
},
"format": {
"enabled": true,
"settings": {
"url": null,
"profile": null
},
"comments": {
"enabled": true
},
"onType": {
"enabled": true
},
"insertSpaces": true,
"tabSize": 4
},
"saveActions": {
"organizeImports": false
},
"project": {
"referencedLibraries": [
"lib/**/*.jar"
],
"importOnFirstTimeStartup": "automatic",
"importHint": true,
"resourceFilters": [
"node_modules",
"\\.git"
],
"encoding": "ignore",
"exportJar": {
"targetPath": "${workspaceFolder}/${workspaceFolderBasename}.jar"
}
},
"contentProvider": {
"preferred": null
},
"autobuild": {
"enabled": true
},
"maxConcurrentBuilds": 1,
"recommendations": {
"dependency": {
"analytics": {
"show": true
}
}
},
"completion": {
"maxResults": 0,
"enabled": true,
"guessMethodArguments": true,
"favoriteStaticMembers": [
"org.junit.Assert.*",
"org.junit.Assume.*",
"org.junit.jupiter.api.Assertions.*",
"org.junit.jupiter.api.Assumptions.*",
"org.junit.jupiter.api.DynamicContainer.*",
"org.junit.jupiter.api.DynamicTest.*",
"org.mockito.Mockito.*",
"org.mockito.ArgumentMatchers.*",
"org.mockito.Answers.*"
],
"filteredTypes": [
"java.awt.*",
"com.sun.*",
"sun.*",
"jdk.*",
"org.graalvm.*",
"io.micrometer.shaded.*"
],
"importOrder": [
"#",
"java",
"javax",
"org",
"com",
""
],
"postfix": {
"enabled": false
},
"matchCase": "off"
},
"foldingRange": {
"enabled": true
},
"progressReports": {
"enabled": false
},
"codeGeneration": {
"hashCodeEquals": {
"useJava7Objects": false,
"useInstanceof": false
},
"useBlocks": false,
"generateComments": false,
"toString": {
"template": "${object.className} [${member.name()}=${member.value}, ${otherMembers}]",
"codeStyle": "STRING_CONCATENATION",
"skipNullValues": false,
"listArrayContents": true,
"limitElements": 0
},
"insertionLocation": "afterCursor"
},
"selectionRange": {
"enabled": true
},
"showBuildStatusOnStart": {
"enabled": "notification"
},
"server": {
"launchMode": "Standard"
},
"sources": {
"organizeImports": {
"starThreshold": 99,
"staticStarThreshold": 99
}
},
"imports": {
"gradle": {
"wrapper": {
"checksums": []
}
}
},
"templates": {
"fileHeader": [],
"typeComment": []
},
"references": {
"includeAccessors": true,
"includeDecompiledSources": true
},
"typeHierarchy": {
"lazyLoad": false
},
"settings": {
"url": null
},
"symbols": {
"includeSourceMethodDeclarations": false
},
"quickfix": {
"showAt": "line"
},
"inlayHints": {
"parameterNames": {
"enabled": "literals",
"exclusions": []
}
},
"codeAction": {
"sortMembers": {
"avoidVolatileChanges": true
}
},
"compile": {
"nullAnalysis": {
"nonnull": [
"javax.annotation.Nonnull",
"org.eclipse.jdt.annotation.NonNull",
"org.springframework.lang.NonNull"
],
"nullable": [
"javax.annotation.Nullable",
"org.eclipse.jdt.annotation.Nullable",
"org.springframework.lang.Nullable"
],
"mode": "automatic"
}
},
"cleanup": {
"actionsOnSave": []
},
"sharedIndexes": {
"enabled": "auto",
"location": ""
},
"refactoring": {
"extract": {
"interface": {
"replace": true
}
}
},
"debug": {
"logLevel": "verbose",
"settings": {
"showHex": false,
"showStaticVariables": false,
"showQualifiedNames": false,
"showLogicalStructure": true,
"showToString": true,
"maxStringLength": 0,
"numericPrecision": 0,
"hotCodeReplace": "manual",
"enableRunDebugCodeLens": true,
"forceBuildBeforeLaunch": true,
"onBuildFailureProceed": false,
"console": "integratedTerminal",
"exceptionBreakpoint": {
"skipClasses": []
},
"stepping": {
"skipClasses": [],
"skipSynthetics": false,
"skipStaticInitializers": false,
"skipConstructors": false
},
"jdwp": {
"limitOfVariablesPerJdwpRequest": 100,
"requestTimeout": 3000,
"async": "auto"
},
"vmArgs": ""
}
},
"silentNotification": false,
"dependency": {
"showMembers": false,
"syncWithFolderExplorer": true,
"autoRefresh": true,
"refreshDelay": 2000,
"packagePresentation": "flat"
},
"help": {
"firstView": "auto",
"showReleaseNotes": true,
"collectErrorLog": false
},
"test": {
"defaultConfig": "",
"config": {}
}
}
},
"extendedClientCapabilities": {
"progressReportProvider": false,
"classFileContentsSupport": true,
"overrideMethodsPromptSupport": true,
"hashCodeEqualsPromptSupport": true,
"advancedOrganizeImportsSupport": true,
"generateToStringPromptSupport": true,
"advancedGenerateAccessorsSupport": true,
"generateConstructorsPromptSupport": true,
"generateDelegateMethodsPromptSupport": true,
"advancedExtractRefactoringSupport": true,
"inferSelectionSupport": [
"extractMethod",
"extractVariable",
"extractField"
],
"moveRefactoringSupport": true,
"clientHoverProvider": true,
"clientDocumentSymbolProvider": true,
"gradleChecksumWrapperPromptSupport": true,
"resolveAdditionalTextEditsSupport": true,
"advancedIntroduceParameterRefactoringSupport": true,
"actionableRuntimeNotificationSupport": true,
"shouldLanguageServerExitOnShutdown": true,
"onCompletionItemSelectedCommand": "editor.action.triggerParameterHints",
"extractInterfaceSupport": true,
"advancedUpgradeGradleSupport": true
},
"triggerFiles": []
},
"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,72 @@
{
"_description": "This file lists the runtime dependencies for the Java Language Server",
"gradle": {
"platform-agnostic": {
"url": "https://services.gradle.org/distributions/gradle-7.3.3-bin.zip",
"archiveType": "zip",
"relative_extraction_path": "."
}
},
"vscode-java": {
"darwin-arm64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-arm64-1.42.0-561.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java"
},
"osx-arm64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-arm64-1.42.0-561.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"jre_home_path": "extension/jre/21.0.7-macosx-aarch64",
"jre_path": "extension/jre/21.0.7-macosx-aarch64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jdtls_readonly_config_path": "extension/server/config_mac_arm"
},
"osx-x64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-x64-1.42.0-561.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"jre_home_path": "extension/jre/21.0.7-macosx-x86_64",
"jre_path": "extension/jre/21.0.7-macosx-x86_64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jdtls_readonly_config_path": "extension/server/config_mac"
},
"linux-arm64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-arm64-1.42.0-561.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java"
},
"linux-x64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-x64-1.42.0-561.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"jre_home_path": "extension/jre/21.0.7-linux-x86_64",
"jre_path": "extension/jre/21.0.7-linux-x86_64/bin/java",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jdtls_readonly_config_path": "extension/server/config_linux"
},
"win-x64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-win32-x64-1.42.0-561.vsix",
"archiveType": "zip",
"relative_extraction_path": "vscode-java",
"jre_home_path": "extension/jre/21.0.7-win32-x86_64",
"jre_path": "extension/jre/21.0.7-win32-x86_64/bin/java.exe",
"lombok_jar_path": "extension/lombok/lombok-1.18.36.jar",
"jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar",
"jdtls_readonly_config_path": "extension/server/config_win"
}
},
"intellicode": {
"platform-agnostic": {
"url": "https://VisualStudioExptTeam.gallery.vsassets.io/_apis/public/gallery/publisher/VisualStudioExptTeam/extension/vscodeintellicode/1.2.30/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage",
"alternate_url": "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioExptTeam/vsextensions/vscodeintellicode/1.2.30/vspackage",
"archiveType": "zip",
"relative_extraction_path": "intellicode",
"intellicode_jar_path": "extension/dist/com.microsoft.jdtls.intellicode.core-0.7.0.jar",
"intellisense_members_path": "extension/dist/bundledModels/java_intellisense-members"
}
}
}
@@ -0,0 +1,154 @@
import asyncio
import json
import logging
import os
import pathlib
import subprocess
from contextlib import asynccontextmanager
from typing import AsyncIterator, List
from overrides import override
from multilspy import multilspy_types
from multilspy.lsp_protocol_handler import lsp_types
from multilspy.multilspy_exceptions import MultilspyException
from multilspy.multilspy_logger import MultilspyLogger
from multilspy.language_server import LanguageServer
from multilspy.lsp_protocol_handler.server import Error, ProcessLaunchInfo
from multilspy.lsp_protocol_handler.lsp_types import InitializeParams
from multilspy.multilspy_config import MultilspyConfig
class Gopls(LanguageServer):
"""
Provides Go specific instantiation of the LanguageServer class using gopls.
"""
@override
def is_ignored_dirname(self, dirname: str) -> bool:
# For Go projects, we should ignore:
# - vendor: third-party dependencies vendored into the project
# - node_modules: if the project has JavaScript components
# - dist/build: common output directories
return super().is_ignored_dirname(dirname) or dirname in ["vendor", "node_modules", "dist", "build"]
@staticmethod
def _get_go_version():
"""Get the installed Go version or None if not found."""
try:
result = subprocess.run(['go', 'version'], capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip()
except FileNotFoundError:
return None
return None
@staticmethod
def _get_gopls_version():
"""Get the installed gopls version or None if not found."""
try:
result = subprocess.run(['gopls', 'version'], capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip()
except FileNotFoundError:
return None
return None
@classmethod
def setup_runtime_dependency(cls):
"""
Check if required Go runtime dependencies are available.
Raises RuntimeError with helpful message if dependencies are missing.
"""
go_version = cls._get_go_version()
if not go_version:
raise RuntimeError("Go is not installed. Please install Go from https://golang.org/doc/install and make sure it is added to your PATH.")
gopls_version = cls._get_gopls_version()
if not gopls_version:
raise RuntimeError(
"Found a Go version but gopls is not installed.\n"
"Please install gopls as described in https://pkg.go.dev/golang.org/x/tools/gopls#section-readme\n\n"
"After installation, make sure it is added to your PATH (it might be installed in a different location than Go)."
)
return True
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
self.setup_runtime_dependency()
super().__init__(
config,
logger,
repository_root_path,
ProcessLaunchInfo(cmd="gopls", cwd=repository_root_path),
"go",
)
self.server_ready = asyncio.Event()
self.request_id = 0
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the TypeScript Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") 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["Gopls"]:
"""Start gopls server process"""
async def register_capability_handler(params):
return
async def window_log_message(msg):
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
async def do_nothing(params):
return
self.server.on_request("client/registerCapability", register_capability_handler)
self.server.on_notification("window/logMessage", window_log_message)
self.server.on_notification("$/progress", do_nothing)
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
async with super().start_server():
self.logger.log("Starting gopls 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)
# Verify server capabilities
assert "textDocumentSync" in init_response["capabilities"]
assert "completionProvider" in init_response["capabilities"]
assert "definitionProvider" in init_response["capabilities"]
self.server.notify.initialized({})
self.completions_available.set()
# gopls server is typically ready immediately after initialization
self.server_ready.set()
await self.server_ready.wait()
yield self
@@ -0,0 +1,46 @@
{
"_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
},
"documentSymbol": {
"dynamicRegistration": true,
"hierarchicalDocumentSymbolSupport": 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
]
}
}
},
"workspace": {
"workspaceFolders": true,
"didChangeConfiguration": {
"dynamicRegistration": true
}
}
},
"workspaceFolders": [
{
"uri": "$uri",
"name": "$name"
}
]
}
@@ -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,200 @@
"""
Provides PHP specific instantiation of the LanguageServer class using Intelephense.
"""
import asyncio
import json
import shutil
import logging
import os
import subprocess
import pathlib
from contextlib import asynccontextmanager
from time import sleep
from typing import AsyncIterator
from overrides import override
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 DefinitionParams, InitializeParams
from multilspy.multilspy_config import MultilspyConfig
from multilspy.multilspy_utils import PlatformUtils, PlatformId
class Intelephense(LanguageServer):
"""
Provides PHP specific instantiation of the LanguageServer class using Intelephense.
"""
@override
def is_ignored_dirname(self, dirname: str) -> bool:
# For PHP projects, we should ignore:
# - vendor: third-party dependencies managed by Composer
# - node_modules: if the project has JavaScript components
# - cache: commonly used for caching
return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "cache"]
def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str:
"""
Setup runtime dependencies for Intelephense.
"""
platform_id = PlatformUtils.get_platform_id()
valid_platforms = [
PlatformId.LINUX_x64,
PlatformId.LINUX_arm64,
PlatformId.OSX,
PlatformId.OSX_x64,
PlatformId.OSX_arm64,
PlatformId.WIN_x64,
PlatformId.WIN_arm64,
]
assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy PHP at the moment"
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f:
d = json.load(f)
del d["_description"]
runtime_dependencies = d.get("runtimeDependencies", [])
intelephense_ls_dir = os.path.join(os.path.dirname(__file__), "static", "php-lsp")
# Verify both node and npm are installed
is_node_installed = shutil.which('node') is not None
assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again."
is_npm_installed = shutil.which('npm') is not None
assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again."
# Install intelephense if not already installed
if not os.path.exists(intelephense_ls_dir):
os.makedirs(intelephense_ls_dir, exist_ok=True)
for dependency in runtime_dependencies:
# 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=intelephense_ls_dir,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
else:
# On Unix-like systems, run as non-root user
import pwd
user = pwd.getpwuid(os.getuid()).pw_name
subprocess.run(
dependency["command"],
shell=True,
check=True,
user=user,
cwd=intelephense_ls_dir,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
intelephense_executable_path = os.path.join(intelephense_ls_dir, "node_modules", ".bin", "intelephense")
assert os.path.exists(intelephense_executable_path), "intelephense executable not found. Please install intelephense and try again."
return f"{intelephense_executable_path} --stdio"
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
# Setup runtime dependencies before initializing
intelephense_cmd = self.setup_runtime_dependencies(logger, config)
super().__init__(
config,
logger,
repository_root_path,
ProcessLaunchInfo(cmd=intelephense_cmd, cwd=repository_root_path),
"php"
)
self.server_ready = asyncio.Event()
self.request_id = 0
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the TypeScript Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") 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["Intelephense"]:
"""Start Intelephense server process"""
async def register_capability_handler(params):
return
async def window_log_message(msg):
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
async def do_nothing(params):
return
self.server.on_request("client/registerCapability", register_capability_handler)
self.server.on_notification("window/logMessage", window_log_message)
self.server.on_notification("$/progress", do_nothing)
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
async with super().start_server():
self.logger.log("Starting Intelephense 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)
self.logger.log(
"After sent initialize params",
logging.INFO,
)
# Verify server capabilities
assert "textDocumentSync" in init_response["capabilities"]
assert "completionProvider" in init_response["capabilities"]
assert "definitionProvider" in init_response["capabilities"]
self.server.notify.initialized({})
self.completions_available.set()
# Intelephense server is typically ready immediately after initialization
self.server_ready.set()
await self.server_ready.wait()
yield self
@override
# For some reason, the LS may need longer to process this, so we just retry
async def _send_references_request(self, relative_file_path: str, line: int, column: int):
# TODO: The LS doesn't return references contained in other files if it doesn't sleep. This is
# despite the LS having processed requests already. I don't know what causes this, but sleeping
# one second helps. It may be that sleeping only once is enough but that's hard to reliably test.
# May be related to the time it takes to read the files or something like that.
# The sleeping doesn't seem to be needed on all systems
sleep(1)
return await super()._send_references_request(relative_file_path, line, column)
@override
async def _send_definition_request(self, definition_params: DefinitionParams):
# TODO: same as above, also only a problem if the definition is in another file
sleep(1)
return await super()._send_definition_request(definition_params)
@@ -0,0 +1,10 @@
{
"_description": "Used to download the runtime dependencies for running intelephense. Obtained from https://www.npmjs.com/package/intelephense",
"runtimeDependencies": [
{
"id": "intelephense",
"description": "Intelephense package for Linux, OSX, and Windows. Both x64 and arm64 are supported.",
"command": "npm install --prefix ./ intelephense@1.14.4"
}
]
}
@@ -0,0 +1,905 @@
{
"_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": "Visual Studio Code - Insiders",
"version": "1.81.0-insider"
},
"locale": "en",
"rootPath": "$rootPath",
"rootUri": "$rootUri",
"capabilities": {
"workspace": {
"applyEdit": true,
"workspaceEdit": {
"documentChanges": true,
"resourceOperations": [
"create",
"rename",
"delete"
],
"failureHandling": "textOnlyTransactional",
"normalizesLineEndings": true,
"changeAnnotationSupport": {
"groupsOnLabel": true
}
},
"configuration": 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
},
"didChangeConfiguration": {
"dynamicRegistration": 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": true,
"commitCharactersSupport": true,
"documentationFormat": [
"markdown",
"plaintext"
],
"deprecatedSupport": true,
"preselectSupport": true,
"tagSupport": {
"valueSet": [
1
]
},
"insertReplaceSupport": true,
"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,
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": false
},
"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",
"allowedTags": [
"ul",
"li",
"p",
"code",
"blockquote",
"ol",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"hr",
"em",
"pre",
"table",
"thead",
"tbody",
"tr",
"th",
"td",
"div",
"del",
"a",
"strong",
"br",
"img",
"span"
]
},
"positionEncodings": [
"utf-16"
]
},
"notebookDocument": {
"synchronization": {
"dynamicRegistration": true,
"executionSummarySupport": true
}
},
"experimental": {
"snippetTextEdit": true,
"codeActionGroup": true,
"hoverActions": true,
"serverStatusNotification": true,
"colorDiagnosticOutput": true,
"openServerLogs": true,
"commands": {
"commands": [
"rust-analyzer.runSingle",
"rust-analyzer.debugSingle",
"rust-analyzer.showReferences",
"rust-analyzer.gotoLocation",
"editor.action.triggerParameterHints"
]
}
}
},
"initializationOptions": {
"cargoRunner": null,
"runnables": {
"extraEnv": null,
"problemMatcher": [
"$rustc"
],
"command": null,
"extraArgs": []
},
"server": {
"path": null,
"extraEnv": null
},
"trace": {
"server": "verbose",
"extension": true
},
"debug": {
"engine": "auto",
"sourceFileMap": {
"/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
},
"openDebugPane": false,
"engineSettings": {}
},
"restartServerOnConfigChange": false,
"typing": {
"continueCommentsOnNewline": true,
"autoClosingAngleBrackets": {
"enable": false
}
},
"diagnostics": {
"previewRustcOutput": false,
"useRustcErrorCode": false,
"disabled": [],
"enable": true,
"experimental": {
"enable": false
},
"remapPrefix": {},
"warningsAsHint": [],
"warningsAsInfo": []
},
"discoverProjectCommand": null,
"showUnlinkedFileNotification": true,
"showDependenciesExplorer": true,
"assist": {
"emitMustUse": false,
"expressionFillDefault": "todo"
},
"cachePriming": {
"enable": true,
"numThreads": 0
},
"cargo": {
"autoreload": true,
"buildScripts": {
"enable": true,
"invocationLocation": "workspace",
"invocationStrategy": "per_workspace",
"overrideCommand": null,
"useRustcWrapper": true
},
"cfgs": {},
"extraArgs": [],
"extraEnv": {},
"features": [],
"noDefaultFeatures": false,
"sysroot": "discover",
"sysrootSrc": null,
"target": null,
"unsetTest": [
"core"
]
},
"checkOnSave": true,
"check": {
"allTargets": true,
"command": "check",
"extraArgs": [],
"extraEnv": {},
"features": null,
"invocationLocation": "workspace",
"invocationStrategy": "per_workspace",
"noDefaultFeatures": null,
"overrideCommand": null,
"targets": null
},
"completion": {
"autoimport": {
"enable": true
},
"autoself": {
"enable": true
},
"callable": {
"snippets": "fill_arguments"
},
"limit": null,
"postfix": {
"enable": true
},
"privateEditable": {
"enable": false
},
"snippets": {
"custom": {
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"scope": "expr"
},
"Box::pin": {
"postfix": "pinbox",
"body": "Box::pin(${receiver})",
"requires": "std::boxed::Box",
"description": "Put the expression into a pinned `Box`",
"scope": "expr"
},
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"scope": "expr"
},
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"scope": "expr"
},
"Some": {
"postfix": "some",
"body": "Some(${receiver})",
"description": "Wrap the expression in an `Option::Some`",
"scope": "expr"
}
}
}
},
"files": {
"excludeDirs": [],
"watcher": "client"
},
"highlightRelated": {
"breakPoints": {
"enable": true
},
"closureCaptures": {
"enable": true
},
"exitPoints": {
"enable": true
},
"references": {
"enable": true
},
"yieldPoints": {
"enable": true
}
},
"hover": {
"actions": {
"debug": {
"enable": true
},
"enable": true,
"gotoTypeDef": {
"enable": true
},
"implementations": {
"enable": true
},
"references": {
"enable": false
},
"run": {
"enable": true
}
},
"documentation": {
"enable": true,
"keywords": {
"enable": true
}
},
"links": {
"enable": true
},
"memoryLayout": {
"alignment": "hexadecimal",
"enable": true,
"niches": false,
"offset": "hexadecimal",
"size": "both"
}
},
"imports": {
"granularity": {
"enforce": false,
"group": "crate"
},
"group": {
"enable": true
},
"merge": {
"glob": true
},
"prefer": {
"no": {
"std": false
}
},
"prefix": "plain"
},
"inlayHints": {
"bindingModeHints": {
"enable": false
},
"chainingHints": {
"enable": true
},
"closingBraceHints": {
"enable": true,
"minLines": 25
},
"closureCaptureHints": {
"enable": false
},
"closureReturnTypeHints": {
"enable": "never"
},
"closureStyle": "impl_fn",
"discriminantHints": {
"enable": "never"
},
"expressionAdjustmentHints": {
"enable": "never",
"hideOutsideUnsafe": false,
"mode": "prefix"
},
"lifetimeElisionHints": {
"enable": "never",
"useParameterNames": false
},
"maxLength": 25,
"parameterHints": {
"enable": true
},
"reborrowHints": {
"enable": "never"
},
"renderColons": true,
"typeHints": {
"enable": true,
"hideClosureInitialization": false,
"hideNamedConstructor": false
}
},
"interpret": {
"tests": false
},
"joinLines": {
"joinAssignments": true,
"joinElseIf": true,
"removeTrailingComma": true,
"unwrapTrivialBlock": true
},
"lens": {
"debug": {
"enable": true
},
"enable": true,
"forceCustomCommands": true,
"implementations": {
"enable": true
},
"location": "above_name",
"references": {
"adt": {
"enable": false
},
"enumVariant": {
"enable": false
},
"method": {
"enable": false
},
"trait": {
"enable": false
}
},
"run": {
"enable": true
}
},
"linkedProjects": [],
"lru": {
"capacity": null,
"query": {
"capacities": {}
}
},
"notifications": {
"cargoTomlNotFound": true
},
"numThreads": null,
"procMacro": {
"attributes": {
"enable": true
},
"enable": true,
"ignored": {},
"server": null
},
"references": {
"excludeImports": false
},
"rustc": {
"source": null
},
"rustfmt": {
"extraArgs": [],
"overrideCommand": null,
"rangeFormatting": {
"enable": false
}
},
"semanticHighlighting": {
"doc": {
"comment": {
"inject": {
"enable": true
}
}
},
"nonStandardTokens": true,
"operator": {
"enable": true,
"specialization": {
"enable": false
}
},
"punctuation": {
"enable": false,
"separate": {
"macro": {
"bang": false
}
},
"specialization": {
"enable": false
}
},
"strings": {
"enable": true
}
},
"signatureInfo": {
"detail": "full",
"documentation": {
"enable": true
}
},
"workspace": {
"symbols": {
"maxSymbols": 0
}
}
},
"trace": "verbose",
"workspaceFolders": [
{
"uri": "$uri",
"name": "$name"
}
]
}
@@ -0,0 +1,123 @@
"""
Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python.
"""
import json
import logging
import os
import pathlib
from contextlib import asynccontextmanager
from typing import AsyncIterator, Tuple
from overrides import override
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
class JediServer(LanguageServer):
"""
Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python.
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates a JediServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
"""
super().__init__(
config,
logger,
repository_root_path,
ProcessLaunchInfo(cmd="jedi-language-server", cwd=repository_root_path),
"python",
)
@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"]
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the Jedi Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") 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["JediServer"]:
"""
Starts the JEDI 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 check_experimental_status(params):
if params["quiescent"] == True:
self.completions_available.set()
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 jedi-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 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": True,
}
self.server.notify.initialized({})
yield self
@@ -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,227 @@
"""
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", encoding="utf-8") 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", encoding="utf-8") 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
@@ -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.42.0/java-win32-x64-1.42.0-561.vsix",
"archiveType": "zip",
"java_home_path": "extension/jre/21.0.7-win32-x86_64",
"java_path": "extension/jre/21.0.7-win32-x86_64/bin/java.exe"
},
"linux-x64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-x64-1.42.0-561.vsix",
"archiveType": "zip",
"java_home_path": "extension/jre/21.0.7-linux-x86_64",
"java_path": "extension/jre/21.0.7-linux-x86_64/bin/java"
},
"linux-arm64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-arm64-1.42.0-561.vsix",
"archiveType": "zip",
"java_home_path": "extension/jre/21.0.7-linux-aarch64",
"java_path": "extension/jre/21.0.7-linux-aarch64/bin/java"
},
"osx-x64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-x64-1.42.0-561.vsix",
"archiveType": "zip",
"java_home_path": "extension/jre/21.0.7-macosx-x86_64",
"java_path": "extension/jre/21.0.7-macosx-x86_64/bin/java"
},
"osx-arm64": {
"url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-arm64-1.42.0-561.vsix",
"archiveType": "zip",
"java_home_path": "extension/jre/21.0.7-macosx-aarch64",
"java_path": "extension/jre/21.0.7-macosx-aarch64/bin/java"
}
}
}
@@ -0,0 +1,631 @@
{
"_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": "Visual Studio Code - Insiders",
"version": "1.82.0-insider"
},
"locale": "en",
"rootPath": "$rootPath",
"rootUri": "$rootUri",
"capabilities": {
"workspace": {
"applyEdit": true,
"workspaceEdit": {
"documentChanges": true,
"resourceOperations": [
"create",
"rename",
"delete"
],
"failureHandling": "textOnlyTransactional",
"normalizesLineEndings": true,
"changeAnnotationSupport": {
"groupsOnLabel": true
}
},
"configuration": false,
"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
},
"didChangeConfiguration": {
"dynamicRegistration": 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": true,
"commitCharactersSupport": true,
"documentationFormat": [
"markdown",
"plaintext"
],
"deprecatedSupport": true,
"preselectSupport": true,
"tagSupport": {
"valueSet": [
1
]
},
"insertReplaceSupport": true,
"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,
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": false
},
"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",
"allowedTags": [
"ul",
"li",
"p",
"code",
"blockquote",
"ol",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"hr",
"em",
"pre",
"table",
"thead",
"tbody",
"tr",
"th",
"td",
"div",
"del",
"a",
"strong",
"br",
"img",
"span"
]
},
"positionEncodings": [
"utf-16"
]
},
"notebookDocument": {
"synchronization": {
"dynamicRegistration": true,
"executionSummarySupport": true
}
},
"experimental": {
"snippetTextEdit": true,
"codeActionGroup": true,
"hoverActions": true,
"serverStatusNotification": true,
"colorDiagnosticOutput": true,
"openServerLogs": true,
"commands": {
"commands": [
"editor.action.triggerParameterHints"
]
}
}
},
"initializationOptions": {
"RoslynExtensionsOptions": {
"EnableDecompilationSupport": false,
"EnableAnalyzersSupport": true,
"EnableImportCompletion": true,
"EnableAsyncCompletion": false,
"DocumentAnalysisTimeoutMs": 30000,
"DiagnosticWorkersThreadCount": 18,
"AnalyzeOpenDocumentsOnly": true,
"InlayHintsOptions": {
"EnableForParameters": false,
"ForLiteralParameters": false,
"ForIndexerParameters": false,
"ForObjectCreationParameters": false,
"ForOtherParameters": false,
"SuppressForParametersThatDifferOnlyBySuffix": false,
"SuppressForParametersThatMatchMethodIntent": false,
"SuppressForParametersThatMatchArgumentName": false,
"EnableForTypes": false,
"ForImplicitVariableTypes": false,
"ForLambdaParameterTypes": false,
"ForImplicitObjectCreation": false
},
"LocationPaths": null
},
"FormattingOptions": {
"OrganizeImports": false,
"EnableEditorConfigSupport": true,
"NewLine": "\n",
"UseTabs": false,
"TabSize": 4,
"IndentationSize": 4,
"SpacingAfterMethodDeclarationName": false,
"SeparateImportDirectiveGroups": false,
"SpaceWithinMethodDeclarationParenthesis": false,
"SpaceBetweenEmptyMethodDeclarationParentheses": false,
"SpaceAfterMethodCallName": false,
"SpaceWithinMethodCallParentheses": false,
"SpaceBetweenEmptyMethodCallParentheses": false,
"SpaceAfterControlFlowStatementKeyword": true,
"SpaceWithinExpressionParentheses": false,
"SpaceWithinCastParentheses": false,
"SpaceWithinOtherParentheses": false,
"SpaceAfterCast": false,
"SpaceBeforeOpenSquareBracket": false,
"SpaceBetweenEmptySquareBrackets": false,
"SpaceWithinSquareBrackets": false,
"SpaceAfterColonInBaseTypeDeclaration": true,
"SpaceAfterComma": true,
"SpaceAfterDot": false,
"SpaceAfterSemicolonsInForStatement": true,
"SpaceBeforeColonInBaseTypeDeclaration": true,
"SpaceBeforeComma": false,
"SpaceBeforeDot": false,
"SpaceBeforeSemicolonsInForStatement": false,
"SpacingAroundBinaryOperator": "single",
"IndentBraces": false,
"IndentBlock": true,
"IndentSwitchSection": true,
"IndentSwitchCaseSection": true,
"IndentSwitchCaseSectionWhenBlock": true,
"LabelPositioning": "oneLess",
"WrappingPreserveSingleLine": true,
"WrappingKeepStatementsOnSingleLine": true,
"NewLinesForBracesInTypes": true,
"NewLinesForBracesInMethods": true,
"NewLinesForBracesInProperties": true,
"NewLinesForBracesInAccessors": true,
"NewLinesForBracesInAnonymousMethods": true,
"NewLinesForBracesInControlBlocks": true,
"NewLinesForBracesInAnonymousTypes": true,
"NewLinesForBracesInObjectCollectionArrayInitializers": true,
"NewLinesForBracesInLambdaExpressionBody": true,
"NewLineForElse": true,
"NewLineForCatch": true,
"NewLineForFinally": true,
"NewLineForMembersInObjectInit": true,
"NewLineForMembersInAnonymousTypes": true,
"NewLineForClausesInQuery": true
},
"FileOptions": {
"SystemExcludeSearchPatterns": [
"**/node_modules/**/*",
"**/bin/**/*",
"**/obj/**/*",
"**/.git/**/*",
"**/.git",
"**/.svn",
"**/.hg",
"**/CVS",
"**/.DS_Store",
"**/Thumbs.db"
],
"ExcludeSearchPatterns": []
},
"RenameOptions": {
"RenameOverloads": false,
"RenameInStrings": false,
"RenameInComments": false
},
"ImplementTypeOptions": {
"InsertionBehavior": 0,
"PropertyGenerationBehavior": 0
},
"DotNetCliOptions": {
"LocationPaths": null
},
"Plugins": {
"LocationPaths": null
}
},
"trace": "verbose",
"workspaceFolders": [
{
"uri": "$uri",
"name": "$name"
}
]
}
@@ -0,0 +1,406 @@
"""
Provides C# specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C#.
"""
import asyncio
import json
import logging
import os
import pathlib
import stat
from contextlib import asynccontextmanager
from typing import AsyncIterator, Iterable
from overrides import override
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_exceptions import MultilspyException
from multilspy.multilspy_utils import FileUtils, PlatformUtils, PlatformId, DotnetVersion
def breadth_first_file_scan(root) -> Iterable[str]:
"""
This function was obtained from https://stackoverflow.com/questions/49654234/is-there-a-breadth-first-search-option-available-in-os-walk-or-equivalent-py
It traverses the directory tree in breadth first order.
"""
dirs = [root]
# while we has dirs to scan
while len(dirs):
next_dirs = []
for parent in dirs:
# scan each dir
for f in os.listdir(parent):
# if there is a dir, then save for next ittr
# if it is a file then yield it (we'll return later)
ff = os.path.join(parent, f)
if os.path.isdir(ff):
next_dirs.append(ff)
else:
yield ff
# once we've done all the current dirs then
# we set up the next itter as the child dirs
# from the current itter.
dirs = next_dirs
def find_least_depth_sln_file(root_dir) -> str | None:
for filename in breadth_first_file_scan(root_dir):
if filename.endswith(".sln"):
return filename
return None
class OmniSharp(LanguageServer):
"""
Provides C# specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C#.
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates an OmniSharp instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
"""
omnisharp_executable_path, dll_path = self.setupRuntimeDependencies(logger, config)
slnfilename = find_least_depth_sln_file(repository_root_path)
if slnfilename is None:
logger.log("No *.sln file found in repository", logging.ERROR)
raise MultilspyException("No SLN file found in repository")
cmd = " ".join(
[
omnisharp_executable_path,
"-lsp",
"--encoding",
"ascii",
"-z",
"-s",
f'"{slnfilename}"',
"--hostPID",
str(os.getpid()),
"DotNet:enablePackageRestore=false",
"--loglevel",
"trace",
"--plugin",
dll_path,
"FileOptions:SystemExcludeSearchPatterns:0=**/.git",
"FileOptions:SystemExcludeSearchPatterns:1=**/.svn",
"FileOptions:SystemExcludeSearchPatterns:2=**/.hg",
"FileOptions:SystemExcludeSearchPatterns:3=**/CVS",
"FileOptions:SystemExcludeSearchPatterns:4=**/.DS_Store",
"FileOptions:SystemExcludeSearchPatterns:5=**/Thumbs.db",
"RoslynExtensionsOptions:EnableAnalyzersSupport=true",
"FormattingOptions:EnableEditorConfigSupport=true",
"RoslynExtensionsOptions:EnableImportCompletion=true",
"Sdk:IncludePrereleases=true",
"RoslynExtensionsOptions:AnalyzeOpenDocumentsOnly=true",
"formattingOptions:useTabs=false",
"formattingOptions:tabSize=4",
"formattingOptions:indentationSize=4",
]
)
super().__init__(
config, logger, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp"
)
self.definition_available = asyncio.Event()
self.references_available = asyncio.Event()
@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj"]
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the Omnisharp Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") 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
def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> tuple[str, str]:
"""
Setup runtime dependencies for OmniSharp.
"""
platform_id = PlatformUtils.get_platform_id()
dotnet_version = PlatformUtils.get_dotnet_version()
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f:
d = json.load(f)
del d["_description"]
assert platform_id in [
PlatformId.LINUX_x64,
PlatformId.WIN_x64,
], "Only linux-x64 and win-x64 platform is supported for in multilspy at the moment"
assert dotnet_version in [
DotnetVersion.V6,
DotnetVersion.V7,
DotnetVersion.V8
], "Only dotnet version 6 and 7 are supported in multilspy at the moment"
# TODO: Do away with this assumption
# Currently, runtime binaries are not available for .Net 7 and .Net 8. Hence, we assume .Net 6 runtime binaries to be compatible with .Net 7, .Net 8
if dotnet_version in [DotnetVersion.V7, DotnetVersion.V8]:
dotnet_version = DotnetVersion.V6
runtime_dependencies = d["runtimeDependencies"]
runtime_dependencies = [
dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value
]
runtime_dependencies = [
dependency
for dependency in runtime_dependencies
if not ("dotnet_version" in dependency) or dependency["dotnet_version"] == dotnet_version.value
]
assert len(runtime_dependencies) == 2
runtime_dependencies = {
runtime_dependencies[0]["id"]: runtime_dependencies[0],
runtime_dependencies[1]["id"]: runtime_dependencies[1],
}
assert "OmniSharp" in runtime_dependencies
assert "RazorOmnisharp" in runtime_dependencies
omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "OmniSharp")
if not os.path.exists(omnisharp_ls_dir):
os.makedirs(omnisharp_ls_dir)
FileUtils.download_and_extract_archive(
logger, runtime_dependencies["OmniSharp"]["url"], omnisharp_ls_dir, "zip"
)
omnisharp_executable_path = os.path.join(omnisharp_ls_dir, runtime_dependencies["OmniSharp"]["binaryName"])
assert os.path.exists(omnisharp_executable_path)
os.chmod(omnisharp_executable_path, stat.S_IEXEC)
razor_omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "RazorOmnisharp")
if not os.path.exists(razor_omnisharp_ls_dir):
os.makedirs(razor_omnisharp_ls_dir)
FileUtils.download_and_extract_archive(
logger, runtime_dependencies["RazorOmnisharp"]["url"], razor_omnisharp_ls_dir, "zip"
)
razor_omnisharp_dll_path = os.path.join(
razor_omnisharp_ls_dir, runtime_dependencies["RazorOmnisharp"]["dll_path"]
)
assert os.path.exists(razor_omnisharp_dll_path)
return omnisharp_executable_path, razor_omnisharp_dll_path
@asynccontextmanager
async def start_server(self) -> AsyncIterator["OmniSharp"]:
"""
Starts the Omnisharp 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"] == "textDocument/definition":
self.definition_available.set()
if registration["method"] == "textDocument/references":
self.references_available.set()
if registration["method"] == "textDocument/completion":
self.completions_available.set()
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()
pass
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)
async def workspace_configuration_handler(params):
# TODO: We do not know the appropriate way to handle this request. Should ideally contact the OmniSharp dev team
return [
{
"RoslynExtensionsOptions": {
"EnableDecompilationSupport": False,
"EnableAnalyzersSupport": True,
"EnableImportCompletion": True,
"EnableAsyncCompletion": False,
"DocumentAnalysisTimeoutMs": 30000,
"DiagnosticWorkersThreadCount": 18,
"AnalyzeOpenDocumentsOnly": True,
"InlayHintsOptions": {
"EnableForParameters": False,
"ForLiteralParameters": False,
"ForIndexerParameters": False,
"ForObjectCreationParameters": False,
"ForOtherParameters": False,
"SuppressForParametersThatDifferOnlyBySuffix": False,
"SuppressForParametersThatMatchMethodIntent": False,
"SuppressForParametersThatMatchArgumentName": False,
"EnableForTypes": False,
"ForImplicitVariableTypes": False,
"ForLambdaParameterTypes": False,
"ForImplicitObjectCreation": False,
},
"LocationPaths": None,
},
"FormattingOptions": {
"OrganizeImports": False,
"EnableEditorConfigSupport": True,
"NewLine": "\n",
"UseTabs": False,
"TabSize": 4,
"IndentationSize": 4,
"SpacingAfterMethodDeclarationName": False,
"SeparateImportDirectiveGroups": False,
"SpaceWithinMethodDeclarationParenthesis": False,
"SpaceBetweenEmptyMethodDeclarationParentheses": False,
"SpaceAfterMethodCallName": False,
"SpaceWithinMethodCallParentheses": False,
"SpaceBetweenEmptyMethodCallParentheses": False,
"SpaceAfterControlFlowStatementKeyword": True,
"SpaceWithinExpressionParentheses": False,
"SpaceWithinCastParentheses": False,
"SpaceWithinOtherParentheses": False,
"SpaceAfterCast": False,
"SpaceBeforeOpenSquareBracket": False,
"SpaceBetweenEmptySquareBrackets": False,
"SpaceWithinSquareBrackets": False,
"SpaceAfterColonInBaseTypeDeclaration": True,
"SpaceAfterComma": True,
"SpaceAfterDot": False,
"SpaceAfterSemicolonsInForStatement": True,
"SpaceBeforeColonInBaseTypeDeclaration": True,
"SpaceBeforeComma": False,
"SpaceBeforeDot": False,
"SpaceBeforeSemicolonsInForStatement": False,
"SpacingAroundBinaryOperator": "single",
"IndentBraces": False,
"IndentBlock": True,
"IndentSwitchSection": True,
"IndentSwitchCaseSection": True,
"IndentSwitchCaseSectionWhenBlock": True,
"LabelPositioning": "oneLess",
"WrappingPreserveSingleLine": True,
"WrappingKeepStatementsOnSingleLine": True,
"NewLinesForBracesInTypes": True,
"NewLinesForBracesInMethods": True,
"NewLinesForBracesInProperties": True,
"NewLinesForBracesInAccessors": True,
"NewLinesForBracesInAnonymousMethods": True,
"NewLinesForBracesInControlBlocks": True,
"NewLinesForBracesInAnonymousTypes": True,
"NewLinesForBracesInObjectCollectionArrayInitializers": True,
"NewLinesForBracesInLambdaExpressionBody": True,
"NewLineForElse": True,
"NewLineForCatch": True,
"NewLineForFinally": True,
"NewLineForMembersInObjectInit": True,
"NewLineForMembersInAnonymousTypes": True,
"NewLineForClausesInQuery": True,
},
"FileOptions": {
"SystemExcludeSearchPatterns": [
"**/node_modules/**/*",
"**/bin/**/*",
"**/obj/**/*",
"**/.git/**/*",
"**/.git",
"**/.svn",
"**/.hg",
"**/CVS",
"**/.DS_Store",
"**/Thumbs.db",
],
"ExcludeSearchPatterns": [],
},
"RenameOptions": {
"RenameOverloads": False,
"RenameInStrings": False,
"RenameInComments": False,
},
"ImplementTypeOptions": {
"InsertionBehavior": 0,
"PropertyGenerationBehavior": 0,
},
"DotNetCliOptions": {"LocationPaths": None},
"Plugins": {"LocationPaths": None},
}
]
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)
self.server.on_request("workspace/configuration", workspace_configuration_handler)
async with super().start_server():
self.logger.log("Starting OmniSharp 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)
self.server.notify.initialized({})
with open(os.path.join(os.path.dirname(__file__), "workspace_did_change_configuration.json"), "r", encoding="utf-8") as f:
self.server.notify.workspace_did_change_configuration({
"settings": json.load(f)
})
assert "capabilities" in init_response
if (
"definitionProvider" in init_response["capabilities"]
and init_response["capabilities"]["definitionProvider"]
):
self.definition_available.set()
if (
"referencesProvider" in init_response["capabilities"]
and init_response["capabilities"]["referencesProvider"]
):
self.references_available.set()
await self.definition_available.wait()
await self.references_available.wait()
yield self
@@ -0,0 +1,441 @@
{
"_description": "Used to download the runtime dependencies for running OmniSharp. Obtained from https://github.com/dotnet/vscode-csharp/blob/main/package.json",
"runtimeDependencies": [
{
"id": "OmniSharp",
"description": "OmniSharp for Windows (.NET 4 / x86)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x86-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"win32"
],
"architectures": [
"x86"
],
"installTestPath": "./.omnisharp/1.39.10/OmniSharp.exe",
"platformId": "win-x86",
"isFramework": true,
"integrity": "C81CE2099AD494EF63F9D88FAA70D55A68CF175810F944526FF94AAC7A5109F9",
"dotnet_version": "4",
"binaryName": "OmniSharp.exe"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Windows (.NET 6 / x86)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x86-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"win32"
],
"architectures": [
"x86"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "win-x86",
"isFramework": false,
"integrity": "B7E62415CFC3DAC2154AC636C5BF0FB4B2C9BBF11B5A1FBF72381DDDED59791E",
"dotnet_version": "6",
"binaryName": "OmniSharp.exe"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Windows (.NET 4 / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x64-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"win32"
],
"architectures": [
"x86_64"
],
"installTestPath": "./.omnisharp/1.39.10/OmniSharp.exe",
"platformId": "win-x64",
"isFramework": true,
"integrity": "BE0ED10AACEA17E14B78BD0D887DE5935D4ECA3712192A701F3F2100CA3C8B6E",
"dotnet_version": "4",
"binaryName": "OmniSharp.exe"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Windows (.NET 6 / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"win32"
],
"architectures": [
"x86_64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "win-x64",
"isFramework": false,
"integrity": "A73327395E7EF92C1D8E307055463DA412662C03F077ECC743462FD2760BB537",
"dotnet_version": "6",
"binaryName": "OmniSharp.exe"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Windows (.NET 4 / arm64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-arm64-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"win32"
],
"architectures": [
"arm64"
],
"installTestPath": "./.omnisharp/1.39.10/OmniSharp.exe",
"platformId": "win-arm64",
"isFramework": true,
"integrity": "32FA0067B0639F87760CD1A769B16E6A53588C137C4D31661836CA4FB28D3DD6",
"dotnet_version": "4",
"binaryName": "OmniSharp.exe"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Windows (.NET 6 / arm64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-arm64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"win32"
],
"architectures": [
"arm64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "win-arm64",
"isFramework": false,
"integrity": "433F9B360CAA7B4DDD85C604D5C5542C1A718BCF2E71B2BCFC7526E6D41F4E8F",
"dotnet_version": "6",
"binaryName": "OmniSharp.exe"
},
{
"id": "OmniSharp",
"description": "OmniSharp for OSX (Mono / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-osx-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"darwin"
],
"architectures": [
"x86_64",
"arm64"
],
"binaries": [
"./mono.osx",
"./run"
],
"installTestPath": "./.omnisharp/1.39.10/run",
"platformId": "osx",
"isFramework": true,
"integrity": "2CC42F0EC7C30CFA8858501D12ECB6FB685A1FCFB8ECB35698A4B12406551968",
"dotnet_version": "mono"
},
{
"id": "OmniSharp",
"description": "OmniSharp for OSX (.NET 6 / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-osx-x64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"darwin"
],
"architectures": [
"x86_64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "osx-x64",
"isFramework": false,
"integrity": "C9D6E9F2C839A66A7283AE6A9EC545EE049B48EB230D33E91A6322CB67FF9D97",
"dotnet_version": "6"
},
{
"id": "OmniSharp",
"description": "OmniSharp for OSX (.NET 6 / arm64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-osx-arm64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"darwin"
],
"architectures": [
"arm64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "osx-arm64",
"isFramework": false,
"integrity": "851350F52F83E3BAD5A92D113E4B9882FCD1DEB16AA84FF94B6F2CEE3C70051E",
"dotnet_version": "6"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux (Mono / x86)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-x86-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"linux"
],
"architectures": [
"x86",
"i686"
],
"binaries": [
"./mono.linux-x86",
"./run"
],
"installTestPath": "./.omnisharp/1.39.10/run",
"platformId": "linux-x86",
"isFramework": true,
"integrity": "474B1CDBAE64CFEC655FB6B0659BCE481023C48274441C72991E67B6E13E56A1",
"dotnet_version": "mono"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux (Mono / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-x64-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"linux"
],
"architectures": [
"x86_64"
],
"binaries": [
"./mono.linux-x86_64",
"./run"
],
"installTestPath": "./.omnisharp/1.39.10/run",
"platformId": "linux-x64",
"isFramework": true,
"integrity": "FB4CAA47343265100349375D79DBCCE1868950CED675CB07FCBE8462EDBCDD37",
"dotnet_version": "mono"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux (.NET 6 / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-x64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"linux"
],
"architectures": [
"x86_64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "linux-x64",
"isFramework": false,
"integrity": "0926D3BEA060BF4373356B2FC0A68C10D0DE1B1150100B551BA5932814CE51E2",
"dotnet_version": "6",
"binaryName": "OmniSharp"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux (Mono / arm64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-arm64-1.39.10.zip",
"installPath": ".omnisharp/1.39.10",
"platforms": [
"linux"
],
"architectures": [
"arm64"
],
"binaries": [
"./mono.linux-arm64",
"./run"
],
"installTestPath": "./.omnisharp/1.39.10/run",
"platformId": "linux-arm64",
"isFramework": true,
"integrity": "478F3594DFD0167E9A56E36F0364A86C73F8132A3E7EA916CA1419EFE141D2CC",
"dotnet_version": "mono"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux (.NET 6 / arm64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-arm64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"linux"
],
"architectures": [
"arm64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "linux-arm64",
"isFramework": false,
"integrity": "6FB6A572043A74220A92F6C19C7BB0C3743321C7563A815FD2702EF4FA7D688E",
"dotnet_version": "6"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux musl (.NET 6 / x64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-musl-x64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"linux-musl"
],
"architectures": [
"x86_64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "linux-musl-x64",
"isFramework": false,
"integrity": "6BFDA3AD11DBB0C6514B86ECC3E1597CC41C6E309B7575F7C599E07D9E2AE610",
"dotnet_version": "6"
},
{
"id": "OmniSharp",
"description": "OmniSharp for Linux musl (.NET 6 / arm64)",
"url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-musl-arm64-net6.0-1.39.10.zip",
"installPath": ".omnisharp/1.39.10-net6.0",
"platforms": [
"linux-musl"
],
"architectures": [
"arm64"
],
"installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll",
"platformId": "linux-musl-arm64",
"isFramework": false,
"integrity": "DA63619EA024EB9BBF6DB5A85C6150CAB5C0BD554544A3596ED1B17F926D6875",
"dotnet_version": "6"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Windows / x64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/8d42e62ea4051381c219b3e31bc4eced/razorlanguageserver-win-x64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"win32"
],
"architectures": [
"x86_64"
],
"platformId": "win-x64",
"dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Windows / x86)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/e440c4f3a4a96334fe177513935fa010/razorlanguageserver-win-x86-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"win32"
],
"architectures": [
"x86"
],
"platformId": "win-x86",
"dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Windows / ARM64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/4ef26e45cf32fe8d51c0e7dd21f1fef6/razorlanguageserver-win-arm64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"win32"
],
"architectures": [
"arm64"
],
"platformId": "win-arm64",
"dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Linux / x64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/6d4e23a3c7cf0465743950a39515a716/razorlanguageserver-linux-x64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"linux"
],
"architectures": [
"x86_64"
],
"binaries": [
"./rzls"
],
"platformId": "linux-x64",
"dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Linux ARM64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/85deebd44647ebf65724cc291d722283/razorlanguageserver-linux-arm64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"linux"
],
"architectures": [
"arm64"
],
"binaries": [
"./rzls"
],
"platformId": "linux-arm64"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Linux musl / x64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/4f0caa94ae182785655efb15eafcef23/razorlanguageserver-linux-musl-x64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"linux-musl"
],
"architectures": [
"x86_64"
],
"binaries": [
"./rzls"
],
"platformId": "linux-musl-x64"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (Linux musl ARM64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/0a24828206a6f3b4bc743d058ef88ce7/razorlanguageserver-linux-musl-arm64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"linux-musl"
],
"architectures": [
"arm64"
],
"binaries": [
"./rzls"
],
"platformId": "linux-musl-arm64"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (macOS / x64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/2afcafaf41082989efcc10405abb9314/razorlanguageserver-osx-x64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"darwin"
],
"architectures": [
"x86_64"
],
"binaries": [
"./rzls"
],
"platformId": "osx-x64"
},
{
"id": "RazorOmnisharp",
"description": "Razor Language Server for OmniSharp (macOS ARM64)",
"url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/8bf2ed2f00d481a5987e3eb5165afddd/razorlanguageserver-osx-arm64-7.0.0-preview.23363.1.zip",
"installPath": ".razoromnisharp",
"platforms": [
"darwin"
],
"architectures": [
"arm64"
],
"binaries": [
"./rzls"
],
"platformId": "osx-arm64"
}
]
}
@@ -0,0 +1,111 @@
{
"RoslynExtensionsOptions": {
"EnableDecompilationSupport": false,
"EnableAnalyzersSupport": true,
"EnableImportCompletion": true,
"EnableAsyncCompletion": false,
"DocumentAnalysisTimeoutMs": 30000,
"DiagnosticWorkersThreadCount": 18,
"AnalyzeOpenDocumentsOnly": true,
"InlayHintsOptions": {
"EnableForParameters": false,
"ForLiteralParameters": false,
"ForIndexerParameters": false,
"ForObjectCreationParameters": false,
"ForOtherParameters": false,
"SuppressForParametersThatDifferOnlyBySuffix": false,
"SuppressForParametersThatMatchMethodIntent": false,
"SuppressForParametersThatMatchArgumentName": false,
"EnableForTypes": false,
"ForImplicitVariableTypes": false,
"ForLambdaParameterTypes": false,
"ForImplicitObjectCreation": false
},
"LocationPaths": null
},
"FormattingOptions": {
"OrganizeImports": false,
"EnableEditorConfigSupport": true,
"NewLine": "\n",
"UseTabs": false,
"TabSize": 4,
"IndentationSize": 4,
"SpacingAfterMethodDeclarationName": false,
"SeparateImportDirectiveGroups": false,
"SpaceWithinMethodDeclarationParenthesis": false,
"SpaceBetweenEmptyMethodDeclarationParentheses": false,
"SpaceAfterMethodCallName": false,
"SpaceWithinMethodCallParentheses": false,
"SpaceBetweenEmptyMethodCallParentheses": false,
"SpaceAfterControlFlowStatementKeyword": true,
"SpaceWithinExpressionParentheses": false,
"SpaceWithinCastParentheses": false,
"SpaceWithinOtherParentheses": false,
"SpaceAfterCast": false,
"SpaceBeforeOpenSquareBracket": false,
"SpaceBetweenEmptySquareBrackets": false,
"SpaceWithinSquareBrackets": false,
"SpaceAfterColonInBaseTypeDeclaration": true,
"SpaceAfterComma": true,
"SpaceAfterDot": false,
"SpaceAfterSemicolonsInForStatement": true,
"SpaceBeforeColonInBaseTypeDeclaration": true,
"SpaceBeforeComma": false,
"SpaceBeforeDot": false,
"SpaceBeforeSemicolonsInForStatement": false,
"SpacingAroundBinaryOperator": "single",
"IndentBraces": false,
"IndentBlock": true,
"IndentSwitchSection": true,
"IndentSwitchCaseSection": true,
"IndentSwitchCaseSectionWhenBlock": true,
"LabelPositioning": "oneLess",
"WrappingPreserveSingleLine": true,
"WrappingKeepStatementsOnSingleLine": true,
"NewLinesForBracesInTypes": true,
"NewLinesForBracesInMethods": true,
"NewLinesForBracesInProperties": true,
"NewLinesForBracesInAccessors": true,
"NewLinesForBracesInAnonymousMethods": true,
"NewLinesForBracesInControlBlocks": true,
"NewLinesForBracesInAnonymousTypes": true,
"NewLinesForBracesInObjectCollectionArrayInitializers": true,
"NewLinesForBracesInLambdaExpressionBody": true,
"NewLineForElse": true,
"NewLineForCatch": true,
"NewLineForFinally": true,
"NewLineForMembersInObjectInit": true,
"NewLineForMembersInAnonymousTypes": true,
"NewLineForClausesInQuery": true
},
"FileOptions": {
"SystemExcludeSearchPatterns": [
"**/node_modules/**/*",
"**/bin/**/*",
"**/obj/**/*",
"**/.git/**/*",
"**/.git",
"**/.svn",
"**/.hg",
"**/CVS",
"**/.DS_Store",
"**/Thumbs.db"
],
"ExcludeSearchPatterns": []
},
"RenameOptions": {
"RenameOverloads": false,
"RenameInStrings": false,
"RenameInComments": false
},
"ImplementTypeOptions": {
"InsertionBehavior": 0,
"PropertyGenerationBehavior": 0
},
"DotNetCliOptions": {
"LocationPaths": null
},
"Plugins": {
"LocationPaths": null
}
}
@@ -0,0 +1,246 @@
"""
Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python.
"""
import asyncio
import json
import logging
import os
import pathlib
import re
from contextlib import asynccontextmanager
from typing import AsyncIterator, Tuple
from overrides import override
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
class PyrightServer(LanguageServer):
"""
Provides Python specific instantiation of the LanguageServer class using Pyright.
Contains various configurations and settings specific to Python.
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates a PyrightServer instance. This class is not meant to be instantiated directly.
Use LanguageServer.create() instead.
"""
super().__init__(
config,
logger,
repository_root_path,
# Note 1: we can also use `pyright-langserver --stdio` but it requires pyright to be installed with npm
# Note 2: we can also use `bpyright-langserver --stdio` if we ever are unhappy with pyright
ProcessLaunchInfo(cmd="python -m pyright.langserver --stdio", cwd=repository_root_path),
"python",
)
# Event to signal when initial workspace analysis is complete
self.analysis_complete = asyncio.Event()
self.found_source_files = False
@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"]
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the Pyright Language Server.
"""
# Create basic initialization parameters
initialize_params: InitializeParams = { # type: ignore
"processId": os.getpid(),
"rootPath": repository_absolute_path,
"rootUri": pathlib.Path(repository_absolute_path).as_uri(),
"initializationOptions": {
"exclude": [
"**/__pycache__",
"**/.venv",
"**/.env",
"**/build",
"**/dist",
"**/.pixi",
],
"reportMissingImports": "error",
},
"capabilities": {
"workspace": {
"applyEdit": True,
"workspaceEdit": {"documentChanges": True},
"didChangeConfiguration": {"dynamicRegistration": True},
"didChangeWatchedFiles": {"dynamicRegistration": 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]
},
},
"executeCommand": {"dynamicRegistration": True},
},
"textDocument": {
"synchronization": {"dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True},
"completion": {
"dynamicRegistration": True,
"contextSupport": True,
"completionItem": {
"snippetSupport": True,
"commitCharactersSupport": True,
"documentationFormat": ["markdown", "plaintext"],
"deprecatedSupport": True,
"preselectSupport": True,
},
"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]
},
},
"hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]},
"signatureHelp": {
"dynamicRegistration": True,
"signatureInformation": {
"documentationFormat": ["markdown", "plaintext"],
"parameterInformation": {"labelOffsetSupport": True},
},
},
"definition": {"dynamicRegistration": 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,
},
"codeAction": {
"dynamicRegistration": True,
"codeActionLiteralSupport": {
"codeActionKind": {
"valueSet": [
"",
"quickfix",
"refactor",
"refactor.extract",
"refactor.inline",
"refactor.rewrite",
"source",
"source.organizeImports",
]
}
},
},
"codeLens": {"dynamicRegistration": True},
"formatting": {"dynamicRegistration": True},
"rangeFormatting": {"dynamicRegistration": True},
"onTypeFormatting": {"dynamicRegistration": True},
"rename": {"dynamicRegistration": True},
"publishDiagnostics": {"relatedInformation": True},
},
},
"workspaceFolders": [
{"uri": pathlib.Path(repository_absolute_path).as_uri(), "name": os.path.basename(repository_absolute_path)}
],
}
return initialize_params
@asynccontextmanager
async def start_server(self) -> AsyncIterator["PyrightServer"]:
"""
Starts the Pyright Language Server and waits for initial workspace analysis to complete.
This prevents zombie processes by ensuring Pyright has finished its initial background
tasks before we consider the server ready.
Usage:
```
async with lsp.start_server():
# LanguageServer has been initialized and workspace analysis is complete
await lsp.request_definition(...)
await lsp.request_references(...)
# Shutdown the LanguageServer on exit from scope
# LanguageServer has been shutdown cleanly
```
"""
async def execute_client_command_handler(params):
return []
async def do_nothing(params):
return
async def window_log_message(msg):
"""
Monitor Pyright's log messages to detect when initial analysis is complete.
Pyright logs "Found X source files" when it finishes scanning the workspace.
"""
message_text = msg.get("message", "")
self.logger.log(f"LSP: window/logMessage: {message_text}", logging.INFO)
# Look for "Found X source files" which indicates workspace scanning is complete
# Unfortunately, pyright is unreliable and there seems to be no better way
if re.search(r"Found \d+ source files?", message_text):
self.logger.log("Pyright workspace scanning complete", logging.INFO)
self.found_source_files = True
self.analysis_complete.set()
self.completions_available.set()
async def check_experimental_status(params):
"""
Also listen for experimental/serverStatus as a backup signal
"""
if params.get("quiescent") == True:
self.logger.log("Received experimental/serverStatus with quiescent=true", logging.INFO)
if not self.found_source_files:
self.analysis_complete.set()
self.completions_available.set()
# Set up notification handlers
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 pyright-langserver server process", logging.INFO)
await self.server.start()
# Send proper initialization parameters
initialize_params = self._get_initialize_params(self.repository_root_path)
self.logger.log(
"Sending initialize request from LSP client to pyright server and awaiting response",
logging.INFO,
)
init_response = await self.server.send.initialize(initialize_params)
self.logger.log(f"Received initialize response from pyright server: {init_response}", logging.INFO)
# Verify that the server supports our required features
assert "textDocumentSync" in init_response["capabilities"]
assert "completionProvider" in init_response["capabilities"]
assert "definitionProvider" in init_response["capabilities"]
# Complete the initialization handshake
self.server.notify.initialized({})
# Wait for Pyright to complete its initial workspace analysis
# This prevents zombie processes by ensuring background tasks finish
self.logger.log("Waiting for Pyright to complete initial workspace analysis...", logging.INFO)
try:
await asyncio.wait_for(self.analysis_complete.wait(), timeout=1.0)
self.logger.log("Pyright initial analysis complete, server ready", logging.INFO)
except asyncio.TimeoutError:
self.logger.log("Timeout waiting for Pyright analysis completion, proceeding anyway", logging.WARNING)
# Fallback: assume analysis is complete after timeout
self.analysis_complete.set()
self.completions_available.set()
yield self
@@ -0,0 +1,917 @@
{
"_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": "Visual Studio Code - Insiders",
"version": "1.82.0-insider"
},
"locale": "en",
"rootPath": "$rootPath",
"rootUri": "$rootUri",
"capabilities": {
"workspace": {
"applyEdit": true,
"workspaceEdit": {
"documentChanges": true,
"resourceOperations": [
"create",
"rename",
"delete"
],
"failureHandling": "textOnlyTransactional",
"normalizesLineEndings": true,
"changeAnnotationSupport": {
"groupsOnLabel": true
}
},
"configuration": 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
},
"didChangeConfiguration": {
"dynamicRegistration": 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": true,
"commitCharactersSupport": true,
"documentationFormat": [
"markdown",
"plaintext"
],
"deprecatedSupport": true,
"preselectSupport": true,
"tagSupport": {
"valueSet": [
1
]
},
"insertReplaceSupport": true,
"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,
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": false
},
"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",
"allowedTags": [
"ul",
"li",
"p",
"code",
"blockquote",
"ol",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"hr",
"em",
"pre",
"table",
"thead",
"tbody",
"tr",
"th",
"td",
"div",
"del",
"a",
"strong",
"br",
"img",
"span"
]
},
"positionEncodings": [
"utf-16"
]
},
"notebookDocument": {
"synchronization": {
"dynamicRegistration": true,
"executionSummarySupport": true
}
},
"experimental": {
"snippetTextEdit": true,
"codeActionGroup": true,
"hoverActions": true,
"serverStatusNotification": true,
"colorDiagnosticOutput": true,
"openServerLogs": true,
"localDocs": true,
"commands": {
"commands": [
"rust-analyzer.runSingle",
"rust-analyzer.debugSingle",
"rust-analyzer.showReferences",
"rust-analyzer.gotoLocation",
"editor.action.triggerParameterHints"
]
}
}
},
"initializationOptions": {
"cargoRunner": null,
"runnables": {
"extraEnv": null,
"problemMatcher": [
"$rustc"
],
"command": null,
"extraArgs": []
},
"statusBar": {
"clickAction": "openLogs"
},
"server": {
"path": null,
"extraEnv": null
},
"trace": {
"server": "verbose",
"extension": false
},
"debug": {
"engine": "auto",
"sourceFileMap": {
"/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
},
"openDebugPane": false,
"engineSettings": {}
},
"restartServerOnConfigChange": false,
"typing": {
"continueCommentsOnNewline": true,
"autoClosingAngleBrackets": {
"enable": false
}
},
"diagnostics": {
"previewRustcOutput": false,
"useRustcErrorCode": false,
"disabled": [],
"enable": true,
"experimental": {
"enable": false
},
"remapPrefix": {},
"warningsAsHint": [],
"warningsAsInfo": []
},
"discoverProjectRunner": null,
"showUnlinkedFileNotification": true,
"showDependenciesExplorer": true,
"assist": {
"emitMustUse": false,
"expressionFillDefault": "todo"
},
"cachePriming": {
"enable": true,
"numThreads": 0
},
"cargo": {
"autoreload": true,
"buildScripts": {
"enable": true,
"invocationLocation": "workspace",
"invocationStrategy": "per_workspace",
"overrideCommand": null,
"useRustcWrapper": true
},
"cfgs": {},
"extraArgs": [],
"extraEnv": {},
"features": [],
"noDefaultFeatures": false,
"sysroot": "discover",
"sysrootSrc": null,
"target": null,
"unsetTest": [
"core"
]
},
"checkOnSave": true,
"check": {
"allTargets": true,
"command": "check",
"extraArgs": [],
"extraEnv": {},
"features": null,
"ignore": [],
"invocationLocation": "workspace",
"invocationStrategy": "per_workspace",
"noDefaultFeatures": null,
"overrideCommand": null,
"targets": null
},
"completion": {
"autoimport": {
"enable": true
},
"autoself": {
"enable": true
},
"callable": {
"snippets": "fill_arguments"
},
"fullFunctionSignatures": {
"enable": false
},
"limit": null,
"postfix": {
"enable": true
},
"privateEditable": {
"enable": false
},
"snippets": {
"custom": {
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"scope": "expr"
},
"Box::pin": {
"postfix": "pinbox",
"body": "Box::pin(${receiver})",
"requires": "std::boxed::Box",
"description": "Put the expression into a pinned `Box`",
"scope": "expr"
},
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"scope": "expr"
},
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"scope": "expr"
},
"Some": {
"postfix": "some",
"body": "Some(${receiver})",
"description": "Wrap the expression in an `Option::Some`",
"scope": "expr"
}
}
}
},
"files": {
"excludeDirs": [],
"watcher": "client"
},
"highlightRelated": {
"breakPoints": {
"enable": true
},
"closureCaptures": {
"enable": true
},
"exitPoints": {
"enable": true
},
"references": {
"enable": true
},
"yieldPoints": {
"enable": true
}
},
"hover": {
"actions": {
"debug": {
"enable": true
},
"enable": true,
"gotoTypeDef": {
"enable": true
},
"implementations": {
"enable": true
},
"references": {
"enable": false
},
"run": {
"enable": true
}
},
"documentation": {
"enable": true,
"keywords": {
"enable": true
}
},
"links": {
"enable": true
},
"memoryLayout": {
"alignment": "hexadecimal",
"enable": true,
"niches": false,
"offset": "hexadecimal",
"size": "both"
}
},
"imports": {
"granularity": {
"enforce": false,
"group": "crate"
},
"group": {
"enable": true
},
"merge": {
"glob": true
},
"preferNoStd": false,
"preferPrelude": false,
"prefix": "plain"
},
"inlayHints": {
"bindingModeHints": {
"enable": false
},
"chainingHints": {
"enable": true
},
"closingBraceHints": {
"enable": true,
"minLines": 25
},
"closureCaptureHints": {
"enable": false
},
"closureReturnTypeHints": {
"enable": "never"
},
"closureStyle": "impl_fn",
"discriminantHints": {
"enable": "never"
},
"expressionAdjustmentHints": {
"enable": "never",
"hideOutsideUnsafe": false,
"mode": "prefix"
},
"lifetimeElisionHints": {
"enable": "never",
"useParameterNames": false
},
"maxLength": 25,
"parameterHints": {
"enable": true
},
"reborrowHints": {
"enable": "never"
},
"renderColons": true,
"typeHints": {
"enable": true,
"hideClosureInitialization": false,
"hideNamedConstructor": false
}
},
"interpret": {
"tests": false
},
"joinLines": {
"joinAssignments": true,
"joinElseIf": true,
"removeTrailingComma": true,
"unwrapTrivialBlock": true
},
"lens": {
"debug": {
"enable": true
},
"enable": true,
"forceCustomCommands": true,
"implementations": {
"enable": true
},
"location": "above_name",
"references": {
"adt": {
"enable": false
},
"enumVariant": {
"enable": false
},
"method": {
"enable": false
},
"trait": {
"enable": false
}
},
"run": {
"enable": true
}
},
"linkedProjects": [],
"lru": {
"capacity": null,
"query": {
"capacities": {}
}
},
"notifications": {
"cargoTomlNotFound": true
},
"numThreads": null,
"procMacro": {
"attributes": {
"enable": true
},
"enable": true,
"ignored": {},
"server": null
},
"references": {
"excludeImports": false
},
"rust": {
"analyzerTargetDir": null
},
"rustc": {
"source": null
},
"rustfmt": {
"extraArgs": [],
"overrideCommand": null,
"rangeFormatting": {
"enable": false
}
},
"semanticHighlighting": {
"doc": {
"comment": {
"inject": {
"enable": true
}
}
},
"nonStandardTokens": true,
"operator": {
"enable": true,
"specialization": {
"enable": false
}
},
"punctuation": {
"enable": false,
"separate": {
"macro": {
"bang": false
}
},
"specialization": {
"enable": false
}
},
"strings": {
"enable": true
}
},
"signatureInfo": {
"detail": "full",
"documentation": {
"enable": true
}
},
"workspace": {
"symbol": {
"search": {
"kind": "only_types",
"limit": 128,
"scope": "workspace"
}
}
}
},
"trace": "verbose",
"workspaceFolders": [
{
"uri": "$uri",
"name": "$name"
}
]
}
@@ -0,0 +1,29 @@
{
"_description": "Used to download the runtime dependencies for running RustAnalyzer. Obtained from https://github.com/rust-lang/rust-analyzer/releases",
"runtimeDependencies": [
{
"id": "RustAnalyzer",
"description": "RustAnalyzer for Linux (x64)",
"url": "https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-aarch64-apple-darwin.gz",
"platformId": "osx-arm64",
"archiveType": "gz",
"binaryName": "rust_analyzer"
},
{
"id": "RustAnalyzer",
"description": "RustAnalyzer for Linux (x64)",
"url": "https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-x86_64-unknown-linux-gnu.gz",
"platformId": "linux-x64",
"archiveType": "gz",
"binaryName": "rust_analyzer"
},
{
"id": "RustAnalyzer",
"description": "RustAnalyzer for Windows (x64)",
"url": "https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-x86_64-pc-windows-msvc.zip",
"platformId": "win-x64",
"archiveType": "zip",
"binaryName": "rust-analyzer.exe"
}
]
}
@@ -0,0 +1,185 @@
"""
Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust.
"""
import asyncio
import json
import logging
import os
import stat
import pathlib
from contextlib import asynccontextmanager
from typing import AsyncIterator
from overrides import override
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 RustAnalyzer(LanguageServer):
"""
Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust.
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates a RustAnalyzer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
"""
rustanalyzer_executable_path = self.setup_runtime_dependencies(logger, config)
super().__init__(
config,
logger,
repository_root_path,
ProcessLaunchInfo(cmd=rustanalyzer_executable_path, cwd=repository_root_path),
"rust",
)
self.server_ready = asyncio.Event()
@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in ["target"]
def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str:
"""
Setup runtime dependencies for rust_analyzer.
"""
platform_id = PlatformUtils.get_platform_id()
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f:
d = json.load(f)
del d["_description"]
# assert platform_id.value in [
# "linux-x64",
# "win-x64",
# ], "Only linux-x64 and win-x64 platform 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]
rustanalyzer_ls_dir = os.path.join(os.path.dirname(__file__), "static", "RustAnalyzer")
rustanalyzer_executable_path = os.path.join(rustanalyzer_ls_dir, dependency["binaryName"])
if not os.path.exists(rustanalyzer_ls_dir):
os.makedirs(rustanalyzer_ls_dir)
if dependency["archiveType"] == "gz":
FileUtils.download_and_extract_archive(
logger, dependency["url"], rustanalyzer_executable_path, dependency["archiveType"]
)
else:
FileUtils.download_and_extract_archive(
logger, dependency["url"], rustanalyzer_ls_dir, dependency["archiveType"]
)
assert os.path.exists(rustanalyzer_executable_path)
os.chmod(rustanalyzer_executable_path, stat.S_IEXEC)
return rustanalyzer_executable_path
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the Rust Analyzer Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") 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["RustAnalyzer"]:
"""
Starts the Rust Analyzer 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 RustAnalyzer 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"] == {
"resolveProvider": True,
"triggerCharacters": [":", ".", "'", "("],
"completionItem": {"labelDetailsSupport": True},
}
self.server.notify.initialized({})
self.completions_available.set()
await self.server_ready.wait()
yield self
@@ -0,0 +1,15 @@
{
"_description": "This file contains the initialization parameters for the Solargraph Language Server.",
"processId": "$processId",
"rootPath": "$rootPath",
"rootUri": "$rootUri",
"capabilities": {
},
"trace": "verbose",
"workspaceFolders": [
{
"uri": "$uri",
"name": "$name"
}
]
}
@@ -0,0 +1,11 @@
{
"_description": "This file contains URLs and other metadata required for downloading and installing the Solargraph language server.",
"runtimeDependencies": [
{
"url": "https://rubygems.org/downloads/solargraph-0.51.1.gem",
"installCommand": "gem install solargraph -v 0.51.1",
"binaryName": "solargraph",
"archiveType": "gem"
}
]
}
@@ -0,0 +1,189 @@
"""
Provides Ruby specific instantiation of the LanguageServer class using Solargraph.
Contains various configurations and settings specific to Ruby.
"""
import asyncio
import json
import logging
import os
import stat
import subprocess
import pathlib
from contextlib import asynccontextmanager
from typing import AsyncIterator, override
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, PlatformId
class Solargraph(LanguageServer):
"""
Provides Ruby specific instantiation of the LanguageServer class using Solargraph.
Contains various configurations and settings specific to Ruby.
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates a Solargraph instance. This class is not meant to be instantiated directly.
Use LanguageServer.create() instead.
"""
solargraph_executable_path = self.setup_runtime_dependencies(logger, config, repository_root_path)
super().__init__(
config,
logger,
repository_root_path,
ProcessLaunchInfo(cmd=f"{solargraph_executable_path} stdio", cwd=repository_root_path),
"ruby",
)
self.server_ready = asyncio.Event()
@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in ["vendor"]
def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig, repository_root_path: str) -> str:
"""
Setup runtime dependencies for Solargraph.
"""
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f:
d = json.load(f)
del d["_description"]
dependency = d["runtimeDependencies"][0]
# Check if Ruby is installed
try:
result = subprocess.run(["ruby", "--version"], check=True, capture_output=True, cwd=repository_root_path)
ruby_version = result.stdout.strip()
logger.log(f"Ruby version: {ruby_version}", logging.INFO)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Error checking for Ruby installation: {e.stderr}")
except FileNotFoundError:
raise RuntimeError("Ruby is not installed. Please install Ruby before continuing.")
# Check if solargraph is installed
try:
result = subprocess.run(["gem", "list", "^solargraph$", "-i"], check=False, capture_output=True, text=True, cwd=repository_root_path)
if result.stdout.strip() == "false":
logger.log("Installing Solargraph...", logging.INFO)
subprocess.run(dependency["installCommand"].split(), check=True, capture_output=True, cwd=repository_root_path)
# Get the gem executable path directly
result = subprocess.run(["gem", "which", "solargraph"], check=True, capture_output=True, text=True, cwd=repository_root_path)
gem_path = result.stdout.strip()
bin_dir = os.path.join(os.path.dirname(os.path.dirname(gem_path)), "bin")
executable_path = os.path.join(bin_dir, "solargraph")
if not os.path.exists(executable_path):
raise RuntimeError(f"Solargraph executable not found at {executable_path}")
# Ensure the executable has the right permissions
os.chmod(executable_path, os.stat(executable_path).st_mode | stat.S_IEXEC)
return executable_path
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Failed to check or install Solargraph. {e.stderr}")
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the Solargraph Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") 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["Solargraph"]:
"""
Starts the Solargraph Language Server for Ruby, 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 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)
async with super().start_server():
self.logger.log("Starting solargraph 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,
)
self.logger.log(f"Sending init params: {json.dumps(initialize_params, indent=4)}", logging.INFO)
init_response = await self.server.send.initialize(initialize_params)
self.logger.log(f"Received init response: {init_response}", logging.INFO)
assert init_response["capabilities"]["textDocumentSync"] == 2
assert "completionProvider" in init_response["capabilities"]
assert init_response["capabilities"]["completionProvider"] == {
"resolveProvider": True,
"triggerCharacters": [".", ":", "@"],
}
self.server.notify.initialized({})
self.completions_available.set()
self.server_ready.set()
await self.server_ready.wait()
yield self
@@ -0,0 +1,906 @@
{
"_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": "Visual Studio Code - Insiders",
"version": "1.82.0-insider"
},
"locale": "en",
"rootPath": "$rootPath",
"rootUri": "$rootUri",
"capabilities": {
"workspace": {
"applyEdit": true,
"workspaceEdit": {
"documentChanges": true,
"resourceOperations": [
"create",
"rename",
"delete"
],
"failureHandling": "textOnlyTransactional",
"normalizesLineEndings": true,
"changeAnnotationSupport": {
"groupsOnLabel": true
}
},
"configuration": 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
},
"didChangeConfiguration": {
"dynamicRegistration": 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": true,
"commitCharactersSupport": true,
"documentationFormat": [
"markdown",
"plaintext"
],
"deprecatedSupport": true,
"preselectSupport": true,
"tagSupport": {
"valueSet": [
1
]
},
"insertReplaceSupport": true,
"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,
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": false
},
"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
}
},
"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",
"allowedTags": [
"ul",
"li",
"p",
"code",
"blockquote",
"ol",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"hr",
"em",
"pre",
"table",
"thead",
"tbody",
"tr",
"th",
"td",
"div",
"del",
"a",
"strong",
"br",
"img",
"span"
]
},
"positionEncodings": [
"utf-16"
]
},
"notebookDocument": {
"synchronization": {
"dynamicRegistration": true,
"executionSummarySupport": true
}
},
"experimental": {
"snippetTextEdit": true,
"codeActionGroup": true,
"hoverActions": true,
"serverStatusNotification": true,
"colorDiagnosticOutput": true,
"openServerLogs": true,
"localDocs": true,
"commands": {
"commands": [
"rust-analyzer.runSingle",
"rust-analyzer.debugSingle",
"rust-analyzer.showReferences",
"rust-analyzer.gotoLocation",
"editor.action.triggerParameterHints"
]
}
}
},
"initializationOptions": {
"cargoRunner": null,
"runnables": {
"extraEnv": null,
"problemMatcher": [
"$rustc"
],
"command": null,
"extraArgs": []
},
"statusBar": {
"clickAction": "openLogs"
},
"server": {
"path": null,
"extraEnv": null
},
"trace": {
"server": "verbose",
"extension": false
},
"debug": {
"engine": "auto",
"sourceFileMap": {
"/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
},
"openDebugPane": false,
"engineSettings": {}
},
"restartServerOnConfigChange": false,
"typing": {
"continueCommentsOnNewline": true,
"autoClosingAngleBrackets": {
"enable": false
}
},
"diagnostics": {
"previewRustcOutput": false,
"useRustcErrorCode": false,
"disabled": [],
"enable": true,
"experimental": {
"enable": false
},
"remapPrefix": {},
"warningsAsHint": [],
"warningsAsInfo": []
},
"discoverProjectRunner": null,
"showUnlinkedFileNotification": true,
"showDependenciesExplorer": true,
"assist": {
"emitMustUse": false,
"expressionFillDefault": "todo"
},
"cachePriming": {
"enable": true,
"numThreads": 0
},
"cargo": {
"autoreload": true,
"buildScripts": {
"enable": true,
"invocationLocation": "workspace",
"invocationStrategy": "per_workspace",
"overrideCommand": null,
"useRustcWrapper": true
},
"cfgs": {},
"extraArgs": [],
"extraEnv": {},
"features": [],
"noDefaultFeatures": false,
"sysroot": "discover",
"sysrootSrc": null,
"target": null,
"unsetTest": [
"core"
]
},
"checkOnSave": true,
"check": {
"allTargets": true,
"command": "check",
"extraArgs": [],
"extraEnv": {},
"features": null,
"ignore": [],
"invocationLocation": "workspace",
"invocationStrategy": "per_workspace",
"noDefaultFeatures": null,
"overrideCommand": null,
"targets": null
},
"completion": {
"autoimport": {
"enable": true
},
"autoself": {
"enable": true
},
"callable": {
"snippets": "fill_arguments"
},
"fullFunctionSignatures": {
"enable": false
},
"limit": null,
"postfix": {
"enable": true
},
"privateEditable": {
"enable": false
},
"snippets": {
"custom": {
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"scope": "expr"
},
"Box::pin": {
"postfix": "pinbox",
"body": "Box::pin(${receiver})",
"requires": "std::boxed::Box",
"description": "Put the expression into a pinned `Box`",
"scope": "expr"
},
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"scope": "expr"
},
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"scope": "expr"
},
"Some": {
"postfix": "some",
"body": "Some(${receiver})",
"description": "Wrap the expression in an `Option::Some`",
"scope": "expr"
}
}
}
},
"files": {
"excludeDirs": [],
"watcher": "client"
},
"highlightRelated": {
"breakPoints": {
"enable": true
},
"closureCaptures": {
"enable": true
},
"exitPoints": {
"enable": true
},
"references": {
"enable": true
},
"yieldPoints": {
"enable": true
}
},
"hover": {
"actions": {
"debug": {
"enable": true
},
"enable": true,
"gotoTypeDef": {
"enable": true
},
"implementations": {
"enable": true
},
"references": {
"enable": false
},
"run": {
"enable": true
}
},
"documentation": {
"enable": true,
"keywords": {
"enable": true
}
},
"links": {
"enable": true
},
"memoryLayout": {
"alignment": "hexadecimal",
"enable": true,
"niches": false,
"offset": "hexadecimal",
"size": "both"
}
},
"imports": {
"granularity": {
"enforce": false,
"group": "crate"
},
"group": {
"enable": true
},
"merge": {
"glob": true
},
"preferNoStd": false,
"preferPrelude": false,
"prefix": "plain"
},
"inlayHints": {
"bindingModeHints": {
"enable": false
},
"chainingHints": {
"enable": true
},
"closingBraceHints": {
"enable": true,
"minLines": 25
},
"closureCaptureHints": {
"enable": false
},
"closureReturnTypeHints": {
"enable": "never"
},
"closureStyle": "impl_fn",
"discriminantHints": {
"enable": "never"
},
"expressionAdjustmentHints": {
"enable": "never",
"hideOutsideUnsafe": false,
"mode": "prefix"
},
"lifetimeElisionHints": {
"enable": "never",
"useParameterNames": false
},
"maxLength": 25,
"parameterHints": {
"enable": true
},
"reborrowHints": {
"enable": "never"
},
"renderColons": true,
"typeHints": {
"enable": true,
"hideClosureInitialization": false,
"hideNamedConstructor": false
}
},
"interpret": {
"tests": false
},
"joinLines": {
"joinAssignments": true,
"joinElseIf": true,
"removeTrailingComma": true,
"unwrapTrivialBlock": true
},
"lens": {
"debug": {
"enable": true
},
"enable": true,
"forceCustomCommands": true,
"implementations": {
"enable": true
},
"location": "above_name",
"references": {
"adt": {
"enable": false
},
"enumVariant": {
"enable": false
},
"method": {
"enable": false
},
"trait": {
"enable": false
}
},
"run": {
"enable": true
}
},
"linkedProjects": [],
"lru": {
"capacity": null,
"query": {
"capacities": {}
}
},
"notifications": {
"cargoTomlNotFound": true
},
"numThreads": null,
"procMacro": {
"attributes": {
"enable": true
},
"enable": true,
"ignored": {},
"server": null
},
"references": {
"excludeImports": false
},
"rust": {
"analyzerTargetDir": null
},
"rustc": {
"source": null
},
"rustfmt": {
"extraArgs": [],
"overrideCommand": null,
"rangeFormatting": {
"enable": false
}
},
"semanticHighlighting": {
"doc": {
"comment": {
"inject": {
"enable": true
}
}
},
"nonStandardTokens": true,
"operator": {
"enable": true,
"specialization": {
"enable": false
}
},
"punctuation": {
"enable": false,
"separate": {
"macro": {
"bang": false
}
},
"specialization": {
"enable": false
}
},
"strings": {
"enable": true
}
},
"signatureInfo": {
"detail": "full",
"documentation": {
"enable": true
}
},
"workspace": {
"symbol": {
"search": {
"kind": "only_types",
"limit": 128,
"scope": "workspace"
}
}
}
},
"trace": "verbose",
"workspaceFolders": [
{
"uri": "$uri",
"name": "$name"
}
]
}
@@ -0,0 +1,16 @@
{
"_description": "Used to download the runtime dependencies for running typescript-language-server. Obtained from https://github.com/typescript-language-server/typescript-language-server/releases",
"runtimeDependencies": [
{
"id": "typescript",
"description": "typescript package for Linux, OSX, and Windows. Both x64 and arm64 are supported.",
"command": "npm install --prefix ./ typescript@5.5.4"
},
{
"id": "typescript-language-server",
"description": "typescript-language-server package for Linux, OSX, and Windows. Both x64 and arm64 are supported.",
"command": "npm install --prefix ./ typescript-language-server@4.3.3"
}
]
}
@@ -0,0 +1,245 @@
"""
Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript.
"""
import asyncio
import json
import logging
import os
import pathlib
import shutil
import subprocess
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from time import sleep
from overrides import override
from multilspy.language_server import LanguageServer
from multilspy.lsp_protocol_handler.lsp_types import InitializeParams
from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo
from multilspy.multilspy_config import MultilspyConfig
from multilspy.multilspy_logger import MultilspyLogger
from multilspy.multilspy_utils import PlatformId, PlatformUtils
# Platform-specific imports
if os.name != 'nt': # Unix-like systems
import pwd
else:
# Dummy pwd module for Windows
class pwd:
@staticmethod
def getpwuid(uid):
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.
"""
def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str):
"""
Creates a TypeScriptLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
"""
ts_lsp_executable_path = self.setup_runtime_dependencies(logger, config)
super().__init__(
config,
logger,
repository_root_path,
ProcessLaunchInfo(cmd=ts_lsp_executable_path, cwd=repository_root_path),
"typescript",
)
self.server_ready = asyncio.Event()
@override
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in [
"node_modules",
"dist",
"build",
"coverage",
]
def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str:
"""
Setup runtime dependencies for TypeScript Language Server.
"""
platform_id = PlatformUtils.get_platform_id()
valid_platforms = [
PlatformId.LINUX_x64,
PlatformId.LINUX_arm64,
PlatformId.OSX,
PlatformId.OSX_x64,
PlatformId.OSX_arm64,
PlatformId.WIN_x64,
PlatformId.WIN_arm64,
]
assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy javascript/typescript at the moment"
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json")) as f:
d = json.load(f)
del d["_description"]
runtime_dependencies = d.get("runtimeDependencies", [])
tsserver_ls_dir = os.path.join(os.path.dirname(__file__), "static", "ts-lsp")
tsserver_executable_path = os.path.join(tsserver_ls_dir, "typescript-language-server")
# Verify both node and npm are installed
is_node_installed = shutil.which('node') is not None
assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again."
is_npm_installed = shutil.which('npm') is not None
assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again."
# Install typescript and typescript-language-server if not already installed
if not os.path.exists(tsserver_ls_dir):
os.makedirs(tsserver_ls_dir, exist_ok=True)
for dependency in runtime_dependencies:
# 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.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."
return f"{tsserver_executable_path} --stdio"
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the TypeScript Language Server.
"""
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json")) 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["TypeScriptLanguageServer"]:
"""
Starts the TypeScript 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()
# TypeScript doesn't have a direct equivalent to resolve_main_method
# You might want to set a different flag or remove this line
# self.resolve_main_method_available.set()
return
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)
async def check_experimental_status(params):
"""
Also listen for experimental/serverStatus as a backup signal
"""
if params.get("quiescent") == True:
self.server_ready.set()
self.completions_available.set()
self.server.on_request("client/registerCapability", register_capability_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("experimental/serverStatus", check_experimental_status)
async with super().start_server():
self.logger.log("Starting TypeScript 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)
# TypeScript-specific capability checks
assert init_response["capabilities"]["textDocumentSync"] == 2
assert "completionProvider" in init_response["capabilities"]
assert init_response["capabilities"]["completionProvider"] == {
"triggerCharacters": ['.', '"', "'", '/', '@', '<'],
"resolveProvider": True
}
self.server.notify.initialized({})
try:
await asyncio.wait_for(self.server_ready.wait(), timeout=1.0)
except asyncio.TimeoutError:
self.logger.log("Timeout waiting for TypeScript server to become ready, proceeding anyway", logging.INFO)
# Fallback: assume server is ready after timeout
self.server_ready.set()
self.completions_available.set()
yield self
@override
# For some reason, the LS may need longer to process this, so we just retry
async def _send_references_request(self, relative_file_path: str, line: int, column: int):
# TODO: The LS doesn't return references contained in other files if it doesn't sleep. This is
# despite the LS having processed requests already. I don't know what causes this, but sleeping
# one second helps. It may be that sleeping only once is enough but that's hard to reliably test.
# It may be that even this 1sec is not enough in larger TS projects, at some point we should find what
# causes this and solve it.
sleep(1)
return await super()._send_references_request(relative_file_path, line, column)