New tool and LS method: get_document_overview

This commit is contained in:
Michael Panchenko committed 2025-03-26 22:58:28 +01:00
1 parent eb3d2d3ec4
commit 1afe45805f
4 files changed
+60 -9

No files matched your search

+28 -9
View File
@@ -843,16 +843,7 @@ class LanguageServer:
Maps relative paths of all contained files to info about top-level symbols in the file
(name, kind, line, column).
"""
if not self.server_started:
self.logger.log(
"request_dir_overview called before Language Server started",
logging.ERROR,
)
raise MultilspyException("Language Server not started")
# Get the full symbol tree for the directory
symbol_tree = await self.request_full_symbol_tree(relative_dir_path)
# Initialize result dictionary
result: dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]] = defaultdict(list)
@@ -879,6 +870,22 @@ class LanguageServer:
process_symbol(root)
return result
async def request_document_overview(self, relative_file_path: str) -> list[tuple[str, multilspy_types.SymbolKind, int, int]]:
"""
An overview of the given file.
Returns the 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
]
async def request_hover(self, relative_file_path: str, line: int, column: int) -> Union[multilspy_types.Hover, None]:
"""
Raise a [textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) request to the Language Server
@@ -1528,6 +1535,18 @@ class SyncLanguageServer:
self.language_server.request_dir_overview(relative_dir_path), self.loop
).result()
return result
def request_document_overview(self, relative_file_path: str) -> list[tuple[str, multilspy_types.SymbolKind, int, int]]:
"""
An overview of the given file.
Returns the the list of tuples (name, kind, line, column) of all top-level symbols in the file.
"""
assert self.loop
result = asyncio.run_coroutine_threadsafe(
self.language_server.request_document_overview(relative_file_path), self.loop
).result()
return result
def request_hover(self, relative_file_path: str, line: int, column: int) -> Union[multilspy_types.Hover, None]:
"""
+2
View File
@@ -17,6 +17,8 @@ def ensure_all_methods_implemented(
def check_all_methods_implemented(target_cls: R) -> R:
for name, _ in inspect.getmembers(source_cls, inspect.isfunction):
if name.startswith("_"):
continue
if name not in target_cls.__dict__ or not callable(target_cls.__dict__[name]):
raise NotImplementedError(f"{name} is not implemented in {target_cls}")
+21
View File
@@ -244,6 +244,27 @@ def get_dir_overview(ctx: Context, relative_path: str, max_answer_chars: int = _
return GetDirOverviewTool(ctx).execute(max_answer_chars=max_answer_chars)
@mcp.tool()
def get_document_overview(ctx: Context, relative_path: str, max_answer_chars: int = _DEFAULT_MAX_ANSWER_LENGTH) -> str:
"""
Get an overview of the given file.
:param ctx: the context object, which will be created and provided automatically
:param relative_path: the relative path to the file to get the overview of
:param max_answer_chars: if the overview is longer than this number of characters,
no content will be returned. Don't adjust unless there is really no other way to get the content
required for the task.
:return: a JSON object with the list of tuples (name, kind, line, column) of all top-level symbols in the file.
"""
log.info(f"get_document_overview: {relative_path=}")
class GetDocumentOverviewTool(Tool):
def _execute(self) -> str:
return json.dumps(self.langsrv.request_document_overview(relative_path))
return GetDocumentOverviewTool(ctx).execute(max_answer_chars=max_answer_chars)
@mcp.tool()
def find_symbol(
ctx: Context,
+9
View File
@@ -407,3 +407,12 @@ class TestLanguageServerSymbols:
for symbol in expected_symbols:
assert symbol in services_symbols
def test_request_document_overview(self, language_server: SyncLanguageServer, repo_path: Path):
"""Test that request_document_overview returns correct symbol information for a file."""
# Get overview of the user_management.py file
overview = language_server.request_document_overview(os.path.join("examples", "user_management.py"))
# Verify that we have entries for both files
symbol_names = {s_info[0] for s_info in overview}
assert {"UserStats", "UserManager", "process_user_data", "main"}.issubset(symbol_names)