diff --git a/CHANGELOG.md b/CHANGELOG.md index 3057e7381e..ac0545a765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,8 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixes +- JSX child components keep their render links when plain text or a line break precedes the tag. + - **Indexing a mid-size project is faster.** Below 150,000 pending references the resolver used to stay on one thread for the whole run. It now boots its worker pool part-way through once at least 20,000 references are still unresolved and they would take the main thread at least 0.4 s (an explicit `CODEGRAPH_PARALLEL_RESOLVE_MIN` turns this off). Full index of JetBrains Exposed (110,854 references) 18.5 s to 9.6 s, koel 4.9 s to 4.1 s, Alamofire 1.65 s to 1.41 s (median of 3 interleaved runs each, identical graph), at 56–132% more peak memory; a 13,900-reference project never starts the pool and is unchanged. - **More C++ links land on the right target.** `Ptr p(0);` for `using Ptr = Widget*;` declares a pointer, so it no longer counts as constructing a `Widget`. A class nested in a function-local class is as local to that function as the class around it. A class an included `.cc` file declares is seen by the file that includes it, and of two headers declaring one class an object creation takes the one it includes. A `using Base::foo;` in a nested class no longer counts as the outer class's. `NULL` is an integer zero, not a `nullptr`, when an overload is chosen. A header's unnamed namespace belongs to the files that include the header, not to every file. Two annotation-macro cases read as extraction reads them: `const int FOO_BAR = 1;` keeps its name, and `n * FOO_BAR::VALUE` stays a product. - **Syncing a changed file in a large Python project is faster.** When a Python class has no method of the name being linked (a test class that passes `Handler.__init__` as a value, say), the resolver checks every class with that method for one that derives from it. For each class whose base is a package import (`import unittest`, then `unittest.TestCase`) it parsed the whole file again, and its cache holds only a few files, so two such references took about a second each. A plain one-line `import a.b` is now read from its line; other forms still use the parse. In CPython a one-file sync takes a median 5.5 s instead of 7.7 s on Node and 5.2 s instead of 8.1 s on Bun (4 alternating runs per build, ranges not overlapping; the phase that retries unresolved references drops from 4.6 s to 2.3 s). A full index is within the run-to-run variation, and the graph is the same on CPython and Django (no edge differs). diff --git a/README.md b/README.md index 8eae8b64c1..7174c632e6 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ Compared with upstream `main` at `1d3619d6` (after v1.6.2). Each item here and i The other languages are the same in both, listed under [Supported Languages](#supported-languages). +JSX render links include child components after plain text or a line break. Type arguments, comments and quoted examples do not create render links. + C# property accessors and expression-bodied properties contribute calls and references owned by the property. VB.NET member bodies and field initializers also contribute their calls and references. C# field and property initializers retain their calls and references under the member that owns them. Target-typed `new()` resolves relative declared types through enclosing namespaces and honors `global::` qualification without requiring a redundant `using`. VB.NET resolves typed receivers, enclosing and inherited members, and field or property reads through values and Shared types without choosing unrelated project declarations. Names beginning with keywords, such as `SharedCache`, `Dimension` and `NewItem`, retain their declarations. diff --git a/__tests__/jsx-child-disambiguation.test.ts b/__tests__/jsx-child-disambiguation.test.ts index 482b812133..3b3879a027 100644 --- a/__tests__/jsx-child-disambiguation.test.ts +++ b/__tests__/jsx-child-disambiguation.test.ts @@ -604,10 +604,7 @@ export function AuthShell() { it('renders the declaration itself when the module it loads is out of reach', () => declarationWhenOutOfReach('Sign in: ')); - // Known limit of the fork: `opensTag` skips the whitespace before `<` and reads a word there - // as a type or comparison (`count < Max`), so upstream's `Sign in ` is no tag - // here. `it.fails` keeps this red-by-design case visible and turns the suite red once fixed. - it.fails('renders the declaration itself when a word precedes the tag (upstream fixture)', () => declarationWhenOutOfReach('Sign in ')); + it.each(['Sign in ', 'Sign in\n'])('renders the declaration itself after JSX text %j', declarationWhenOutOfReach); it('reads next/dynamic’s `(await import(…)).default` and a `.then` that picks a named export', async () => { // excalidraw's docs site has a component of the same name. @@ -1191,9 +1188,7 @@ describe('JSX child: a tag written in a comment or a string', () => { '
', '

Don\'t have an account? Sign up

', '

Read https://example.com/docs first:

', - // Upstream writes `Press `Ctrl` and `; the fork's opensTag reads "and <" as a comparison (see the - // `it.fails` case above), so the backtick case uses a `+` before the tag. - '

Press `Ctrl` + K

', + '

Press `Ctrl` and K

', ' ', ' ', ' );', diff --git a/docs/design/framework-coverage.md b/docs/design/framework-coverage.md index 0f2f50b1c5..68d2a71fda 100644 --- a/docs/design/framework-coverage.md +++ b/docs/design/framework-coverage.md @@ -644,3 +644,5 @@ false-positive controls through full indexing. React Router lazy modules follow default or `Component` exports through local named and wildcard re-exports. Local exports take precedence; ambiguous wildcard exports and re-export cycles stay unresolved. JSX component lookup follows its import and excludes names bound as local values or parameters. Regression coverage: `__tests__/react-router-lazy-barrel.test.ts`, `__tests__/jsx-child-disambiguation.test.ts` and `__tests__/sync-jsx-import-forwarding.test.ts`. These deterministic cases do not establish a new agent-evaluation or performance result. + +JSX child rendering preserves tags after plain text and line breaks. The lexer blanks child text without shifting source positions; type arguments and quoted examples remain excluded. Regression coverage is in `__tests__/jsx-child-disambiguation.test.ts`. diff --git a/site/src/content/docs/guides/framework-routes.md b/site/src/content/docs/guides/framework-routes.md index bde25a25c9..5b754f9977 100644 --- a/site/src/content/docs/guides/framework-routes.md +++ b/site/src/content/docs/guides/framework-routes.md @@ -96,4 +96,4 @@ React Router also reads imported route tables from their module-level export bin Cross-file route-table nodes and their references are persisted in one transaction per framework. A failed write rolls back that reconciliation, allowing the next pass to retry it. -React Router lazy modules follow default or `Component` exports through local named and wildcard re-exports. Local exports take precedence; ambiguous wildcard exports and re-export cycles stay unresolved. JSX component lookup follows its import and excludes names bound as local values or parameters. +React Router lazy modules follow default or `Component` exports through local named and wildcard re-exports. Local exports take precedence; ambiguous wildcard exports and re-export cycles stay unresolved. JSX component lookup follows its import and excludes names bound as local values or parameters. Child components after JSX text or line breaks retain their render links; type arguments, comments and quoted examples do not create them. diff --git a/src/resolution/jsx-code.ts b/src/resolution/jsx-code.ts index 32ae05618c..bdbb7c3596 100644 --- a/src/resolution/jsx-code.ts +++ b/src/resolution/jsx-code.ts @@ -6,8 +6,8 @@ * commented out (`{/* *\/}`), an error message ("You cannot * render a inside another "), an attribute's value. So * comments, and the contents of string, template and regex literals and of - * JSX attribute strings, are blanked. Every character keeps its offset and - * every line break its place, so a match in the result sits at the same line + * JSX attribute strings and child text, are blanked. Every character keeps its + * offset and every line break its place, so a match in the result sits at the same line * and column as in the source. * * Inside an element, a quote, a slash or a backtick is text: `

Don't have an @@ -513,6 +513,8 @@ class JsxLexer { if (c === 62 || c === 125) return this.reread(); let end = i + 1; for (let e = src.charCodeAt(end); end < this.n && e !== 123 && e !== 60 && e !== 62 && e !== 125; e = src.charCodeAt(++end)); + // Text before a child tag is not a type name or a local binding. + this.blank(i, end); this.i = end; return true; }