diff --git a/.serena/memories/adding_new_language_support_guide.md b/.serena/memories/adding_new_language_support_guide.md index 2ac65b31..6eeed48e 100644 --- a/.serena/memories/adding_new_language_support_guide.md +++ b/.serena/memories/adding_new_language_support_guide.md @@ -67,31 +67,45 @@ You should look at at least one existing implementation of each base class to un ### 1.2 LSP Initialization -Override initialization methods if needed: +Override `_create_base_initialize_params` to provide server-specific initialization +parameters. The common keys — `processId`, `rootPath`, `rootUri`, `clientInfo` and +`workspaceFolders` — are set centrally by the `InitializeParamsBuilder` (see +`src/solidlsp/initialize_params.py`), so your override MUST NOT set them. Just return +the server-specific settings (typically `capabilities` and `initializationOptions`): ```python -def _get_initialize_params(self) -> InitializeParams: - """Return language-specific initialization parameters.""" +def _create_base_initialize_params(self) -> dict: + """Return language-specific initialization parameters (server-specific keys only).""" return { - "processId": os.getpid(), - "rootUri": PathUtils.path_to_uri(self.repository_root_path), "capabilities": { # Language-specific capabilities - } + }, + # "initializationOptions": {...}, # if the server needs them } def _start_server(self): """Start the language server with custom handlers.""" # Set up notification handlers self.server.on_notification("window/logMessage", self._handle_log_message) - - # Start server and initialize + + # Start server and initialize. Do NOT call _create_base_initialize_params directly; + # _create_initialize_params() wraps it with the builder to add the common keys. self.server.start() - init_response = self.server.send.initialize(self._get_initialize_params()) - + init_response = self.server.send.initialize(self._create_initialize_params()) + self.server.notify.initialized({}) ``` +Notes: +- The builder resolves `workspaceFolders` from the language server config (indexed + folders + `ls_additional_workspace_folders`); don't build the folder list yourself. +- To send a folder list nested inside `initializationOptions` (some servers, e.g. + `EclipseJDTLS`/`KotlinLanguageServer`, need this), set it explicitly there — only the + *top-level* `workspaceFolders` is builder-managed. +- To suppress the top-level `workspaceFolders` entirely, override + `_create_initialize_params_builder` and construct `DefaultInitializeParamsBuilder` + with `set_workspace_folders=False`. + After `_start_server` returns, the language server should be fully operational. If the server requires that one waits for certain notifications or responses before being ready, implement that logic here. For an example, see `EclipseJDTLS._start_server`. diff --git a/.serena/project.yml b/.serena/project.yml index f738bebf..e2cafdba 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -1,23 +1,25 @@ -# the name by which the project can be referenced within Serena +# the name by which the project can be referenced within Serena/when chatting with the LLM. project_name: "serena" - -# list of languages for which language servers are started; choose from: -# al angular ansible bash clojure -# cpp cpp_ccls crystal csharp csharp_omnisharp -# dart elixir elm erlang fortran -# fsharp go groovy haskell haxe -# hlsl html java json julia -# kotlin lean4 lua luau markdown +# list of languages for which language servers are started (LSP backend only); choose from: +# ada al angular ansible bash +# bsl clojure cpp cpp_ccls crystal +# csharp csharp_omnisharp cue dart elixir +# elm erlang fortran fsharp gdscript +# go groovy haskell haxe hlsl +# html java json julia kotlin +# latex lean4 lua luau markdown # matlab msl nix ocaml pascal -# perl php php_phpactor powershell python -# python_jedi python_ty r rego ruby -# ruby_solargraph rust scala scss solidity -# svelte swift systemverilog terraform toml -# typescript typescript_vts vue yaml zig -# (This list may be outdated. For the current list, see values of Language enum here: -# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py -# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# perl php php_phpactor php_phpantom powershell +# python python_jedi python_pyrefly python_ty r +# rego ruby ruby_solargraph rust scala +# scss solidity svelte swift systemverilog +# terraform toml typescript typescript_vts vue +# yaml zig +# (This list may be outdated; generated with scripts/print_language_list.py; +# For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py) +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) # Note: # - For C, use cpp # - For JavaScript, use typescript @@ -148,17 +150,6 @@ ls_specific_settings: {} # See https://oraios.github.io/serena/02-usage/050_configuration.html#modes added_modes: -# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos). -# Paths can be absolute or relative to the project root. -# Each folder is registered as an LSP workspace folder, enabling language servers to discover -# symbols and references across package boundaries. -# Currently supported for: TypeScript. -# Example: -# additional_workspace_folders: -# - ../sibling-package -# - ../shared-lib -additional_workspace_folders: [] - # optional shell command to run before the language backend (LSP or JetBrains) is initialised. # the command runs in the project root directory and is only executed if the project is trusted # (see trusted_project_path_patterns in the global configuration). @@ -171,3 +162,26 @@ activation_command: # maximum time in seconds to wait for activation_command to complete before killing it (default 180s). # must be a positive number. activation_command_timeout: 180.0 + +# list of workspace folder paths (LSP backend only). +# These folders will be used to build up Serena's symbol index. +# Paths must be within the project root and should thus be relative to the project root. +# Furthermore, the paths should not be filtered by ignore settings. +# Default setting: The entire project root folder (".") is considered. +# In (large) monorepos, this can be used to index only subfolders of the project root, e.g. +# ls_workspace_folders: +# - "./subproject1" +# - "./subproject2" +ls_workspace_folders: +- "." + +# list of additional workspace folder paths for cross-package reference support. +# Paths can be absolute or relative to the project root. +# Each folder is registered as an LSP workspace folder, enabling language servers to discover +# symbols and references across package boundaries, but these folders are not indexed by Serena, +# i.e. the respective symbols will not be found using Serena's symbol search tool. +# Example: +# additional_workspace_folders: +# - "../sibling-package" +# - "../shared-lib" +ls_additional_workspace_folders: [] diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c34aae5..7c951222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,11 @@ Status of the `main` branch. Changes prior to the next official version change w - Add **LaTeX** support (experimental) via [texlab](https://github.com/latex-lsp/texlab). - PHP: add support for PHPantom as alternative to the already supported PHP LS #1554. - Add new launch command customization options: `ls_args`, `ls_extra_args` and `ls_base_cmd` + - Add new configuration option `ls_workspace_folders` to allow indexed source folders to be specified + explicitly. In monorepos, this allows the set of indexed folders to be restricted to a subset of + the repository. #1627 + - Rename configuration option `additional_workspace_folders` to `ls_additional_workspace_folders` + and support the option across all language servers (previously limited to TypeScript). * JetBrains: - Add configuration option `jetbrains_launch_command`, allowing Serena to spawn IDE instances automatically diff --git a/docs/02-usage/040_workflow.md b/docs/02-usage/040_workflow.md index fa710498..b95c6f90 100644 --- a/docs/02-usage/040_workflow.md +++ b/docs/02-usage/040_workflow.md @@ -51,7 +51,7 @@ The file allows you to configure ... * the encoding used in source files * ignore rules * write access - * [additional workspace folders](additional-workspace-folders) for cross-package reference support in monorepos + * the list of workspace folders to be processed by language servers (when using the LSP backend) * an initial prompt that shall be passed to the LLM whenever the project is activated * a shell command to run upon project activation (prior to language backend initialisation) * the set of tools and modes to use for the project @@ -70,33 +70,6 @@ You can specify local overrides for the settings in a `project.local.yml` file i (which, by default, is ignored by git). Any keys defined therein will override the respective key in `project.yml`. -(additional-workspace-folders)= -#### Additional Workspace Folders (Cross-Package References) - -In monorepos or multi-package setups, Serena's language server normally only sees symbols within the -project root. To enable cross-package references (e.g. `find_referencing_symbols` discovering usages -in sibling packages), configure `additional_workspace_folders` in your `project.yml`: - -```yaml -additional_workspace_folders: - - ../shared-lib - - ../api-client - - /absolute/path/to/another-package -``` - -Paths can be absolute or relative to the project root. Each folder is registered as an LSP workspace -folder, and the language server will discover symbols and references across all listed packages. - -**Currently supported for:** TypeScript. Other language servers will raise an error if this setting -is used with them. Support for additional languages can be added by implementing the -`_find_representative_source_file()` method in the respective language server class. - -:::{note} -Each additional workspace folder adds startup time, as the language server needs to index the -additional projects. For large monorepos, consider listing only the packages you actively need -cross-references for. -::: - (indexing)= ### Indexing diff --git a/docs/02-usage/050_configuration.md b/docs/02-usage/050_configuration.md index dd8aa138..3429de33 100644 --- a/docs/02-usage/050_configuration.md +++ b/docs/02-usage/050_configuration.md @@ -1002,9 +1002,6 @@ Supported settings: | `npm_registry` | `null` | Override the npm registry Serena uses for the managed install. | | `indexing_timeout` | `30.0` | Timeout in seconds for waiting on tsserver's `$/progress` project-indexing signal (both at startup and before the first cross-file reference query). If indexing does not complete within this window, Serena logs a warning and proceeds anyway. Increase it for very large projects. | -TypeScript supports [additional workspace folders](additional-workspace-folders) for cross-package -reference discovery. Configure `additional_workspace_folders` in `project.yml` to enable this feature. - #### Svelte Serena uses `svelte-language-server` for the `svelte` language key. Use `svelte` for Svelte projects instead of also listing `typescript`, unless you intentionally want multiple language servers active for the same files. diff --git a/src/serena/config/serena_config.py b/src/serena/config/serena_config.py index 390eb2b2..6cb2a721 100644 --- a/src/serena/config/serena_config.py +++ b/src/serena/config/serena_config.py @@ -303,7 +303,8 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): project_name: str languages: list[Language] ignored_paths: list[str] = field(default_factory=list) - additional_workspace_folders: list[str] = field(default_factory=list) + ls_workspace_folders: list[str] = field(default_factory=lambda: ["."]) + ls_additional_workspace_folders: list[str] = field(default_factory=list) read_only: bool = False ignore_all_files_in_gitignore: bool = True initial_prompt: str = "" @@ -318,6 +319,7 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): SERENA_PROJECT_FILE = "project.yml" SERENA_LOCAL_PROJECT_FILE = "project.local.yml" FIELDS_WITHOUT_DEFAULTS = {"project_name", "languages"} + RENAMED_FIELDS = {"additional_workspace_folders": "ls_additional_workspace_folders"} YAML_COMMENT_NORMALISATION = YamlCommentNormalisation.LEADING """ the comment normalisation strategy to use when loading/saving project configuration files. @@ -443,9 +445,22 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): and `was_complete` will always be True. """ data = load_yaml(yml_path, comment_normalisation=comment_normalisation) + was_complete = True + + # backward compatibility + # NOTE: This must also work for project.local.yml files, which may be highly incomplete + # * handle single "language" field + if "languages" not in data and "language" in data: + data["languages"] = [data["language"]] + del data["language"] + # * handle renamed fields + for old_key, new_key in cls.RENAMED_FIELDS.items(): + if old_key in data and new_key not in data: + data[new_key] = data[old_key] + del data[old_key] + was_complete = False # apply defaults - was_complete = True if apply_defaults: for field_info in dataclasses.fields(cls): key = field_info.name @@ -458,13 +473,6 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): default_value = get_dataclass_default(cls, key) data.setdefault(key, default_value) - # backward compatibility - # NOTE: This must also work for project.local.yml files, which may be highly incomplete - # * handle single "language" field - if "languages" not in data and "language" in data: - data["languages"] = [data["language"]] - del data["language"] - # Note: Checks for validity of fields must not happen here but in _from_dict. # Here, the data may be incomplete, because this function is also used for # loading project.local.yml files. @@ -480,6 +488,7 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): the ProjectConfig dataclass :param local_override_keys: the list of keys that have been overridden from project.local.yml """ + # map languages to list of enum items, checking for errors lang_name_mapping = {"javascript": "typescript"} languages: list[Language] = [] for language_str in data["languages"]: @@ -526,7 +535,7 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): fixed_tools = data["fixed_tools"] or [] excluded_tools = data["excluded_tools"] or [] included_optional_tools = data["included_optional_tools"] or [] - additional_workspace_folders = data.get("additional_workspace_folders") or [] + additional_workspace_folders = data.get("ls_additional_workspace_folders") or [] if "base_modes" in data and data["base_modes"] is not None: log.warning("The base_modes setting in project.yml is deprecated and will be ignored.") @@ -535,7 +544,8 @@ class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes): project_name=data["project_name"], languages=languages, ignored_paths=ignored_paths, - additional_workspace_folders=additional_workspace_folders, + ls_workspace_folders=data["ls_workspace_folders"], + ls_additional_workspace_folders=additional_workspace_folders, excluded_tools=excluded_tools, fixed_tools=fixed_tools, included_optional_tools=included_optional_tools, diff --git a/src/serena/ls_manager.py b/src/serena/ls_manager.py index 11d6feab..3b44a9af 100644 --- a/src/serena/ls_manager.py +++ b/src/serena/ls_manager.py @@ -5,7 +5,7 @@ from collections.abc import Iterator from sensai.util.logging import LogTime -from serena.config.serena_config import SerenaPaths +from serena.config.serena_config import ProjectConfig, SerenaPaths from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.settings import SolidLSPSettings @@ -22,25 +22,27 @@ class LanguageServerFactory: def __init__( self, project_root: str, + project_config: ProjectConfig, project_data_path: str, encoding: str, ignored_patterns: list[str], ls_timeout: float | None = None, ls_specific_settings: dict | None = None, - additional_workspace_folders: list[str] | None = None, trace_lsp_communication: bool = False, ): self.project_root = project_root + self.project_config = project_config self.project_data_path = project_data_path self.encoding = encoding self.ignored_patterns = ignored_patterns self.ls_timeout = ls_timeout self.ls_specific_settings = ls_specific_settings - self.additional_workspace_folders = additional_workspace_folders or [] self.trace_lsp_communication = trace_lsp_communication def create_language_server(self, language: Language) -> SolidLanguageServer: ls_config = LanguageServerConfig( + workspace_folders=self.project_config.ls_workspace_folders, + additional_workspace_folders=self.project_config.ls_additional_workspace_folders, code_language=language, ignored_paths=self.ignored_patterns, trace_lsp_communication=self.trace_lsp_communication, @@ -56,7 +58,6 @@ class LanguageServerFactory: solidlsp_dir=SerenaPaths().serena_user_home_dir, project_data_path=self.project_data_path, ls_specific_settings=self.ls_specific_settings or {}, - additional_workspace_folders=self.additional_workspace_folders, ), ) diff --git a/src/serena/project.py b/src/serena/project.py index 8152a52a..87a54adb 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -466,12 +466,12 @@ class Project(ToStringMixin): ) factory = LanguageServerFactory( project_root=self.project_root, + project_config=self.project_config, project_data_path=self._serena_data_folder, encoding=self.project_config.encoding, ignored_patterns=self._ignored_patterns, ls_timeout=ls_timeout, ls_specific_settings=ls_specific_settings, - additional_workspace_folders=self.project_config.additional_workspace_folders, trace_lsp_communication=self.serena_config.trace_lsp_communication, ) self.language_server_manager = LanguageServerManager.from_languages(self.project_config.languages, factory) diff --git a/src/serena/resources/project.template.yml b/src/serena/resources/project.template.yml index 5d0b7441..aa4247d7 100644 --- a/src/serena/resources/project.template.yml +++ b/src/serena/resources/project.template.yml @@ -1,23 +1,25 @@ -# the name by which the project can be referenced within Serena +# the name by which the project can be referenced within Serena/when chatting with the LLM. project_name: "project_name" - -# list of languages for which language servers are started; choose from: -# al angular ansible bash clojure -# cpp cpp_ccls crystal csharp csharp_omnisharp -# dart elixir elm erlang fortran -# fsharp go groovy haskell haxe -# hlsl html java json julia -# kotlin lean4 lua luau markdown +# list of languages for which language servers are started (LSP backend only); choose from: +# ada al angular ansible bash +# bsl clojure cpp cpp_ccls crystal +# csharp csharp_omnisharp cue dart elixir +# elm erlang fortran fsharp gdscript +# go groovy haskell haxe hlsl +# html java json julia kotlin +# latex lean4 lua luau markdown # matlab msl nix ocaml pascal -# perl php php_phpactor powershell python -# python_jedi python_ty r rego ruby -# ruby_solargraph rust scala scss solidity -# svelte swift systemverilog terraform toml -# typescript typescript_vts vue yaml zig -# (This list may be outdated. For the current list, see values of Language enum here: -# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py -# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# perl php php_phpactor php_phpantom powershell +# python python_jedi python_pyrefly python_ty r +# rego ruby ruby_solargraph rust scala +# scss solidity svelte swift systemverilog +# terraform toml typescript typescript_vts vue +# yaml zig +# (This list may be outdated; generated with scripts/print_language_list.py; +# For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py) +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) # Note: # - For C, use cpp # - For JavaScript, use typescript @@ -71,16 +73,27 @@ ignore_all_files_in_gitignore: true # See https://oraios.github.io/serena/02-usage/050_configuration.html#language-server-specific-settings ls_specific_settings: {} -# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos). +# list of workspace folder paths (LSP backend only). +# These folders will be used to build up Serena's symbol index. +# Paths must be within the project root and should thus be relative to the project root. +# Furthermore, the paths should not be filtered by ignore settings. +# Default setting: The entire project root folder (".") is considered. +# In (large) monorepos, this can be used to index only subfolders of the project root, e.g. +# ls_workspace_folders: +# - "./subproject1" +# - "./subproject2" +ls_workspace_folders: ["."] + +# list of additional workspace folder paths for cross-package reference support. # Paths can be absolute or relative to the project root. # Each folder is registered as an LSP workspace folder, enabling language servers to discover -# symbols and references across package boundaries. -# Currently supported for: TypeScript. +# symbols and references across package boundaries, but these folders are not indexed by Serena, +# i.e. the respective symbols will not be found using Serena's symbol search tools. # Example: # additional_workspace_folders: # - ../sibling-package # - ../shared-lib -additional_workspace_folders: [] +ls_additional_workspace_folders: [] # list of additional paths to ignore in this project. # Same syntax as gitignore, so you can use * and **. diff --git a/src/solidlsp/initialize_params.py b/src/solidlsp/initialize_params.py new file mode 100644 index 00000000..4c3a8912 --- /dev/null +++ b/src/solidlsp/initialize_params.py @@ -0,0 +1,69 @@ +import logging +import os +import pathlib +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, Self, cast + +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, WorkspaceFolder + +if TYPE_CHECKING: + from solidlsp import SolidLanguageServer + + +log = logging.getLogger(__name__) + + +class InitializeParamsBuilder(ABC): + def __init__(self): + self._params = {} + + def with_base_options(self, options: dict | InitializeParams) -> Self: + self._params.update(options) + return self + + def _set(self, key: str, value: Any) -> None: + if key in self._params: + log.debug("Overriding existing option '%s' with new value: %s (old value: %s)", key, value, self._params[key]) + self._params[key] = value + + @abstractmethod + def _apply_updates(self) -> None: + """ + Applies implementation-specific updates to the options. + """ + + def build(self) -> InitializeParams: + self._apply_updates() + return cast(InitializeParams, self._params) + + +class DefaultInitializeParamsBuilder(InitializeParamsBuilder): + def __init__(self, ls: "SolidLanguageServer", set_workspace_folders: bool = True): + super().__init__() + self._ls = ls + self._set_workspace_folders = set_workspace_folders + + @staticmethod + def _create_workspace_folder_entry(path: str) -> WorkspaceFolder: + abs_path = os.path.abspath(path) + return {"uri": pathlib.Path(abs_path).as_uri(), "name": os.path.basename(abs_path)} + + def _apply_updates(self): + root_abs_path = self._ls.repository_root_path + + self._set("processId", os.getpid()) + self._set("rootPath", root_abs_path) + self._set("rootUri", pathlib.Path(root_abs_path).as_uri()) + self._set("clientInfo", {"name": "Serena"}) + + if self._set_workspace_folders: + abs_workspace_paths = self._ls.config.get_absolute_workspace_folders(root_abs_path) + log.info("Workspace folders: %s", abs_workspace_paths) + workspace_folders = [self._create_workspace_folder_entry(abs_path) for abs_path in abs_workspace_paths] + additional_abs_workspace_paths = [ + p for p in self._ls.config.get_absolute_additional_workspace_folders(root_abs_path) if p not in abs_workspace_paths + ] + if additional_abs_workspace_paths: + log.info("Additional workspace folders (not indexed by SolidLSP): %s", additional_abs_workspace_paths) + workspace_folders.extend([self._create_workspace_folder_entry(abs_path) for abs_path in additional_abs_workspace_paths]) + self._set("workspaceFolders", workspace_folders) diff --git a/src/solidlsp/language_servers/ada_language_server.py b/src/solidlsp/language_servers/ada_language_server.py index bf015bd4..30040ec2 100644 --- a/src/solidlsp/language_servers/ada_language_server.py +++ b/src/solidlsp/language_servers/ada_language_server.py @@ -9,15 +9,12 @@ pragmas/aspects in source rather than file extension, so a single import logging import os -import pathlib import threading -from typing import cast from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -155,15 +152,9 @@ class AdaLanguageServer(SolidLanguageServer): def _create_dependency_provider(self) -> LanguageServerDependencyProvider: return self.DependencyProvider(self._custom_settings, self._ls_resources_dir) - def _get_initialize_params(self) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Returns the initialization params for the Ada Language Server.""" - repository_absolute_path = self.repository_root_path - root_uri = pathlib.Path(repository_absolute_path).as_uri() - result = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "workspace": { "applyEdit": True, @@ -188,9 +179,8 @@ class AdaLanguageServer(SolidLanguageServer): }, "initializationOptions": {}, "trace": "off", - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, result) + return result def _start_server(self) -> None: """Start the Ada Language Server process and complete the LSP handshake.""" @@ -213,7 +203,7 @@ class AdaLanguageServer(SolidLanguageServer): log.info("Starting ada_language_server process") self.server.start() - initialize_params = self._get_initialize_params() + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/al_language_server.py b/src/solidlsp/language_servers/al_language_server.py index f954dbc7..f67ee2ba 100644 --- a/src/solidlsp/language_servers/al_language_server.py +++ b/src/solidlsp/language_servers/al_language_server.py @@ -7,7 +7,6 @@ You can pass the following entries in ``ls_specific_settings["al"]``: import logging import os -import pathlib import platform import re import stat @@ -448,20 +447,12 @@ class ALLanguageServer(SolidLanguageServer): return None - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> dict: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the AL Language Server. """ - # Ensure we have an absolute path for URI generation - repository_path = pathlib.Path(repository_absolute_path).resolve() - root_uri = repository_path.as_uri() - # AL requires extensive capabilities based on VS Code trace initialize_params = { - "processId": os.getpid(), - "rootPath": str(repository_path), - "rootUri": root_uri, "capabilities": { "workspace": { "applyEdit": True, @@ -512,7 +503,6 @@ class ALLanguageServer(SolidLanguageServer): }, }, "trace": "verbose", - "workspaceFolders": [{"uri": root_uri, "name": repository_path.name}], } return initialize_params @@ -556,12 +546,12 @@ class ALLanguageServer(SolidLanguageServer): self.server.start() # Send initialize request - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to AL LSP server and awaiting response") # Send initialize and wait for response - resp = self.server.send_request("initialize", initialize_params) + resp = self.server.send_request("initialize", dict(initialize_params)) if resp is None: raise RuntimeError("AL Language Server initialization failed - no response") diff --git a/src/solidlsp/language_servers/angular_language_server.py b/src/solidlsp/language_servers/angular_language_server.py index 03d7414e..5edd1dca 100644 --- a/src/solidlsp/language_servers/angular_language_server.py +++ b/src/solidlsp/language_servers/angular_language_server.py @@ -166,8 +166,8 @@ class AngularTypeScriptServer(TypeScriptLanguageServer): ) @override - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - params = super()._get_initialize_params(repository_absolute_path) + def _create_base_initialize_params(self) -> dict: + params = super()._create_base_initialize_params() # Load @angular/language-service as a tsserver plugin via typescript-language-server's # initializationOptions.plugins API (the same API Vue uses for @vue/typescript-plugin). params["initializationOptions"] = { diff --git a/src/solidlsp/language_servers/ansible_language_server.py b/src/solidlsp/language_servers/ansible_language_server.py index 3fb1a3b6..39edcdb1 100644 --- a/src/solidlsp/language_servers/ansible_language_server.py +++ b/src/solidlsp/language_servers/ansible_language_server.py @@ -6,7 +6,6 @@ Contains various configurations and settings specific to Ansible YAML files (pla import fnmatch import logging import os -import pathlib import shutil from typing import Any, ClassVar @@ -15,7 +14,6 @@ from overrides import override from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -217,14 +215,12 @@ class AnsibleLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Returns the initialize params for the Ansible Language Server. Reads shortcut keys and the ``ansible_settings`` dict from ``_custom_settings`` to build ``initializationOptions``. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - # default ansible settings, populated from shortcut keys ansible_settings: dict[str, Any] = { "ansible": { @@ -282,18 +278,9 @@ class AnsibleLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "initializationOptions": {"ansible": ansible_settings}, } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Starts the Ansible Language Server, waits for the server to be ready.""" @@ -325,7 +312,7 @@ class AnsibleLanguageServer(SolidLanguageServer): log.info("Starting Ansible server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/bash_language_server.py b/src/solidlsp/language_servers/bash_language_server.py index 0338a5ef..5d52bbbc 100644 --- a/src/solidlsp/language_servers/bash_language_server.py +++ b/src/solidlsp/language_servers/bash_language_server.py @@ -5,7 +5,6 @@ Contains various configurations and settings specific to Bash scripting. import logging import os -import pathlib import shutil import threading @@ -19,7 +18,6 @@ from solidlsp.ls import ( ) from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import FileUtils, PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -208,12 +206,10 @@ class BashLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "start"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Bash Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -237,17 +233,8 @@ class BashLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -283,7 +270,7 @@ class BashLanguageServer(SolidLanguageServer): log.info("Starting Bash server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/bsl_language_server.py b/src/solidlsp/language_servers/bsl_language_server.py index 98aaa0e1..b3fc8f89 100644 --- a/src/solidlsp/language_servers/bsl_language_server.py +++ b/src/solidlsp/language_servers/bsl_language_server.py @@ -14,7 +14,6 @@ You can configure the following options in ls_specific_settings (in serena_confi import logging import os -import pathlib import re import shutil import subprocess @@ -27,7 +26,6 @@ from solidlsp.ls import ( SolidLanguageServer, ) from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -166,10 +164,8 @@ class BSLLanguageServer(SolidLanguageServer): _verify_java_available() return ["java", "-jar", core_path] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - root_uri = pathlib.Path(repository_absolute_path).as_uri() - return { # type: ignore[return-value] + def _create_base_initialize_params(self) -> dict: + return { "locale": "en", "capabilities": { "textDocument": { @@ -179,9 +175,9 @@ class BSLLanguageServer(SolidLanguageServer): "documentSymbol": { "dynamicRegistration": True, "hierarchicalDocumentSymbolSupport": True, - "symbolKind": {"valueSet": list(range(1, 27))}, # type: ignore + "symbolKind": {"valueSet": list(range(1, 27))}, }, - "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, # type: ignore + "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, "rename": {"dynamicRegistration": True, "prepareSupport": True}, }, "workspace": { @@ -190,12 +186,6 @@ class BSLLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - {"uri": root_uri, "name": os.path.basename(repository_absolute_path)}, - ], } def _start_server(self) -> None: @@ -216,7 +206,7 @@ class BSLLanguageServer(SolidLanguageServer): log.info("Starting BSL language server process") self.server.start() - init_params = self._get_initialize_params(self.repository_root_path) + init_params = self._create_initialize_params() init_response = self.server.send.initialize(init_params) log.debug("BSL LSP initialize response: %s", init_response) diff --git a/src/solidlsp/language_servers/ccls_language_server.py b/src/solidlsp/language_servers/ccls_language_server.py index a27d80d6..1c7c1fa6 100644 --- a/src/solidlsp/language_servers/ccls_language_server.py +++ b/src/solidlsp/language_servers/ccls_language_server.py @@ -29,10 +29,8 @@ https://github.com/MaskRay/ccls """ import logging -import os -import pathlib import threading -from typing import Any, cast +from typing import Any from overrides import override @@ -43,7 +41,6 @@ from solidlsp.ls import ( SolidLanguageServer, ) from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -105,12 +102,10 @@ class CCLS(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the ccls Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -123,18 +118,9 @@ class CCLS(SolidLanguageServer): }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], # ccls supports initializationOptions but none are required for basic functionality } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -154,7 +140,7 @@ class CCLS(SolidLanguageServer): log.info("Starting ccls server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to ccls and awaiting response") self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index b5c5d40a..4db49125 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -2,10 +2,9 @@ import hashlib import json import logging import os -import pathlib import threading from collections.abc import Hashable -from typing import Any, cast +from typing import Any from overrides import override @@ -13,7 +12,6 @@ from solidlsp.language_servers.common import UE_IGNORED_DIRNAMES from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, ProcessLaunchInfo, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection, is_unreal_engine_project @@ -315,12 +313,10 @@ class ClangdLanguageServer(SolidLanguageServer): # which is required for finding cross-file references return [core_path, "--background-index"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the clangd Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -336,18 +332,9 @@ class ClangdLanguageServer(SolidLanguageServer): }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -403,7 +390,7 @@ class ClangdLanguageServer(SolidLanguageServer): log.info("Starting Clangd server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index 1835ce99..2a8554d1 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -9,13 +9,11 @@ import re import shutil import subprocess import threading -from typing import cast from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -308,10 +306,8 @@ class ClojureLSP(SolidLanguageServer): log.info(f"clojure-lsp source-paths scanned from project descriptors: {scanned}") return scanned - def _get_initialize_params(self) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Returns the init params for clojure-lsp.""" - repository_absolute_path = self.repository_root_path - root_uri = pathlib.Path(repository_absolute_path).as_uri() source_paths = self._resolve_source_paths() initialization_options: dict = {"dependency-scheme": "jar", "text-document-sync-kind": "incremental"} @@ -319,9 +315,6 @@ class ClojureLSP(SolidLanguageServer): initialization_options["source-paths"] = source_paths result = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "workspace": { "applyEdit": True, @@ -344,9 +337,8 @@ class ClojureLSP(SolidLanguageServer): }, "initializationOptions": initialization_options, "trace": "off", - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, result) + return result def _start_server(self) -> None: def register_capability_handler(params: dict) -> None: @@ -389,7 +381,7 @@ class ClojureLSP(SolidLanguageServer): log.info("Starting clojure-lsp server process") self.server.start() - initialize_params = self._get_initialize_params() + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/crystal_language_server.py b/src/solidlsp/language_servers/crystal_language_server.py index 75ce704f..2e04c995 100644 --- a/src/solidlsp/language_servers/crystal_language_server.py +++ b/src/solidlsp/language_servers/crystal_language_server.py @@ -3,14 +3,11 @@ Provides Crystal specific instantiation of the LanguageServer class using Crysta """ import logging -import os -import pathlib import shutil import time from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -83,12 +80,10 @@ class CrystalLanguageServer(SolidLanguageServer): log.info(f"Waiting {remaining_delay:.1f}s for Crystalline to compile the project") time.sleep(remaining_delay) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Return the initialize params for the Crystal language server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -119,17 +114,8 @@ class CrystalLanguageServer(SolidLanguageServer): "configuration": True, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """Start the Crystal language server process.""" @@ -150,7 +136,7 @@ class CrystalLanguageServer(SolidLanguageServer): log.info("Starting Crystal language server (Crystalline) process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 296acad0..1281c5a0 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -26,7 +26,7 @@ from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException from solidlsp.ls_types import Hover from solidlsp.ls_utils import FileUtils, PathUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, InitializeResult +from solidlsp.lsp_protocol_handler.lsp_types import InitializeResult from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -486,60 +486,51 @@ class CSharpLanguageServer(SolidLanguageServer): except Exception as e: raise SolidLSPException(f"Failed to download package {package_name} version {package_version} from NuGet.org: {e}") from e - def _get_initialize_params(self) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Microsoft.CodeAnalysis.LanguageServer. """ - root_uri = PathUtils.path_to_uri(self.repository_root_path) - root_name = os.path.basename(self.repository_root_path) - return cast( - InitializeParams, - { - "workspaceFolders": [{"uri": root_uri, "name": root_name}], - "processId": os.getpid(), - "rootPath": self.repository_root_path, - "rootUri": root_uri, - "capabilities": { - "window": { - "workDoneProgress": True, - "showMessage": {"messageActionItem": {"additionalPropertiesSupport": True}}, - "showDocument": {"support": True}, + return { + "capabilities": { + "window": { + "workDoneProgress": True, + "showMessage": {"messageActionItem": {"additionalPropertiesSupport": True}}, + "showDocument": {"support": True}, + }, + "workspace": { + "applyEdit": True, + "workspaceEdit": {"documentChanges": True}, + "didChangeConfiguration": {"dynamicRegistration": True}, + "didChangeWatchedFiles": {"dynamicRegistration": True}, + "symbol": { + "dynamicRegistration": True, + "symbolKind": {"valueSet": list(range(1, 27))}, }, - "workspace": { - "applyEdit": True, - "workspaceEdit": {"documentChanges": True}, - "didChangeConfiguration": {"dynamicRegistration": True}, - "didChangeWatchedFiles": {"dynamicRegistration": True}, - "symbol": { - "dynamicRegistration": True, - "symbolKind": {"valueSet": list(range(1, 27))}, + "executeCommand": {"dynamicRegistration": True}, + "configuration": True, + "workspaceFolders": True, + "workDoneProgress": True, + }, + "textDocument": { + "synchronization": {"dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True}, + "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, + "signatureHelp": { + "dynamicRegistration": True, + "signatureInformation": { + "documentationFormat": ["markdown", "plaintext"], + "parameterInformation": {"labelOffsetSupport": True}, }, - "executeCommand": {"dynamicRegistration": True}, - "configuration": True, - "workspaceFolders": True, - "workDoneProgress": True, }, - "textDocument": { - "synchronization": {"dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True}, - "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, - "signatureHelp": { - "dynamicRegistration": True, - "signatureInformation": { - "documentationFormat": ["markdown", "plaintext"], - "parameterInformation": {"labelOffsetSupport": True}, - }, - }, - "definition": {"dynamicRegistration": True}, - "references": {"dynamicRegistration": True}, - "documentSymbol": { - "dynamicRegistration": True, - "symbolKind": {"valueSet": list(range(1, 27))}, - "hierarchicalDocumentSymbolSupport": True, - }, + "definition": {"dynamicRegistration": True}, + "references": {"dynamicRegistration": True}, + "documentSymbol": { + "dynamicRegistration": True, + "symbolKind": {"valueSet": list(range(1, 27))}, + "hierarchicalDocumentSymbolSupport": True, }, }, }, - ) + } def _start_server(self) -> None: indexing_complete = threading.Event() @@ -672,7 +663,7 @@ class CSharpLanguageServer(SolidLanguageServer): raise SolidLSPException(f"Failed to start C# language server: {e}") # Send initialization - initialize_params = self._get_initialize_params() + initialize_params = self._create_initialize_params() log.info("Sending initialize request to language server") try: diff --git a/src/solidlsp/language_servers/cue_language_server.py b/src/solidlsp/language_servers/cue_language_server.py index b9a90a33..444c5f26 100644 --- a/src/solidlsp/language_servers/cue_language_server.py +++ b/src/solidlsp/language_servers/cue_language_server.py @@ -7,15 +7,12 @@ from __future__ import annotations import logging import os -import pathlib import threading -from typing import cast from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -184,15 +181,9 @@ class CueLanguageServer(SolidLanguageServer): # cue's LSP mode is activated via the `lsp` subcommand; it speaks LSP over stdio. return [core_path, "lsp"] - def _get_initialize_params(self) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Returns the init params for ``cue lsp``.""" - repository_absolute_path = self.repository_root_path - root_uri = pathlib.Path(repository_absolute_path).as_uri() - result = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "workspace": { "applyEdit": True, @@ -221,9 +212,8 @@ class CueLanguageServer(SolidLanguageServer): }, "initializationOptions": {}, "trace": "off", - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, result) + return result def _start_server(self) -> None: def register_capability_handler(params: dict) -> None: @@ -252,7 +242,7 @@ class CueLanguageServer(SolidLanguageServer): log.info("Starting cue lsp server process") self.server.start() - initialize_params = self._get_initialize_params() + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index 800f69eb..59fd4327 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -1,8 +1,6 @@ import logging import os -import pathlib from collections.abc import Hashable -from typing import cast from overrides import override @@ -11,7 +9,6 @@ from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings from ..ls_config import Language, LanguageServerConfig -from ..lsp_protocol_handler.lsp_types import InitializeParams from .common import RuntimeDependency, RuntimeDependencyCollection log = logging.getLogger(__name__) @@ -149,12 +146,10 @@ class DartLanguageServer(SolidLanguageServer): return f"{dart_executable_path} language-server --client-id multilspy.dart --client-version 1.2" - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Dart Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "capabilities": { "textDocument": { @@ -171,18 +166,9 @@ class DartLanguageServer(SolidLanguageServer): "allowOpenUri": False, }, "trace": "verbose", - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": pathlib.Path(repository_absolute_path).as_uri(), - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -212,7 +198,7 @@ class DartLanguageServer(SolidLanguageServer): log.info("Starting dart-language-server server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.debug("Sending initialize request to dart-language-server") init_response = self.server.send_request("initialize", initialize_params) # type: ignore log.info(f"Received initialize response from dart-language-server: {init_response}") diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 4614d9fe..320f7ed1 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -25,7 +25,7 @@ from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException from solidlsp.ls_types import UnifiedSymbolInformation from solidlsp.ls_utils import FileUtils, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import DocumentSymbol, InitializeParams, SymbolInformation +from solidlsp.lsp_protocol_handler.lsp_types import DocumentSymbol, SymbolInformation from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -904,15 +904,13 @@ class EclipseJDTLS(SolidLanguageServer): log.info(f"Using bundled JRE for JDTLS: {java_home}") return {"syntaxserver": "false", "JAVA_HOME": java_home} - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize parameters for the EclipseJDTLS server. """ # Look into https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java to understand all the options available - if not os.path.isabs(repository_absolute_path): - repository_absolute_path = os.path.abspath(repository_absolute_path) - repo_uri = pathlib.Path(repository_absolute_path).as_uri() + repo_uri = pathlib.Path(self.repository_root_path).as_uri() # Load user's Maven and Gradle configuration paths from ls_specific_settings["java"] @@ -996,8 +994,6 @@ class EclipseJDTLS(SolidLanguageServer): initialize_params = { "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": pathlib.Path(repository_absolute_path).as_uri(), "capabilities": { "workspace": { "applyEdit": True, @@ -1290,13 +1286,6 @@ class EclipseJDTLS(SolidLanguageServer): }, }, "trace": "verbose", - "processId": os.getpid(), - "workspaceFolders": [ - { - "uri": repo_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } initialize_params["initializationOptions"]["workspaceFolders"] = [repo_uri] @@ -1324,7 +1313,7 @@ class EclipseJDTLS(SolidLanguageServer): if self.runtime_dependency_paths.gradle_path is not None: gradle_settings["home"] = self.runtime_dependency_paths.gradle_path gradle_settings["java"] = {"home": gradle_java_home if gradle_java_home is not None else self.runtime_dependency_paths.jre_path} - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -1377,7 +1366,7 @@ class EclipseJDTLS(SolidLanguageServer): log.info("Starting EclipseJDTLS server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py index 6005df89..d80e43e3 100644 --- a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py +++ b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py @@ -5,14 +5,13 @@ import stat import subprocess import threading from collections.abc import Hashable -from typing import Any, cast +from typing import Any from overrides import override from solidlsp.ls import RawDocumentSymbol, SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_utils import FileUtils, PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -256,19 +255,12 @@ class ElixirTools(SolidLanguageServer): return None - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Expert Language Server. """ - # Ensure the path is absolute - abs_path = os.path.abspath(repository_absolute_path) - root_uri = pathlib.Path(abs_path).as_uri() initialize_params = { - "processId": os.getpid(), "locale": "en", - "rootPath": abs_path, - "rootUri": root_uri, "initializationOptions": { "mix_env": "dev", "mix_target": "host", @@ -319,10 +311,9 @@ class ElixirTools(SolidLanguageServer): "workDoneProgress": True, }, }, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """Start Expert server process""" @@ -381,7 +372,7 @@ class ElixirTools(SolidLanguageServer): log.debug("Starting Expert server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.debug("Sending initialize request to Expert") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/elm_language_server.py b/src/solidlsp/language_servers/elm_language_server.py index e5823811..bd488500 100644 --- a/src/solidlsp/language_servers/elm_language_server.py +++ b/src/solidlsp/language_servers/elm_language_server.py @@ -4,7 +4,6 @@ Provides Elm specific instantiation of the LanguageServer class. Contains variou import logging import os -import pathlib import shutil import threading @@ -14,7 +13,6 @@ from sensai.util.logging import LogTime from solidlsp import ls_types from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -131,13 +129,10 @@ class ElmLanguageServer(SolidLanguageServer): ) return [elm_ls_executable_path, "--stdio"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Elm Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params = { "locale": "en", "capabilities": { @@ -169,17 +164,8 @@ class ElmLanguageServer(SolidLanguageServer): "skipInstallPackageConfirmation": True, "onlyUpdateDiagnosticsOnSave": False, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """ @@ -204,7 +190,7 @@ class ElmLanguageServer(SolidLanguageServer): log.info("Starting Elm server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/fortran_language_server.py b/src/solidlsp/language_servers/fortran_language_server.py index 39498971..134460ab 100644 --- a/src/solidlsp/language_servers/fortran_language_server.py +++ b/src/solidlsp/language_servers/fortran_language_server.py @@ -3,8 +3,6 @@ Fortran Language Server implementation using fortls. """ import logging -import os -import pathlib import re from overrides import override @@ -19,7 +17,6 @@ from solidlsp.ls import ( SolidLanguageServer, ) from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -190,10 +187,8 @@ class FortranLanguageServer(SolidLanguageServer): version_setting_key="fortls_version", ) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Initialize params for Fortran Language Server.""" - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -231,17 +226,8 @@ class FortranLanguageServer(SolidLanguageServer): }, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """Start Fortran Language Server process.""" @@ -264,7 +250,7 @@ class FortranLanguageServer(SolidLanguageServer): log.info("Starting Fortran Language Server (fortls) process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request to Fortran Language Server") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/fsharp_language_server.py b/src/solidlsp/language_servers/fsharp_language_server.py index a8cd6847..4b4fc820 100644 --- a/src/solidlsp/language_servers/fsharp_language_server.py +++ b/src/solidlsp/language_servers/fsharp_language_server.py @@ -4,7 +4,6 @@ Provides F# specific instantiation of the LanguageServer class. import logging import os -import pathlib import shutil import threading from pathlib import Path @@ -16,7 +15,6 @@ from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependenc from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -129,17 +127,11 @@ class FSharpLanguageServer(SolidLanguageServer): # FsAutoComplete uses --lsp flag for LSP mode return f"{fsautocomplete_path} --adaptive-lsp-server-enabled --project-graph-enabled --use-fcs-transparent-compiler" - def _get_initialize_params(self) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the F# Language Server. """ - root_uri = pathlib.Path(self.repository_root_path).as_uri() - initialize_params = { - "processId": os.getpid(), - "rootPath": self.repository_root_path, - "rootUri": root_uri, - "workspaceFolders": [{"name": "workspace", "uri": root_uri}], "capabilities": { "workspace": { "applyEdit": True, @@ -269,7 +261,7 @@ class FSharpLanguageServer(SolidLanguageServer): "trace": "off", } - return initialize_params # type: ignore + return initialize_params def _get_dotnet_root(self) -> str: """ @@ -362,7 +354,7 @@ class FSharpLanguageServer(SolidLanguageServer): raise SolidLSPException(f"Failed to start F# language server: {e}") # Send initialization - initialize_params = self._get_initialize_params() + initialize_params = self._create_initialize_params() log.info("Sending initialize request to F# language server") try: diff --git a/src/solidlsp/language_servers/godot_language_server.py b/src/solidlsp/language_servers/godot_language_server.py index 453f0f92..d0ea8c3f 100644 --- a/src/solidlsp/language_servers/godot_language_server.py +++ b/src/solidlsp/language_servers/godot_language_server.py @@ -8,13 +8,11 @@ The editor must be open with its built-in language server enabled (default). import logging import os -import pathlib from collections.abc import Callable from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_process import LanguageServerInterface, TCPConnectionInfo, TCPLanguageServer -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo, StringDict from solidlsp.settings import SolidLSPSettings @@ -93,14 +91,8 @@ class GodotLanguageServer(SolidLanguageServer): request_timeout=request_timeout, ) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - root_uri = pathlib.Path(repository_absolute_path).as_uri() + def _create_base_initialize_params(self) -> dict: params = { - "processId": os.getpid(), - "rootUri": root_uri, - "rootPath": repository_absolute_path, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -125,7 +117,7 @@ class GodotLanguageServer(SolidLanguageServer): }, }, } - return params # type: ignore + return params def _start_server(self) -> None: def do_nothing(params: dict) -> None: @@ -143,7 +135,7 @@ class GodotLanguageServer(SolidLanguageServer): log.info("Connecting to Godot LSP at %s:%d", self._conn_info.host, self._conn_info.port) self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending LSP initialize request to Godot") self.server.send.initialize(initialize_params) self.server.notify.initialized({}) diff --git a/src/solidlsp/language_servers/gopls.py b/src/solidlsp/language_servers/gopls.py index 3c098178..44164927 100644 --- a/src/solidlsp/language_servers/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -2,16 +2,14 @@ import hashlib import json import logging import os -import pathlib import subprocess from collections.abc import Hashable -from typing import Any, cast +from typing import Any from overrides import override from solidlsp.ls import RawDocumentSymbol, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -103,11 +101,10 @@ class Gopls(SolidLanguageServer): super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd="gopls", cwd=repository_root_path), "go", solidlsp_settings) self.request_id = 0 - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Go Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params: dict = { "locale": "en", "capabilities": { @@ -122,15 +119,6 @@ class Gopls(SolidLanguageServer): }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } # Apply gopls-specific settings via initializationOptions @@ -149,7 +137,7 @@ class Gopls(SolidLanguageServer): log.debug("Applying gopls settings via initializationOptions: keys=%s", list(gopls_settings.keys())) initialize_params["initializationOptions"] = gopls_settings - return cast(InitializeParams, initialize_params) + return initialize_params def _validate_gopls_settings_dict(self, gopls_settings: object) -> dict: if not isinstance(gopls_settings, dict): @@ -233,7 +221,7 @@ class Gopls(SolidLanguageServer): log.info("Starting gopls server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/groovy_language_server.py b/src/solidlsp/language_servers/groovy_language_server.py index 2370e128..f6530733 100644 --- a/src/solidlsp/language_servers/groovy_language_server.py +++ b/src/solidlsp/language_servers/groovy_language_server.py @@ -5,13 +5,11 @@ Provides Groovy specific instantiation of the LanguageServer class. Contains var import dataclasses import logging import os -import pathlib import shlex from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_utils import FileUtils, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -244,19 +242,11 @@ class GroovyLanguageServer(SolidLanguageServer): " Ensure the JAR file is available at the configured path\n" ) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Groovy Language Server. """ - if not os.path.isabs(repository_absolute_path): - repository_absolute_path = os.path.abspath(repository_absolute_path) - - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "clientInfo": {"name": "Serena Groovy Client", "version": "1.0.0"}, - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"dynamicRegistration": True, "didSave": True}, @@ -282,15 +272,8 @@ class GroovyLanguageServer(SolidLanguageServer): } }, }, - "processId": os.getpid(), - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -316,7 +299,7 @@ class GroovyLanguageServer(SolidLanguageServer): log.info("Starting Groovy server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/haskell_language_server.py b/src/solidlsp/language_servers/haskell_language_server.py index be3ed0db..c732c4b2 100644 --- a/src/solidlsp/language_servers/haskell_language_server.py +++ b/src/solidlsp/language_servers/haskell_language_server.py @@ -4,7 +4,6 @@ Provides Haskell specific instantiation of the LanguageServer class. Contains va import logging import os -import pathlib import shutil import time from typing import Any @@ -13,7 +12,6 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -107,14 +105,11 @@ class HaskellLanguageServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["dist", "dist-newstyle", ".stack-work", ".cabal-sandbox"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Haskell Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "clientInfo": {"name": "Serena", "version": "0.1.0"}, "locale": "en", "capabilities": { "workspace": { @@ -299,17 +294,8 @@ class HaskellLanguageServer(SolidLanguageServer): } }, "trace": "verbose", - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -364,7 +350,7 @@ class HaskellLanguageServer(SolidLanguageServer): log.info("Starting Haskell Language Server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/haxe_language_server.py b/src/solidlsp/language_servers/haxe_language_server.py index 24ca6fe7..3d4a154c 100644 --- a/src/solidlsp/language_servers/haxe_language_server.py +++ b/src/solidlsp/language_servers/haxe_language_server.py @@ -22,7 +22,7 @@ from solidlsp.ls import ( from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException from solidlsp.ls_types import Hover -from solidlsp.lsp_protocol_handler.lsp_types import DiagnosticSeverity, InitializeParams +from solidlsp.lsp_protocol_handler.lsp_types import DiagnosticSeverity from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -202,13 +202,11 @@ class HaxeLanguageServer(SolidLanguageServer): "dump", ] - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Return initialize params for the Haxe Language Server. displayArguments are resolved from user-configured buildFile or auto-discovered .hxml files. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - # 1. Check for user-configured .hxml path configured_build_file = self._custom_settings.get("buildFile") if configured_build_file: @@ -216,7 +214,7 @@ class HaxeLanguageServer(SolidLanguageServer): display_arguments = [configured_build_file] else: # 2. Auto-discover .hxml files recursively - display_arguments = self._discover_hxml_file(repository_absolute_path) + display_arguments = self._discover_hxml_file(self.repository_root_path) init_options: dict = {"displayArguments": display_arguments} rename_source_folders = self._custom_settings.get("renameSourceFolders") @@ -258,17 +256,8 @@ class HaxeLanguageServer(SolidLanguageServer): }, }, "initializationOptions": init_options, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params @staticmethod def _discover_hxml_file(repository_absolute_path: str) -> list[str]: @@ -377,7 +366,7 @@ class HaxeLanguageServer(SolidLanguageServer): log.info("Starting Haxe server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/hlsl_language_server.py b/src/solidlsp/language_servers/hlsl_language_server.py index 839e0381..41b945ef 100644 --- a/src/solidlsp/language_servers/hlsl_language_server.py +++ b/src/solidlsp/language_servers/hlsl_language_server.py @@ -5,9 +5,8 @@ Supports HLSL, GLSL, and WGSL shader file formats. import logging import os -import pathlib import shutil -from typing import Any, cast +from typing import Any from overrides import override @@ -17,7 +16,6 @@ from solidlsp.ls import ( SolidLanguageServer, ) from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -167,13 +165,8 @@ class HlslLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - root_uri = pathlib.Path(repository_absolute_path).as_uri() + def _create_base_initialize_params(self) -> dict: initialize_params = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "locale": "en", "capabilities": { "textDocument": { @@ -207,9 +200,8 @@ class HlslLanguageServer(SolidLanguageServer): "configuration": True, }, }, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, initialize_params) + return initialize_params @override def _start_server(self) -> None: @@ -237,7 +229,7 @@ class HlslLanguageServer(SolidLanguageServer): log.info("Starting shader-language-server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index 47212695..6f68c33e 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -4,7 +4,6 @@ Provides PHP specific instantiation of the LanguageServer class using Intelephen import logging import os -import pathlib import shutil from time import sleep @@ -13,7 +12,7 @@ from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, InitializeParams, LocationLink +from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink from solidlsp.settings import SolidLSPSettings from ..lsp_protocol_handler import lsp_types @@ -110,11 +109,10 @@ class Intelephense(SolidLanguageServer): def _create_dependency_provider(self) -> LanguageServerDependencyProvider: return self.DependencyProvider(self._custom_settings, self._ls_resources_dir) - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialization params for the Intelephense Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -135,15 +133,6 @@ class Intelephense(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } initialization_options = {} # Add license key if provided via environment variable @@ -159,7 +148,7 @@ class Intelephense(SolidLanguageServer): initialization_options["intelephense.files.maxSize"] = max_file_size initialize_params["initializationOptions"] = initialization_options - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Start Intelephense server process""" @@ -180,7 +169,7 @@ class Intelephense(SolidLanguageServer): log.info("Starting Intelephense server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/jedi_server.py b/src/solidlsp/language_servers/jedi_server.py index 49ca91a0..7ad3b227 100644 --- a/src/solidlsp/language_servers/jedi_server.py +++ b/src/solidlsp/language_servers/jedi_server.py @@ -3,16 +3,12 @@ Provides Python specific instantiation of the LanguageServer class. Contains var """ import logging -import os -import pathlib import threading -from typing import cast from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -40,18 +36,12 @@ class JediServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Jedi Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "clientInfo": {"name": "Serena", "version": "0.1.0"}, "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, # Note: this is not necessarily the minimal set of capabilities... "capabilities": { "workspace": { @@ -143,14 +133,8 @@ class JediServer(SolidLanguageServer): }, }, "trace": "verbose", - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -182,7 +166,7 @@ class JediServer(SolidLanguageServer): log.info("Starting jedi-language-server server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/json_language_server.py b/src/solidlsp/language_servers/json_language_server.py index f764418e..654f2cc4 100644 --- a/src/solidlsp/language_servers/json_language_server.py +++ b/src/solidlsp/language_servers/json_language_server.py @@ -5,14 +5,12 @@ Contains various configurations and settings specific to JSON files. import logging import os -import pathlib import shutil from typing import Any from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -101,12 +99,10 @@ class JsonLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the JSON Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -127,17 +123,8 @@ class JsonLanguageServer(SolidLanguageServer): "didChangeConfiguration": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -160,7 +147,7 @@ class JsonLanguageServer(SolidLanguageServer): log.info("Starting JSON server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/julia_server.py b/src/solidlsp/language_servers/julia_server.py index 7b7579b6..cde52982 100644 --- a/src/solidlsp/language_servers/julia_server.py +++ b/src/solidlsp/language_servers/julia_server.py @@ -1,6 +1,5 @@ import logging import os -import pathlib import platform import shutil import subprocess @@ -10,7 +9,7 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import DiagnosticTag, InitializeParams +from solidlsp.lsp_protocol_handler.lsp_types import DiagnosticTag from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -136,15 +135,11 @@ class JuliaLanguageServer(SolidLanguageServer): # runs (standalone test invocations, CI cold start). return 30.0 - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Julia Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params: InitializeParams = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, + initialize_params: dict = { "capabilities": { # workspace.configuration MUST be true: LanguageServer.jl pulls all julia.lint.* # settings via workspace/configuration after initialize. Without it, the server @@ -164,12 +159,6 @@ class JuliaLanguageServer(SolidLanguageServer): "documentSymbol": {"dynamicRegistration": True}, }, }, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } return initialize_params @@ -202,7 +191,7 @@ class JuliaLanguageServer(SolidLanguageServer): log.info("Starting LanguageServer.jl server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request to Julia Language Server") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server.py index 3998dafa..45901ede 100644 --- a/src/solidlsp/language_servers/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -21,7 +21,6 @@ import os import pathlib import stat import threading -from typing import cast from overrides import override @@ -32,7 +31,6 @@ from solidlsp.ls import ( ) from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import FileUtils, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -185,20 +183,13 @@ class KotlinLanguageServer(SolidLanguageServer): env["JAVA_TOOL_OPTIONS"] = jvm_options return env - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Kotlin Language Server. """ - if not os.path.isabs(repository_absolute_path): - repository_absolute_path = os.path.abspath(repository_absolute_path) - - root_uri = pathlib.Path(repository_absolute_path).as_uri() + root_uri = pathlib.Path(self.repository_root_path).as_uri() initialize_params = { - "clientInfo": {"name": "Multilspy Kotlin Client", "version": "1.0.0"}, "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "workspace": { "applyEdit": True, @@ -429,15 +420,8 @@ class KotlinLanguageServer(SolidLanguageServer): }, }, "trace": "off", - "processId": os.getpid(), - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -503,7 +487,7 @@ class KotlinLanguageServer(SolidLanguageServer): log.info("Starting Kotlin server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/lean4_language_server.py b/src/solidlsp/language_servers/lean4_language_server.py index 961a65b6..4f4af40a 100644 --- a/src/solidlsp/language_servers/lean4_language_server.py +++ b/src/solidlsp/language_servers/lean4_language_server.py @@ -4,17 +4,13 @@ Uses the built-in Lean 4 language server (lean --server). """ import logging -import os -import pathlib import shutil import subprocess -from typing import cast from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -96,16 +92,11 @@ class Lean4LanguageServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in [".lake", "build"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Lean 4 Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -131,14 +122,8 @@ class Lean4LanguageServer(SolidLanguageServer): "didChangeConfiguration": {"dynamicRegistration": True}, }, }, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """Start the Lean 4 language server process.""" @@ -160,7 +145,7 @@ class Lean4LanguageServer(SolidLanguageServer): log.info("Starting Lean 4 language server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/lua_ls.py b/src/solidlsp/language_servers/lua_ls.py index 3176e41f..7b31cc71 100644 --- a/src/solidlsp/language_servers/lua_ls.py +++ b/src/solidlsp/language_servers/lua_ls.py @@ -7,8 +7,6 @@ You can pass the following entries in ``ls_specific_settings["lua"]``: """ import logging -import os -import pathlib import platform import shutil from collections.abc import Hashable @@ -20,7 +18,6 @@ from solidlsp.ls import RawDocumentSymbol, SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_types import SymbolKind from solidlsp.ls_utils import FileUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -220,12 +217,10 @@ class LuaLanguageServer(SolidLanguageServer): return original_name - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Lua Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -270,15 +265,6 @@ class LuaLanguageServer(SolidLanguageServer): }, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "initializationOptions": { # Lua Language Server specific options "runtime": { @@ -304,7 +290,7 @@ class LuaLanguageServer(SolidLanguageServer): }, }, } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """Start Lua Language Server process""" @@ -325,7 +311,7 @@ class LuaLanguageServer(SolidLanguageServer): log.info("Starting Lua Language Server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/luau_lsp.py b/src/solidlsp/language_servers/luau_lsp.py index f0cb06bf..a23daed7 100644 --- a/src/solidlsp/language_servers/luau_lsp.py +++ b/src/solidlsp/language_servers/luau_lsp.py @@ -20,8 +20,6 @@ See: https://github.com/JohnnyMorganz/luau-lsp """ import logging -import os -import pathlib import platform import shutil import threading @@ -32,7 +30,6 @@ from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import FileUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -270,12 +267,10 @@ class LuauLanguageServer(SolidLanguageServer): super().__init__(config, repository_root_path, None, "luau", solidlsp_settings) self.server_ready = threading.Event() - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Luau Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -322,20 +317,11 @@ class LuauLanguageServer(SolidLanguageServer): }, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], # luau-lsp initialization options # These can be overridden via .luaurc in the project root "initializationOptions": {}, } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """Start Luau Language Server process""" @@ -366,7 +352,7 @@ class LuauLanguageServer(SolidLanguageServer): log.info("Starting Luau Language Server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/marksman.py b/src/solidlsp/language_servers/marksman.py index 72d7268a..98696f53 100644 --- a/src/solidlsp/language_servers/marksman.py +++ b/src/solidlsp/language_servers/marksman.py @@ -5,7 +5,6 @@ Contains various configurations and settings specific to Markdown. import logging import os -import pathlib from collections.abc import Hashable from overrides import override @@ -19,7 +18,6 @@ from solidlsp.ls import ( ) from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_types import SymbolKind, UnifiedSymbolInformation -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -195,17 +193,12 @@ class Marksman(SolidLanguageServer): return document_symbols - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Marksman Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params: InitializeParams = { - "processId": os.getpid(), + initialize_params: dict = { "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -215,9 +208,9 @@ class Marksman(SolidLanguageServer): "documentSymbol": { "dynamicRegistration": True, "hierarchicalDocumentSymbolSupport": True, - "symbolKind": {"valueSet": list(range(1, 27))}, # type: ignore[arg-type] + "symbolKind": {"valueSet": list(range(1, 27))}, }, - "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, # type: ignore[list-item] + "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, "codeAction": {"dynamicRegistration": True}, }, "workspace": { @@ -226,12 +219,6 @@ class Marksman(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } return initialize_params @@ -256,7 +243,7 @@ class Marksman(SolidLanguageServer): log.info("Starting marksman server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to marksman server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/matlab_language_server.py b/src/solidlsp/language_servers/matlab_language_server.py index 5a83a75f..d6694355 100644 --- a/src/solidlsp/language_servers/matlab_language_server.py +++ b/src/solidlsp/language_servers/matlab_language_server.py @@ -37,12 +37,12 @@ import pathlib import platform import shutil import threading -from typing import Any, cast +from typing import Any from solidlsp.ls import LanguageServerDependencyProvider, LSPFileBuffer, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import FileUtils -from solidlsp.lsp_protocol_handler.lsp_types import DocumentSymbol, InitializeParams, SymbolInformation +from solidlsp.lsp_protocol_handler.lsp_types import DocumentSymbol, SymbolInformation from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -361,10 +361,8 @@ class MatlabLanguageServer(SolidLanguageServer): "MATLAB_INSTALL_PATH": self.get_matlab_path(), } - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Return the initialize params for the MATLAB Language Server.""" - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -394,17 +392,8 @@ class MatlabLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """Start the MATLAB Language Server and wait for it to be ready.""" @@ -460,7 +449,7 @@ class MatlabLanguageServer(SolidLanguageServer): log.info("Starting MATLAB server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/msl_language_server.py b/src/solidlsp/language_servers/msl_language_server.py index e2596a05..e8c89c1c 100644 --- a/src/solidlsp/language_servers/msl_language_server.py +++ b/src/solidlsp/language_servers/msl_language_server.py @@ -8,7 +8,6 @@ and launched as a subprocess using the current Python interpreter. import logging import os -import pathlib import sys import threading @@ -16,7 +15,6 @@ from solidlsp.ls import ( SolidLanguageServer, ) from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -46,10 +44,8 @@ class MslLanguageServer(SolidLanguageServer): ) self.server_ready = threading.Event() - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Returns the initialize params for the mSL Language Server.""" - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -69,12 +65,8 @@ class MslLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Starts the mSL Language Server.""" @@ -91,7 +83,7 @@ class MslLanguageServer(SolidLanguageServer): log.info("Starting mSL server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request to mSL LSP server") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/nixd_ls.py b/src/solidlsp/language_servers/nixd_ls.py index b697292c..eacd6364 100644 --- a/src/solidlsp/language_servers/nixd_ls.py +++ b/src/solidlsp/language_servers/nixd_ls.py @@ -6,8 +6,6 @@ Note: Windows is not supported as Nix itself doesn't support Windows natively. """ import logging -import os -import pathlib import platform import shutil import subprocess @@ -18,7 +16,6 @@ from overrides import override from solidlsp import ls_types from solidlsp.ls import DocumentSymbols, LSPFileBuffer, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -250,12 +247,10 @@ class NixLanguageServer(SolidLanguageServer): super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=nixd_path, cwd=repository_root_path), "nix", solidlsp_settings) self.request_id = 0 - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for nixd. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -318,15 +313,6 @@ class NixLanguageServer(SolidLanguageServer): }, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "initializationOptions": { # nixd specific options "nixpkgs": {"expr": "import { }"}, @@ -360,7 +346,7 @@ class NixLanguageServer(SolidLanguageServer): log.info("Starting nixd server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/ocaml_lsp_server.py b/src/solidlsp/language_servers/ocaml_lsp_server.py index b2e9511a..3a26e50e 100644 --- a/src/solidlsp/language_servers/ocaml_lsp_server.py +++ b/src/solidlsp/language_servers/ocaml_lsp_server.py @@ -5,7 +5,6 @@ Contains various configurations and settings specific to OCaml and Reason. import logging import os -import pathlib import platform import re import shutil @@ -18,7 +17,6 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings from solidlsp.util.subprocess_util import subprocess_kwargs @@ -321,19 +319,13 @@ class OcamlLanguageServer(SolidLanguageServer): """Define language-specific directories to ignore for OCaml projects.""" return super().is_ignored_dirname(dirname) or dirname in ["_build", "_opam", ".opam"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the OCaml Language Server. Supports both OCaml and Reason. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "clientInfo": {"name": "Serena", "version": "0.1.0"}, "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "workspace": { "workspaceFolders": True, @@ -376,14 +368,8 @@ class OcamlLanguageServer(SolidLanguageServer): }, }, "trace": "verbose", - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """ @@ -417,7 +403,7 @@ class OcamlLanguageServer(SolidLanguageServer): log.info("Starting OCaml LSP server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/omnisharp.py b/src/solidlsp/language_servers/omnisharp.py index 58dda4ee..e64746e5 100644 --- a/src/solidlsp/language_servers/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp.py @@ -5,7 +5,6 @@ Provides C# specific instantiation of the LanguageServer class. Contains various import json import logging import os -import pathlib import threading from collections.abc import Iterable @@ -15,7 +14,6 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException from solidlsp.ls_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -127,8 +125,7 @@ class OmniSharp(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Omnisharp Language Server. """ @@ -137,18 +134,10 @@ class OmniSharp(SolidLanguageServer): del d["_description"] - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) + # processId, rootPath, rootUri and workspaceFolders are set centrally by the InitializeParamsBuilder + del d["rootPath"] + del d["rootUri"] + del d["workspaceFolders"] return d @@ -402,7 +391,7 @@ class OmniSharp(SolidLanguageServer): log.info("Starting OmniSharp server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/pascal_server.py b/src/solidlsp/language_servers/pascal_server.py index 1f1488e7..e27831fd 100644 --- a/src/solidlsp/language_servers/pascal_server.py +++ b/src/solidlsp/language_servers/pascal_server.py @@ -49,7 +49,6 @@ import hashlib import json import logging import os -import pathlib import platform import shutil import tarfile @@ -64,7 +63,6 @@ import zipfile from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, quote_windows_path from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -777,8 +775,7 @@ class PascalLanguageServer(SolidLanguageServer): log.info(f"Using pasls at: {pasls_executable_path}") return quote_windows_path(pasls_executable_path) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Pascal Language Server. @@ -788,8 +785,6 @@ class PascalLanguageServer(SolidLanguageServer): We only pass target OS/CPU in initializationOptions if explicitly set. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - # Build initializationOptions from environment variables # pasls reads these to configure CodeTools: # - PP: Path to FPC compiler executable @@ -881,18 +876,9 @@ class PascalLanguageServer(SolidLanguageServer): }, }, "initializationOptions": initialization_options, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -926,7 +912,7 @@ class PascalLanguageServer(SolidLanguageServer): log.info("Starting Pascal server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/perl_language_server.py b/src/solidlsp/language_servers/perl_language_server.py index c2e40b39..9b785ded 100644 --- a/src/solidlsp/language_servers/perl_language_server.py +++ b/src/solidlsp/language_servers/perl_language_server.py @@ -5,8 +5,6 @@ Note: Windows is not supported as Nix itself doesn't support Windows natively. """ import logging -import os -import pathlib import subprocess import time from typing import Any @@ -16,7 +14,7 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import DidChangeConfigurationParams, InitializeParams +from solidlsp.lsp_protocol_handler.lsp_types import DidChangeConfigurationParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -112,17 +110,12 @@ class PerlLanguageServer(SolidLanguageServer): ) self.request_id = 0 - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for Perl::LanguageServer. Based on the expected structure from Perl::LanguageServer::Methods::_rpcreq_initialize. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -138,15 +131,9 @@ class PerlLanguageServer(SolidLanguageServer): }, }, "initializationOptions": {}, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Start Perl::LanguageServer process""" @@ -180,7 +167,7 @@ class PerlLanguageServer(SolidLanguageServer): log.info("Starting Perl::LanguageServer process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/phpactor.py b/src/solidlsp/language_servers/phpactor.py index 181cb3ee..306374d9 100644 --- a/src/solidlsp/language_servers/phpactor.py +++ b/src/solidlsp/language_servers/phpactor.py @@ -4,7 +4,6 @@ Provides PHP specific instantiation of the LanguageServer class using Phpactor. import logging import os -import pathlib import re import shutil import stat @@ -15,7 +14,6 @@ from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_utils import FileUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -122,15 +120,11 @@ class PhpactorServer(SolidLanguageServer): def _create_dependency_provider(self) -> LanguageServerDependencyProvider: return self.DependencyProvider(self._custom_settings, self._ls_resources_dir) - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialization params for the Phpactor Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -145,19 +139,13 @@ class PhpactorServer(SolidLanguageServer): "didChangeConfiguration": {"dynamicRegistration": True}, }, }, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "initializationOptions": { "language_server_phpstan.enabled": False, "language_server_psalm.enabled": False, "language_server_php_cs_fixer.enabled": False, }, } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Start Phpactor server process.""" @@ -178,7 +166,7 @@ class PhpactorServer(SolidLanguageServer): log.info("Starting Phpactor server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/phpantom.py b/src/solidlsp/language_servers/phpantom.py index 6fd42f0a..f0f11dd1 100644 --- a/src/solidlsp/language_servers/phpantom.py +++ b/src/solidlsp/language_servers/phpantom.py @@ -4,11 +4,9 @@ Provides PHP specific instantiation of the LanguageServer class using PHPantom. import logging import os -import pathlib import shutil import stat from time import sleep -from typing import cast from overrides import override @@ -22,7 +20,7 @@ from solidlsp.ls import ( from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_utils import PlatformId, PlatformUtils from solidlsp.lsp_protocol_handler import lsp_types as protocol_lsp_types -from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, InitializeParams, LocationLink +from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -178,12 +176,10 @@ class PHPantomServer(SolidLanguageServer): def _create_dependency_provider(self) -> LanguageServerDependencyProvider: return self.DependencyProvider(self._custom_settings, self._ls_resources_dir) - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialization params for the PHPantom Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - # declaring client capabilities initialize_params = { "locale": "en", @@ -213,17 +209,8 @@ class PHPantomServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """Start PHPantom server process.""" @@ -244,7 +231,7 @@ class PHPantomServer(SolidLanguageServer): log.info("Starting PHPantom server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() # negotiating server capabilities log.info("Sending initialize request from LSP client to LSP server and awaiting response") diff --git a/src/solidlsp/language_servers/powershell_language_server.py b/src/solidlsp/language_servers/powershell_language_server.py index 1879ac02..99bac570 100644 --- a/src/solidlsp/language_servers/powershell_language_server.py +++ b/src/solidlsp/language_servers/powershell_language_server.py @@ -11,7 +11,6 @@ You can pass the following entries in ``ls_specific_settings["powershell"]``: import logging import os -import pathlib import platform import shutil import subprocess @@ -27,7 +26,6 @@ from solidlsp.ls import LSPConstants, RawDocumentSymbol, SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_types import SymbolKind from solidlsp.ls_utils import FileUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -277,12 +275,10 @@ class PowerShellLanguageServer(SolidLanguageServer): return original_name - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the PowerShell Editor Services. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -326,17 +322,8 @@ class PowerShellLanguageServer(SolidLanguageServer): }, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """ @@ -375,7 +362,7 @@ class PowerShellLanguageServer(SolidLanguageServer): log.info("Starting PowerShell Editor Services process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/pyrefly_server.py b/src/solidlsp/language_servers/pyrefly_server.py index 3c5f26e2..b657895b 100644 --- a/src/solidlsp/language_servers/pyrefly_server.py +++ b/src/solidlsp/language_servers/pyrefly_server.py @@ -15,7 +15,7 @@ import pathlib import sys import threading import time -from typing import Any, cast +from typing import Any from typing_extensions import override @@ -23,7 +23,7 @@ from solidlsp import ls_types from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderUvx, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, LSPErrorCodes +from solidlsp.lsp_protocol_handler.lsp_types import LSPErrorCodes from solidlsp.lsp_protocol_handler.server import LSPError, PayloadLike from solidlsp.settings import SolidLSPSettings @@ -171,17 +171,11 @@ class PyreflyLanguageServer(SolidLanguageServer): def _get_language_id_for_file(self, relative_file_path: str) -> str: return "python" - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the pyrefly language server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "clientInfo": {"name": "Serena", "version": "0.1.0"}, - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "window": { "workDoneProgress": True, @@ -216,9 +210,8 @@ class PyreflyLanguageServer(SolidLanguageServer): "publishDiagnostics": {"relatedInformation": True}, }, }, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -280,7 +273,7 @@ class PyreflyLanguageServer(SolidLanguageServer): self.server.start() # retry requests that pyrefly cancels while re-indexing the workspace in the background self._install_mutation_retry() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to pyrefly server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/pyright_server.py b/src/solidlsp/language_servers/pyright_server.py index f369706c..b808d8f7 100644 --- a/src/solidlsp/language_servers/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_server.py @@ -3,8 +3,6 @@ Provides Python specific instantiation of the LanguageServer class. Contains var """ import logging -import os -import pathlib import re import threading from typing import cast @@ -13,7 +11,6 @@ from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderUvx, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -59,16 +56,8 @@ class PyrightServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Pyright Language Server. - """ - # Create basic initialization parameters + def _create_base_initialize_params(self) -> dict: initialize_params = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": pathlib.Path(repository_absolute_path).as_uri(), "initializationOptions": { "exclude": [ "**/__pycache__", @@ -111,12 +100,8 @@ class PyrightServer(SolidLanguageServer): "publishDiagnostics": {"relatedInformation": True}, }, }, - "workspaceFolders": [ - {"uri": pathlib.Path(repository_absolute_path).as_uri(), "name": os.path.basename(repository_absolute_path)} - ], } - - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -230,7 +215,7 @@ class PyrightServer(SolidLanguageServer): self.server.start() # Send proper initialization parameters - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to pyright server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/r_language_server.py b/src/solidlsp/language_servers/r_language_server.py index 087eccca..8caeeed1 100644 --- a/src/solidlsp/language_servers/r_language_server.py +++ b/src/solidlsp/language_servers/r_language_server.py @@ -1,6 +1,4 @@ import logging -import os -import pathlib import subprocess from typing import Any @@ -8,7 +6,6 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -68,10 +65,8 @@ class RLanguageServer(SolidLanguageServer): super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=r_cmd, cwd=repository_root_path), "r", solidlsp_settings) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Initialize params for R Language Server.""" - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -107,17 +102,8 @@ class RLanguageServer(SolidLanguageServer): }, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """Start R Language Server process.""" @@ -140,7 +126,7 @@ class RLanguageServer(SolidLanguageServer): log.info("Starting R Language Server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info( "Sending initialize request to R Language Server", ) diff --git a/src/solidlsp/language_servers/regal_server.py b/src/solidlsp/language_servers/regal_server.py index 25870a04..c19f556e 100644 --- a/src/solidlsp/language_servers/regal_server.py +++ b/src/solidlsp/language_servers/regal_server.py @@ -1,16 +1,12 @@ """Regal Language Server implementation for Rego policy files.""" import logging -import os import shutil -from typing import cast from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.ls_utils import PathUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -54,20 +50,14 @@ class RegalLanguageServer(SolidLanguageServer): solidlsp_settings, ) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Regal Language Server. - :param repository_absolute_path: Absolute path to the repository :return: LSP initialization parameters """ - root_uri = PathUtils.path_to_uri(repository_absolute_path) initialize_params = { - "processId": os.getpid(), "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -89,14 +79,8 @@ class RegalLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "workspaceFolders": [ - { - "name": os.path.basename(repository_absolute_path), - "uri": root_uri, - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """Start Regal language server process and wait for initialization.""" @@ -117,7 +101,7 @@ class RegalLanguageServer(SolidLanguageServer): log.info("Starting Regal language server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info( "Sending initialize request from LSP client to LSP server and awaiting response", diff --git a/src/solidlsp/language_servers/ruby_lsp.py b/src/solidlsp/language_servers/ruby_lsp.py index fd72b289..0e66b16b 100644 --- a/src/solidlsp/language_servers/ruby_lsp.py +++ b/src/solidlsp/language_servers/ruby_lsp.py @@ -11,7 +11,6 @@ You can pass the following entries in ``ls_specific_settings["ruby"]``: import json import logging import os -import pathlib import shutil import subprocess import threading @@ -20,7 +19,7 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, InitializeResult +from solidlsp.lsp_protocol_handler.lsp_types import InitializeResult from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -329,16 +328,13 @@ class RubyLsp(SolidLanguageServer): return base_patterns - def _get_initialize_params(self) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns ruby-lsp specific initialization parameters. """ exclude_patterns = self._get_ruby_exclude_patterns(self.repository_root_path) initialize_params = { - "processId": os.getpid(), - "rootPath": self.repository_root_path, - "rootUri": pathlib.Path(self.repository_root_path).as_uri(), "capabilities": { "workspace": { "workspaceEdit": {"documentChanges": True}, @@ -371,7 +367,7 @@ class RubyLsp(SolidLanguageServer): }, } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -437,7 +433,7 @@ class RubyLsp(SolidLanguageServer): log.info("Starting ruby-lsp server process") self.server.start() - initialize_params = self._get_initialize_params() + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") log.info(f"Sending init params: {json.dumps(initialize_params, indent=4)}") diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 2e222b5a..65f29665 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -9,13 +9,11 @@ import platform import shutil import subprocess import threading -from typing import cast from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -210,14 +208,11 @@ class RustAnalyzer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["target"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Rust Analyzer Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "clientInfo": {"name": "Visual Studio Code - Insiders", "version": "1.82.0-insider"}, "locale": "en", "capabilities": { "workspace": { @@ -672,17 +667,8 @@ class RustAnalyzer(SolidLanguageServer): "workspace": {"symbol": {"search": {"kind": "only_types", "limit": 128, "scope": "workspace"}}}, }, "trace": "verbose", - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -728,7 +714,7 @@ class RustAnalyzer(SolidLanguageServer): log.info("Starting RustAnalyzer server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/scala_language_server.py b/src/solidlsp/language_servers/scala_language_server.py index 563806cd..f360d1ce 100644 --- a/src/solidlsp/language_servers/scala_language_server.py +++ b/src/solidlsp/language_servers/scala_language_server.py @@ -4,7 +4,6 @@ Provides Scala specific instantiation of the LanguageServer class. Contains vari import logging import os -import pathlib import shutil import subprocess from enum import Enum @@ -14,7 +13,6 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -259,17 +257,12 @@ class ScalaLanguageServer(SolidLanguageServer): log.info("Bootstrapping metals finished.") return [metals_executable] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Scala Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "initializationOptions": { "compilerOptions": { "completionCommand": None, @@ -304,7 +297,7 @@ class ScalaLanguageServer(SolidLanguageServer): }, "capabilities": {"textDocument": {"documentSymbol": {"hierarchicalDocumentSymbolSupport": True}}}, } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -315,7 +308,7 @@ class ScalaLanguageServer(SolidLanguageServer): log.info("Sending initialize request from LSP client to LSP server and awaiting response") - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() self.server.send.initialize(initialize_params) self.server.notify.initialized({}) diff --git a/src/solidlsp/language_servers/solargraph.py b/src/solidlsp/language_servers/solargraph.py index 9379bc6d..b0ae3b00 100644 --- a/src/solidlsp/language_servers/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -6,7 +6,6 @@ Contains various configurations and settings specific to Ruby. import json import logging import os -import pathlib import re import shutil import subprocess @@ -16,7 +15,6 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -267,18 +265,13 @@ class Solargraph(SolidLanguageServer): return base_patterns - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Solargraph Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - exclude_patterns = Solargraph._get_ruby_exclude_patterns(repository_absolute_path) + exclude_patterns = Solargraph._get_ruby_exclude_patterns(self.repository_root_path) - initialize_params: InitializeParams = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, + initialize_params = { "initializationOptions": { "exclude": exclude_patterns, }, @@ -289,17 +282,11 @@ class Solargraph(SolidLanguageServer): "textDocument": { "documentSymbol": { "hierarchicalDocumentSymbolSupport": True, - "symbolKind": {"valueSet": list(range(1, 27))}, # type: ignore[arg-type] + "symbolKind": {"valueSet": list(range(1, 27))}, }, }, }, - "trace": "verbose", # type: ignore[typeddict-item] - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], + "trace": "verbose", } return initialize_params @@ -341,7 +328,7 @@ class Solargraph(SolidLanguageServer): log.info("Starting solargraph server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") log.info(f"Sending init params: {json.dumps(initialize_params, indent=4)}") diff --git a/src/solidlsp/language_servers/solidity_language_server.py b/src/solidlsp/language_servers/solidity_language_server.py index 7ee35953..515bd5ce 100644 --- a/src/solidlsp/language_servers/solidity_language_server.py +++ b/src/solidlsp/language_servers/solidity_language_server.py @@ -19,7 +19,6 @@ from solidlsp import ls_types from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, LSPConstants, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -178,21 +177,10 @@ class SolidityLanguageServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in {"artifacts", "cache", "typechain-types"} - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Return LSP InitializeParams for the Solidity language server.""" - root_uri = pathlib.Path(repository_absolute_path).as_uri() - return { # type: ignore + return { "locale": "en", - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "capabilities": { "textDocument": { "synchronization": { @@ -208,11 +196,11 @@ class SolidityLanguageServer(SolidLanguageServer): "documentSymbol": { "dynamicRegistration": True, "hierarchicalDocumentSymbolSupport": True, - "symbolKind": {"valueSet": list(range(1, 27))}, # type: ignore[arg-type] + "symbolKind": {"valueSet": list(range(1, 27))}, }, "hover": { "dynamicRegistration": True, - "contentFormat": ["markdown", "plaintext"], # type: ignore[list-item] + "contentFormat": ["markdown", "plaintext"], }, "publishDiagnostics": {"relatedInformation": True}, }, @@ -263,7 +251,7 @@ class SolidityLanguageServer(SolidLanguageServer): log.info("Starting Solidity language server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.debug("Sending initialize request to Solidity language server") init_response = self.server.send.initialize(initialize_params) log.debug(f"Received initialize response from Solidity server: {init_response}") diff --git a/src/solidlsp/language_servers/some_sass_language_server.py b/src/solidlsp/language_servers/some_sass_language_server.py index 770b80ae..abce6596 100644 --- a/src/solidlsp/language_servers/some_sass_language_server.py +++ b/src/solidlsp/language_servers/some_sass_language_server.py @@ -25,7 +25,6 @@ from __future__ import annotations import logging import os -import pathlib import shutil import threading @@ -34,7 +33,6 @@ from overrides import override from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -157,9 +155,7 @@ class SomeSassLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - root_uri = pathlib.Path(repository_absolute_path).as_uri() + def _create_base_initialize_params(self) -> dict: initialize_params: dict = { "locale": "en", "capabilities": { @@ -183,17 +179,8 @@ class SomeSassLanguageServer(SolidLanguageServer): }, }, "initializationOptions": SOMESASS_INIT_OPTIONS, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params @staticmethod def _handle_workspace_configuration(params: dict) -> list[dict]: @@ -231,7 +218,7 @@ class SomeSassLanguageServer(SolidLanguageServer): log.info("Starting some-sass-language-server") self.server.start() - init_params = self._get_initialize_params(self.repository_root_path) + init_params = self._create_initialize_params() init_response = self.server.send.initialize(init_params) log.debug("Some Sass LS initialize response: %s", init_response) assert "completionProvider" in init_response["capabilities"], "Some Sass LSP did not advertise completionProvider" diff --git a/src/solidlsp/language_servers/sourcekit_lsp.py b/src/solidlsp/language_servers/sourcekit_lsp.py index 8d30daeb..e77ba74e 100644 --- a/src/solidlsp/language_servers/sourcekit_lsp.py +++ b/src/solidlsp/language_servers/sourcekit_lsp.py @@ -1,6 +1,5 @@ import logging import os -import pathlib import subprocess import time from collections.abc import Hashable @@ -11,7 +10,6 @@ from solidlsp import ls_types from solidlsp.ls import RawDocumentSymbol, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_types import SymbolKind -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -75,13 +73,10 @@ class SourceKitLSP(SolidLanguageServer): return original_name.split("(", 1)[0].strip() - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Swift Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params = { "capabilities": { "general": { @@ -295,7 +290,6 @@ class SourceKitLSP(SolidLanguageServer): "workspaceFolders": True, }, }, - "clientInfo": {"name": "Visual Studio Code", "version": "1.102.2"}, "initializationOptions": { "backgroundIndexing": True, "backgroundPreparationMode": "enabled", @@ -305,18 +299,9 @@ class SourceKitLSP(SolidLanguageServer): "workspace/peekDocuments": True, }, "locale": "en", - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """Start sourcekit-lsp server process""" @@ -337,7 +322,7 @@ class SourceKitLSP(SolidLanguageServer): log.info("Starting sourcekit-lsp server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/svelte_language_server.py b/src/solidlsp/language_servers/svelte_language_server.py index c04730d8..8b65c822 100644 --- a/src/solidlsp/language_servers/svelte_language_server.py +++ b/src/solidlsp/language_servers/svelte_language_server.py @@ -120,8 +120,8 @@ class SvelteTypeScriptServer(TypeScriptLanguageServer): return "typescript" @override - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - params = super()._get_initialize_params(repository_absolute_path) + def _create_base_initialize_params(self) -> dict: + params = super()._create_base_initialize_params() params["initializationOptions"] = { "plugins": [ { diff --git a/src/solidlsp/language_servers/systemverilog_server.py b/src/solidlsp/language_servers/systemverilog_server.py index bd1782c6..dd7f7b0c 100644 --- a/src/solidlsp/language_servers/systemverilog_server.py +++ b/src/solidlsp/language_servers/systemverilog_server.py @@ -4,14 +4,12 @@ SystemVerilog language server using verible-verilog-ls. import logging import os -import pathlib import shutil import subprocess -from typing import Any, cast +from typing import Any from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection @@ -150,13 +148,8 @@ class SystemVerilogLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - root_uri = pathlib.Path(repository_absolute_path).as_uri() + def _create_base_initialize_params(self) -> dict: initialize_params = { - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, "locale": "en", "capabilities": { "textDocument": { @@ -186,9 +179,8 @@ class SystemVerilogLanguageServer(SolidLanguageServer): "didChangeConfiguration": {"dynamicRegistration": True}, }, }, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: def do_nothing(params: Any) -> None: @@ -205,7 +197,7 @@ class SystemVerilogLanguageServer(SolidLanguageServer): log.info("Starting verible-verilog-ls process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/taplo_server.py b/src/solidlsp/language_servers/taplo_server.py index ee9be952..69511d7b 100644 --- a/src/solidlsp/language_servers/taplo_server.py +++ b/src/solidlsp/language_servers/taplo_server.py @@ -18,8 +18,7 @@ from overrides import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.ls_utils import FileUtils, PathUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.ls_utils import FileUtils from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -208,12 +207,10 @@ class TaploServer(SolidLanguageServer): f"Failed to download Taplo from {download_url}. Try installing manually: cargo install taplo-cli --locked" ) from e - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Taplo Language Server. """ - root_uri = PathUtils.path_to_uri(repository_absolute_path) initialize_params = { "locale": "en", "capabilities": { @@ -236,17 +233,8 @@ class TaploServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -269,7 +257,7 @@ class TaploServer(SolidLanguageServer): log.info("Starting Taplo server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request to Taplo server") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py index c654fd95..a298fee1 100644 --- a/src/solidlsp/language_servers/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -1,14 +1,12 @@ import logging import os import shutil -from typing import cast from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig -from solidlsp.ls_utils import PathUtils, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.ls_utils import PlatformUtils from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -219,17 +217,12 @@ class TerraformLS(SolidLanguageServer): ) self.request_id = 0 - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Terraform Language Server. """ - root_uri = PathUtils.path_to_uri(repository_absolute_path) result = { - "processId": os.getpid(), "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -243,14 +236,8 @@ class TerraformLS(SolidLanguageServer): }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, }, - "workspaceFolders": [ - { - "name": os.path.basename(repository_absolute_path), - "uri": root_uri, - } - ], } - return cast(InitializeParams, result) + return result def _start_server(self) -> None: """Start terraform-ls server process""" @@ -271,7 +258,7 @@ class TerraformLS(SolidLanguageServer): log.info("Starting terraform-ls server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/texlab_language_server.py b/src/solidlsp/language_servers/texlab_language_server.py index ea95f6e6..91f0e09b 100644 --- a/src/solidlsp/language_servers/texlab_language_server.py +++ b/src/solidlsp/language_servers/texlab_language_server.py @@ -5,14 +5,12 @@ texlab is downloaded as a prebuilt binary from the latex-lsp/texlab GitHub relea import logging import os -from typing import cast from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.ls_utils import PathUtils, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.ls_utils import PlatformUtils from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -134,15 +132,10 @@ class TexlabLanguageServer(SolidLanguageServer): solidlsp_settings, ) - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """Returns the initialize params for the texlab Language Server.""" - root_uri = PathUtils.path_to_uri(repository_absolute_path) result = { - "processId": os.getpid(), "locale": "en", - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -159,14 +152,8 @@ class TexlabLanguageServer(SolidLanguageServer): }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, }, - "workspaceFolders": [ - { - "name": os.path.basename(repository_absolute_path), - "uri": root_uri, - } - ], } - return cast(InitializeParams, result) + return result def _start_server(self) -> None: """Start the texlab server process.""" @@ -187,7 +174,7 @@ class TexlabLanguageServer(SolidLanguageServer): log.info("Starting texlab server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to texlab and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/ty_server.py b/src/solidlsp/language_servers/ty_server.py index 1e0832f0..4ce67355 100644 --- a/src/solidlsp/language_servers/ty_server.py +++ b/src/solidlsp/language_servers/ty_server.py @@ -8,15 +8,11 @@ You can pass the following entries in ``ls_specific_settings["python_ty"]``: """ import logging -import os -import pathlib -from typing import cast from typing_extensions import override from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderUvx, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -61,17 +57,11 @@ class TyLanguageServer(SolidLanguageServer): def _get_language_id_for_file(self, relative_file_path: str) -> str: return "python" - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Ty language server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { - "processId": os.getpid(), - "clientInfo": {"name": "Serena", "version": "0.1.0"}, - "rootPath": repository_absolute_path, - "rootUri": root_uri, "capabilities": { "workspace": { "workspaceEdit": {"documentChanges": True}, @@ -101,9 +91,8 @@ class TyLanguageServer(SolidLanguageServer): "publishDiagnostics": {"relatedInformation": True}, }, }, - "workspaceFolders": [{"uri": root_uri, "name": os.path.basename(repository_absolute_path)}], } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -130,7 +119,7 @@ class TyLanguageServer(SolidLanguageServer): # starting and initializing the server log.info("Starting ty language server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to ty server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index e056e5de..278cd10c 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -4,11 +4,10 @@ Provides TypeScript specific instantiation of the LanguageServer class. Contains import logging import os -import pathlib import shutil import threading import time -from typing import Any, cast +from typing import Any from overrides import override from sensai.util.logging import LogTime @@ -17,7 +16,6 @@ from solidlsp import ls_types from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_utils import PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings from .common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command @@ -226,11 +224,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): return "javascriptreact" return self.language_id - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the TypeScript Language Server. - """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() + def _create_base_initialize_params(self) -> dict: initialize_params = { "locale": "en", # Disable Automatic Type Acquisition (ATA): with ATA enabled, tsserver fetches @@ -269,12 +263,8 @@ class TypeScriptLanguageServer(SolidLanguageServer): "workDoneProgress": True, # Enables $/progress notifications for project loading }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": self._build_workspace_folders_param(repository_absolute_path), } - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -377,7 +367,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): log.info("Starting TypeScript server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info( "Sending initialize request from LSP client to LSP server and awaiting response", diff --git a/src/solidlsp/language_servers/vscode_html_language_server.py b/src/solidlsp/language_servers/vscode_html_language_server.py index 7b32fc08..9b329bc3 100644 --- a/src/solidlsp/language_servers/vscode_html_language_server.py +++ b/src/solidlsp/language_servers/vscode_html_language_server.py @@ -20,7 +20,6 @@ from __future__ import annotations import logging import os -import pathlib import shutil import threading @@ -29,7 +28,6 @@ from overrides import override from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -112,9 +110,7 @@ class VsCodeHtmlLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: - root_uri = pathlib.Path(repository_absolute_path).as_uri() + def _create_base_initialize_params(self) -> dict: initialize_params: dict = { "locale": "en", "capabilities": { @@ -142,17 +138,8 @@ class VsCodeHtmlLanguageServer(SolidLanguageServer): "handledSchemas": ["file"], "provideFormatter": False, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: def do_nothing(_params: dict) -> None: @@ -169,7 +156,7 @@ class VsCodeHtmlLanguageServer(SolidLanguageServer): log.info("Starting vscode-html-language-server") self.server.start() - init_params = self._get_initialize_params(self.repository_root_path) + init_params = self._create_initialize_params() init_response = self.server.send.initialize(init_params) log.debug("HTML LS initialize response: %s", init_response) # Sanity-check key capabilities; HTML LSP always provides documentSymbol + completion diff --git a/src/solidlsp/language_servers/vts_language_server.py b/src/solidlsp/language_servers/vts_language_server.py index 6cd092bb..d0c5dd5f 100644 --- a/src/solidlsp/language_servers/vts_language_server.py +++ b/src/solidlsp/language_servers/vts_language_server.py @@ -6,17 +6,14 @@ which provides TypeScript language server functionality via VSCode's TypeScript import logging import os -import pathlib import shutil import threading -from typing import cast from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import Language, LanguageServerConfig from solidlsp.ls_utils import PlatformId, PlatformUtils -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -141,14 +138,13 @@ class VtsLanguageServer(SolidLanguageServer): raise ValueError(f"ls_specific_settings.typescript_vts.initialization_options must be a dict, got {type(opts).__name__}") return opts - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the VTS Language Server. If ``initialization_options`` is set in ``ls_specific_settings["typescript_vts"]``, it is forwarded verbatim as LSP ``initializationOptions``. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params: dict = { "locale": "en", "initializationOptions": { @@ -177,22 +173,13 @@ class VtsLanguageServer(SolidLanguageServer): "configuration": True, # This might be needed for vtsls }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], } if self._initialization_options: log.info("Forwarding user-provided initializationOptions to vtsls: %s", self._initialization_options) initialize_params["initializationOptions"] = self._initialization_options - return cast(InitializeParams, initialize_params) + return initialize_params def _start_server(self) -> None: """ @@ -249,7 +236,7 @@ class VtsLanguageServer(SolidLanguageServer): log.info("Starting VTS server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/vue_language_server.py b/src/solidlsp/language_servers/vue_language_server.py index f5112c0f..a29ab7f5 100644 --- a/src/solidlsp/language_servers/vue_language_server.py +++ b/src/solidlsp/language_servers/vue_language_server.py @@ -121,8 +121,8 @@ class VueTypeScriptServer(TypeScriptLanguageServer): ) @override - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - params = super()._get_initialize_params(repository_absolute_path) + def _create_base_initialize_params(self) -> dict: + params = super()._create_base_initialize_params() params["initializationOptions"] = { "plugins": [ diff --git a/src/solidlsp/language_servers/yaml_language_server.py b/src/solidlsp/language_servers/yaml_language_server.py index b52c760e..b229f15f 100644 --- a/src/solidlsp/language_servers/yaml_language_server.py +++ b/src/solidlsp/language_servers/yaml_language_server.py @@ -5,14 +5,12 @@ Contains various configurations and settings specific to YAML files. import logging import os -import pathlib import shutil from typing import Any from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) @@ -114,12 +112,10 @@ class YamlLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the YAML Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -142,15 +138,6 @@ class YamlLanguageServer(SolidLanguageServer): "symbol": {"dynamicRegistration": True}, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "initializationOptions": { "yaml": { "schemaStore": {"enable": True, "url": "https://www.schemastore.org/api/json/catalog.json"}, @@ -161,7 +148,7 @@ class YamlLanguageServer(SolidLanguageServer): } }, } - return initialize_params # type: ignore + return initialize_params def _start_server(self) -> None: """ @@ -184,7 +171,7 @@ class YamlLanguageServer(SolidLanguageServer): log.info("Starting YAML server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/language_servers/zls.py b/src/solidlsp/language_servers/zls.py index cb5652fc..c05ec371 100644 --- a/src/solidlsp/language_servers/zls.py +++ b/src/solidlsp/language_servers/zls.py @@ -13,7 +13,6 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.settings import SolidLSPSettings @@ -100,12 +99,10 @@ class ZigLanguageServer(SolidLanguageServer): super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd="zls", cwd=repository_root_path), "zig", solidlsp_settings) self.request_id = 0 - @staticmethod - def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + def _create_base_initialize_params(self) -> dict: """ Returns the initialize params for the Zig Language Server. """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", "capabilities": { @@ -140,15 +137,6 @@ class ZigLanguageServer(SolidLanguageServer): "configuration": True, }, }, - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": root_uri, - "workspaceFolders": [ - { - "uri": root_uri, - "name": os.path.basename(repository_absolute_path), - } - ], "initializationOptions": { # ZLS specific options based on schema.json # Critical paths for ZLS to understand the project @@ -178,7 +166,7 @@ class ZigLanguageServer(SolidLanguageServer): "inlay_hints_hide_redundant_param_names_last_token": False, }, } - return initialize_params # type: ignore[return-value] + return initialize_params def _start_server(self) -> None: """Start ZLS server process""" @@ -199,7 +187,7 @@ class ZigLanguageServer(SolidLanguageServer): log.info("Starting ZLS server process") self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + initialize_params = self._create_initialize_params() log.info("Sending initialize request from LSP client to LSP server and awaiting response") init_response = self.server.send.initialize(initialize_params) diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index af3e5f07..65793bec 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -11,6 +11,7 @@ from collections import defaultdict from collections.abc import Callable, Hashable, Iterator, Sequence from contextlib import contextmanager from copy import copy +from dataclasses import dataclass from pathlib import Path, PurePath from time import monotonic, perf_counter, sleep from typing import Any, Self, Union, cast @@ -22,6 +23,7 @@ from sensai.util.string import ToStringMixin from serena.util.file_system import match_path from serena.util.text_utils import MatchedConsecutiveLines from solidlsp import ls_types +from solidlsp.initialize_params import DefaultInitializeParamsBuilder, InitializeParamsBuilder from solidlsp.ls_config import FilenameMatcher, Language, LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException from solidlsp.ls_process import LanguageServerInterface, StdioLanguageServer @@ -35,6 +37,7 @@ from solidlsp.lsp_protocol_handler.lsp_types import ( DefinitionParams, DocumentSymbol, ImplementationParams, + InitializeParams, LocationLink, RenameParams, SymbolInformation, @@ -651,7 +654,7 @@ class SolidLanguageServer(ABC): Do not instantiate this class directly. Use `LanguageServer.create` method instead. - :param config: the global SolidLSP configuration. + :param config: the language server's configuration. :param repository_root_path: the root path of the repository. :param process_launch_info: (DEPRECATED: pass None and implement _create_dependency_provider instead) the command used to start the actual language server. @@ -661,12 +664,13 @@ class SolidLanguageServer(ABC): notification by default. If the language server uses multiple language identifiers, it must override the method `get_language_id_for_file` to provide the appropriate identifier for each type of file. + :param solidlsp_settings: the global SolidLSP settings :param cache_version_raw_document_symbols: the version, for caching, of the raw document symbols coming from this specific language server. This should be incremented by subclasses calling this constructor whenever the format of the raw document symbols changes (typically because the language server improves/fixes its output). """ - self._solidlsp_config = config + self.config = config self._solidlsp_settings = solidlsp_settings lang = self.get_language_enum_instance() self._custom_settings = solidlsp_settings.get_ls_specific_settings(lang) @@ -744,28 +748,13 @@ class SolidLanguageServer(ABC): self._has_waited_for_cross_file_references = False - # resolving additional workspace folders - self._additional_workspace_abs_paths: list[str] = [] - _seen: set[str] = set() - for additional_workspace_path in self._solidlsp_settings.additional_workspace_folders: - if not additional_workspace_path or additional_workspace_path == ".": - continue - if not os.path.isabs(additional_workspace_path): - additional_workspace_abs_path = os.path.realpath(os.path.join(self.repository_root_path, additional_workspace_path)) - else: - additional_workspace_abs_path = str(Path(additional_workspace_path).resolve()) - if not os.path.isdir(additional_workspace_abs_path): - log.error( - "additional_workspace_folders: skipping non-existent directory %s (resolved to %s)", - additional_workspace_abs_path, - additional_workspace_abs_path, - ) - continue - if additional_workspace_abs_path in _seen: - log.info("additional_workspace_folders: skipping duplicate %s", additional_workspace_path) - continue - _seen.add(additional_workspace_abs_path) - self._additional_workspace_abs_paths.append(additional_workspace_abs_path) + self._abs_workspace_folders_indexed = self.config.get_absolute_workspace_folders(self.repository_root_path) + self._abs_workspace_folders_additional = self.config.get_absolute_additional_workspace_folders(self.repository_root_path) + """ + additional workspace folders, which are passed to the language server in the initialization request + but which are not indexed by SolidLSP, i.e. not traversed for full symbol tree + """ + self._abs_workspace_folders_all = self._abs_workspace_folders_indexed + self._abs_workspace_folders_additional def _create_dependency_provider(self) -> LanguageServerDependencyProvider: """ @@ -804,7 +793,7 @@ class SolidLanguageServer(ABC): language=self.language, determine_log_level=self._determine_log_level, logger=logging_fn, - start_independent_lsp_process=self._solidlsp_config.start_independent_lsp_process, + start_independent_lsp_process=self.config.start_independent_lsp_process, ) # --- diagnostics-related functions --- @@ -1206,6 +1195,48 @@ class SolidLanguageServer(ABC): """Check if a relative path traverses outside the workspace root via '..' components.""" return ".." in PurePath(relative_file_path).parts + @dataclass + class PathWorkspaceStatus: + """ + Represents the status of a path with respect to the language server's workspace folders. + """ + + ls: "SolidLanguageServer" + resolve_abs_path: Path + is_in_workspace_folder: bool + workspace_root: str | None = None + """ + the workspace root the path is within/relative to, if any + """ + + @classmethod + def from_abs_resolved_path(cls, path: Path, ls: "SolidLanguageServer") -> "SolidLanguageServer.PathWorkspaceStatus": + """ + :param path: an absolute, resolved Path instance + :param ls: the language server instance + """ + for workspace in ls._abs_workspace_folders_all: + if path.is_relative_to(workspace): + return SolidLanguageServer.PathWorkspaceStatus( + ls=ls, is_in_workspace_folder=True, workspace_root=workspace, resolve_abs_path=path + ) + return SolidLanguageServer.PathWorkspaceStatus(ls=ls, is_in_workspace_folder=False, resolve_abs_path=path) + + @classmethod + def from_relative_path(cls, relative_path: str, ls: "SolidLanguageServer") -> "SolidLanguageServer.PathWorkspaceStatus": + """ + :param relative_path: a relative path from the repository root + :param ls: the language server instance + """ + return cls.from_abs_resolved_path(pathlib.Path(ls.repository_root_path, relative_path).resolve(), ls) + + def check_within_workspace_or_raise(self): + if not self.is_in_workspace_folder: + raise ValueError( + f"Path {self.resolve_abs_path} is outside of configured workspaces. " + f"Configured workspaces: {self.ls._abs_workspace_folders_all}." + ) + def _resolve_file_uri(self, relative_file_path: str) -> str: """Construct a canonical file URI from a relative path. @@ -1215,36 +1246,9 @@ class SolidLanguageServer(ABC): p = pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)) if self._path_contains_dots(relative_file_path): p = p.resolve() - is_outside_of_configured_workspaces = True - configured_workspaces = [*self._additional_workspace_abs_paths, self.repository_root_path] - for workspace in configured_workspaces: - if p.is_relative_to(workspace): - is_outside_of_configured_workspaces = False - break - if is_outside_of_configured_workspaces: - raise ValueError( - f"Path {relative_file_path} contains '..' segments and is outside of configured workspaces. " - f"Configured workspaces: {configured_workspaces}. Resolved path: {p}." - ) + self.PathWorkspaceStatus.from_abs_resolved_path(p, self).check_within_workspace_or_raise() return p.as_uri() - def _build_workspace_folders_param(self, repository_absolute_path: str) -> list[dict[str, str]]: - """Build the ``workspaceFolders`` list for LSP initialization. - - Returns a list containing the primary workspace folder followed by any - additional workspace folders configured in settings. - """ - root_uri = pathlib.Path(repository_absolute_path).as_uri() - folders: list[dict[str, str]] = [ - {"uri": root_uri, "name": os.path.basename(repository_absolute_path)}, - ] - for abs_folder in self._additional_workspace_abs_paths: - folder_uri = pathlib.Path(abs_folder).as_uri() - folders.append({"uri": folder_uri, "name": os.path.basename(abs_folder)}) - if len(folders) > 1: - log.info("LSP multi-root workspace: %d folders", len(folders)) - return folders - def _activate_additional_workspaces(self) -> None: """Open a representative file from each additional workspace folder to trigger project loading in the language server. @@ -1258,7 +1262,7 @@ class SolidLanguageServer(ABC): enable this feature; the default implementation raises ``NotImplementedError``. """ opened_count = 0 - for additional_workspace in self._additional_workspace_abs_paths: + for additional_workspace in self._abs_workspace_folders_additional: source_file = self._find_representative_source_file(additional_workspace) if source_file is None: log.warning("No source file found in additional workspace folder: %s", additional_workspace) @@ -2188,24 +2192,17 @@ class SolidLanguageServer(ABC): If a directory is passed, all files within this directory will be considered. :return: A list of root symbols representing the top-level packages/modules in the project. """ - if within_relative_path is not None: - within_abs_path = os.path.join(self.repository_root_path, within_relative_path) - if not os.path.exists(within_abs_path): - raise FileNotFoundError(f"File or directory not found: {within_abs_path}") - if os.path.isfile(within_abs_path): - if self.is_ignored_path(within_relative_path): - log.error("You passed a file explicitly, but it is ignored. This is probably an error. File: %s", within_relative_path) - return [] - else: - root_nodes = self.request_document_symbols(within_relative_path).root_symbols - return root_nodes # Helper function to recursively process directories - def process_directory(rel_dir_path: str) -> list[ls_types.UnifiedSymbolInformation]: - abs_dir_path = self.repository_root_path if rel_dir_path == "." else os.path.join(self.repository_root_path, rel_dir_path) + def process_directory(abs_dir_path: str) -> list[ls_types.UnifiedSymbolInformation]: abs_dir_path = os.path.realpath(abs_dir_path) - if self.is_ignored_path(str(Path(abs_dir_path).relative_to(self.repository_root_path))): + rel_dir_path: str | None + try: + rel_dir_path = str(Path(abs_dir_path).relative_to(self.repository_root_path)) + except ValueError: # not relative to repository root + rel_dir_path = None + if rel_dir_path and self.is_ignored_path(rel_dir_path): log.debug("Skipping directory: %s (because it should be ignored)", rel_dir_path) return [] @@ -2252,7 +2249,7 @@ class SolidLanguageServer(ABC): continue if os.path.isdir(contained_dir_or_file_abs_path): - child_symbols = process_directory(contained_dir_or_file_rel_path) + child_symbols = process_directory(contained_dir_or_file_abs_path) package_symbol["children"].extend(child_symbols) for child in child_symbols: child["parent"] = package_symbol @@ -2302,9 +2299,23 @@ class SolidLanguageServer(ABC): return result - # Start from the root or the specified directory - start_rel_path = within_relative_path or "." - return process_directory(start_rel_path) + if within_relative_path: + within_abs_path = os.path.join(self.repository_root_path, within_relative_path) + if not os.path.exists(within_abs_path): + raise FileNotFoundError(f"File or directory not found: {within_abs_path}") + if self.is_ignored_path(within_relative_path): + raise ValueError(f"Explicitly requested symbols in '{within_relative_path}' while the path is ignored") + if os.path.isfile(within_abs_path): + root_nodes = self.request_document_symbols(within_relative_path).root_symbols + return root_nodes + else: + self.PathWorkspaceStatus.from_abs_resolved_path(Path(within_abs_path).resolve(), self).check_within_workspace_or_raise() + return process_directory(within_abs_path) + else: + full_result = [] + for root in self.config.get_absolute_workspace_folders(self.repository_root_path): + full_result.extend(process_directory(root)) + return full_result @staticmethod def _get_range_from_file_content(file_content: str) -> ls_types.Range: @@ -3292,3 +3303,33 @@ class SolidLanguageServer(ABC): def is_running(self) -> bool: return self.server.is_running() + + def _create_initialize_params_builder(self) -> InitializeParamsBuilder: + return DefaultInitializeParamsBuilder(self) + + def _create_base_initialize_params(self) -> dict | InitializeParams: + """ + Subclasses should override this method to provide server-specific InitializeParams settings, + which provide the basis for the InitializeParams object sent to the language server during initialization. + + The returned dictionary is passed to the builder constructed by _create_initialize_params_builder() + in order to create the final InitializeParams object. + + The default builder implementation already sets the following keys, so implementations of this method + should not set them: + + - processId + - rootPath + - rootUri + - clientInfo + - workspaceFolders + + :return: the base InitializeParams settings + """ + raise NotImplementedError + + def _create_initialize_params(self) -> InitializeParams: + """ + Create the InitializeParams object to send to the language server during initialization. + """ + return self._create_initialize_params_builder().with_base_options(self._create_base_initialize_params()).build() diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index 6be2fc80..c666a2df 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -2,15 +2,20 @@ Configuration objects for language servers """ +import logging +import os import re from collections.abc import Iterable from dataclasses import dataclass, field from enum import Enum +from pathlib import Path from typing import TYPE_CHECKING, Self if TYPE_CHECKING: from solidlsp import SolidLanguageServer +log = logging.getLogger(__name__) + class FilenameMatcher: def __init__(self, *file_extensions: str, case_sensitive: bool = True) -> None: @@ -826,13 +831,25 @@ class Language(str, Enum): raise ValueError(f"Unhandled language server class: {ls_class}") -@dataclass +@dataclass(frozen=True) class LanguageServerConfig: """ Configuration parameters """ code_language: Language + workspace_folders: list[str] = field(default_factory=lambda: ["."]) + """ + list of workspace folders to be used by the language server and to be fully indexed by SolidLSP. + Paths can either be absolute or relative to the project root. + These folders must be descendants of the project root. + """ + additional_workspace_folders: list[str] = field(default_factory=list) + """ + list of additional workspace folders to be passed to the language server, but which are not to be indexed by SolidLSP. + Paths can either be absolute or relative to the project root. + These folders can potentially be outside of the project root, e.g. for cross-package reference support. + """ trace_lsp_communication: bool = False start_independent_lsp_process: bool = True ignored_paths: list[str] = field(default_factory=list) @@ -845,3 +862,38 @@ class LanguageServerConfig: import inspect return cls(**{k: v for k, v in env.items() if k in inspect.signature(cls).parameters}) + + @staticmethod + def _absolute_workspace_folders(folders: list[str], project_root: str) -> list[str]: + abs_workspace_folders = [] + for path in folders: + if os.path.isabs(path): + abs_path = str(Path(path).resolve()) + else: + abs_path = os.path.realpath(os.path.join(project_root, path)) + if not os.path.exists(abs_path): + log.error("Workspace folder does not exist: %s; skipping", abs_path) + continue + if abs_path in abs_workspace_folders: + log.warning("Duplicate workspace folder: %s; skipping", abs_path) + continue + abs_workspace_folders.append(abs_path) + return abs_workspace_folders + + def get_absolute_workspace_folders(self, project_root: str) -> list[str]: + """ + Get the absolute paths of the workspace folders, resolving relative paths against the project root. + + :param project_root: The root path of the project + :return: List of absolute workspace folder paths + """ + return self._absolute_workspace_folders(self.workspace_folders, project_root) + + def get_absolute_additional_workspace_folders(self, project_root: str) -> list[str]: + """ + Get the absolute paths of the additional workspace folders, resolving relative paths against the project root. + + :param project_root: The root path of the project + :return: List of absolute additional workspace folder paths + """ + return self._absolute_workspace_folders(self.additional_workspace_folders, project_root) diff --git a/test/conftest.py b/test/conftest.py index a8cf3394..fc046273 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -66,6 +66,7 @@ def _create_ls( ignored_paths: list[str] | None = None, trace_lsp_communication: bool = False, ls_specific_settings: dict[Language, dict[str, Any]] | None = None, + workspace_folders: list[str] | None = None, additional_workspace_folders: list[str] | None = None, solidlsp_dir: Path | None = None, ) -> SolidLanguageServer: @@ -79,6 +80,8 @@ def _create_ls( code_language=language, ignored_paths=ignored_paths, trace_lsp_communication=trace_lsp_communication, + workspace_folders=workspace_folders or ["."], + additional_workspace_folders=additional_workspace_folders or [], ) effective_solidlsp_dir = solidlsp_dir if solidlsp_dir is not None else SerenaPaths().serena_user_home_dir project_data_path = os.path.join(repo_path, SERENA_MANAGED_DIR_NAME) @@ -89,7 +92,6 @@ def _create_ls( solidlsp_dir=effective_solidlsp_dir, project_data_path=project_data_path, ls_specific_settings=ls_specific_settings or {}, - additional_workspace_folders=additional_workspace_folders or [], ), ) @@ -101,11 +103,19 @@ def start_ls_context( ignored_paths: list[str] | None = None, trace_lsp_communication: bool = False, ls_specific_settings: dict[Language, dict[str, Any]] | None = None, + workspace_folders: list[str] | None = None, additional_workspace_folders: list[str] | None = None, solidlsp_dir: Path | None = None, ) -> Iterator[SolidLanguageServer]: ls = _create_ls( - language, repo_path, ignored_paths, trace_lsp_communication, ls_specific_settings, additional_workspace_folders, solidlsp_dir + language, + repo_path, + ignored_paths, + trace_lsp_communication, + ls_specific_settings, + workspace_folders, + additional_workspace_folders, + solidlsp_dir, ) log.info(f"Starting language server for {language} {repo_path}") with ls.start_server_context(): diff --git a/test/solidlsp/csharp/test_omnisharp_startup.py b/test/solidlsp/csharp/test_omnisharp_startup.py deleted file mode 100644 index 74b9df34..00000000 --- a/test/solidlsp/csharp/test_omnisharp_startup.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from pathlib import Path -from types import SimpleNamespace - -import pytest - -from solidlsp.language_servers.omnisharp import OmniSharp - - -class _NoWaitEvent: - def is_set(self) -> bool: - return False - - def wait(self, timeout: float | None = None) -> bool: - raise AssertionError("OmniSharp startup must not wait for dynamic capability registration") - - -@pytest.mark.csharp -def test_omnisharp_startup_does_not_wait_for_capability_registration(tmp_path: Path) -> None: - omnisharp = OmniSharp.__new__(OmniSharp) - omnisharp.repository_root_path = str(tmp_path) - omnisharp.server = SimpleNamespace( - send=SimpleNamespace(initialize=lambda _params: {"capabilities": {}}), - notify=SimpleNamespace(initialized=lambda _params: None, workspace_did_change_configuration=lambda _params: None), - on_request=lambda *_args: None, - on_notification=lambda *_args: None, - start=lambda: None, - ) - omnisharp.server_ready = _NoWaitEvent() - omnisharp.definition_available = _NoWaitEvent() - omnisharp.references_available = _NoWaitEvent() - omnisharp.completions_available = _NoWaitEvent() - - omnisharp._start_server() diff --git a/test/solidlsp/test_ls_common.py b/test/solidlsp/test_ls_common.py index cfa1d11d..fab8f215 100644 --- a/test/solidlsp/test_ls_common.py +++ b/test/solidlsp/test_ls_common.py @@ -1,9 +1,12 @@ import os +from collections.abc import Sequence import pytest from solidlsp import SolidLanguageServer -from test.conftest import PYTHON_LANGUAGE_BACKENDS +from solidlsp.ls_config import Language +from solidlsp.ls_utils import SymbolUtils +from test.conftest import PYTHON_LANGUAGE_BACKENDS, start_ls_context class TestLanguageServerCommonFunctionality: @@ -41,3 +44,35 @@ class TestLanguageServerCommonFunctionality: finally: os.remove(file_path) + + def test_workspace_folders_affect_full_symbol_tree(self): + """ + Tests that workspace folder configurations are respected when requesting the full symbol tree. + """ + + def check(langsrv: SolidLanguageServer, present: Sequence[str] = (), absent: Sequence[str] = ()) -> None: + symbols = langsrv.request_full_symbol_tree() + for name in present: + assert SymbolUtils.symbol_tree_contains_name(symbols, name) + for name in absent: + assert not SymbolUtils.symbol_tree_contains_name(symbols, name) + + symbols_in_subfolder_test_repo = ["OuterClass", "UserService"] + symbols_in_subfolder_scripts = ["parse_args", "create_sample_users"] + all_symbols = symbols_in_subfolder_test_repo + symbols_in_subfolder_scripts + + with start_ls_context(language=Language.PYTHON, workspace_folders=["."]) as ls: + ls.request_full_symbol_tree() + check(ls, present=all_symbols) + + with start_ls_context(language=Language.PYTHON, workspace_folders=["./test_repo"]) as ls: + ls.request_full_symbol_tree() + check(ls, present=symbols_in_subfolder_test_repo, absent=symbols_in_subfolder_scripts) + + with start_ls_context(language=Language.PYTHON, workspace_folders=["./scripts"]) as ls: + ls.request_full_symbol_tree() + check(ls, present=symbols_in_subfolder_scripts, absent=symbols_in_subfolder_test_repo) + + # test that explicit requests for symbols in other workspace folders are rejected + with pytest.raises(ValueError, match="outside"): + ls.request_full_symbol_tree(within_relative_path="./test_repo")