Adapt RustAnalyzer to solidlsp

This commit is contained in:
Dominik Jain committed 2025-06-21 15:40:46 +02:00
1 parent befd13b0ea
commit cf7e4eadcc
1 file changed
+36 -48
@@ -2,27 +2,25 @@
Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust.
""" """
import asyncio
import json import json
import logging import logging
import os import os
import stat
import pathlib import pathlib
from contextlib import asynccontextmanager import stat
from typing import AsyncIterator import threading
from overrides import override 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.lsp_protocol_handler.lsp_types import InitializeParams
from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo
from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_config import MultilspyConfig
from multilspy.multilspy_logger import MultilspyLogger
from multilspy.multilspy_utils import FileUtils from multilspy.multilspy_utils import FileUtils
from multilspy.multilspy_utils import PlatformUtils from multilspy.multilspy_utils import PlatformUtils
from solidlsp.ls import SolidLanguageServer
class RustAnalyzer(LanguageServer): class RustAnalyzer(SolidLanguageServer):
""" """
Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust.
""" """
@@ -39,7 +37,10 @@ class RustAnalyzer(LanguageServer):
ProcessLaunchInfo(cmd=rustanalyzer_executable_path, cwd=repository_root_path), ProcessLaunchInfo(cmd=rustanalyzer_executable_path, cwd=repository_root_path),
"rust", "rust",
) )
self.server_ready = asyncio.Event() self.server_ready = threading.Event()
self.service_ready_event = threading.Event()
self.initialize_searcher_command_available = threading.Event()
self.resolve_main_method_available = threading.Event()
@override @override
def is_ignored_dirname(self, dirname: str) -> bool: def is_ignored_dirname(self, dirname: str) -> bool:
@@ -108,22 +109,12 @@ class RustAnalyzer(LanguageServer):
return d return d
@asynccontextmanager def _start_server(self):
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. Starts the Rust Analyzer Language Server
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): def register_capability_handler(params):
assert "registrations" in params assert "registrations" in params
for registration in params["registrations"]: for registration in params["registrations"]:
if registration["method"] == "workspace/executeCommand": if registration["method"] == "workspace/executeCommand":
@@ -131,24 +122,24 @@ class RustAnalyzer(LanguageServer):
self.resolve_main_method_available.set() self.resolve_main_method_available.set()
return return
async def lang_status_handler(params): def lang_status_handler(params):
# TODO: Should we wait for # TODO: Should we wait for
# server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}}
# Before proceeding? # Before proceeding?
if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": if params["type"] == "ServiceReady" and params["message"] == "ServiceReady":
self.service_ready_event.set() self.service_ready_event.set()
async def execute_client_command_handler(params): def execute_client_command_handler(params):
return [] return []
async def do_nothing(params): def do_nothing(params):
return return
async def check_experimental_status(params): def check_experimental_status(params):
if params["quiescent"] == True: if params["quiescent"] == True:
self.server_ready.set() self.server_ready.set()
async def window_log_message(msg): def window_log_message(msg):
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
self.server.on_request("client/registerCapability", register_capability_handler) self.server.on_request("client/registerCapability", register_capability_handler)
@@ -160,26 +151,23 @@ class RustAnalyzer(LanguageServer):
self.server.on_notification("language/actionableNotification", do_nothing) self.server.on_notification("language/actionableNotification", do_nothing)
self.server.on_notification("experimental/serverStatus", check_experimental_status) self.server.on_notification("experimental/serverStatus", check_experimental_status)
async with super().start_server(): self.logger.log("Starting RustAnalyzer server process", logging.INFO)
self.logger.log("Starting RustAnalyzer server process", logging.INFO) self.server.start()
await self.server.start() initialize_params = self._get_initialize_params(self.repository_root_path)
initialize_params = self._get_initialize_params(self.repository_root_path)
self.logger.log( self.logger.log(
"Sending initialize request from LSP client to LSP server and awaiting response", "Sending initialize request from LSP client to LSP server and awaiting response",
logging.INFO, logging.INFO,
) )
init_response = await self.server.send.initialize(initialize_params) init_response = self.server.send.initialize(initialize_params)
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 assert init_response["capabilities"]["textDocumentSync"]["change"] == 2
assert "completionProvider" in init_response["capabilities"] assert "completionProvider" in init_response["capabilities"]
assert init_response["capabilities"]["completionProvider"] == { assert init_response["capabilities"]["completionProvider"] == {
"resolveProvider": True, "resolveProvider": True,
"triggerCharacters": [":", ".", "'", "("], "triggerCharacters": [":", ".", "'", "("],
"completionItem": {"labelDetailsSupport": True}, "completionItem": {"labelDetailsSupport": True},
} }
self.server.notify.initialized({}) self.server.notify.initialized({})
self.completions_available.set() self.completions_available.set()
await self.server_ready.wait() self.server_ready.wait()
yield self