From d7852341f7f92747db8c95b78fdba3866a673b99 Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Thu, 30 Apr 2026 13:20:40 +0200 Subject: [PATCH] Refactoring: Make FilenameMatcher accept file extensions instead of patterns Makes the matching both simpler and faster --- .../adding_new_language_support_guide.md | 2 +- src/solidlsp/ls_config.py | 149 +++++++++--------- 2 files changed, 78 insertions(+), 73 deletions(-) diff --git a/.serena/memories/adding_new_language_support_guide.md b/.serena/memories/adding_new_language_support_guide.md index 8ffa4684..bafa7526 100644 --- a/.serena/memories/adding_new_language_support_guide.md +++ b/.serena/memories/adding_new_language_support_guide.md @@ -97,7 +97,7 @@ class Language(str, Enum): match self: # Existing cases... case self.NEW_LANGUAGE: - return FilenameMatcher("*.newlang", "*.nl") # File extensions + return FilenameMatcher(".newlang", ".nl") # File extensions ``` ### 2.2 Update Language Server Factory diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index eb527dc3..030a6f71 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -2,7 +2,6 @@ Configuration objects for language servers """ -import fnmatch from collections.abc import Iterable from dataclasses import dataclass, field from enum import Enum @@ -13,15 +12,21 @@ if TYPE_CHECKING: class FilenameMatcher: - def __init__(self, *patterns: str) -> None: + def __init__(self, *file_extensions: str) -> None: """ - :param patterns: fnmatch-compatible patterns + :param file_extensions: file extensions, e.g., `.py, .yml` """ - self.patterns = patterns + self._file_extensions = file_extensions def is_relevant_filename(self, fn: str) -> bool: - for pattern in self.patterns: - if fnmatch.fnmatch(fn, pattern): + for ext in self._file_extensions: + if fn.endswith(ext): + return True + return False + + def string_contains_relevant_filename(self, string: str) -> bool: + for ext in self._file_extensions: + if ext in string: return True return False @@ -151,7 +156,7 @@ class Language(str, Enum): """ ANSIBLE = "ansible" """Ansible language server (experimental) using @ansible/ansible-language-server. - Supports *.yaml and *.yml files (same extensions as YAML, hence experimental). + 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. """ @@ -210,134 +215,134 @@ class Language(str, Enum): def get_source_fn_matcher(self) -> FilenameMatcher: match self: case self.PYTHON | self.PYTHON_JEDI | self.PYTHON_TY: - return FilenameMatcher("*.py", "*.pyi") + return FilenameMatcher(".py", ".pyi") case self.JAVA: - return FilenameMatcher("*.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}") + path_patterns.append(f".{prefix}{base_pattern}{postfix}") return FilenameMatcher(*path_patterns) case self.CSHARP | self.CSHARP_OMNISHARP: - return FilenameMatcher("*.cs") + return FilenameMatcher(".cs") case self.RUST: - return FilenameMatcher("*.rs") + return FilenameMatcher(".rs") case self.GO: - return FilenameMatcher("*.go") + return FilenameMatcher(".go") case self.RUBY: - return FilenameMatcher("*.rb", "*.erb") + return FilenameMatcher(".rb", ".erb") case self.RUBY_SOLARGRAPH: - return FilenameMatcher("*.rb") + return FilenameMatcher(".rb") case self.CPP | self.CPP_CCLS: - return FilenameMatcher("*.cpp", "*.h", "*.hpp", "*.c", "*.hxx", "*.cc", "*.cxx") + return FilenameMatcher(".cpp", ".h", ".hpp", ".c", ".hxx", ".cc", ".cxx") case self.KOTLIN: - return FilenameMatcher("*.kt", "*.kts") + return FilenameMatcher(".kt", ".kts") case self.DART: - return FilenameMatcher("*.dart") + return FilenameMatcher(".dart") case self.PHP | self.PHP_PHPACTOR: - return FilenameMatcher("*.php") + return FilenameMatcher(".php") case self.R: - return FilenameMatcher("*.R", "*.r", "*.Rmd", "*.Rnw") + return FilenameMatcher(".R", ".r", ".Rmd", ".Rnw") case self.PERL: - return FilenameMatcher("*.pl", "*.pm", "*.t") + return FilenameMatcher(".pl", ".pm", ".t") case self.CLOJURE: - return FilenameMatcher("*.clj", "*.cljs", "*.cljc", "*.edn") # codespell:ignore edn + return FilenameMatcher(".clj", ".cljs", ".cljc", ".edn") # codespell:ignore edn case self.ELIXIR: - return FilenameMatcher("*.ex", "*.exs") + return FilenameMatcher(".ex", ".exs") case self.ELM: - return FilenameMatcher("*.elm") + return FilenameMatcher(".elm") case self.TERRAFORM: - return FilenameMatcher("*.tf", "*.tfvars", "*.tfstate") + return FilenameMatcher(".tf", ".tfvars", ".tfstate") case self.SWIFT: - return FilenameMatcher("*.swift") + return FilenameMatcher(".swift") case self.BASH: - return FilenameMatcher("*.sh", "*.bash") + return FilenameMatcher(".sh", ".bash") case self.CRYSTAL: - return FilenameMatcher("*.cr") + return FilenameMatcher(".cr") case self.YAML: - return FilenameMatcher("*.yaml", "*.yml") + return FilenameMatcher(".yaml", ".yml") case self.JSON: - return FilenameMatcher("*.json", "*.jsonc") + return FilenameMatcher(".json", ".jsonc") case self.TOML: - return FilenameMatcher("*.toml") + return FilenameMatcher(".toml") case self.ZIG: - return FilenameMatcher("*.zig", "*.zon") + return FilenameMatcher(".zig", ".zon") case self.LUA: - return FilenameMatcher("*.lua") + return FilenameMatcher(".lua") case self.LUAU: - return FilenameMatcher("*.luau") + return FilenameMatcher(".luau") case self.NIX: - return FilenameMatcher("*.nix") + return FilenameMatcher(".nix") case self.ERLANG: - return FilenameMatcher("*.erl", "*.hrl", "*.escript", "*.config", "*.app", "*.app.src") + return FilenameMatcher(".erl", ".hrl", ".escript", ".config", ".app", ".app.src") case self.OCAML: - return FilenameMatcher("*.ml", "*.mli", "*.re", "*.rei") + return FilenameMatcher(".ml", ".mli", ".re", ".rei") case self.AL: - return FilenameMatcher("*.al", "*.dal") + return FilenameMatcher(".al", ".dal") case self.FSHARP: - return FilenameMatcher("*.fs", "*.fsx", "*.fsi") + return FilenameMatcher(".fs", ".fsx", ".fsi") case self.REGO: - return FilenameMatcher("*.rego") + return FilenameMatcher(".rego") case self.MARKDOWN: - return FilenameMatcher("*.md", "*.markdown") + return FilenameMatcher(".md", ".markdown") case self.SCALA: - return FilenameMatcher("*.scala", "*.sbt") + return FilenameMatcher(".scala", ".sbt") case self.JULIA: - return FilenameMatcher("*.jl") + return FilenameMatcher(".jl") case self.FORTRAN: return FilenameMatcher( - "*.f90", "*.F90", "*.f95", "*.F95", "*.f03", "*.F03", "*.f08", "*.F08", "*.f", "*.F", "*.for", "*.FOR", "*.fpp", "*.FPP" + ".f90", ".F90", ".f95", ".F95", ".f03", ".F03", ".f08", ".F08", ".f", ".F", ".for", ".FOR", ".fpp", ".FPP" ) case self.HASKELL: - return FilenameMatcher("*.hs", "*.lhs") + return FilenameMatcher(".hs", ".lhs") case self.HAXE: - return FilenameMatcher("*.hx") + return FilenameMatcher(".hx") case self.LEAN4: - return FilenameMatcher("*.lean") + return FilenameMatcher(".lean") case self.VUE: - path_patterns = ["*.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}") + path_patterns.append(f".{prefix}{base_pattern}{postfix}") return FilenameMatcher(*path_patterns) case self.POWERSHELL: - return FilenameMatcher("*.ps1", "*.psm1", "*.psd1") + return FilenameMatcher(".ps1", ".psm1", ".psd1") case self.PASCAL: - return FilenameMatcher("*.pas", "*.pp", "*.lpr", "*.dpr", "*.dpk", "*.inc") + return FilenameMatcher(".pas", ".pp", ".lpr", ".dpr", ".dpk", ".inc") case self.GROOVY: - return FilenameMatcher("*.groovy", "*.gvy") + return FilenameMatcher(".groovy", ".gvy") case self.MATLAB: - return FilenameMatcher("*.m", "*.mlx", "*.mlapp") + 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", + ".hlsl", + ".hlsli", + ".fx", + ".fxh", + ".cginc", + ".compute", + ".shader", + ".glsl", + ".vert", + ".frag", + ".geom", + ".tesc", + ".tese", + ".comp", + ".wgsl", ) case self.SYSTEMVERILOG: - return FilenameMatcher("*.sv", "*.svh", "*.v", "*.vh") + return FilenameMatcher(".sv", ".svh", ".v", ".vh") case self.SOLIDITY: - return FilenameMatcher("*.sol") + return FilenameMatcher(".sol") case self.ANSIBLE: - return FilenameMatcher("*.yaml", "*.yml") + return FilenameMatcher(".yaml", ".yml") case self.MSL: - return FilenameMatcher("*.mrc") + return FilenameMatcher(".mrc") case _: raise ValueError(f"Unhandled language: {self}")