Merge pull request #85 from oraios/pr/84

Better fallbacks for missing keys (selectionRange etc). Download clang executable if needed
This commit is contained in:
Michael Panchenko authored and GitHub committed 2025-04-24 14:45:05 +02:00
commit d5a1754574
4 files changed
+49 -16

No files matched your search

+28 -10
View File
@@ -821,8 +821,10 @@ class LanguageServer:
def turn_item_into_symbol_with_children(item: GenericDocumentSymbol):
item = cast(multilspy_types.UnifiedSymbolInformation, item)
absolute_path = os.path.join(self.repository_root_path, relative_file_path)
# handle missing entries in location
if "location" not in item:
absolute_path = os.path.join(self.repository_root_path, relative_file_path)
uri = pathlib.Path(absolute_path).as_uri()
assert "range" in item
tree_location = multilspy_types.Location(
@@ -832,8 +834,19 @@ class LanguageServer:
relativePath=relative_file_path,
)
item['location'] = tree_location
location = item["location"]
if "absolutePath" not in location:
location["absolutePath"] = absolute_path
if "relativePath" not in location:
location["relativePath"] = relative_file_path
if include_body:
item['body'] = self.retrieve_symbol_body(item)
# handle missing selectionRange
if "selectionRange" not in item:
if "range" in item:
item["selectionRange"] = item["range"]
else:
item["selectionRange"] = item["location"]["range"]
item[LSPConstants.CHILDREN] = item.get(LSPConstants.CHILDREN, [])
flat_all_symbol_list: List[multilspy_types.UnifiedSymbolInformation] = []
@@ -1044,15 +1057,20 @@ class LanguageServer:
Returns the list of tuples (name, kind, line, column) of all top-level symbols in the file.
"""
_, document_roots = await self.request_document_symbols(relative_file_path)
return [
(
root["name"],
root["kind"],
root["selectionRange"]["start"]["line"], # type: ignore
root["selectionRange"]["start"]["character"] # type: ignore
)
for root in document_roots
]
result = []
for root in document_roots:
try:
result.append(
( root["name"],
root["kind"],
root["selectionRange"]["start"]["line"],
root["selectionRange"]["start"]["character"],)
)
except KeyError as e:
raise KeyError(
f"Could not process symbol of name {root.get('name', 'unknown')} in {relative_file_path=}"
) from e
return result
async def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]:
"""
@@ -64,13 +64,19 @@ class ClangdLanguageServer(LanguageServer):
clangd_ls_dir = os.path.join(os.path.dirname(__file__), "static/clangd")
clangd_executable_path = os.path.join(clangd_ls_dir, "clangd_19.1.2", "bin", dependency["binaryName"])
if not os.path.exists(clangd_ls_dir):
os.makedirs(clangd_ls_dir)
if not os.path.exists(clangd_executable_path):
clangd_url = dependency["url"]
logger.log(f"Clangd executable not found at {clangd_executable_path}. Downloading from {clangd_url}", logging.INFO)
os.makedirs(clangd_ls_dir, exist_ok=True)
if dependency["archiveType"] == "zip":
FileUtils.download_and_extract_archive(
logger, dependency["url"], clangd_ls_dir, dependency["archiveType"]
logger, clangd_url, clangd_ls_dir, dependency["archiveType"]
)
assert os.path.exists(clangd_executable_path)
if not os.path.exists(clangd_executable_path):
raise FileNotFoundError(
f"Clangd executable not found at {clangd_executable_path}.\n"
"Make sure you have installed clangd. See https://clangd.llvm.org/installation"
)
os.chmod(clangd_executable_path, stat.S_IEXEC)
return clangd_executable_path
+7 -1
View File
@@ -117,7 +117,10 @@ class FileUtils:
"""
Reads the file at the given path and returns the contents as a string.
"""
encodings = ["utf-8-sig", "utf-16"]
if not os.path.exists(file_path):
logger.log(f"File read '{file_path}' failed: File does not exist.", logging.ERROR)
raise MultilspyException(f"File read '{file_path}' failed: File does not exist.")
encodings = ["utf-8-sig", "utf-16", "utf-8", "latin-1"]
try:
for encoding in encodings:
try:
@@ -125,6 +128,9 @@ class FileUtils:
return inp_file.read()
except UnicodeError:
continue
# Try system default encoding as a last resort
with open(file_path, "r") as inp_file:
return inp_file.read()
except Exception as exc:
logger.log(f"File read '{file_path}' failed: {exc}", logging.ERROR)
raise MultilspyException("File read failed.") from None
+4 -1
View File
@@ -73,7 +73,10 @@ class ProjectConfig(ToStringMixin):
def __init__(self, config_dict: dict[str, Any], project_name: str, project_root: Path | None = None):
self.project_name: str = project_name
self.language: Language = Language(config_dict["language"])
try:
self.language: Language = Language(config_dict["language"].lower())
except ValueError as e:
raise ValueError(f"Invalid language: {config_dict['language']}.\nValid languages are: {[l.value for l in Language]}") from e
if project_root is None:
project_root = Path(config_dict["project_root"])
self.project_root: str = str(project_root.resolve())