CCLS Support added (#975)

Also added a test suite for both clangd and ccls
This commit is contained in:
Azraf Nahian authored and GitHub committed 2026-02-01 20:01:51 +07:00
1 parent 01dc6f8734
commit 71a6f1ff82
15 files changed
+337 -5

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
"""
Basic tests for C/C++ language server integration (clangd and ccls).
This module tests both Language.CPP (clangd) and Language.CPP_CCLS (ccls)
using the same test repository. Tests are skipped if the respective language
server is not available.
"""
import os
import shutil
from typing import cast
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_utils import SymbolUtils
def _clangd_available() -> bool:
return shutil.which("clangd") is not None
def _ccls_available() -> bool:
return shutil.which("ccls") is not None
# Build parametrize list based on availability
_cpp_servers: list[Language] = []
if _clangd_available():
_cpp_servers.append(Language.CPP)
if _ccls_available():
_cpp_servers.append(Language.CPP_CCLS)
@pytest.mark.cpp
@pytest.mark.skipif(not _cpp_servers, reason="No C++ language server (clangd or ccls) available")
class TestCppLanguageServer:
"""Tests for C/C++ language servers (clangd and ccls)."""
@pytest.mark.parametrize("language_server", _cpp_servers, indirect=True)
def test_find_symbol(self, language_server: SolidLanguageServer) -> None:
"""Test that symbol tree contains expected functions."""
symbols = language_server.request_full_symbol_tree()
assert SymbolUtils.symbol_tree_contains_name(symbols, "add"), "Function 'add' not found in symbol tree"
assert SymbolUtils.symbol_tree_contains_name(symbols, "main"), "Function 'main' not found in symbol tree"
@pytest.mark.parametrize("language_server", _cpp_servers, indirect=True)
def test_get_document_symbols(self, language_server: SolidLanguageServer) -> None:
"""Test document symbols for a.cpp."""
file_path = os.path.join("a.cpp")
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
# Flatten nested structure if needed
symbol_list = symbols[0] if symbols and isinstance(symbols[0], list) else symbols
names = [s.get("name") for s in symbol_list]
assert "main" in names, f"Expected 'main' in document symbols, got: {names}"
@pytest.mark.parametrize("language_server", _cpp_servers, indirect=True)
def test_find_referencing_symbols_across_files(self, language_server: SolidLanguageServer) -> None:
"""Test finding references to 'add' function across files."""
# Locate 'add' in b.cpp
file_path = os.path.join("b.cpp")
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
symbol_list = symbols[0] if symbols and isinstance(symbols[0], list) else symbols
add_symbol = None
for sym in symbol_list:
if sym.get("name") == "add":
add_symbol = sym
break
assert add_symbol is not None, "Could not find 'add' function symbol in b.cpp"
sel_start = add_symbol["selectionRange"]["start"]
refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1)
ref_files = cast(list[str], [ref.get("relativePath", "") for ref in refs])
assert any("a.cpp" in ref_file for ref_file in ref_files), "Should find reference in a.cpp"
# Verify second call returns same results (stability check)
def _ref_key(ref: dict) -> tuple:
rp = ref.get("relativePath", "")
rng = ref.get("range") or {}
s = rng.get("start") or {}
e = rng.get("end") or {}
return (
rp,
s.get("line", -1),
s.get("character", -1),
e.get("line", -1),
e.get("character", -1),
)
refs2 = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1)
assert sorted(map(_ref_key, refs2)) == sorted(map(_ref_key, refs)), "Reference results should be stable across calls"