Merge pull request #1555 from oraios/bug/svelte_ts_document_symbols

Fix Svelte (svelte-only) symbol discovery in .ts/.js files (#1552)
This commit is contained in:
Michael Panchenko authored and GitHub committed 2026-06-08 16:16:41 +02:00
commit 4385741e8c
4 files changed
+122

No files matched your search

+4
View File
@@ -18,6 +18,10 @@ Status of the `main` branch. Changes prior to the next official version change w
at the Yarn-generated SDK.
- `SvelteLanguageServer`: Fix diagnostics requests for TypeScript/JavaScript files incorrectly being
processed by the Svelte LS instead of the TypeScript LS.
- `SvelteLanguageServer`: Fix document-symbol requests for TypeScript/JavaScript files returning empty
results in svelte-only mode (`languages: [svelte]`). They are now routed to the companion TS server,
so symbols defined in plain `.ts`/`.js` files are again discoverable via `find_symbol`/
`get_symbols_overview`, and `find_referencing_symbols` no longer fails to locate `.ts`/`.js` symbols. #1552
- Improve quoting of arguments in shell executions
* JetBrains:
@@ -21,8 +21,10 @@ from solidlsp.language_servers.common import (
)
from solidlsp.language_servers.typescript_language_server import TypeScriptLanguageServer
from solidlsp.ls import (
DocumentSymbols,
LanguageServerDependencyProvider,
LanguageServerDependencyProviderSinglePath,
LSPFileBuffer,
SolidLanguageServer,
)
from solidlsp.ls_config import FilenameMatcher, Language, LanguageServerConfig
@@ -647,6 +649,25 @@ class SvelteLanguageServer(SolidLanguageServer):
return self._ts_server.request_definition(relative_file_path, line, column)
return super().request_definition(relative_file_path, line, column)
@override
def request_document_symbols(self, relative_file_path: str, file_buffer: LSPFileBuffer | None = None) -> DocumentSymbols:
"""Delegate document-symbol requests for .ts/.js files to the companion TS server.
The base svelte LS only provides ``documentSymbol`` for .svelte files and returns nothing for
.ts/.js files. Without this override, symbols defined in plain .ts/.js modules would be
undiscoverable via ``find_symbol``/``get_symbols_overview`` and ``find_referencing_symbols``
would fail outright, since it must first locate the target symbol via ``documentSymbol``.
Routing .ts/.js files to the companion (svelte-plugin-aware) typescript LS mirrors the
existing references/definition/rename delegations.
The companion's own ``file_buffer`` is not reused here: the provided buffer (if any) is bound
to this svelte LS, so the companion opens and reads the file itself.
Falls back to the svelte LS when the companion is unavailable or when the file is .svelte.
"""
if _is_ts_file(relative_file_path) and self._ts_server is not None:
return self._ts_server.request_document_symbols(relative_file_path)
return super().request_document_symbols(relative_file_path, file_buffer)
@override
def request_text_document_diagnostics(
self,
+30
View File
@@ -30,6 +30,12 @@ class TestSvelteLanguageServer:
assert SymbolUtils.symbol_tree_contains_name(symbols, "Game"), "Game class not found in symbol tree"
assert SymbolUtils.symbol_tree_contains_name(symbols, "words"), "words export not found in symbol tree"
assert SymbolUtils.symbol_tree_contains_name(symbols, "count"), "count export not found in symbol tree"
# GAME_VERSION is defined *only* in a .ts file (game.ts); it is merely imported elsewhere.
# It therefore guards against the regression where .ts document symbols are not routed to the
# companion TS server (unlike game/Game/words/count, which also appear in .svelte files).
assert SymbolUtils.symbol_tree_contains_name(symbols, "GAME_VERSION"), (
"GAME_VERSION (defined only in a .ts file) not found in symbol tree"
)
@pytest.mark.parametrize("language_server", [Language.SVELTE], indirect=True)
def test_document_symbols_inside_svelte_file(self, language_server: SolidLanguageServer) -> None:
@@ -40,6 +46,30 @@ class TestSvelteLanguageServer:
assert "offset" in symbol_names
assert "modulo" in symbol_names
@pytest.mark.parametrize("language_server", [Language.SVELTE], indirect=True)
def test_document_symbols_inside_typescript_file(self, language_server: SolidLanguageServer) -> None:
# document symbols of a plain .ts file must be served by the companion TS server, not the
# base svelte LS (which only provides documentSymbol for .svelte files); see issue #1552.
file_path = os.path.join("src", "lib", "game.ts")
symbol_names = {symbol["name"] for symbol in language_server.request_document_symbols(file_path).iter_symbols()}
# top-level symbols
assert "GAME_VERSION" in symbol_names, symbol_names
assert "Game" in symbol_names, symbol_names
# members nested inside the Game class
assert "enter" in symbol_names, symbol_names
assert "toString" in symbol_names, symbol_names
@pytest.mark.parametrize("language_server", [Language.SVELTE], indirect=True)
def test_overview_of_typescript_file(self, language_server: SolidLanguageServer) -> None:
# get_symbols_overview on a .ts file must not be empty in svelte-only mode (issue #1552).
file_path = os.path.join("src", "lib", "game.ts")
overview = language_server.request_overview(file_path)
top_level_names = {symbol["name"] for symbols in overview.values() for symbol in symbols}
assert "GAME_VERSION" in top_level_names, overview
assert "Game" in top_level_names, overview
@pytest.mark.parametrize("language_server", [Language.SVELTE], indirect=True)
def test_definition_from_component_import_to_svelte_file(self, language_server: SolidLanguageServer) -> None:
file_path = os.path.join("src", "lib", "components", "Header.svelte")
@@ -0,0 +1,67 @@
"""Agent-level symbol discovery/navigation for plain .ts files of a svelte-only project.
Reproduces https://github.com/oraios/serena/issues/1552: in ``languages: [svelte]`` mode,
document-symbol requests for .ts/.js files were answered by the base svelte LS (which only
serves .svelte files) instead of being routed to the companion, svelte-plugin-aware TS server.
As a consequence, symbols defined in plain .ts files were undiscoverable through ``find_symbol``/
``get_symbols_overview``, and ``find_referencing_symbols`` failed with ``No symbol matching ...``
because it must first locate the target symbol via ``documentSymbol``.
These tests exercise the high-level :class:`LanguageServerSymbolRetriever` that backs those agent
tools, complementing the lower-level checks in ``test_svelte_basic.py``.
"""
import os
import pytest
from serena.project import Project
from serena.symbol import LanguageServerSymbolRetriever
from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
pytestmark = pytest.mark.svelte
# a plain .ts module whose exported symbols are consumed by both .ts and .svelte files
GAME_TS = os.path.join("src", "lib", "game.ts")
class TestSvelteTypeScriptSymbolDiscovery:
@pytest.mark.parametrize("project_with_ls", [Language.SVELTE], indirect=True)
def test_find_symbol_in_typescript_file(self, project_with_ls: Project) -> None:
retriever = LanguageServerSymbolRetriever(project_with_ls)
# GAME_VERSION is *defined* only in game.ts (merely imported elsewhere)
version_symbols = retriever.find("GAME_VERSION", within_relative_path=GAME_TS)
assert [s.name for s in version_symbols] == ["GAME_VERSION"]
assert version_symbols[0].relative_path is not None
assert version_symbols[0].relative_path.replace("\\", "/") == "src/lib/game.ts"
# the Game class is discoverable and correctly typed
game_symbols = retriever.find("Game", within_relative_path=GAME_TS, include_kinds=[SymbolKind.Class])
assert len(game_symbols) == 1, game_symbols
assert game_symbols[0].symbol_kind == SymbolKind.Class
# nested members of the .ts class are discoverable via their name path
enter_symbols = retriever.find("Game/enter", within_relative_path=GAME_TS)
assert len(enter_symbols) == 1, enter_symbols
assert enter_symbols[0].get_name_path() == "Game/enter"
@pytest.mark.parametrize("project_with_ls", [Language.SVELTE], indirect=True)
def test_find_referencing_symbols_locates_typescript_symbol(self, project_with_ls: Project) -> None:
retriever = LanguageServerSymbolRetriever(project_with_ls)
# The core of issue #1552 for find-references: the target symbol must first be located via
# documentSymbol. Before the fix, .ts document symbols were empty, so locating the symbol
# raised ValueError("No symbol matching 'GAME_VERSION' found") *before the companion TS
# server was ever consulted*. Verify the symbol is now uniquely discoverable.
symbol = retriever.find_unique("GAME_VERSION", within_relative_path=GAME_TS)
assert symbol.name == "GAME_VERSION"
assert symbol.relative_path is not None
assert symbol.relative_path.replace("\\", "/") == "src/lib/game.ts"
# The end-to-end find_referencing_symbols call must now get past symbol discovery without
# raising. (The cross-file/.svelte reference *content* is covered by test_svelte_references.py
# and depends on the companion TS server's reference graph rather than on this fix.)
references = retriever.find_referencing_symbols("GAME_VERSION", GAME_TS)
assert isinstance(references, list)