How To Extend the Default LanguageClient¶
There will likely come a point where you will want to modify some aspect of the default language client’s behaviour - or replace it entirely with your own. This guide will walk through the various options for adjusting the client and its behaviour
Adding new methods¶
If you need to add support to the client for an LSP method it does not yet support, this can be done inside your setup fixture.
@pytest_lsp.fixture(
config=ClientServerConfig(server_command=[sys.executable, "server.py"]),
)
async def client(lsp_client: LanguageClient):
# Register a custom `workspace/diagnostic/refresh` implementation
lsp_client.refresh_requests = 0
@lsp_client.feature(types.WORKSPACE_DIAGNOSTIC_REFRESH)
def refresh(cl: LanguageClient, params: None):
cl.refresh_requests += 1
# Setup
await lsp_client.initialize_session(
types.InitializeParams(capabilities=types.ClientCapabilities())
)
yield
# Teardown
await lsp_client.shutdown_session()
Replacing methods¶
Replacing an existing method’s implementation will require you to setup your own client factory function.
First write your custom method implementation. As an example, we’ll fail the test if the server tries to send diagnostics via the textDocument/publishDiagnostics notification.
def disallow_publish_diagnostics( client: LanguageClient, params: types.PublishDiagnosticsParams ): """Raises an error if the server calls ``textDocument/publishDiagnostics``""" raise RuntimeError("The server should not use `textDocument/publishDiagnostics`")
Write your own client factory function, you will need to construct your own mapping from lsp method names to the corresponding handler functions.
The
DEFAULT_CLIENT_FEATURESdictionary will include all of the built in handlers.Pass your mapping and client instance to the
register_lsp_features()function to register them.from pygls.protocol import default_converter from pytest_lsp.client import DEFAULT_CLIENT_FEATURES, register_lsp_features def my_make_test_lsp_client() -> LanguageClient: """Return a customised ``LanguageClient`` instance""" client = LanguageClient( converter_factory=default_converter, ) features = { **DEFAULT_CLIENT_FEATURES, types.TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS: disallow_publish_diagnostics, } register_lsp_features(client, features) return client
Finally, use your custom client factory function with the
ClientServerConfigyou pass to your fixture function.@pytest_lsp.fixture( config=ClientServerConfig( client_factory=my_make_test_lsp_client, server_command=[sys.executable, "server.py"], ), ) async def client(lsp_client: LanguageClient): # Setup await lsp_client.initialize_session( types.InitializeParams(capabilities=types.ClientCapabilities()) ) yield # Teardown await lsp_client.shutdown_session()
Using a Custom Client Class¶
Using your own custom LanguageClient class is very similar to Replacing methods, just create an instance of your language client in your factory function.
Important
Your custom language client must inherit from the default LanguageCient class
from pygls.protocol import default_converter
from pytest_lsp import LanguageClient
from pytest_lsp.client import DEFAULT_CLIENT_FEATURES, register_lsp_features
class MyLanguageClient(LanguageClient):
pass
def my_make_test_lsp_client() -> LanguageClient:
"""Return a custom language client"""
client = MyLanguageClient(
converter_factory=default_converter,
)
register_lsp_features(client, DEFAULT_CLIENT_FEATURES)
return client