multilspy icon indicating copy to clipboard operation
multilspy copied to clipboard

Make it simpler to start up a language server with a known path

Open jason-sachs opened this issue 9 months ago • 2 comments

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.

jason-sachs avatar May 02 '25 18:05 jason-sachs

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")

jason-sachs avatar May 02 '25 18:05 jason-sachs

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)

jason-sachs avatar May 02 '25 19:05 jason-sachs