Make it simpler to start up a language server with a known path
I've looked at https://github.com/microsoft/multilspy/blob/main/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py and it looks like it auto-locates / auto-installs clangd.
This supports uneducated users well, and removes the need for end users to worry about installing clangd... but I don't think that's everyone's use case. In my case, I've installed clangd already and I know where it is.
I would like to just point multilspy at where clangd is, and start hacking.
Please help support this use case in the future.
This kind of create classmethod in LanguageServer seems like a bit of an antipattern; there is no value added here, so why not just let someone subclass LanguageServer?
@classmethod
def create(cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str) -> "LanguageServer":
"""
Creates a language specific LanguageServer instance based on the given configuration, and appropriate settings for the programming language.
If language is Java, then ensure that jdk-17.0.6 or higher is installed, `java` is in PATH, and JAVA_HOME is set to the installation directory.
If language is JS/TS, then ensure that node (v18.16.0 or higher) is installed and in PATH.
:param repository_root_path: The root path of the repository.
:param config: The Multilspy configuration.
:param logger: The logger to use.
:return LanguageServer: A language specific LanguageServer instance.
"""
if config.code_language == Language.PYTHON:
from multilspy.language_servers.jedi_language_server.jedi_server import (
JediServer,
)
return JediServer(config, logger, repository_root_path)
elif config.code_language == Language.JAVA:
from multilspy.language_servers.eclipse_jdtls.eclipse_jdtls import (
EclipseJDTLS,
)
return EclipseJDTLS(config, logger, repository_root_path)
elif config.code_language == Language.KOTLIN:
from multilspy.language_servers.kotlin_language_server.kotlin_language_server import (
KotlinLanguageServer,
)
return KotlinLanguageServer(config, logger, repository_root_path)
elif config.code_language == Language.RUST:
from multilspy.language_servers.rust_analyzer.rust_analyzer import (
RustAnalyzer,
)
return RustAnalyzer(config, logger, repository_root_path)
elif config.code_language == Language.CSHARP:
from multilspy.language_servers.omnisharp.omnisharp import OmniSharp
return OmniSharp(config, logger, repository_root_path)
elif config.code_language in [Language.TYPESCRIPT, Language.JAVASCRIPT]:
from multilspy.language_servers.typescript_language_server.typescript_language_server import (
TypeScriptLanguageServer,
)
return TypeScriptLanguageServer(config, logger, repository_root_path)
elif config.code_language == Language.GO:
from multilspy.language_servers.gopls.gopls import Gopls
return Gopls(config, logger, repository_root_path)
elif config.code_language == Language.RUBY:
from multilspy.language_servers.solargraph.solargraph import Solargraph
return Solargraph(config, logger, repository_root_path)
elif config.code_language == Language.DART:
from multilspy.language_servers.dart_language_server.dart_language_server import DartLanguageServer
return DartLanguageServer(config, logger, repository_root_path)
elif config.code_language == Language.CPP:
from multilspy.language_servers.clangd_language_server.clangd_language_server import ClangdLanguageServer
return ClangdLanguageServer(config, logger, repository_root_path)
else:
logger.log(f"Language {config.code_language} is not supported", logging.ERROR)
raise MultilspyException(f"Language {config.code_language} is not supported")
Argh. I wish multilspy included a simple lower-level library, and just took care of the HTTP-like aspects (providing Content-Length, etc.), so I could do something like:
config = ...
lsp = LanguageServer(config, 'path/to/code', 'path/to/clangd')
with lsp.start_server():
result = lsp.request("textDocument/formatting",
options={"insertSpaces":true,"tabSize":2},
textDocument={"uri":"file:///path/to/somefile.c"})
do_something_with(result)