Skip to content

fix: double-clicking a code block in preview keeps your place - #47

Open
productengineered wants to merge 1 commit into
seamoss:mainfrom
productengineered:fix/code-block-dblclick
Open

productengineered wants to merge 1 commit into
seamoss:mainfrom
productengineered:fix/code-block-dblclick

Conversation

@productengineered

Copy link
Copy Markdown
Contributor

In preview, double-clicking inside a code block opened the editor at the top of the document instead of at the click. Double-clicking in the margin, or a gap between blocks, did the same.

Smallest repro: any document long enough to scroll, with a fence below the fold:

(a few screens of paragraphs)

```
Create two empty folders in this project: drafts and published.
```

Open it (it lands in preview), scroll to the fence, and double-click a word in it. The editor opens at line 1.

What

Cause: Shiki replaces each <pre> it highlights with a new node, and the data-pos stamp from rehypeSourcePositions is lost along with the old one. The dblclick handler then finds no stamped ancestor and calls onEdit(0).

  • src/shared/markdown.ts (only when sourcePositions is on): record the <pre> stamps before the highlighter runs and put them back after it, in document order. Shiki swaps one <pre> for one <pre> in place, so the order holds. The stamps are stored on the vfile rather than in plugin state, because the processor is shared and renders can overlap.
  • src/renderer/ui/preview.ts:
    • A double-click that lands on no stamped element uses the nearest block, the same lookup visibleAnchor uses. The page holds still and the caret goes to that block.
    • New sourceOffsetNear handles a click on whitespace, such as the gap between words or a code line's indentation. It maps the neighbouring word, then steps across the gap when the source has the same characters there (in a code block it always does). This puts the caret on the clicked character rather than at the start of the block.

Tests

  • Layer: unit tests for the pipeline and the pure function, plus an end-to-end case for preview.ts.
  • Case / test added or extended:
    • markdown.test.ts: code blocks keep their stamps when sourcePositions is on. The cases are a plain fence, a lazily loaded language, an unknown language, and a fence nested in a list. It failed before the fix ([]).
    • Golden files: no diff. The change only runs when sourcePositions is on, and exports never turn it on.
    • preview.test.ts: sourceOffsetNear covers clicks in a word, in a gap between words, in indentation, across a gap whose source differs, and on a line with no word at all.
    • scripts/e2e/cases/preview-dblclick.mjs: double-clicks a word in a code block deep in a long document, then the margin beside a paragraph. It checks that the editor isn't at the top, that the caret is on the clicked character or paragraph, and that the line holds its height on screen. On main it fails with the editor's scrollTop at 0.
  • pnpm typecheck && pnpm test pass (376 tests). pnpm e2e passes (7/7), and pnpm test:coverage stays above the floors.

Shiki swaps every <pre> it highlights for a fresh node, and the data-pos
stamp went with the old one. A double-click inside a code block found no
stamped ancestor and called onEdit(0), so the editor opened at the top of
the document. A double-click in the margin or a gap between blocks did
the same.

- markdown.ts (sourcePositions only): record the <pre> stamps before the
  highlighter and put them back after it, in document order. They ride on
  the vfile, since the processor is shared and renders overlap. Exports are
  unchanged, and the golden files still match byte for byte.
- preview.ts: a double-click off every stamped element uses the nearest
  block, the same lookup visibleAnchor uses. sourceOffsetNear handles a
  click on whitespace (a gap between words, a code line's indentation) by
  mapping the neighbouring word and crossing the gap when the source holds
  the same characters there.

Tests: markdown unit test for code-block stamps (plain, lazy-loaded,
unknown-language, and in-list fences); unit tests for sourceOffsetNear;
e2e case preview-dblclick, which fails on main (editor scrollTop 0).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant