From 234b4076210d933478fff44da45f0262eb7a63f2 Mon Sep 17 00:00:00 2001 From: "extern.shiji.yan" Date: Fri, 24 Apr 2026 14:15:08 +0800 Subject: [PATCH 1/4] feat: add typescript_tsgo language backend (TypeScript 7 native LSP) Add support for tsgo as an alternative TypeScript language backend. tsgo is the native Go-based TypeScript 7 compiler with built-in LSP, which does not require Node.js. Closes #1402 --- CHANGELOG.md | 1 + docs/01-about/020_programming-languages.md | 4 +- pyproject.toml | 1 + .../language_servers/tsgo_language_server.py | 156 ++++++++++++++++++ src/solidlsp/ls_config.py | 13 +- test/conftest.py | 1 + .../typescript/test_typescript_tsgo.py | 61 +++++++ 7 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 src/solidlsp/language_servers/tsgo_language_server.py create mode 100644 test/solidlsp/typescript/test_typescript_tsgo.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e76ce726..04379081f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,7 @@ Status of the `main` branch. Changes prior to the next official version change w - Fix: Lean4 stale cache — empty document symbol responses (returned before `lake build` completes) are no longer persisted, preventing symbols from being permanently hidden #1356 - Add JSON language server support via `vscode-json-languageserver` (experimental) #1391 - Fix: Elixir/Expert deadlock on startup — Expert's build pipeline requires a `textDocument/didOpen` notification to start; Serena now opens `mix.exs` immediately after `initialized` so Expert begins compiling instead of waiting indefinitely #1397 + - New: Add `typescript_tsgo` language backend — TypeScript support via tsgo (native Go-based TypeScript 7 compiler with built-in LSP); automatically installed via npm #1402 * Dashboard: - Add configurable dashboard interface mode (new global configuration setting `web_dashboard_interface`): diff --git a/docs/01-about/020_programming-languages.md b/docs/01-about/020_programming-languages.md index 02a374328..056d20875 100644 --- a/docs/01-about/020_programming-languages.md +++ b/docs/01-about/020_programming-languages.md @@ -121,7 +121,9 @@ Some languages require additional installations or setup steps, as noted. works best with a `foundry.toml` or `hardhat.config.js` in the project root) * **Swift** * **TypeScript** -* **Vue** + (by default, uses [typescript-language-server](https://github.com/typescript-language-server/typescript-language-server) (language `typescript`); + we also support [tsgo](https://github.com/nicolo-ribaudo/tc39-proposal-type-annotations) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — does not require Node.js, must be installed separately via `npm install -g @typescript/native-preview`) +* **Vue** (3.x with TypeScript; requires Node.js v18+ and npm; supports .vue Single File Components with monorepo detection) * **YAML** * **JSON** diff --git a/pyproject.toml b/pyproject.toml index f9a61d3e2..b50c35c2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -352,6 +352,7 @@ markers = [ "html: language server running for HTML (vscode-html-language-server, used as companion by Angular)", "scss: language server running for SCSS / Sass / CSS (some-sass-language-server)", "angular: language server running for Angular (uses @angular/language-server + @angular/language-service tsserver plugin)", + "typescript_tsgo: language server running for TypeScript via tsgo (native Go-based TS7 compiler)", ] [tool.codespell] diff --git a/src/solidlsp/language_servers/tsgo_language_server.py b/src/solidlsp/language_servers/tsgo_language_server.py new file mode 100644 index 000000000..04b9d26b7 --- /dev/null +++ b/src/solidlsp/language_servers/tsgo_language_server.py @@ -0,0 +1,156 @@ +""" +Language server implementation for tsgo — TypeScript 7's native Go-based compiler with built-in LSP. + +tsgo (part of @typescript/native-preview) implements the Language Server Protocol natively, +without requiring Node.js or the traditional typescript-language-server wrapper. + +Launch command: tsgo --lsp --stdio +""" + +import logging +import os +import pathlib +import subprocess +from typing import cast + +from overrides import override + +from solidlsp.ls import LanguageServerDependencyProviderSinglePath, SolidLanguageServer +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.settings import SolidLSPSettings + +from .typescript_language_server import prefer_non_node_modules_definition + +log = logging.getLogger(__name__) + + +class TsgoLanguageServer(SolidLanguageServer): + """ + TypeScript support via tsgo — the native Go-based TypeScript 7 compiler with built-in LSP. + + tsgo does not require Node.js. It must be installed separately + (e.g. via ``npm install -g @typescript/native-preview``). + + You can pass the following entries in ls_specific_settings["typescript_tsgo"]: + - ls_path: Path to the tsgo binary (default: discovered from PATH) + """ + + @override + def is_ignored_dirname(self, dirname: str) -> bool: + return super().is_ignored_dirname(dirname) or dirname in [ + "node_modules", + "dist", + "build", + "coverage", + ".next", + "out", + ] + + class DependencyProvider(LanguageServerDependencyProviderSinglePath): + def _get_or_install_core_dependency(self) -> str: + """Locate the tsgo binary on the system. tsgo must be pre-installed by the user.""" + import shutil + + tsgo_path = shutil.which("tsgo") + if tsgo_path is None: + raise RuntimeError( + "tsgo is not installed or not found in PATH.\n" + "Install it via: npm install -g @typescript/native-preview\n" + "Or set the 'ls_path' option in ls_specific_settings['typescript_tsgo'] " + "to point to your tsgo binary." + ) + + # Verify it works + try: + result = subprocess.run([tsgo_path, "--version"], capture_output=True, text=True, check=False, timeout=10) + if result.returncode == 0: + log.info(f"Found tsgo: {result.stdout.strip()}") + else: + log.warning(f"tsgo --version returned non-zero exit code: {result.returncode}") + except (FileNotFoundError, subprocess.TimeoutExpired) as e: + raise RuntimeError(f"Failed to verify tsgo binary at {tsgo_path}: {e}") from e + + return tsgo_path + + def _create_launch_command(self, core_path: str) -> list[str]: + return [core_path, "--lsp", "--stdio"] + + def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings): + super().__init__( + config, + repository_root_path, + None, + "typescript", + solidlsp_settings, + ) + + def _create_dependency_provider(self) -> "TsgoLanguageServer.DependencyProvider": + return self.DependencyProvider(self._custom_settings, self._ls_resources_dir) + + def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params: dict = { + "locale": "en", + "capabilities": { + "textDocument": { + "synchronization": {"didSave": True, "dynamicRegistration": True}, + "definition": {"dynamicRegistration": True}, + "references": {"dynamicRegistration": True}, + "documentSymbol": { + "dynamicRegistration": True, + "hierarchicalDocumentSymbolSupport": True, + "symbolKind": {"valueSet": list(range(1, 27))}, + }, + "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, + "rename": {"dynamicRegistration": True, "prepareSupport": True}, + "implementation": {"dynamicRegistration": True}, + }, + "workspace": { + "workspaceFolders": True, + "didChangeConfiguration": {"dynamicRegistration": True}, + }, + }, + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } + return cast(InitializeParams, initialize_params) + + def _start_server(self) -> None: + def register_capability_handler(params: dict) -> None: + return + + def window_log_message(msg: dict) -> None: + log.info(f"LSP: window/logMessage: {msg}") + + def do_nothing(params: dict) -> None: + return + + self.server.on_request("client/registerCapability", register_capability_handler) + self.server.on_notification("window/logMessage", window_log_message) + self.server.on_notification("$/progress", do_nothing) + self.server.on_notification("textDocument/publishDiagnostics", do_nothing) + + log.info("Starting tsgo language server process") + self.server.start() + initialize_params = self._get_initialize_params(self.repository_root_path) + + log.info("Sending initialize request to tsgo LSP") + init_response = self.server.send.initialize(initialize_params) + + assert "definitionProvider" in init_response.get("capabilities", {}), ( + "tsgo did not report definitionProvider capability" + ) + + self.server.notify.initialized({}) + + @override + def _get_preferred_definition(self, definitions: list) -> dict: + return prefer_non_node_modules_definition(definitions) diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index e00d942ed..820145f06 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -105,6 +105,10 @@ class Language(str, Enum): a virtual environment with pygls dependencies on first use. """ # Experimental or deprecated Language Servers + TYPESCRIPT_TSGO = "typescript_tsgo" + """TypeScript support via tsgo — the native Go-based TypeScript 7 compiler with built-in LSP. + Does not require Node.js. Install via: npm install -g @typescript/native-preview + """ TYPESCRIPT_VTS = "typescript_vts" """Use the typescript language server through the natively bundled vscode extension via https://github.com/yioneko/vtsls""" PYTHON_JEDI = "python_jedi" @@ -210,6 +214,7 @@ def is_experimental(self) -> bool: """ return self in { self.ANSIBLE, + self.TYPESCRIPT_TSGO, self.TYPESCRIPT_VTS, self.PYTHON_JEDI, self.PYTHON_TY, @@ -265,8 +270,8 @@ def get_source_fn_matcher(self) -> FilenameMatcher: case self.PYTHON | self.PYTHON_JEDI | self.PYTHON_TY: return FilenameMatcher(".py", ".pyi") case self.JAVA: - return FilenameMatcher(".java") - case self.TYPESCRIPT | self.TYPESCRIPT_VTS: + return FilenameMatcher("*.java") + case self.TYPESCRIPT | self.TYPESCRIPT_TSGO | self.TYPESCRIPT_VTS: # see https://github.com/oraios/serena/issues/204 path_patterns = [] for prefix in ["c", "m", ""]: @@ -452,6 +457,10 @@ def get_ls_class(self) -> type["SolidLanguageServer"]: from solidlsp.language_servers.vts_language_server import VtsLanguageServer return VtsLanguageServer + case self.TYPESCRIPT_TSGO: + from solidlsp.language_servers.tsgo_language_server import TsgoLanguageServer + + return TsgoLanguageServer case self.VUE: from solidlsp.language_servers.vue_language_server import VueLanguageServer diff --git a/test/conftest.py b/test/conftest.py index aefd34ea2..8e46ed755 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -45,6 +45,7 @@ class LanguageParamRequest: Language.PYTHON_TY: Language.PYTHON, Language.RUBY_SOLARGRAPH: Language.RUBY, Language.PYTHON_TY: Language.PYTHON, + Language.TYPESCRIPT_TSGO: Language.TYPESCRIPT, } PYTHON_LANGUAGE_BACKENDS = [Language.PYTHON, Language.PYTHON_TY] diff --git a/test/solidlsp/typescript/test_typescript_tsgo.py b/test/solidlsp/typescript/test_typescript_tsgo.py new file mode 100644 index 000000000..562e77f53 --- /dev/null +++ b/test/solidlsp/typescript/test_typescript_tsgo.py @@ -0,0 +1,61 @@ +import shutil + +import pytest + +from solidlsp import SolidLanguageServer +from solidlsp.ls_config import Language +from solidlsp.ls_utils import SymbolUtils +from test.conftest import language_tests_enabled +from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols + +_tsgo_servers: list[Language] = [] +if language_tests_enabled(Language.TYPESCRIPT_TSGO) and shutil.which("tsgo"): + _tsgo_servers.append(Language.TYPESCRIPT_TSGO) + +pytestmark = pytest.mark.typescript_tsgo + + +@pytest.mark.skipif(not _tsgo_servers, reason="tsgo not installed or tests disabled") +class TestTsgoLanguageServer: + """Tests for the tsgo (TypeScript 7 native) language server.""" + + @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) + def test_find_symbol(self, language_server: SolidLanguageServer) -> None: + """Test that tsgo can discover symbols in the TypeScript test repo.""" + symbols = language_server.request_full_symbol_tree() + assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoClass"), "DemoClass not found in symbol tree" + assert SymbolUtils.symbol_tree_contains_name(symbols, "helperFunction"), "helperFunction not found in symbol tree" + assert SymbolUtils.symbol_tree_contains_name(symbols, "printValue"), "printValue method not found in symbol tree" + + @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) + def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: + """Test that tsgo can find references across files.""" + import os + + file_path = os.path.join("index.ts") + symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() + helper_symbol = None + for sym in symbols[0]: + if sym.get("name") == "helperFunction": + helper_symbol = sym + break + assert helper_symbol is not None, "Could not find 'helperFunction' symbol in index.ts" + sel_start = helper_symbol["selectionRange"]["start"] + refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + assert any("index.ts" in ref.get("relativePath", "") for ref in refs), ( + "index.ts should reference helperFunction" + ) + + @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) + def test_bare_symbol_names(self, language_server: SolidLanguageServer) -> None: + """Test that symbol names are clean (no malformed characters).""" + all_symbols = request_all_symbols(language_server) + malformed_symbols = [] + for s in all_symbols: + if has_malformed_name(s): + malformed_symbols.append(s) + if malformed_symbols: + pytest.fail( + f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}", + pytrace=False, + ) From 4bbcbd528da7fa753cecbff4094db010c3522698 Mon Sep 17 00:00:00 2001 From: "extern.shiji.yan" Date: Fri, 24 Apr 2026 14:26:43 +0800 Subject: [PATCH 2/4] style: apply ruff format to new tsgo files --- src/solidlsp/language_servers/tsgo_language_server.py | 4 +--- test/solidlsp/typescript/test_typescript_tsgo.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/solidlsp/language_servers/tsgo_language_server.py b/src/solidlsp/language_servers/tsgo_language_server.py index 04b9d26b7..a0cc10bb6 100644 --- a/src/solidlsp/language_servers/tsgo_language_server.py +++ b/src/solidlsp/language_servers/tsgo_language_server.py @@ -145,9 +145,7 @@ def do_nothing(params: dict) -> None: log.info("Sending initialize request to tsgo LSP") init_response = self.server.send.initialize(initialize_params) - assert "definitionProvider" in init_response.get("capabilities", {}), ( - "tsgo did not report definitionProvider capability" - ) + assert "definitionProvider" in init_response.get("capabilities", {}), "tsgo did not report definitionProvider capability" self.server.notify.initialized({}) diff --git a/test/solidlsp/typescript/test_typescript_tsgo.py b/test/solidlsp/typescript/test_typescript_tsgo.py index 562e77f53..ada312a77 100644 --- a/test/solidlsp/typescript/test_typescript_tsgo.py +++ b/test/solidlsp/typescript/test_typescript_tsgo.py @@ -42,9 +42,7 @@ def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> assert helper_symbol is not None, "Could not find 'helperFunction' symbol in index.ts" sel_start = helper_symbol["selectionRange"]["start"] refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) - assert any("index.ts" in ref.get("relativePath", "") for ref in refs), ( - "index.ts should reference helperFunction" - ) + assert any("index.ts" in ref.get("relativePath", "") for ref in refs), "index.ts should reference helperFunction" @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) def test_bare_symbol_names(self, language_server: SolidLanguageServer) -> None: From c3ea1c85301d6798763e6090dc7338329374653e Mon Sep 17 00:00:00 2001 From: "extern.shiji.yan" Date: Tue, 28 Apr 2026 10:54:02 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20address=20PR=20review=20?= =?UTF-8?q?=E2=80=94=20auto-install=20tsgo=20via=20npm,=20merge=20tests=20?= =?UTF-8?q?into=20existing=20suite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace shutil.which() lookup with npm-based auto-install pattern (using RuntimeDependencyCollection like typescript_language_server) - Remove standalone test_typescript_tsgo.py; parametrize existing typescript tests with TYPESCRIPT_TSGO (always in CI via is_ci) - Add tsgo installation step to pytest CI workflow - Update docs to reflect automatic installation --- .github/workflows/pytest.yml | 4 + docs/01-about/020_programming-languages.md | 2 +- .../language_servers/tsgo_language_server.py | 85 +++++++++++++------ test/conftest.py | 1 + .../typescript/test_typescript_basic.py | 16 ++-- .../typescript/test_typescript_tsgo.py | 59 ------------- 6 files changed, 75 insertions(+), 92 deletions(-) delete mode 100644 test/solidlsp/typescript/test_typescript_tsgo.py diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index a17b81481..501f8b21b 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -399,6 +399,10 @@ jobs: uses: krdlab/setup-haxe@v2 with: haxe-version: 4.3.7 + - name: Install tsgo (TypeScript native LSP) + shell: bash + run: npm install -g @typescript/native-preview + - name: Install Elm shell: bash run: npm install -g elm@0.19.1-6 diff --git a/docs/01-about/020_programming-languages.md b/docs/01-about/020_programming-languages.md index 056d20875..1db37410f 100644 --- a/docs/01-about/020_programming-languages.md +++ b/docs/01-about/020_programming-languages.md @@ -122,7 +122,7 @@ Some languages require additional installations or setup steps, as noted. * **Swift** * **TypeScript** (by default, uses [typescript-language-server](https://github.com/typescript-language-server/typescript-language-server) (language `typescript`); - we also support [tsgo](https://github.com/nicolo-ribaudo/tc39-proposal-type-annotations) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — does not require Node.js, must be installed separately via `npm install -g @typescript/native-preview`) + alternatively supports [tsgo](https://github.com/nicolo-ribaudo/tc39-proposal-type-annotations) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — automatically installed via npm) * **Vue** (3.x with TypeScript; requires Node.js v18+ and npm; supports .vue Single File Components with monorepo detection) * **YAML** diff --git a/src/solidlsp/language_servers/tsgo_language_server.py b/src/solidlsp/language_servers/tsgo_language_server.py index a0cc10bb6..8a3a133a1 100644 --- a/src/solidlsp/language_servers/tsgo_language_server.py +++ b/src/solidlsp/language_servers/tsgo_language_server.py @@ -10,16 +10,18 @@ import logging import os import pathlib -import subprocess +import shutil from typing import cast from overrides import override +from sensai.util.logging import LogTime from solidlsp.ls import LanguageServerDependencyProviderSinglePath, SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.settings import SolidLSPSettings +from .common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command from .typescript_language_server import prefer_non_node_modules_definition log = logging.getLogger(__name__) @@ -29,11 +31,11 @@ class TsgoLanguageServer(SolidLanguageServer): """ TypeScript support via tsgo — the native Go-based TypeScript 7 compiler with built-in LSP. - tsgo does not require Node.js. It must be installed separately - (e.g. via ``npm install -g @typescript/native-preview``). + tsgo is installed automatically via npm (``@typescript/native-preview``). + It does not require the traditional typescript-language-server wrapper. You can pass the following entries in ls_specific_settings["typescript_tsgo"]: - - ls_path: Path to the tsgo binary (default: discovered from PATH) + - tsgo_version: Version of @typescript/native-preview to install (default: "7.0.0-dev.20250601") """ @override @@ -49,29 +51,58 @@ def is_ignored_dirname(self, dirname: str) -> bool: class DependencyProvider(LanguageServerDependencyProviderSinglePath): def _get_or_install_core_dependency(self) -> str: - """Locate the tsgo binary on the system. tsgo must be pre-installed by the user.""" - import shutil - - tsgo_path = shutil.which("tsgo") - if tsgo_path is None: - raise RuntimeError( - "tsgo is not installed or not found in PATH.\n" - "Install it via: npm install -g @typescript/native-preview\n" - "Or set the 'ls_path' option in ls_specific_settings['typescript_tsgo'] " - "to point to your tsgo binary." - ) - - # Verify it works - try: - result = subprocess.run([tsgo_path, "--version"], capture_output=True, text=True, check=False, timeout=10) - if result.returncode == 0: - log.info(f"Found tsgo: {result.stdout.strip()}") - else: - log.warning(f"tsgo --version returned non-zero exit code: {result.returncode}") - except (FileNotFoundError, subprocess.TimeoutExpired) as e: - raise RuntimeError(f"Failed to verify tsgo binary at {tsgo_path}: {e}") from e - - return tsgo_path + """Install @typescript/native-preview via npm and return the path to the tsgo executable.""" + language_specific_config = self._custom_settings + tsgo_version = language_specific_config.get("tsgo_version", "7.0.0-dev.20250601") + npm_registry = language_specific_config.get("npm_registry") + + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="typescript-native-preview", + description="@typescript/native-preview (tsgo) package", + command=build_npm_install_command("@typescript/native-preview", tsgo_version, npm_registry), + platform_id="any", + ), + ] + ) + + # Verify npm is installed + is_npm_installed = shutil.which("npm") is not None + assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again." + + tsgo_ls_dir = os.path.join(self._ls_resources_dir, "tsgo-lsp") + tsgo_executable_path = os.path.join(tsgo_ls_dir, "node_modules", ".bin", "tsgo") + + # Check if installation is needed + version_file = os.path.join(tsgo_ls_dir, ".installed_version") + expected_version = tsgo_version + + needs_install = False + if not os.path.exists(tsgo_executable_path): + log.info(f"tsgo executable not found at {tsgo_executable_path}.") + needs_install = True + elif os.path.exists(version_file): + with open(version_file) as f: + installed_version = f.read().strip() + if installed_version != expected_version: + log.info(f"tsgo version mismatch: installed={installed_version}, expected={expected_version}. Reinstalling...") + needs_install = True + else: + log.info("tsgo version file not found. Reinstalling to ensure correct version...") + needs_install = True + + if needs_install: + log.info("Installing tsgo dependencies...") + with LogTime("Installation of tsgo (typescript/native-preview)", logger=log): + deps.install(tsgo_ls_dir) + with open(version_file, "w") as f: + f.write(expected_version) + log.info("tsgo installed successfully") + + if not os.path.exists(tsgo_executable_path): + raise FileNotFoundError(f"tsgo executable not found at {tsgo_executable_path}, something went wrong with the installation.") + return tsgo_executable_path def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--lsp", "--stdio"] diff --git a/test/conftest.py b/test/conftest.py index 8e46ed755..85f59e0dd 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -281,6 +281,7 @@ def project_with_ls(request: LanguageParamRequest) -> Iterator[Project]: Language.ANGULAR: [pytest.mark.angular], Language.HTML: [pytest.mark.html], Language.SCSS: [pytest.mark.scss], + Language.TYPESCRIPT_TSGO: [pytest.mark.typescript], } diff --git a/test/solidlsp/typescript/test_typescript_basic.py b/test/solidlsp/typescript/test_typescript_basic.py index 1c9ed1ae1..201a5c4b5 100644 --- a/test/solidlsp/typescript/test_typescript_basic.py +++ b/test/solidlsp/typescript/test_typescript_basic.py @@ -5,20 +5,26 @@ from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_utils import SymbolUtils -from test.conftest import find_identifier_position, get_repo_path, language_has_verified_implementation_support +from test.conftest import find_identifier_position, get_repo_path, is_ci, language_has_verified_implementation_support, language_tests_enabled from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols +_typescript_servers: list[Language] = [Language.TYPESCRIPT] + +# tsgo is always tested in CI; locally only if the language is enabled +if is_ci or language_tests_enabled(Language.TYPESCRIPT_TSGO): + _typescript_servers.append(Language.TYPESCRIPT_TSGO) + @pytest.mark.typescript class TestTypescriptLanguageServer: - @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) + @pytest.mark.parametrize("language_server", _typescript_servers, indirect=True) def test_find_symbol(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoClass"), "DemoClass not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "helperFunction"), "helperFunction not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "printValue"), "printValue method not found in symbol tree" - @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) + @pytest.mark.parametrize("language_server", _typescript_servers, indirect=True) def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("index.ts") symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() @@ -91,8 +97,8 @@ def test_tsx_symbol_range_not_truncated_by_jsx(self, language_server: SolidLangu assert any(s.get("name") == "trailingHelper" for s in roots), ( "trailingHelper missing from jsx_component.tsx root symbols; tsserver likely stopped parsing at the first JSX expression." ) - - @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) + @pytest.mark.parametrize("language_server", _typescript_servers, indirect=True) +>>>>>>> a2eb8afb (refactor: address PR review — auto-install tsgo via npm, merge tests into existing suite) def test_bare_symbol_names(self, language_server) -> None: all_symbols = request_all_symbols(language_server) malformed_symbols = [] diff --git a/test/solidlsp/typescript/test_typescript_tsgo.py b/test/solidlsp/typescript/test_typescript_tsgo.py deleted file mode 100644 index ada312a77..000000000 --- a/test/solidlsp/typescript/test_typescript_tsgo.py +++ /dev/null @@ -1,59 +0,0 @@ -import shutil - -import pytest - -from solidlsp import SolidLanguageServer -from solidlsp.ls_config import Language -from solidlsp.ls_utils import SymbolUtils -from test.conftest import language_tests_enabled -from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols - -_tsgo_servers: list[Language] = [] -if language_tests_enabled(Language.TYPESCRIPT_TSGO) and shutil.which("tsgo"): - _tsgo_servers.append(Language.TYPESCRIPT_TSGO) - -pytestmark = pytest.mark.typescript_tsgo - - -@pytest.mark.skipif(not _tsgo_servers, reason="tsgo not installed or tests disabled") -class TestTsgoLanguageServer: - """Tests for the tsgo (TypeScript 7 native) language server.""" - - @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) - def test_find_symbol(self, language_server: SolidLanguageServer) -> None: - """Test that tsgo can discover symbols in the TypeScript test repo.""" - symbols = language_server.request_full_symbol_tree() - assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoClass"), "DemoClass not found in symbol tree" - assert SymbolUtils.symbol_tree_contains_name(symbols, "helperFunction"), "helperFunction not found in symbol tree" - assert SymbolUtils.symbol_tree_contains_name(symbols, "printValue"), "printValue method not found in symbol tree" - - @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) - def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: - """Test that tsgo can find references across files.""" - import os - - file_path = os.path.join("index.ts") - symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() - helper_symbol = None - for sym in symbols[0]: - if sym.get("name") == "helperFunction": - helper_symbol = sym - break - assert helper_symbol is not None, "Could not find 'helperFunction' symbol in index.ts" - sel_start = helper_symbol["selectionRange"]["start"] - refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) - assert any("index.ts" in ref.get("relativePath", "") for ref in refs), "index.ts should reference helperFunction" - - @pytest.mark.parametrize("language_server", _tsgo_servers, indirect=True) - def test_bare_symbol_names(self, language_server: SolidLanguageServer) -> None: - """Test that symbol names are clean (no malformed characters).""" - all_symbols = request_all_symbols(language_server) - malformed_symbols = [] - for s in all_symbols: - if has_malformed_name(s): - malformed_symbols.append(s) - if malformed_symbols: - pytest.fail( - f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}", - pytrace=False, - ) From bd13492982d94e2b6157955e02238abfb57e2ed0 Mon Sep 17 00:00:00 2001 From: "extern.shiji.yan" Date: Thu, 30 Apr 2026 14:00:16 +0800 Subject: [PATCH 4/4] fix: use 'latest' as default tsgo version instead of non-existent pinned version --- docs/01-about/020_programming-languages.md | 2 +- src/solidlsp/language_servers/tsgo_language_server.py | 4 ++-- src/solidlsp/ls_config.py | 2 +- test/solidlsp/typescript/test_typescript_basic.py | 10 ++++++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/01-about/020_programming-languages.md b/docs/01-about/020_programming-languages.md index 1db37410f..f9694aae9 100644 --- a/docs/01-about/020_programming-languages.md +++ b/docs/01-about/020_programming-languages.md @@ -122,7 +122,7 @@ Some languages require additional installations or setup steps, as noted. * **Swift** * **TypeScript** (by default, uses [typescript-language-server](https://github.com/typescript-language-server/typescript-language-server) (language `typescript`); - alternatively supports [tsgo](https://github.com/nicolo-ribaudo/tc39-proposal-type-annotations) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — automatically installed via npm) + alternatively supports [tsgo](https://github.com/microsoft/typescript-go) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — automatically installed via npm) * **Vue** (3.x with TypeScript; requires Node.js v18+ and npm; supports .vue Single File Components with monorepo detection) * **YAML** diff --git a/src/solidlsp/language_servers/tsgo_language_server.py b/src/solidlsp/language_servers/tsgo_language_server.py index 8a3a133a1..17823bd1e 100644 --- a/src/solidlsp/language_servers/tsgo_language_server.py +++ b/src/solidlsp/language_servers/tsgo_language_server.py @@ -35,7 +35,7 @@ class TsgoLanguageServer(SolidLanguageServer): It does not require the traditional typescript-language-server wrapper. You can pass the following entries in ls_specific_settings["typescript_tsgo"]: - - tsgo_version: Version of @typescript/native-preview to install (default: "7.0.0-dev.20250601") + - tsgo_version: Version of @typescript/native-preview to install (default: "latest") """ @override @@ -53,7 +53,7 @@ class DependencyProvider(LanguageServerDependencyProviderSinglePath): def _get_or_install_core_dependency(self) -> str: """Install @typescript/native-preview via npm and return the path to the tsgo executable.""" language_specific_config = self._custom_settings - tsgo_version = language_specific_config.get("tsgo_version", "7.0.0-dev.20250601") + tsgo_version = language_specific_config.get("tsgo_version", "latest") npm_registry = language_specific_config.get("npm_registry") deps = RuntimeDependencyCollection( diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index 820145f06..a90849e9c 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -270,7 +270,7 @@ def get_source_fn_matcher(self) -> FilenameMatcher: case self.PYTHON | self.PYTHON_JEDI | self.PYTHON_TY: return FilenameMatcher(".py", ".pyi") case self.JAVA: - return FilenameMatcher("*.java") + return FilenameMatcher(".java") case self.TYPESCRIPT | self.TYPESCRIPT_TSGO | self.TYPESCRIPT_VTS: # see https://github.com/oraios/serena/issues/204 path_patterns = [] diff --git a/test/solidlsp/typescript/test_typescript_basic.py b/test/solidlsp/typescript/test_typescript_basic.py index 201a5c4b5..a1f58da26 100644 --- a/test/solidlsp/typescript/test_typescript_basic.py +++ b/test/solidlsp/typescript/test_typescript_basic.py @@ -5,7 +5,13 @@ from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_utils import SymbolUtils -from test.conftest import find_identifier_position, get_repo_path, is_ci, language_has_verified_implementation_support, language_tests_enabled +from test.conftest import ( + find_identifier_position, + get_repo_path, + is_ci, + language_has_verified_implementation_support, + language_tests_enabled, +) from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols _typescript_servers: list[Language] = [Language.TYPESCRIPT] @@ -97,8 +103,8 @@ def test_tsx_symbol_range_not_truncated_by_jsx(self, language_server: SolidLangu assert any(s.get("name") == "trailingHelper" for s in roots), ( "trailingHelper missing from jsx_component.tsx root symbols; tsserver likely stopped parsing at the first JSX expression." ) + @pytest.mark.parametrize("language_server", _typescript_servers, indirect=True) ->>>>>>> a2eb8afb (refactor: address PR review — auto-install tsgo via npm, merge tests into existing suite) def test_bare_symbol_names(self, language_server) -> None: all_symbols = request_all_symbols(language_server) malformed_symbols = []