mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 06:17:58 +00:00
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:
17 files changed
+263
-18
No files matched your search
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "b.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
int x = add(3, 4);
|
||||
return x;
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -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,
|
||||
|
||||
@@ -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}"
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user