Refactoring: Make FilenameMatcher accept file extensions instead of patterns

Makes the matching both simpler and faster
This commit is contained in:
Michael Panchenko committed 2026-04-30 13:20:40 +02:00
1 parent 84e117c5c2
commit d7852341f7
2 files changed
+78 -73

No files matched your search

@@ -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
+77 -72
View File
@@ -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}")