mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-07 12:14:11 +00:00
C/C++ language server fixes (#987)
Fixes longstanding clangd issues (wrong selectionRange, failure to take compile_commands.json into account) and enhances documentation about C/C++ support
This commit is contained in:
1 parent
8288bf200d
commit
e588bc7f89
8 files changed
+281
-38
No files matched your search
@@ -7,8 +7,8 @@ server is not available.
|
||||
"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -17,18 +17,11 @@ 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)
|
||||
_cpp_servers: list[Language] = [Language.CPP]
|
||||
if _ccls_available():
|
||||
_cpp_servers.append(Language.CPP_CCLS)
|
||||
|
||||
@@ -70,9 +63,9 @@ class TestCppLanguageServer:
|
||||
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"
|
||||
refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"])
|
||||
ref_files = [ref.get("relativePath", "") for ref in refs]
|
||||
assert any("a.cpp" in ref_file for ref_file in ref_files), f"Should find reference in a.cpp, {refs=}"
|
||||
|
||||
# Verify second call returns same results (stability check)
|
||||
def _ref_key(ref: dict) -> tuple:
|
||||
@@ -88,5 +81,64 @@ class TestCppLanguageServer:
|
||||
e.get("character", -1),
|
||||
)
|
||||
|
||||
refs2 = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1)
|
||||
refs2 = language_server.request_references(file_path, sel_start["line"], sel_start["character"])
|
||||
assert sorted(map(_ref_key, refs2)) == sorted(map(_ref_key, refs)), "Reference results should be stable across calls"
|
||||
|
||||
@pytest.mark.parametrize("language_server", _cpp_servers, indirect=True)
|
||||
@pytest.mark.xfail(
|
||||
strict=True,
|
||||
reason=("Both clangd and ccls do not support cross-file references for newly created files that were never opened by the LS."),
|
||||
)
|
||||
def test_find_references_in_newly_written_file(self, language_server: SolidLanguageServer) -> None:
|
||||
# Create a new file that references the 'add' function from b.cpp
|
||||
new_file_path = os.path.join("temp_new_file.cpp")
|
||||
new_file_abs_path = os.path.join(language_server.repository_root_path, new_file_path)
|
||||
|
||||
try:
|
||||
# Write the new file with a reference to add()
|
||||
with open(new_file_abs_path, "w", encoding="utf-8") as f:
|
||||
f.write(
|
||||
"""
|
||||
#include "b.hpp"
|
||||
|
||||
int use_add() {
|
||||
int result = add(5, 3);
|
||||
return result;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
# Open the new file so clangd knows about it
|
||||
with language_server.open_file(new_file_path):
|
||||
# Request document symbols to ensure the file is fully loaded by clangd
|
||||
new_file_symbols = language_server.request_document_symbols(new_file_path).get_all_symbols_and_roots()
|
||||
assert new_file_symbols, "New file should have symbols"
|
||||
|
||||
# Verify the file stays in open_file_buffers after the context exits
|
||||
uri = pathlib.Path(new_file_abs_path).as_uri()
|
||||
assert uri in language_server.open_file_buffers, "File should remain in open_file_buffers"
|
||||
|
||||
# Find the 'add' symbol in b.cpp
|
||||
b_file_path = os.path.join("b.cpp")
|
||||
symbols = language_server.request_document_symbols(b_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"
|
||||
|
||||
# Request references for 'add'
|
||||
sel_start = add_symbol["selectionRange"]["start"]
|
||||
refs = language_server.request_references(b_file_path, sel_start["line"], sel_start["character"])
|
||||
ref_files = [ref.get("relativePath", "") for ref in refs]
|
||||
|
||||
# Should find reference in the newly written file
|
||||
assert any(
|
||||
"temp_new_file.cpp" in ref_file for ref_file in ref_files
|
||||
), f"Should find reference in newly written temp_new_file.cpp, {ref_files=}"
|
||||
finally:
|
||||
# Clean up the new file
|
||||
if os.path.exists(new_file_abs_path):
|
||||
os.remove(new_file_abs_path)
|
||||
Reference in new issue
Block a user