From 37c837ffd337d054bcada6879c94008781db5a0d Mon Sep 17 00:00:00 2001 From: Robin Jehn <71655429+RobinJehn@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:03:41 +0200 Subject: [PATCH] Send languageId 'typescriptreact' / 'javascriptreact' for .tsx / .jsx files (#1436) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: send languageId 'typescriptreact' / 'javascriptreact' for .tsx / .jsx Without this, .tsx files are opened with languageId 'typescript', so tsserver parses JSX as syntax errors and recovers by truncating the enclosing symbol's range at the first multi-line JSX expression. The resulting symptoms: - find_symbol(name, include_body=True) on a React component returns a body that ends mid-component, hiding everything below. - The same component shows up as multiple Method-kind symbols (one per call site) plus a separately-tracked Constant for the definition, because tsserver mistakes JSX-tag tokens for member access. Reproducer (before fix, real-world TSX file): SidebarPDF range = [11:1-25:207] (truncated at first JSX, line 25) After fix: SidebarPDF range = [11:1-281:2] (full body) The base SolidLanguageServer._get_language_id_for_file is explicitly designed for this — its docstring says "Override in subclasses to return file-specific language IDs." This change just exercises that hook for the React file extensions. Adds a regression test + jsx_component.tsx fixture covering the same arrow-function-with-multi-line-JSX-ternary pattern that triggers the truncation in real codebases (see oraios/serena#1029 for an earlier report whose root cause was not identified). --- .../typescript_language_server.py | 9 ++++ .../typescript/test_repo/jsx_component.tsx | 44 +++++++++++++++++++ .../repos/typescript/test_repo/tsconfig.json | 5 ++- .../typescript/test_typescript_basic.py | 31 +++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 test/resources/repos/typescript/test_repo/jsx_component.tsx diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 5b7ec4fe..ddf3f605 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -217,6 +217,15 @@ class TypeScriptLanguageServer(SolidLanguageServer): def _create_launch_command(self, core_path: str) -> list[str]: return [core_path, "--stdio"] + def _get_language_id_for_file(self, relative_file_path: str) -> str: + # JSX is parsed as TS without this, which silently truncates symbol + # ranges at the first multi-line JSX expression. + if relative_file_path.endswith(".tsx"): + return "typescriptreact" + if relative_file_path.endswith(".jsx"): + return "javascriptreact" + return self.language_id + def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the TypeScript Language Server. diff --git a/test/resources/repos/typescript/test_repo/jsx_component.tsx b/test/resources/repos/typescript/test_repo/jsx_component.tsx new file mode 100644 index 00000000..c8dea77d --- /dev/null +++ b/test/resources/repos/typescript/test_repo/jsx_component.tsx @@ -0,0 +1,44 @@ +// Regression fixture: mirrors the real-world pattern that caused +// languageId=typescript (instead of typescriptreact) to truncate symbol +// ranges at the first multi-line JSX expression. + +import * as React from "react" + +type SectionProps = { title: string; emphasised?: boolean } + +function Section({ title, emphasised }: SectionProps) { + return emphasised ? ( +