Cpp extensions (#1451)

* Add more cpp extensions

* Allow FilenameMatcher to be case insensitive
---------

Co-authored-by: Darin Morrison <darinmorrison@gmail.com>
Co-authored-by: silvanshade <silvanshade@users.noreply.github.com>
Co-authored-by: MischaPanch
This commit is contained in:
authored and GitHub committed 2026-05-06 19:04:36 +02:00
1 parent b28f705fee
commit 0e3f665976
17 files changed
+263 -18

No files matched your search

+1
View File
@@ -28,6 +28,7 @@ Status of the `main` branch. Changes prior to the next official version change w
- Add **Angular** (experimental) via a dual-server architecture: `@angular/language-server` (ngserver) handles standalone `.html` template files, while a companion `typescript-language-server` with `@angular/language-service` loaded as a tsserver plugin handles all `.ts` operations including inline templates. Provides type-aware navigation between templates and component classes. Requires Node.js, npm, and `@angular/core` installed in the project (`npm install` in the project root). Subsumes `typescript`+`html` for `.ts`/`.html` files when active; SCSS is not subsumed.
- Add **HTML** (experimental) using `vscode-html-language-server` from the `vscode-langservers-extracted` npm package. Provides in-file element/id symbols via documentSymbol; cross-file references are not meaningful for HTML. Also used as a companion server by the Angular LS for plain HTML documentSymbol support.
- Add **SCSS / Sass / CSS** (experimental) using [some-sass-language-server](https://github.com/wkillerud/some-sass). Handles `.scss`, `.sass`, and `.css` through one server, with full `@use`/`@forward` workspace-wide go-to-definition and find-references for variables, mixins, and functions across Sass files. The `.css` path uses the same `vscode-css-languageservice` engine that powers the standalone CSS LS; CSS feature toggles default off upstream and are flipped on at startup so symbols, hover, completion, and syntax-level diagnostics work for plain CSS as well.
- Add support for more filenames to be considered by ccls and clangd.
* Hooks:
- `serena-hooks auto-approve` now also emits an `allow` decision when Claude Code reports
+71 -7
View File
@@ -13,13 +13,17 @@ if TYPE_CHECKING:
class FilenameMatcher:
def __init__(self, *file_extensions: str) -> None:
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 = file_extensions
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
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
@@ -30,6 +34,8 @@ class FilenameMatcher:
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 self._case_sensitive:
string = string.lower()
for ext in self._file_extensions:
if re.search(rf"{re.escape(ext)}(?:\W|$)", string):
return True
@@ -284,8 +290,68 @@ class Language(str, Enum):
return FilenameMatcher(".rb", ".erb")
case self.RUBY_SOLARGRAPH:
return FilenameMatcher(".rb")
case self.CPP | self.CPP_CCLS:
return FilenameMatcher(".cpp", ".h", ".hpp", ".c", ".hxx", ".cc", ".cxx")
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",
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",
case_sensitive=False,
)
case self.KOTLIN:
return FilenameMatcher(".kt", ".kts")
case self.DART:
@@ -341,9 +407,7 @@ class Language(str, Enum):
case self.JULIA:
return FilenameMatcher(".jl")
case self.FORTRAN:
return FilenameMatcher(
".f90", ".F90", ".f95", ".F95", ".f03", ".F03", ".f08", ".F08", ".f", ".F", ".for", ".FOR", ".fpp", ".FPP"
)
return FilenameMatcher(".f90", ".f95", ".f03", ".f08", ".f", ".for", ".fpp", case_sensitive=False)
case self.HASKELL:
return FilenameMatcher(".hs", ".lhs")
case self.HAXE:
+3
View File
@@ -310,6 +310,9 @@ def _determine_disabled_languages() -> list[Language]:
# Disable CPP_CCLS tests if ccls is not available
ccls_tests_enabled = _sh.which("ccls") is not None
# Skip ccls tests on Windows since no recent binary is available and version
# 0.20220729 from chocolatey crashes when parsing the test files.
ccls_tests_enabled = ccls_tests_enabled and not is_windows
if not ccls_tests_enabled:
result.append(Language.CPP_CCLS)
+7
View File
@@ -0,0 +1,7 @@
#include "b.h"
int main()
{
int x = add(3, 4);
return x;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
int add(int a, int b);
@@ -0,0 +1,16 @@
#include <cstdio>
#include <cstdlib>
__global__ auto
cuda_hello() -> void
{
printf("Hello World from GPU!\n");
}
auto
main() -> int
{
cuda_hello<<<1, 1>>>();
cudaDeviceSynchronize();
return EXIT_SUCCESS;
}
@@ -0,0 +1,9 @@
#include <cstdlib>
import b;
auto
main() noexcept -> int
{
return EXIT_SUCCESS;
}
@@ -0,0 +1,17 @@
module;
#include <cstdint>
module b;
auto
add(auto x, auto y) -> decltype(x + y)
{
return x + y;
}
auto
add_f32(float x, float y) -> float
{
return add(x, y);
}
@@ -0,0 +1,16 @@
module;
#include <cstdint>
export module b;
export auto
add(auto x, auto y) -> decltype(x + y);
export auto
add_f32(float x, float y) -> float;
auto
add_u32(std::int32_t x, std::int32_t y) -> std::int32_t {
return x + y;
}
@@ -0,0 +1,10 @@
// -*- mode: C -*-
#include <hip/hip_runtime.h>
__global__
void add_vectors(const float* A, const float* B, float* C)
{
auto const idx = blockIdx.x * blockDim.x + threadIdx.x;
C[idx] = A[idx] + B[idx];
}
@@ -0,0 +1,7 @@
@interface Hello
- (void)hello;
@end
int main(int argc, const char * argv[]) {
return 0;
}
@@ -0,0 +1,8 @@
// -*- mode: C -*-
__kernel
void add_vectors(__global const float *A, __global const float *B, __global float *C)
{
int idx = get_global_id(0);
C[idx] = A[idx] + B[idx];
}
@@ -13,5 +13,40 @@
"directory": ".",
"command": "g++ -std=c++17 -I . -c diagnostics_sample.cpp",
"file": "diagnostics_sample.cpp"
},
{
"directory": "C",
"command": "clang -std=gnu23 -c a.c",
"file": "a.c"
},
{
"directory": "CUDA",
"command": "nvcc -std=c++20 -c a.cu",
"file": "a.cu"
},
{
"directory": "CXX20",
"command": "clang++ -std=gnu++20 -fmodule-file=b=b.pcm -c a.cpp",
"file": "a.cpp"
},
{
"directory": "CXX20",
"command": "clang++ -std=gnu++20 -fmodule-file=b=b.pcm -c b.cpp",
"file": "b.cpp"
},
{
"directory": "CXX20",
"command": "clang++ -std=gnu++20 -c b.cppm",
"file": "b.cppm"
},
{
"directory": "OpenCL",
"command": "clang -std=cl3.0 -c a.cl",
"file": "a.cl"
},
{
"directory": "HIP",
"command": "hipcc -c a.hip",
"file": "a.hip"
}
]
+4 -4
View File
@@ -327,7 +327,7 @@ FIND_DEFINING_SYMBOL_CASES = [
expected_definition_file="utils.ps1",
).to_pytest_param(),
FindDefiningSymbolCase(
language=Language.CPP_CCLS,
language=Language.CPP,
id="cpp_add_in_a",
relative_path="a.cpp",
identifier="add",
@@ -502,7 +502,7 @@ FIND_SYMBOL_REFERENCES_CASES = [
expected_file="main.ps1",
).to_pytest_param(),
FindSymbolCase(
language=Language.CPP_CCLS, id="cpp_add_function", symbol_name="add", expected_kind="Function", expected_file="b.cpp"
language=Language.CPP, id="cpp_add_function", symbol_name="add", expected_kind="Function", expected_file="b.cpp"
).to_pytest_param(),
FindSymbolCase(
language=Language.LEAN4, id="lean_add_method", symbol_name="add", expected_kind="Method", expected_file="Helper.lean"
@@ -580,7 +580,7 @@ FIND_REFERENCE_CASES = [
reference_file="main.ps1",
).to_pytest_param(),
FindReferenceCase(
language=Language.CPP_CCLS, id="cpp_add_refs", symbol_name="add", definition_file="b.cpp", reference_file="a.cpp"
language=Language.CPP, id="cpp_add_refs", symbol_name="add", definition_file="b.cpp", reference_file="a.cpp"
).to_pytest_param(),
FindReferenceCase(
language=Language.LEAN4, id="lean_add_refs", symbol_name="add", definition_file="Helper.lean", reference_file="Main.lean"
@@ -775,7 +775,7 @@ def serena_config():
Language.CLOJURE,
Language.FSHARP,
Language.POWERSHELL,
Language.CPP_CCLS,
Language.CPP,
Language.HAXE,
Language.LEAN4,
Language.MSL,
+24
View File
@@ -0,0 +1,24 @@
import os
import pytest
from solidlsp.ls_config import Language
from test.conftest import start_ls_context
@pytest.mark.cpp
class TestCCLSLanguages:
@pytest.mark.parametrize(
"lang, unit, names",
[
("C", "a.c", {"main"}),
("Objective-C", "a.m", {"main", "Hello", "-hello"}),
],
)
def test_get_document_symbols(self, lang: str, unit: str, names: set[str]) -> None:
with start_ls_context(Language.CPP) as ccls:
path = os.path.join(lang, unit)
symbols = ccls.request_document_symbols(path).get_all_symbols_and_roots()
symbols = symbols[0] if symbols and isinstance(symbols[0], list) else symbols
symbols = {s.get("name") for s in symbols}
assert names == symbols, f"Expected '{names}' in document symbols, got: {symbols}"
@@ -0,0 +1,30 @@
import os
import pytest
from solidlsp.ls_config import Language
from test.conftest import start_ls_context
@pytest.mark.cpp
class TestClangdLanguages:
@pytest.mark.parametrize(
"lang, unit, names",
[
("C", "a.c", {"main"}),
("CUDA", "a.cu", {"main", "cuda_hello"}),
("CXX20", "a.cpp", {"main"}),
("CXX20", "b.cpp", {"add", "add_f32"}),
("CXX20", "b.cppm", {"add", "add_f32", "add_u32"}),
("HIP", "a.hip", {"add_vectors"}),
("OpenCL", "a.cl", {"add_vectors"}),
("Objective-C", "a.m", {"main", "Hello", "-hello"}),
],
)
def test_get_document_symbols(self, lang: str, unit: str, names: set[str]) -> None:
with start_ls_context(Language.CPP) as clangd:
path = os.path.join(lang, unit)
symbols = clangd.request_document_symbols(path).get_all_symbols_and_roots()
symbols = symbols[0] if symbols and isinstance(symbols[0], list) else symbols
symbols = {s.get("name") for s in symbols}
assert names == symbols, f"Expected '{names}' in document symbols, got: {symbols}"
+2 -7
View File
@@ -16,16 +16,11 @@ import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_utils import SymbolUtils
from test.conftest import get_repo_path, start_ls_context
from test.conftest import get_repo_path, language_tests_enabled, start_ls_context
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
def _ccls_available() -> bool:
return shutil.which("ccls") is not None
_cpp_servers: list[Language] = [Language.CPP]
if _ccls_available():
if language_tests_enabled(Language.CPP_CCLS):
_cpp_servers.append(Language.CPP_CCLS)