c6af9e284a
Tests / catch-all (windows-latest) (push) Has been cancelled
Tests / jvm (macos-latest) (push) Has been cancelled
Tests / jvm (ubuntu-latest) (push) Has been cancelled
Tests / jvm (windows-latest) (push) Has been cancelled
Tests / native (macos-latest) (push) Has been cancelled
Tests / native (ubuntu-latest) (push) Has been cancelled
Tests / native (windows-latest) (push) Has been cancelled
Tests / niche (ubuntu-latest) (push) Has been cancelled
Tests / other-langs (macos-latest) (push) Has been cancelled
Tests / other-langs (ubuntu-latest) (push) Has been cancelled
Tests / other-langs (windows-latest) (push) Has been cancelled
Tests / catch-all (macos-latest) (push) Has been cancelled
Tests / catch-all (ubuntu-latest) (push) Has been cancelled
Docs Build / build (push) Has been cancelled
Docs Build / deploy (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Build and Push Docker Images / build-and-push (push) Has been cancelled
938 lines
39 KiB
Python
938 lines
39 KiB
Python
"""
|
|
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 functools import cache
|
|
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:
|
|
"""
|
|
:param file_extensions: file extensions, e.g., `.py, .yml`
|
|
:param case_sensitive: whether the file extensions are case-sensitive.
|
|
"""
|
|
self._file_extensions = list(set(file_extensions)) if case_sensitive else list(set(ext.lower() for ext in file_extensions))
|
|
self._case_sensitive = case_sensitive
|
|
# Snapshot of the initial configuration, used by ``reset``. Relevant for matchers that are
|
|
# per-language singletons (``Language.get_source_fn_matcher`` is ``@cache``d): extensions added
|
|
# via ``add_extensions`` for one project must not leak into the next, so the singleton is reset
|
|
# to this snapshot at every language server initialisation.
|
|
self._initial_file_extensions = list(self._file_extensions)
|
|
|
|
def reset(self) -> None:
|
|
"""
|
|
Restore the matcher to its initial set of extensions (as provided at construction).
|
|
|
|
Undoes any extensions added via :meth:`add_extensions`. Intended for the per-language
|
|
singleton matchers, which are reset at the start of every language server initialisation so
|
|
that a previous project's reconfiguration does not leak into a newly activated one.
|
|
"""
|
|
self._file_extensions = list(self._initial_file_extensions)
|
|
|
|
def add_extensions(self, *file_extensions: str) -> None:
|
|
"""
|
|
Add further file extensions to this matcher (idempotent).
|
|
|
|
This is intended for matchers that are per-language singletons, i.e. those returned by
|
|
:meth:`Language.get_source_fn_matcher` (which is ``@cache``d): extensions that a user
|
|
configures for a language server (e.g. ``.cgi`` for Perl) can be added here so that every
|
|
consumer of the matcher — symbol index traversal, ignore checks, language composition —
|
|
treats the same set of files as sources, staying in sync with the language server.
|
|
|
|
:param file_extensions: the additional file extensions, e.g. ``.cgi``
|
|
"""
|
|
for ext in file_extensions:
|
|
norm = ext if self._case_sensitive else ext.lower()
|
|
if norm not in self._file_extensions:
|
|
self._file_extensions.append(norm)
|
|
|
|
def is_relevant_filename(self, fn: str) -> bool:
|
|
if not self._case_sensitive:
|
|
fn = fn.lower()
|
|
for ext in self._file_extensions:
|
|
if fn.endswith(ext):
|
|
return True
|
|
return False
|
|
|
|
def string_contains_relevant_filename(self, string: str) -> bool:
|
|
""":return: whether ``string`` contains an occurrence of any registered extension as
|
|
a *complete* extension — i.e. the extension must either end the string or be followed
|
|
by a non-extension-character (anything other than a letter, digit, or underscore).
|
|
"""
|
|
if not self._case_sensitive:
|
|
string = string.lower()
|
|
for ext in self._file_extensions:
|
|
if re.search(rf"{re.escape(ext)}(?:\W|$)", string):
|
|
return True
|
|
return False
|
|
|
|
|
|
class Language(str, Enum):
|
|
"""
|
|
Enumeration of language servers supported by SolidLSP.
|
|
"""
|
|
|
|
CSHARP = "csharp"
|
|
PYTHON = "python"
|
|
RUST = "rust"
|
|
JAVA = "java"
|
|
KOTLIN = "kotlin"
|
|
TYPESCRIPT = "typescript"
|
|
GO = "go"
|
|
RUBY = "ruby"
|
|
DART = "dart"
|
|
CPP = "cpp"
|
|
CPP_CCLS = "cpp_ccls"
|
|
PHP = "php"
|
|
R = "r"
|
|
PERL = "perl"
|
|
CLOJURE = "clojure"
|
|
ELIXIR = "elixir"
|
|
ELM = "elm"
|
|
TERRAFORM = "terraform"
|
|
SWIFT = "swift"
|
|
BASH = "bash"
|
|
CRYSTAL = "crystal"
|
|
CUE = "cue"
|
|
ZIG = "zig"
|
|
LUA = "lua"
|
|
LUAU = "luau"
|
|
"""Luau Language Server for Roblox's Luau language (typed Lua 5.1 superset).
|
|
Uses luau-lsp by JohnnyMorganz. Automatically downloads the binary if not found.
|
|
Supports .luau files. Configure via .luaurc in the project root.
|
|
"""
|
|
NIX = "nix"
|
|
ERLANG = "erlang"
|
|
OCAML = "ocaml"
|
|
AL = "al"
|
|
FSHARP = "fsharp"
|
|
REGO = "rego"
|
|
SCALA = "scala"
|
|
JULIA = "julia"
|
|
FORTRAN = "fortran"
|
|
HASKELL = "haskell"
|
|
HAXE = "haxe"
|
|
"""Haxe language server using vshaxe/haxe-language-server.
|
|
Requires Haxe compiler (3.4.0+) and Node.js.
|
|
Discovered from system PATH or vshaxe VSCode extension, otherwise downloaded from Open VSX.
|
|
"""
|
|
LEAN4 = "lean4"
|
|
GROOVY = "groovy"
|
|
VUE = "vue"
|
|
SVELTE = "svelte"
|
|
"""Svelte language server using svelte-language-server.
|
|
Supports .svelte Single File Components plus TypeScript and JavaScript
|
|
files in Svelte projects. Requires Node.js v18+ and npm.
|
|
"""
|
|
POWERSHELL = "powershell"
|
|
PASCAL = "pascal"
|
|
"""Pascal Language Server (pasls) for Free Pascal and Lazarus projects.
|
|
Automatically downloads pasls binary. Requires FPC for full functionality.
|
|
Set PP and FPCDIR environment variables for source navigation.
|
|
"""
|
|
MATLAB = "matlab"
|
|
"""MATLAB language server using the official MathWorks MATLAB Language Server.
|
|
Requires MATLAB R2021b or later and Node.js.
|
|
Set MATLAB_PATH environment variable or configure matlab_path in ls_specific_settings.
|
|
"""
|
|
MSL = "msl"
|
|
"""mIRC Scripting Language (mSL) language server.
|
|
Supports .mrc files used in mIRC and AdiIRC IRC clients.
|
|
Uses a custom LSP server based on pygls. Automatically sets up
|
|
a virtual environment with pygls dependencies on first use.
|
|
"""
|
|
BSL = "bsl"
|
|
"""BSL Language Server for 1C:Enterprise and OneScript languages.
|
|
Uses bsl-language-server by 1c-syntax. Automatically downloads the JAR.
|
|
Supports .bsl and .os files. Requires Java 21+ on PATH.
|
|
"""
|
|
ADA = "ada"
|
|
"""Ada / SPARK language server using AdaCore's Ada Language Server (ALS).
|
|
Supports .ads (specs), .adb (bodies), and .ada files. Auto-downloads the
|
|
ALS binary from AdaCore's GitHub releases. Works best with a .gpr GNAT
|
|
project file at the repository root. SPARK files are handled transparently
|
|
by the same server, since SPARK is distinguished by pragmas/aspects in
|
|
source rather than by file extension.
|
|
"""
|
|
GDSCRIPT = "gdscript"
|
|
"""GDScript language server for Godot Engine projects (Godot 3 and 4).
|
|
Connects to the Godot editor's built-in LSP server over TCP (port 6008).
|
|
The editor must already be running with its built-in LSP enabled (default).
|
|
Supports .gd and .gdscript files.
|
|
"""
|
|
# Experimental or deprecated Language Servers
|
|
TYPESCRIPT_VTS = "typescript_vts"
|
|
"""Use the typescript language server through the natively bundled vscode extension via https://github.com/yioneko/vtsls"""
|
|
PYTHON_JEDI = "python_jedi"
|
|
"""Jedi language server for Python (instead of pyright, which is the default)"""
|
|
PYTHON_TY = "python_ty"
|
|
"""Ty language server for Python (instead of pyright, which is the default)."""
|
|
PYTHON_PYREFLY = "python_pyrefly"
|
|
"""Pyrefly language server for Python (instead of pyright, which is the default)."""
|
|
CSHARP_OMNISHARP = "csharp_omnisharp"
|
|
"""OmniSharp language server for C# (instead of the default csharp-ls by microsoft).
|
|
Currently has problems with finding references, and generally seems less stable and performant.
|
|
"""
|
|
RUBY_SOLARGRAPH = "ruby_solargraph"
|
|
"""Solargraph language server for Ruby (legacy, experimental).
|
|
Use Language.RUBY (ruby-lsp) for better performance and modern LSP features.
|
|
"""
|
|
PHP_PHPACTOR = "php_phpactor"
|
|
"""Phpactor language server for PHP (instead of Intelephense, which is the default).
|
|
Requires PHP 8.1+ on the system. Fully open-source (MIT license).
|
|
"""
|
|
PHP_PHPANTOM = "php_phpantom"
|
|
"""PHPantom language server for PHP (instead of Intelephense, which is the default).
|
|
Uses the open-source Rust-based phpantom_lsp binary and can be auto-downloaded.
|
|
"""
|
|
MARKDOWN = "markdown"
|
|
"""Marksman language server for Markdown (experimental).
|
|
Must be explicitly specified as the main language, not auto-detected.
|
|
This is an edge case primarily useful when working on documentation-heavy projects.
|
|
"""
|
|
LATEX = "latex"
|
|
"""texlab language server for LaTeX/BibTeX (experimental).
|
|
Must be explicitly specified as the main language, not auto-detected.
|
|
Provides sectioning-hierarchy document symbols plus label/citation definitions and references.
|
|
"""
|
|
YAML = "yaml"
|
|
"""YAML language server (experimental).
|
|
Must be explicitly specified as the main language, not auto-detected.
|
|
"""
|
|
JSON = "json"
|
|
"""JSON language server using vscode-json-languageserver (experimental).
|
|
Provides document symbol navigation and hover for JSON files.
|
|
Must be explicitly specified as the main language, not auto-detected.
|
|
Requires Node.js and npm.
|
|
"""
|
|
TOML = "toml"
|
|
"""TOML language server using Taplo.
|
|
Supports TOML validation, formatting, and schema support.
|
|
"""
|
|
HLSL = "hlsl"
|
|
"""Shader language server using shader-language-server (antaalt/shader-sense).
|
|
Supports .hlsl, .hlsli, .fx, .fxh, .cginc, .compute, .shader, .glsl, .vert, .frag, .geom, .tesc, .tese, .comp, .wgsl files.
|
|
Automatically downloads shader-language-server binary.
|
|
"""
|
|
SYSTEMVERILOG = "systemverilog"
|
|
"""SystemVerilog language server using verible-verilog-ls.
|
|
Supports .sv, .svh, .v, .vh files.
|
|
Automatically downloads verible binary.
|
|
"""
|
|
SOLIDITY = "solidity"
|
|
"""Solidity language server using the Nomic Foundation Solidity Language Server
|
|
(@nomicfoundation/solidity-language-server).
|
|
Supports .sol files. Provides go-to-definition, find references, document symbols,
|
|
hover, and diagnostics. Requires Node.js and npm.
|
|
Works best with a foundry.toml or hardhat.config.js in the project root.
|
|
"""
|
|
ANSIBLE = "ansible"
|
|
"""Ansible language server (experimental) using @ansible/ansible-language-server.
|
|
Supports .yaml and .yml files (same extensions as YAML, hence experimental).
|
|
Must be explicitly specified in project.yml. Requires Node.js and npm.
|
|
Requires ``ansible`` in PATH for full functionality.
|
|
"""
|
|
HTML = "html"
|
|
"""HTML language server (experimental) using vscode-html-language-server from
|
|
Microsoft's vscode-langservers-extracted npm package. Supports *.html and *.htm files.
|
|
Must be explicitly specified in project.yml. Requires Node.js and npm.
|
|
Note: HTML LSP provides in-file element/id symbols only; cross-file references
|
|
are not meaningful for HTML. Also used as a companion server by Angular LS for
|
|
plain HTML documentSymbol support.
|
|
"""
|
|
SCSS = "scss"
|
|
"""SCSS / Sass / CSS language server (experimental) using some-sass-language-server
|
|
(https://github.com/wkillerud/some-sass). Handles *.scss, *.sass, and *.css.
|
|
Must be explicitly specified in project.yml. Requires Node.js and npm.
|
|
Provides full @use/@forward workspace navigation across SCSS files; CSS support
|
|
relies on the same vscode-css-languageservice engine and is enabled at startup
|
|
via the somesass.css.* feature toggles (which default to off upstream).
|
|
"""
|
|
ANGULAR = "angular"
|
|
"""Angular Language Server (experimental) using the official @angular/language-server
|
|
(ngserver). Supports *.ts and *.html files (Angular templates can be external or inline).
|
|
Understands Angular template syntax (*ngIf, [prop], (event), {{ interpolation }},
|
|
@if/@for blocks, etc.) and provides type-aware navigation between templates and
|
|
component classes — which the plain HTML and TypeScript LSPs cannot.
|
|
Requires Node.js, npm, and a valid Angular workspace (angular.json or Nx project.json
|
|
at the repository root). When activated, do not also enable typescript or html in
|
|
project.yml — Angular LS supersedes both for Angular projects.
|
|
Must be explicitly specified in project.yml.
|
|
"""
|
|
|
|
@classmethod
|
|
def iter_all(cls, include_experimental: bool = False, include_non_programming_languages: bool = True) -> Iterable[Self]:
|
|
for lang in cls:
|
|
if include_experimental or not lang.is_experimental():
|
|
if include_non_programming_languages or lang.is_programming_language():
|
|
yield lang
|
|
|
|
def is_experimental(self) -> bool:
|
|
"""
|
|
Check if the language server is experimental or deprecated.
|
|
|
|
Note for serena users/developers:
|
|
Experimental languages are not autodetected and must be explicitly specified
|
|
in the project.yml configuration.
|
|
"""
|
|
return self in {
|
|
self.ANSIBLE,
|
|
self.TYPESCRIPT_VTS,
|
|
self.PYTHON_JEDI,
|
|
self.PYTHON_TY,
|
|
self.PYTHON_PYREFLY,
|
|
self.CSHARP_OMNISHARP,
|
|
self.RUBY_SOLARGRAPH,
|
|
self.PHP_PHPACTOR,
|
|
self.PHP_PHPANTOM,
|
|
self.MARKDOWN,
|
|
self.LATEX,
|
|
self.YAML,
|
|
self.JSON,
|
|
self.TOML,
|
|
self.GROOVY,
|
|
self.CPP_CCLS,
|
|
self.SOLIDITY,
|
|
self.HTML,
|
|
self.SCSS,
|
|
self.ANGULAR,
|
|
}
|
|
|
|
def is_programming_language(self) -> bool:
|
|
"""Whether the supported language should be considered a programming language.
|
|
Solidlsp supports languages like markdown or json, this method returns False for them.
|
|
"""
|
|
return self not in frozenset((self.MARKDOWN, self.LATEX, self.JSON, self.TOML, self.YAML, self.ANSIBLE))
|
|
|
|
def __str__(self) -> str:
|
|
return self.value
|
|
|
|
def get_priority(self) -> int:
|
|
"""
|
|
:return: priority of the language for breaking ties between languages; higher is more important.
|
|
"""
|
|
# experimental languages have the lowest priority
|
|
if self.is_experimental():
|
|
return 0
|
|
# We assign lower priority to languages that are supersets of others, such that
|
|
# the "larger" language is only chosen when it matches more strongly
|
|
match self:
|
|
# languages that are supersets of others (Vue/Svelte are supersets of TypeScript/JavaScript)
|
|
case self.VUE | self.SVELTE:
|
|
return 1
|
|
# regular languages
|
|
case _:
|
|
return 2
|
|
|
|
def supports_implementation_request(self) -> bool:
|
|
"""
|
|
Return whether the default language server for this language supports ``textDocument/implementation``.
|
|
"""
|
|
return self.get_ls_class().supports_implementation_request()
|
|
|
|
# NOTE: Caching results in a singleton per enum item, which is a precondition for persistent configuration of the matcher.
|
|
@cache
|
|
def get_source_fn_matcher(self) -> FilenameMatcher:
|
|
match self:
|
|
case self.PYTHON | self.PYTHON_JEDI | self.PYTHON_TY | self.PYTHON_PYREFLY:
|
|
return FilenameMatcher(".py", ".pyi")
|
|
case self.JAVA:
|
|
return FilenameMatcher(".java")
|
|
case self.TYPESCRIPT | self.TYPESCRIPT_VTS:
|
|
# see https://github.com/oraios/serena/issues/204
|
|
path_patterns = []
|
|
for prefix in ["c", "m", ""]:
|
|
for postfix in ["x", ""]:
|
|
for base_pattern in ["ts", "js"]:
|
|
path_patterns.append(f".{prefix}{base_pattern}{postfix}")
|
|
return FilenameMatcher(*path_patterns)
|
|
case self.CSHARP | self.CSHARP_OMNISHARP:
|
|
return FilenameMatcher(".cs")
|
|
case self.RUST:
|
|
return FilenameMatcher(".rs")
|
|
case self.GO:
|
|
return FilenameMatcher(".go")
|
|
case self.RUBY:
|
|
return FilenameMatcher(".rb", ".erb")
|
|
case self.RUBY_SOLARGRAPH:
|
|
return FilenameMatcher(".rb")
|
|
case self.CPP:
|
|
# From llvm-project/clang/lib/Driver/Types.cpp types::lookupTypeForExtension:
|
|
return FilenameMatcher(
|
|
# C
|
|
".c",
|
|
".h",
|
|
# C++
|
|
".c++",
|
|
".cc",
|
|
".cp",
|
|
".cpp",
|
|
".cxx",
|
|
".hh",
|
|
".hpp",
|
|
".hxx",
|
|
# C++ include files
|
|
".inl",
|
|
".ipp",
|
|
".tpp",
|
|
".txx",
|
|
# Objective-C
|
|
".m",
|
|
".mm",
|
|
# C++20 module interface files
|
|
".c++m",
|
|
".cppm",
|
|
".cxxm",
|
|
".ixx",
|
|
# CUDA
|
|
".cu",
|
|
# HIP
|
|
".hip",
|
|
# OpenCL
|
|
".cl",
|
|
".clcpp",
|
|
# Arduino sketch: not in clang's extension table, but it is C++.
|
|
# Routing it here lets clangd serve symbols; the project must
|
|
# tell clangd it is C++ (a .clangd with CompileFlags Add: [-xc++],
|
|
# or a compile DB), since the clang driver can't infer a job from .ino.
|
|
".ino",
|
|
case_sensitive=False,
|
|
)
|
|
case self.CPP_CCLS:
|
|
# From llvm-project/clang/lib/Driver/Types.cpp types::lookupTypeForExtension:
|
|
return FilenameMatcher(
|
|
# C
|
|
".c",
|
|
".h",
|
|
# C++
|
|
".c++",
|
|
".cc",
|
|
".cp",
|
|
".cpp",
|
|
".cxx",
|
|
".hh",
|
|
".hpp",
|
|
".hxx",
|
|
# C++ include files
|
|
".inl",
|
|
".ipp",
|
|
".tpp",
|
|
".txx",
|
|
# Objective-C
|
|
".m",
|
|
".mm",
|
|
# Arduino sketch (C++); see note in the CPP case above.
|
|
".ino",
|
|
case_sensitive=False,
|
|
)
|
|
case self.KOTLIN:
|
|
return FilenameMatcher(".kt", ".kts")
|
|
case self.DART:
|
|
return FilenameMatcher(".dart")
|
|
case self.PHP | self.PHP_PHPACTOR | self.PHP_PHPANTOM:
|
|
return FilenameMatcher(".php")
|
|
case self.R:
|
|
return FilenameMatcher(".R", ".r", ".Rmd", ".Rnw")
|
|
case self.PERL:
|
|
return FilenameMatcher(".pl", ".pm", ".t")
|
|
case self.CLOJURE:
|
|
return FilenameMatcher(".clj", ".cljs", ".cljc", ".edn") # codespell:ignore edn
|
|
case self.ELIXIR:
|
|
return FilenameMatcher(".ex", ".exs")
|
|
case self.ELM:
|
|
return FilenameMatcher(".elm")
|
|
case self.TERRAFORM:
|
|
return FilenameMatcher(".tf", ".tfvars", ".tfstate")
|
|
case self.SWIFT:
|
|
return FilenameMatcher(".swift")
|
|
case self.BASH:
|
|
return FilenameMatcher(".sh", ".bash")
|
|
case self.CRYSTAL:
|
|
return FilenameMatcher(".cr")
|
|
case self.CUE:
|
|
return FilenameMatcher(".cue")
|
|
case self.YAML:
|
|
return FilenameMatcher(".yaml", ".yml")
|
|
case self.JSON:
|
|
return FilenameMatcher(".json", ".jsonc")
|
|
case self.TOML:
|
|
return FilenameMatcher(".toml")
|
|
case self.ZIG:
|
|
return FilenameMatcher(".zig", ".zon")
|
|
case self.LUA:
|
|
return FilenameMatcher(".lua")
|
|
case self.LUAU:
|
|
return FilenameMatcher(".luau")
|
|
case self.NIX:
|
|
return FilenameMatcher(".nix")
|
|
case self.ERLANG:
|
|
return FilenameMatcher(".erl", ".hrl", ".escript", ".config", ".app", ".app.src")
|
|
case self.OCAML:
|
|
return FilenameMatcher(".ml", ".mli", ".re", ".rei")
|
|
case self.AL:
|
|
return FilenameMatcher(".al", ".dal")
|
|
case self.FSHARP:
|
|
return FilenameMatcher(".fs", ".fsx", ".fsi")
|
|
case self.REGO:
|
|
return FilenameMatcher(".rego")
|
|
case self.MARKDOWN:
|
|
return FilenameMatcher(".md", ".markdown")
|
|
case self.LATEX:
|
|
return FilenameMatcher(".tex", ".bib", ".sty", ".cls")
|
|
case self.SCALA:
|
|
return FilenameMatcher(".scala", ".sbt")
|
|
case self.JULIA:
|
|
return FilenameMatcher(".jl")
|
|
case self.FORTRAN:
|
|
return FilenameMatcher(".f90", ".f95", ".f03", ".f08", ".f", ".for", ".fpp", case_sensitive=False)
|
|
case self.HASKELL:
|
|
return FilenameMatcher(".hs", ".lhs")
|
|
case self.HAXE:
|
|
return FilenameMatcher(".hx")
|
|
case self.LEAN4:
|
|
return FilenameMatcher(".lean")
|
|
case self.VUE:
|
|
path_patterns = [".vue"]
|
|
for prefix in ["c", "m", ""]:
|
|
for postfix in ["x", ""]:
|
|
for base_pattern in ["ts", "js"]:
|
|
path_patterns.append(f".{prefix}{base_pattern}{postfix}")
|
|
return FilenameMatcher(*path_patterns)
|
|
case self.SVELTE:
|
|
path_patterns = [".svelte"]
|
|
for prefix in ["c", "m", ""]:
|
|
for base_pattern in ["ts", "js"]:
|
|
path_patterns.append(f".{prefix}{base_pattern}")
|
|
return FilenameMatcher(*path_patterns)
|
|
case self.POWERSHELL:
|
|
return FilenameMatcher(".ps1", ".psm1", ".psd1")
|
|
case self.PASCAL:
|
|
return FilenameMatcher(".pas", ".pp", ".lpr", ".dpr", ".dpk", ".inc")
|
|
case self.GROOVY:
|
|
return FilenameMatcher(".groovy", ".gvy")
|
|
case self.MATLAB:
|
|
return FilenameMatcher(".m", ".mlx", ".mlapp")
|
|
case self.HLSL:
|
|
return FilenameMatcher(
|
|
".hlsl",
|
|
".hlsli",
|
|
".fx",
|
|
".fxh",
|
|
".cginc",
|
|
".compute",
|
|
".shader",
|
|
".glsl",
|
|
".vert",
|
|
".frag",
|
|
".geom",
|
|
".tesc",
|
|
".tese",
|
|
".comp",
|
|
".wgsl",
|
|
)
|
|
case self.SYSTEMVERILOG:
|
|
return FilenameMatcher(".sv", ".svh", ".v", ".vh")
|
|
case self.SOLIDITY:
|
|
return FilenameMatcher(".sol")
|
|
case self.ANSIBLE:
|
|
return FilenameMatcher(".yaml", ".yml")
|
|
case self.MSL:
|
|
return FilenameMatcher(".mrc")
|
|
case self.BSL:
|
|
return FilenameMatcher(".bsl", ".os")
|
|
case self.ADA:
|
|
return FilenameMatcher(".ads", ".adb", ".ada", case_sensitive=False)
|
|
case self.GDSCRIPT:
|
|
return FilenameMatcher(".gd", ".gdscript")
|
|
case self.HTML:
|
|
return FilenameMatcher(".html", ".htm")
|
|
case self.SCSS:
|
|
# *.css is handled by the same engine (vscode-css-languageservice) that powers
|
|
# Microsoft's CSS LS, so we route plain CSS through Some Sass too. The CSS feature
|
|
# toggles default off upstream and are flipped on at initialization time.
|
|
return FilenameMatcher(".scss", ".sass", ".css")
|
|
case self.ANGULAR:
|
|
# Angular templates can be standalone .html files or inline templates
|
|
# within .ts component files; the dual-server architecture handles both.
|
|
# SCSS / styles are deliberately NOT subsumed — use Language.SCSS for those.
|
|
path_patterns = [".html", ".htm"]
|
|
for prefix in ["c", "m", ""]:
|
|
for postfix in ["x", ""]:
|
|
path_patterns.append(f".{prefix}ts{postfix}")
|
|
return FilenameMatcher(*path_patterns)
|
|
case _:
|
|
raise ValueError(f"Unhandled language: {self}")
|
|
|
|
def get_ls_class(self) -> type["SolidLanguageServer"]:
|
|
match self:
|
|
case self.PYTHON:
|
|
from solidlsp.language_servers.pyright_server import PyrightServer
|
|
|
|
return PyrightServer
|
|
case self.PYTHON_JEDI:
|
|
from solidlsp.language_servers.jedi_server import JediServer
|
|
|
|
return JediServer
|
|
case self.PYTHON_TY:
|
|
from solidlsp.language_servers.ty_server import TyLanguageServer
|
|
|
|
return TyLanguageServer
|
|
case self.PYTHON_PYREFLY:
|
|
from solidlsp.language_servers.pyrefly_server import PyreflyLanguageServer
|
|
|
|
return PyreflyLanguageServer
|
|
case self.JAVA:
|
|
from solidlsp.language_servers.eclipse_jdtls import EclipseJDTLS
|
|
|
|
return EclipseJDTLS
|
|
case self.KOTLIN:
|
|
from solidlsp.language_servers.kotlin_language_server import KotlinLanguageServer
|
|
|
|
return KotlinLanguageServer
|
|
case self.RUST:
|
|
from solidlsp.language_servers.rust_analyzer import RustAnalyzer
|
|
|
|
return RustAnalyzer
|
|
case self.CSHARP:
|
|
from solidlsp.language_servers.csharp_language_server import CSharpLanguageServer
|
|
|
|
return CSharpLanguageServer
|
|
case self.CSHARP_OMNISHARP:
|
|
from solidlsp.language_servers.omnisharp import OmniSharp
|
|
|
|
return OmniSharp
|
|
case self.TYPESCRIPT:
|
|
from solidlsp.language_servers.typescript_language_server import TypeScriptLanguageServer
|
|
|
|
return TypeScriptLanguageServer
|
|
case self.TYPESCRIPT_VTS:
|
|
from solidlsp.language_servers.vts_language_server import VtsLanguageServer
|
|
|
|
return VtsLanguageServer
|
|
case self.VUE:
|
|
from solidlsp.language_servers.vue_language_server import VueLanguageServer
|
|
|
|
return VueLanguageServer
|
|
case self.SVELTE:
|
|
from solidlsp.language_servers.svelte_language_server import SvelteLanguageServer
|
|
|
|
return SvelteLanguageServer
|
|
case self.GO:
|
|
from solidlsp.language_servers.gopls import Gopls
|
|
|
|
return Gopls
|
|
case self.RUBY:
|
|
from solidlsp.language_servers.ruby_lsp import RubyLsp
|
|
|
|
return RubyLsp
|
|
case self.RUBY_SOLARGRAPH:
|
|
from solidlsp.language_servers.solargraph import Solargraph
|
|
|
|
return Solargraph
|
|
case self.DART:
|
|
from solidlsp.language_servers.dart_language_server import DartLanguageServer
|
|
|
|
return DartLanguageServer
|
|
case self.CPP:
|
|
from solidlsp.language_servers.clangd_language_server import ClangdLanguageServer
|
|
|
|
return ClangdLanguageServer
|
|
case self.CPP_CCLS:
|
|
from solidlsp.language_servers.ccls_language_server import CCLS
|
|
|
|
return CCLS
|
|
case self.PHP:
|
|
from solidlsp.language_servers.intelephense import Intelephense
|
|
|
|
return Intelephense
|
|
case self.PHP_PHPACTOR:
|
|
from solidlsp.language_servers.phpactor import PhpactorServer
|
|
|
|
return PhpactorServer
|
|
case self.PHP_PHPANTOM:
|
|
from solidlsp.language_servers.phpantom import PHPantomServer
|
|
|
|
return PHPantomServer
|
|
case self.PERL:
|
|
from solidlsp.language_servers.perl_language_server import PerlLanguageServer
|
|
|
|
return PerlLanguageServer
|
|
case self.CLOJURE:
|
|
from solidlsp.language_servers.clojure_lsp import ClojureLSP
|
|
|
|
return ClojureLSP
|
|
case self.ELIXIR:
|
|
from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools
|
|
|
|
return ElixirTools
|
|
case self.ELM:
|
|
from solidlsp.language_servers.elm_language_server import ElmLanguageServer
|
|
|
|
return ElmLanguageServer
|
|
case self.TERRAFORM:
|
|
from solidlsp.language_servers.terraform_ls import TerraformLS
|
|
|
|
return TerraformLS
|
|
case self.SWIFT:
|
|
from solidlsp.language_servers.sourcekit_lsp import SourceKitLSP
|
|
|
|
return SourceKitLSP
|
|
case self.BASH:
|
|
from solidlsp.language_servers.bash_language_server import BashLanguageServer
|
|
|
|
return BashLanguageServer
|
|
case self.CRYSTAL:
|
|
from solidlsp.language_servers.crystal_language_server import CrystalLanguageServer
|
|
|
|
return CrystalLanguageServer
|
|
case self.CUE:
|
|
from solidlsp.language_servers.cue_language_server import CueLanguageServer
|
|
|
|
return CueLanguageServer
|
|
case self.YAML:
|
|
from solidlsp.language_servers.yaml_language_server import YamlLanguageServer
|
|
|
|
return YamlLanguageServer
|
|
case self.JSON:
|
|
from solidlsp.language_servers.json_language_server import JsonLanguageServer
|
|
|
|
return JsonLanguageServer
|
|
case self.TOML:
|
|
from solidlsp.language_servers.taplo_server import TaploServer
|
|
|
|
return TaploServer
|
|
case self.ZIG:
|
|
from solidlsp.language_servers.zls import ZigLanguageServer
|
|
|
|
return ZigLanguageServer
|
|
case self.NIX:
|
|
from solidlsp.language_servers.nixd_ls import NixLanguageServer
|
|
|
|
return NixLanguageServer
|
|
case self.LUA:
|
|
from solidlsp.language_servers.lua_ls import LuaLanguageServer
|
|
|
|
return LuaLanguageServer
|
|
|
|
case self.LUAU:
|
|
from solidlsp.language_servers.luau_lsp import LuauLanguageServer
|
|
|
|
return LuauLanguageServer
|
|
|
|
case self.ERLANG:
|
|
from solidlsp.language_servers.erlang_language_server import ErlangLanguageServer
|
|
|
|
return ErlangLanguageServer
|
|
case self.OCAML:
|
|
from solidlsp.language_servers.ocaml_lsp_server import OcamlLanguageServer
|
|
|
|
return OcamlLanguageServer
|
|
case self.AL:
|
|
from solidlsp.language_servers.al_language_server import ALLanguageServer
|
|
|
|
return ALLanguageServer
|
|
case self.REGO:
|
|
from solidlsp.language_servers.regal_server import RegalLanguageServer
|
|
|
|
return RegalLanguageServer
|
|
case self.MARKDOWN:
|
|
from solidlsp.language_servers.marksman import Marksman
|
|
|
|
return Marksman
|
|
case self.LATEX:
|
|
from solidlsp.language_servers.texlab_language_server import TexlabLanguageServer
|
|
|
|
return TexlabLanguageServer
|
|
case self.R:
|
|
from solidlsp.language_servers.r_language_server import RLanguageServer
|
|
|
|
return RLanguageServer
|
|
case self.SCALA:
|
|
from solidlsp.language_servers.scala_language_server import ScalaLanguageServer
|
|
|
|
return ScalaLanguageServer
|
|
case self.JULIA:
|
|
from solidlsp.language_servers.julia_server import JuliaLanguageServer
|
|
|
|
return JuliaLanguageServer
|
|
case self.FORTRAN:
|
|
from solidlsp.language_servers.fortran_language_server import FortranLanguageServer
|
|
|
|
return FortranLanguageServer
|
|
case self.HASKELL:
|
|
from solidlsp.language_servers.haskell_language_server import HaskellLanguageServer
|
|
|
|
return HaskellLanguageServer
|
|
case self.HAXE:
|
|
from solidlsp.language_servers.haxe_language_server import HaxeLanguageServer
|
|
|
|
return HaxeLanguageServer
|
|
case self.LEAN4:
|
|
from solidlsp.language_servers.lean4_language_server import Lean4LanguageServer
|
|
|
|
return Lean4LanguageServer
|
|
case self.FSHARP:
|
|
from solidlsp.language_servers.fsharp_language_server import FSharpLanguageServer
|
|
|
|
return FSharpLanguageServer
|
|
case self.POWERSHELL:
|
|
from solidlsp.language_servers.powershell_language_server import PowerShellLanguageServer
|
|
|
|
return PowerShellLanguageServer
|
|
case self.PASCAL:
|
|
from solidlsp.language_servers.pascal_server import PascalLanguageServer
|
|
|
|
return PascalLanguageServer
|
|
case self.GROOVY:
|
|
from solidlsp.language_servers.groovy_language_server import GroovyLanguageServer
|
|
|
|
return GroovyLanguageServer
|
|
case self.MATLAB:
|
|
from solidlsp.language_servers.matlab_language_server import MatlabLanguageServer
|
|
|
|
return MatlabLanguageServer
|
|
case self.HLSL:
|
|
from solidlsp.language_servers.hlsl_language_server import HlslLanguageServer
|
|
|
|
return HlslLanguageServer
|
|
case self.SYSTEMVERILOG:
|
|
from solidlsp.language_servers.systemverilog_server import SystemVerilogLanguageServer
|
|
|
|
return SystemVerilogLanguageServer
|
|
case self.SOLIDITY:
|
|
from solidlsp.language_servers.solidity_language_server import SolidityLanguageServer
|
|
|
|
return SolidityLanguageServer
|
|
case self.ANSIBLE:
|
|
from solidlsp.language_servers.ansible_language_server import AnsibleLanguageServer
|
|
|
|
return AnsibleLanguageServer
|
|
case self.MSL:
|
|
from solidlsp.language_servers.msl_language_server import MslLanguageServer
|
|
|
|
return MslLanguageServer
|
|
case self.BSL:
|
|
from solidlsp.language_servers.bsl_language_server import BSLLanguageServer
|
|
|
|
return BSLLanguageServer
|
|
case self.ADA:
|
|
from solidlsp.language_servers.ada_language_server import AdaLanguageServer
|
|
|
|
return AdaLanguageServer
|
|
case self.GDSCRIPT:
|
|
from solidlsp.language_servers.godot_language_server import GodotLanguageServer
|
|
|
|
return GodotLanguageServer
|
|
case self.HTML:
|
|
from solidlsp.language_servers.vscode_html_language_server import VsCodeHtmlLanguageServer
|
|
|
|
return VsCodeHtmlLanguageServer
|
|
case self.SCSS:
|
|
from solidlsp.language_servers.some_sass_language_server import SomeSassLanguageServer
|
|
|
|
return SomeSassLanguageServer
|
|
case self.ANGULAR:
|
|
from solidlsp.language_servers.angular_language_server import AngularLanguageServer
|
|
|
|
return AngularLanguageServer
|
|
case _:
|
|
raise ValueError(f"Unhandled language: {self}")
|
|
|
|
@classmethod
|
|
def from_ls_class(cls, ls_class: type["SolidLanguageServer"]) -> Self:
|
|
"""
|
|
Get the Language enum value from a SolidLanguageServer class.
|
|
|
|
:param ls_class: The SolidLanguageServer class to find the corresponding Language for
|
|
:return: The Language enum value
|
|
:raises ValueError: If the language server class is not supported
|
|
"""
|
|
for enum_instance in cls:
|
|
if enum_instance.get_ls_class() == ls_class:
|
|
return enum_instance
|
|
raise ValueError(f"Unhandled language server class: {ls_class}")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class LanguageServerConfig:
|
|
"""
|
|
Configuration parameters for a language server instance
|
|
"""
|
|
|
|
code_language: Language
|
|
"""
|
|
defines the language server to use
|
|
"""
|
|
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)
|
|
"""Paths, dirs or glob-like patterns. The matching will follow the same logic as for .gitignore entries"""
|
|
encoding: str = "utf-8"
|
|
"""File encoding to use when reading source files"""
|
|
|
|
@classmethod
|
|
def from_dict(cls, env: dict) -> Self:
|
|
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)
|