| author | |
| committer | |
| log | 8dfe1d43f189dcc9d18b44480ea8fe2d3b021136 |
| tree | e86fc72828986dd6f71d1fccc6e011bbff391861 |
| parent | 5b921620c94fade38cd72a34fe2e1d195ef19cb8 |
| signature |
2 files changed, 18 insertions(+), 1 deletions(-)
src/Memoizer.ts+4-1| ... | ... | @@ -100,6 +100,9 @@ export class Memoizer { |
| 100 | 100 | // much effort), but any extra re-renders get eliminated with AST diffing. |
| 101 | 101 | const processor = UNWRAP(this.#astProcessor); |
| 102 | 102 | const parsed = this.#parsed; |
| 103 | const replacedTailAffectedDocument = parsed | |
| 104 | .slice(blockStart) | |
| 105 | .some((block) => block.some(nodeAffectsDocument)); | |
| 103 | 106 | let parsedTree = processor.parse(content.slice(parseOffset)); |
| 104 | 107 | let newPositions = parsedTree.children.map( |
| 105 | 108 | (child) => UNWRAP(UNWRAP(child.position).start.offset) + parseOffset, |
| ... | ... | @@ -118,7 +121,7 @@ export class Memoizer { |
| 118 | 121 | // If a reflink is involved, fall back to a full reparse. Rehydrating a |
| 119 | 122 | // synthetic root from cached blocks can lose source offsets on transformed |
| 120 | 123 | // top-level nodes, which would scramble block bucketing and key identity. |
| 121 | if (nodeAffectsDocument(parsedTree)) { | |
| 124 | if (replacedTailAffectedDocument || nodeAffectsDocument(parsedTree)) { | |
| 122 | 125 | blockStart = 0; |
| 123 | 126 | parseOffset = 0; |
| 124 | 127 | parsedTree = processor.parse(content); |
tests/Memoizer.test.tsx+14| ... | ... | @@ -140,6 +140,20 @@ describe("Memoizer incremental rendering", () => { |
| 140 | 140 | expect(after).toContain('href="/two"'); |
| 141 | 141 | }); |
| 142 | 142 | |
| 143 | it("keeps earlier gfm footnotes rendered when a later footnote definition is removed", () => { | |
| 144 | const incremental = createMemoizer(gfmProcessor); | |
| 145 | ||
| 146 | incremental.update("a[^b]\n\n[^b]: bee\n\nc"); | |
| 147 | incremental.update("a[^b]\n\n[^b]: bee\n\nc[^d]\n\n[^d]: dee"); | |
| 148 | const after = renderNodes(incremental.update("a[^b]\n\n[^b]: bee\n\nc[^d]")); | |
| 149 | ||
| 150 | const fresh = createMemoizer(gfmProcessor); | |
| 151 | const expected = renderNodes(fresh.update("a[^b]\n\n[^b]: bee\n\nc[^d]")); | |
| 152 | ||
| 153 | expect(after).toBe(expected); | |
| 154 | expect(after).toContain('id="user-content-fn-b"'); | |
| 155 | }); | |
| 156 | ||
| 143 | 157 | it.each([ |
| 144 | 158 | { |
| 145 | 159 | content: ["alpha\n\nbeta", "alpha\nbeta", "alpha\n\nbeta\n\ngamma", "alpha\n\nbeta"], |