From 1aaab5a7c2db387042f6b34de7ee75c20c9ec274 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 9 Apr 2025 19:28:37 +0200 Subject: [PATCH] Change relative path handling: * Assign relativePath using PathUtils.get_relative_path, which returns None if no relative path exists (e.g. different drive); this aligns with how it was originally handled in multilspy * Adapt all places where we can now enounter None instead of a string --- src/multilspy/language_server.py | 16 +++------------- src/serena/symbol.py | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/multilspy/language_server.py b/src/multilspy/language_server.py index 091e7e62..83ec9697 100644 --- a/src/multilspy/language_server.py +++ b/src/multilspy/language_server.py @@ -525,13 +525,7 @@ class LanguageServer: new_item: multilspy_types.Location = {} new_item.update(item) new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) - try: - # TODO can we use PathUtils.get_relative_path? (same elsewhere!) - new_item["relativePath"] = str( - PurePath(os.path.relpath(new_item["absolutePath"], self.repository_root_path)) - ) - except: - new_item["relativePath"] = str(new_item["absolutePath"]) + new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) ret.append(multilspy_types.Location(new_item)) elif ( LSPConstants.ORIGIN_SELECTION_RANGE in item @@ -542,12 +536,7 @@ class LanguageServer: new_item: multilspy_types.Location = {} new_item["uri"] = item[LSPConstants.TARGET_URI] new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) - try: - new_item["relativePath"] = str( - PurePath(os.path.relpath(new_item["absolutePath"], self.repository_root_path)) - ) - except: - new_item["relativePath"] = str(new_item["absolutePath"]) + new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) new_item["range"] = item[LSPConstants.TARGET_SELECTION_RANGE] ret.append(multilspy_types.Location(**new_item)) else: @@ -943,6 +932,7 @@ class LanguageServer: elif os.path.isfile(abs_item_path): _, root_nodes = await self.request_document_symbols(item_path, include_body=include_body) + # TODO: Not sure if this is actually still needed given recent changes to relative path handling def fix_relative_path(nodes: List[multilspy_types.UnifiedSymbolInformation]): for node in nodes: path = Path(node["location"]["relativePath"]) diff --git a/src/serena/symbol.py b/src/serena/symbol.py index 1819b1a2..82280247 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -23,9 +23,9 @@ class SymbolLocation: Represents the (start) location of a symbol identifier """ - relative_path: str + relative_path: str | None """ - the relative path of the file containing the symbol + the relative path of the file containing the symbol; if None, the symbol is defined outside of the project's scope """ line: int | None """ @@ -39,13 +39,14 @@ class SymbolLocation: """ def __post_init__(self) -> None: - self.relative_path = self.relative_path.replace("/", os.path.sep) + if self.relative_path is not None: + self.relative_path = self.relative_path.replace("/", os.path.sep) def to_dict(self) -> dict[str, Any]: return asdict(self) def has_position_in_file(self) -> bool: - return self.line is not None and self.column is not None + return self.relative_path is not None and self.line is not None and self.column is not None class Symbol(ToStringMixin): @@ -71,7 +72,7 @@ class Symbol(ToStringMixin): return self.s["kind"] @property - def relative_path(self) -> str: + def relative_path(self) -> str | None: return self.s["location"]["relativePath"] @property @@ -239,6 +240,8 @@ class SymbolManager: return symbols def find_by_location(self, location: SymbolLocation) -> Symbol | None: + if location.relative_path is None: + return None symbol_dicts, roots = self.lang_server.request_document_symbols(location.relative_path, include_body=False) for symbol_dict in symbol_dicts: symbol = Symbol(symbol_dict) @@ -268,6 +271,7 @@ class SymbolManager: """ if not symbol_location.has_position_in_file(): raise ValueError("Symbol location does not contain a valid position in a file") + assert symbol_location.relative_path is not None assert symbol_location.line is not None assert symbol_location.column is not None symbol_dicts = self.lang_server.request_referencing_symbols( @@ -301,7 +305,8 @@ class SymbolManager: def _edited_symbol_location(self, location: SymbolLocation) -> Iterator[Symbol]: symbol = self.find_by_location(location) if symbol is None: - raise ValueError("Symbol not found") + raise ValueError("Symbol not found/has no defined location within a file") + assert location.relative_path is not None with self._edited_file(location.relative_path): yield symbol @@ -313,6 +318,7 @@ class SymbolManager: :param body: the new body """ with self._edited_symbol_location(location) as symbol: + assert location.relative_path is not None self.lang_server.delete_text_between_positions(location.relative_path, symbol.body_start_position, symbol.body_end_position) self.lang_server.insert_text_at_position( location.relative_path, symbol.body_start_position["line"], symbol.body_start_position["character"], body @@ -327,6 +333,7 @@ class SymbolManager: """ with self._edited_symbol_location(location) as symbol: pos = symbol.body_end_position + assert location.relative_path is not None self.lang_server.insert_text_at_position(location.relative_path, pos["line"], pos["character"], body) def insert_before(self, location: SymbolLocation, body: str) -> None: @@ -338,6 +345,7 @@ class SymbolManager: """ with self._edited_symbol_location(location) as symbol: pos = copy(symbol.body_start_position) + assert location.relative_path is not None self.lang_server.insert_text_at_position(location.relative_path, pos["line"], pos["character"], body) def insert_at_line(self, relative_path: str, line: int, content: str) -> None: