From 5d33a301f30b8bba786c7e7e1d5a74dc06992bda Mon Sep 17 00:00:00 2001 From: Michael Panchenko <35432522+MischaPanch@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:36:21 +0200 Subject: [PATCH] Allow passing LS implementation specific options. (#524) * Allow passing LS implementation specific options. Made use of it in csharp and intelephense LS * Bidirectional mapping from Language to the corresponding LS implementation Use this as key for ls_specifics. Fix to swift installation in CI authored-by: maksim.r --- .github/workflows/pytest.yml | 4 +- src/serena/agent.py | 1 + src/serena/cli.py | 3 +- src/serena/config/serena_config.py | 3 + src/serena/project.py | 10 +- src/serena/resources/project.template.yml | 1 - .../resources/serena_config.template.yml | 7 + src/serena/util/file_system.py | 2 +- .../csharp_language_server.py | 23 ++- src/solidlsp/language_servers/intelephense.py | 22 ++- src/solidlsp/ls.py | 151 ++---------------- src/solidlsp/ls_config.py | 124 +++++++++++++- src/solidlsp/settings.py | 5 +- 13 files changed, 198 insertions(+), 158 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 1515cb56..b99bf7ab 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -106,6 +106,9 @@ jobs: if: runner.os == 'Linux' run: | echo "=== Installing swiftly on Ubuntu ===" + # Install dependencies BEFORE Swift to avoid exit code 1 + sudo apt-get update + sudo apt-get -y install libcurl4-openssl-dev curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz && \ tar zxf swiftly-$(uname -m).tar.gz && \ ./swiftly init --quiet-shell-followup && \ @@ -113,7 +116,6 @@ jobs: hash -r swiftly install --use 6.1.2 swiftly use 6.1.2 - sudo apt-get -y install libcurl4-openssl-dev echo "=== Adding Swift toolchain to PATH ===" echo "$HOME/.local/share/swiftly/bin" >> $GITHUB_PATH echo "Swiftly installed successfully!" diff --git a/src/serena/agent.py b/src/serena/agent.py index 4191985e..90be1420 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -589,6 +589,7 @@ class SerenaAgent: log_level=self.serena_config.log_level, ls_timeout=ls_timeout, trace_lsp_communication=self.serena_config.trace_lsp_communication, + ls_specific_settings=self.serena_config.ls_specific_settings, ) log.info(f"Starting the language server for {self._active_project.project_name}") self.language_server.start() diff --git a/src/serena/cli.py b/src/serena/cli.py index 40d11340..ce59a7cd 100644 --- a/src/serena/cli.py +++ b/src/serena/cli.py @@ -451,9 +451,10 @@ class ProjectCommands(AutoRegisteringGroup): def _index_project(project: str, log_level: str, timeout: float) -> None: lvl = logging.getLevelNamesMapping()[log_level.upper()] logging.configure(level=lvl) + serena_config = SerenaConfig.from_config_file() proj = Project.load(os.path.abspath(project)) click.echo(f"Indexing symbols in project {project}…") - ls = proj.create_language_server(log_level=lvl, ls_timeout=timeout) + ls = proj.create_language_server(log_level=lvl, ls_timeout=timeout, ls_specific_settings=serena_config.ls_specific_settings) log_file = os.path.join(project, ".serena", "logs", "indexing.txt") collected_exceptions: list[Exception] = [] diff --git a/src/serena/config/serena_config.py b/src/serena/config/serena_config.py index 2eac7512..a0b57983 100644 --- a/src/serena/config/serena_config.py +++ b/src/serena/config/serena_config.py @@ -355,6 +355,8 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin): Even though the value of the max_answer_chars can be changed when calling the tool, it may make sense to adjust this default through the global configuration. """ + ls_specific_settings: dict = field(default_factory=dict) + """Advanced configuration option allowing to configure language server implementation specific options, see SolidLSPSettings for more info.""" CONFIG_FILE = "serena_config.yml" CONFIG_FILE_DOCKER = "serena_config.docker.yml" # Docker-specific config file; auto-generated if missing, mounted via docker-compose for user customization @@ -459,6 +461,7 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin): "token_count_estimator", RegisteredTokenCountEstimator.TIKTOKEN_GPT4O.name ) instance.default_max_tool_answer_chars = loaded_commented_yaml.get("default_max_tool_answer_chars", 150_000) + instance.ls_specific_settings = loaded_commented_yaml.get("ls_specific_settings", {}) # re-save the configuration file if any migrations were performed if num_project_migrations > 0: diff --git a/src/serena/project.py b/src/serena/project.py index d2da0d0b..9288209a 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -1,6 +1,7 @@ import logging import os from pathlib import Path +from typing import Any import pathspec @@ -277,6 +278,7 @@ class Project: log_level: int = logging.INFO, ls_timeout: float | None = DEFAULT_TOOL_TIMEOUT - 5, trace_lsp_communication: bool = False, + ls_specific_settings: dict[Language, Any] | None = None, ) -> SolidLanguageServer: """ Create a language server for a project. Note that you will have to start it @@ -287,6 +289,8 @@ class Project: :param log_level: the log level for the language server :param ls_timeout: the timeout for the language server :param trace_lsp_communication: whether to trace LSP communication + :param ls_specific_settings: optional LS specific configuration of the language server, + see docstrings in the inits of subclasses of SolidLanguageServer to see what values may be passed. :return: the language server """ ls_config = LanguageServerConfig( @@ -302,5 +306,9 @@ class Project: ls_logger, self.project_root, timeout=ls_timeout, - solidlsp_settings=SolidLSPSettings(solidlsp_dir=SERENA_MANAGED_DIR_IN_HOME, project_data_relative_path=SERENA_MANAGED_DIR_NAME), + solidlsp_settings=SolidLSPSettings( + solidlsp_dir=SERENA_MANAGED_DIR_IN_HOME, + project_data_relative_path=SERENA_MANAGED_DIR_NAME, + ls_specific_settings=ls_specific_settings, + ), ) diff --git a/src/serena/resources/project.template.yml b/src/serena/resources/project.template.yml index a51cfcf5..59ab269c 100644 --- a/src/serena/resources/project.template.yml +++ b/src/serena/resources/project.template.yml @@ -19,7 +19,6 @@ ignored_paths: [] # Added on 2025-04-18 read_only: false - # list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. # Below is the complete list of tools for convenience. # To make sure you have the latest list of tools, and to view their descriptions, diff --git a/src/serena/resources/serena_config.template.yml b/src/serena/resources/serena_config.template.yml index 9d7bce7c..082c0c32 100644 --- a/src/serena/resources/serena_config.template.yml +++ b/src/serena/resources/serena_config.template.yml @@ -33,6 +33,13 @@ trace_lsp_communication: False # whether to trace the communication between Serena and the language servers. # This is useful for debugging language server issues. +ls_specific_settings: {} +# Added on 23.08.2025 +# Advanced configuration option allowing to configure language server implementation specific options. Maps the language +# (same entry as in project.yml) to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp to see which options are available. +# No documentation on options means no options are available + tool_timeout: 240 # timeout, in seconds, after which tool executions are terminated diff --git a/src/serena/util/file_system.py b/src/serena/util/file_system.py index 6f9739af..15ba3ba3 100644 --- a/src/serena/util/file_system.py +++ b/src/serena/util/file_system.py @@ -154,7 +154,7 @@ class GitignoreParser: """ queue: list[str] = [self.repo_root] - def scan(abs_path: str | None): + def scan(abs_path: str | None) -> Iterator[str]: for entry in os.scandir(abs_path): if entry.is_dir(follow_symlinks=follow_symlinks): queue.append(entry.path) diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 9bc60c81..e5d7fa1c 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -174,6 +174,9 @@ class CSharpLanguageServer(SolidLanguageServer): """ Provides C# specific instantiation of the LanguageServer class using Microsoft.CodeAnalysis.LanguageServer. This is the official Roslyn-based language server from Microsoft. + + You can pass the following entries in ls_specific_settings["csharp"]: + - dotnet_runtime_url: will override the URL from RUNTIME_DEPENDENCIES """ def __init__( @@ -269,7 +272,7 @@ class CSharpLanguageServer(SolidLanguageServer): @classmethod def _ensure_dotnet_runtime( - cls, logger: LanguageServerLogger, runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings + cls, logger: LanguageServerLogger, dotnet_runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings ) -> str: """Ensure .NET runtime is available and return the dotnet executable path.""" # Check if dotnet is already available on the system @@ -285,7 +288,7 @@ class CSharpLanguageServer(SolidLanguageServer): pass # Download .NET 9 runtime using config - return cls._ensure_dotnet_runtime_from_config(logger, runtime_dep, solidlsp_settings) + return cls._ensure_dotnet_runtime_from_config(logger, dotnet_runtime_dep, solidlsp_settings) @classmethod def _ensure_language_server( @@ -404,7 +407,7 @@ class CSharpLanguageServer(SolidLanguageServer): @classmethod def _ensure_dotnet_runtime_from_config( - cls, logger: LanguageServerLogger, runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings + cls, logger: LanguageServerLogger, dotnet_runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings ) -> str: """ Ensure .NET 9 runtime is available using runtime dependency configuration. @@ -424,7 +427,7 @@ class CSharpLanguageServer(SolidLanguageServer): # Download .NET 9 runtime using config dotnet_dir = Path(cls.ls_resources_dir(solidlsp_settings)) / "dotnet-runtime-9.0" - dotnet_exe = dotnet_dir / runtime_dep.binary_name + dotnet_exe = dotnet_dir / dotnet_runtime_dep.binary_name if dotnet_exe.exists(): logger.log(f"Using cached .NET runtime from {dotnet_exe}", logging.INFO) @@ -434,8 +437,16 @@ class CSharpLanguageServer(SolidLanguageServer): logger.log("Downloading .NET 9 runtime...", logging.INFO) dotnet_dir.mkdir(parents=True, exist_ok=True) - url = runtime_dep.url - archive_type = runtime_dep.archive_type + custom_dotnet_runtime_url = solidlsp_settings.ls_specific_settings.get(cls.get_language_enum_instance(), {}).get( + "dotnet_runtime_url" + ) + if custom_dotnet_runtime_url is not None: + logger.log(f"Using custom .NET runtime url: {custom_dotnet_runtime_url}", logging.INFO) + url = custom_dotnet_runtime_url + else: + url = dotnet_runtime_dep.url + + archive_type = dotnet_runtime_dep.archive_type # Download the runtime download_path = dotnet_dir / f"dotnet-runtime.{archive_type}" diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index ee3a61b9..b9e86619 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -24,6 +24,10 @@ from .common import RuntimeDependency, RuntimeDependencyCollection class Intelephense(SolidLanguageServer): """ Provides PHP specific instantiation of the LanguageServer class using Intelephense. + + You can pass the following entries in ls_specific_settings["php"]: + - maxMemory + - maxFileSize """ @override @@ -98,10 +102,9 @@ class Intelephense(SolidLanguageServer): ) self.request_id = 0 - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ - Returns the initialize params for the Intelephense Language Server. + Returns the initialization params for the Intelephense Language Server. """ root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { @@ -123,12 +126,21 @@ class Intelephense(SolidLanguageServer): } ], } - + initialization_options = {} # Add license key if provided via environment variable license_key = os.environ.get("INTELEPHENSE_LICENSE_KEY") if license_key: - initialize_params["initializationOptions"] = {"licenceKey": license_key} + initialization_options["licenceKey"] = license_key + custom_intelephense_settings = self._solidlsp_settings.ls_specific_settings.get(self.get_language_enum_instance(), {}) + max_memory = custom_intelephense_settings.get("maxMemory") + max_file_size = custom_intelephense_settings.get("maxFileSize") + if max_memory is not None: + initialization_options["intelephense.maxMemory"] = max_memory + if max_file_size is not None: + initialization_options["intelephense.files.maxSize"] = max_file_size + + initialize_params["initializationOptions"] = initialization_options return initialize_params def _start_server(self): diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 2b09d98e..8a371df0 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -94,6 +94,10 @@ class SolidLanguageServer(ABC): """ return dirname.startswith(".") + @classmethod + def get_language_enum_instance(cls) -> Language: + return Language.from_ls_class(cls) + @classmethod def ls_resources_dir(cls, solidlsp_settings: SolidLSPSettings, mkdir: bool = True) -> str: """ @@ -131,154 +135,21 @@ class SolidLanguageServer(ABC): 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 config: language server configuration. :param logger: The logger to use. :param timeout: the timeout for requests to the language server. If None, no timeout will be used. + :param solidlsp_settings: additional settings :return LanguageServer: A language specific LanguageServer instance. """ ls: SolidLanguageServer if solidlsp_settings is None: solidlsp_settings = SolidLSPSettings() - if config.code_language == Language.PYTHON: - from solidlsp.language_servers.pyright_server import ( - PyrightServer, - ) - - ls = PyrightServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - elif config.code_language == Language.PYTHON_JEDI: - from solidlsp.language_servers.jedi_server import JediServer - - ls = JediServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - elif config.code_language == Language.JAVA: - from solidlsp.language_servers.eclipse_jdtls import ( - EclipseJDTLS, - ) - - ls = EclipseJDTLS(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.KOTLIN: - from solidlsp.language_servers.kotlin_language_server import ( - KotlinLanguageServer, - ) - - ls = KotlinLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.RUST: - from solidlsp.language_servers.rust_analyzer import ( - RustAnalyzer, - ) - - ls = RustAnalyzer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.CSHARP: - from solidlsp.language_servers.csharp_language_server import CSharpLanguageServer - - ls = CSharpLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - elif config.code_language == Language.CSHARP_OMNISHARP: - from solidlsp.language_servers.omnisharp import OmniSharp - - ls = OmniSharp(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - elif config.code_language == Language.TYPESCRIPT: - from solidlsp.language_servers.typescript_language_server import ( - TypeScriptLanguageServer, - ) - - ls = TypeScriptLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - elif config.code_language == Language.TYPESCRIPT_VTS: - # VTS based Language Server implementation, need to experiment to see if it improves performance - from solidlsp.language_servers.vts_language_server import VtsLanguageServer - - ls = VtsLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - elif config.code_language == Language.GO: - from solidlsp.language_servers.gopls import Gopls - - ls = Gopls(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.RUBY: - from solidlsp.language_servers.ruby_lsp import RubyLsp - - ls = RubyLsp(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.RUBY_SOLARGRAPH: - from solidlsp.language_servers.solargraph import Solargraph - - ls = Solargraph(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.DART: - from solidlsp.language_servers.dart_language_server import DartLanguageServer - - ls = DartLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.CPP: - from solidlsp.language_servers.clangd_language_server import ClangdLanguageServer - - ls = ClangdLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.PHP: - from solidlsp.language_servers.intelephense import Intelephense - - ls = Intelephense(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.R: - from solidlsp.language_servers.r_language_server import RLanguageServer - - ls = RLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.CLOJURE: - from solidlsp.language_servers.clojure_lsp import ClojureLSP - - ls = ClojureLSP(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.ELIXIR: - from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools - - ls = ElixirTools(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.TERRAFORM: - from solidlsp.language_servers.terraform_ls import TerraformLS - - ls = TerraformLS(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.SWIFT: - from solidlsp.language_servers.sourcekit_lsp import SourceKitLSP - - ls = SourceKitLSP(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.BASH: - from solidlsp.language_servers.bash_language_server import BashLanguageServer - - ls = BashLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.ZIG: - from solidlsp.language_servers.zls import ZigLanguageServer - - ls = ZigLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.NIX: - from solidlsp.language_servers.nixd_ls import NixLanguageServer - - ls = NixLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.LUA: - from solidlsp.language_servers.lua_ls import LuaLanguageServer - - ls = LuaLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.ERLANG: - from solidlsp.language_servers.erlang_language_server import ErlangLanguageServer - - ls = ErlangLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - elif config.code_language == Language.AL: - from solidlsp.language_servers.al_language_server import ALLanguageServer - - ls = ALLanguageServer(config, logger, repository_root_path, solidlsp_settings=solidlsp_settings) - - else: - logger.log(f"Language {config.code_language} is not supported", logging.ERROR) - raise SolidLSPException(f"Language {config.code_language} is not supported") - + ls_class = config.code_language.get_ls_class() + # For now, we assume that all language server implementations have the same signature of the constructor + # (which, unfortunately, differs from the signature of the base class). + # If this assumption is ever violated, we need branching logic here. + ls = ls_class(config, logger, repository_root_path, solidlsp_settings) # type: ignore ls.set_request_timeout(timeout) return ls diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index c07e3b34..e32c9027 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -6,7 +6,10 @@ import fnmatch from collections.abc import Iterable from dataclasses import dataclass, field from enum import Enum -from typing import Self +from typing import TYPE_CHECKING, Self + +if TYPE_CHECKING: + from solidlsp import SolidLanguageServer class FilenameMatcher: @@ -136,6 +139,125 @@ class Language(str, Enum): case _: raise ValueError(f"Unhandled language: {self}") + def get_ls_class(self) -> type["SolidLanguageServer"]: + match self: + case self.PYTHON: + from solidlsp.language_servers.pyright_server import PyrightServer + + return PyrightServer + case self.PYTHON_JEDI: + from solidlsp.language_servers.jedi_server import JediServer + + return JediServer + case self.JAVA: + from solidlsp.language_servers.eclipse_jdtls import EclipseJDTLS + + return EclipseJDTLS + case self.KOTLIN: + from solidlsp.language_servers.kotlin_language_server import KotlinLanguageServer + + return KotlinLanguageServer + case self.RUST: + from solidlsp.language_servers.rust_analyzer import RustAnalyzer + + return RustAnalyzer + case self.CSHARP: + from solidlsp.language_servers.csharp_language_server import CSharpLanguageServer + + return CSharpLanguageServer + case self.CSHARP_OMNISHARP: + from solidlsp.language_servers.omnisharp import OmniSharp + + return OmniSharp + case self.TYPESCRIPT: + from solidlsp.language_servers.typescript_language_server import TypeScriptLanguageServer + + return TypeScriptLanguageServer + case self.TYPESCRIPT_VTS: + from solidlsp.language_servers.vts_language_server import VtsLanguageServer + + return VtsLanguageServer + case self.GO: + from solidlsp.language_servers.gopls import Gopls + + return Gopls + case self.RUBY: + from solidlsp.language_servers.solargraph import Solargraph + + return Solargraph + case self.DART: + from solidlsp.language_servers.dart_language_server import DartLanguageServer + + return DartLanguageServer + case self.CPP: + from solidlsp.language_servers.clangd_language_server import ClangdLanguageServer + + return ClangdLanguageServer + case self.PHP: + from solidlsp.language_servers.intelephense import Intelephense + + return Intelephense + case self.CLOJURE: + from solidlsp.language_servers.clojure_lsp import ClojureLSP + + return ClojureLSP + case self.ELIXIR: + from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools + + return ElixirTools + case self.TERRAFORM: + from solidlsp.language_servers.terraform_ls import TerraformLS + + return TerraformLS + case self.SWIFT: + from solidlsp.language_servers.sourcekit_lsp import SourceKitLSP + + return SourceKitLSP + case self.BASH: + from solidlsp.language_servers.bash_language_server import BashLanguageServer + + return BashLanguageServer + case self.ZIG: + from solidlsp.language_servers.zls import ZigLanguageServer + + return ZigLanguageServer + case self.NIX: + from solidlsp.language_servers.nixd_ls import NixLanguageServer + + return NixLanguageServer + case self.LUA: + from solidlsp.language_servers.lua_ls import LuaLanguageServer + + return LuaLanguageServer + case self.ERLANG: + from solidlsp.language_servers.erlang_language_server import ErlangLanguageServer + + return ErlangLanguageServer + case self.AL: + from solidlsp.language_servers.al_language_server import ALLanguageServer + + return ALLanguageServer + case self.R: + from solidlsp.language_servers.r_language_server import RLanguageServer + + return RLanguageServer + case _: + raise ValueError(f"Unhandled language: {self}") + + @classmethod + def from_ls_class(cls, ls_class: type["SolidLanguageServer"]) -> Self: + """ + Get the Language enum value from a SolidLanguageServer class. + + :param ls_class: The SolidLanguageServer class to find the corresponding Language for + :return: The Language enum value + :raises ValueError: If the language server class is not supported + """ + for enum_instance in cls: + if enum_instance.get_ls_class() == ls_class: + return enum_instance + raise ValueError(f"Unhandled language server class: {ls_class}") + @dataclass class LanguageServerConfig: diff --git a/src/solidlsp/settings.py b/src/solidlsp/settings.py index 73acd2d6..00644254 100644 --- a/src/solidlsp/settings.py +++ b/src/solidlsp/settings.py @@ -4,7 +4,8 @@ Defines settings for Solid-LSP import os import pathlib -from dataclasses import dataclass +from dataclasses import dataclass, field +from typing import Any @dataclass @@ -19,6 +20,8 @@ class SolidLSPSettings: For instance, if this is ".solidlsp" and the project is located at "/home/user/myproject", then Solid-LSP will store project-specific data in "/home/user/myproject/.solidlsp". """ + ls_specific_settings: dict[str, Any] = field(default_factory=dict) + """Mapping from language server class names to any specifics that the language server may make use of.""" def __post_init__(self): os.makedirs(str(self.solidlsp_dir), exist_ok=True)