Skip to content

fix(core): detect showLineNumbers anywhere in the meta string - #275

Merged
atomiks merged 2 commits into
rehype-pretty:masterfrom
pablofdezr:fix/show-line-numbers-meta-position
Jul 24, 2026
Merged

fix(core): detect showLineNumbers anywhere in the meta string#275
atomiks merged 2 commits into
rehype-pretty:masterfrom
pablofdezr:fix/show-line-numbers-meta-position

Conversation

@pablofdezr

Copy link
Copy Markdown
Contributor

Summary

Closes #204.

showLineNumbers only worked when it was the first token in the meta
string. Placed after any highlight token (e.g. /age/#v), it was
silently ignored:

```tsx showLineNumbers /age/#v      ✅ line numbers
```tsx /age/#v showLineNumbers      ❌ no line numbers   (this issue)

Root cause

Detection used a reversed-string regex (packages/core/src/index.ts):

/srebmuNeniLwohs(?!(.*)(\/))/.test(reverseString(meta))

srebmuNeniLwohs is showLineNumbers reversed. The negative lookahead
was intended to ignore /showLineNumbers/ when used as a highlight
word, but it actually fails whenever any / precedes
showLineNumbers in the meta. Highlight tokens like /age/#v contain
slashes, so showLineNumbers after them never matched — the behavior was
position-dependent by accident.

Fix

Match showLineNumbers (optionally showLineNumbers{N}) as a standalone,
whitespace-bounded token, wherever it appears, and read the start-at
number directly instead of reversing substrings:

const showLineNumbersMatch = meta.match(
  /(?:^|\s)showLineNumbers(?:\{(\d+)\})?(?=\s|$)/,
);

Bounding on whitespace / string-ends still excludes the slash-delimited
/showLineNumbers/ highlight word, so those cases keep working. The
reverseString import is now unused in this module and was removed.

Behavior

Meta Before After
showLineNumbers /const/
/const/ showLineNumbers
/age/#v showLineNumbers{5} ✅ (counter-set: line 4)
showLineNumbers
/showLineNumbers/ (highlight word) ignored ignored ✅
/anything?showLineNumbers/ ignored ignored ✅

Tests

  • Added showLineNumbersAfterHighlight fixture covering showLineNumbers
    and showLineNumbers{N} after highlight tokens.
  • All existing snapshots are unchanged — including the
    showLineNumbers fixture that asserts /showLineNumbers/ and
    /anything?showLineNumbers/ do not enable line numbers — confirming
    no regression.
  • typecheck and biome check pass; full suite green.

`showLineNumbers` was detected with a reversed-string regex:

    /srebmuNeniLwohs(?!(.*)(\/))/.test(reverseString(meta))

The negative lookahead was meant to ignore `/showLineNumbers/` used as a
highlight word, but it actually rejected `showLineNumbers` whenever any
`/` appeared before it in the meta. Since highlight tokens such as
`/age/#v` contain slashes, `showLineNumbers` placed after them was
silently ignored — so it only worked at the start of the meta string.

Replace the reverse-string hack with a token-boundary match that is
position-independent and captures the optional start-at number directly:

    meta.match(/(?:^|\s)showLineNumbers(?:\{(\d+)\})?(?=\s|$)/)

Bounding on whitespace/string-ends still excludes `/showLineNumbers/`
(slash-delimited), so the highlight-word cases keep working. The
`reverseString` import is now unused in this module and removed.

Added a fixture covering `showLineNumbers` (and `showLineNumbers{N}`)
after highlight tokens; all existing snapshots are unchanged.

Closes rehype-pretty#204

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 236b3e2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 24, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/rehype-pretty-code@275
npm i https://pkg.pr.new/@rehype-pretty/transformers@275

commit: 58b027a

@atomiks

atomiks commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

PR review

The detection fixes option ordering but still mistakes text inside a valid highlight annotation for a standalone option. The replacement remains compatible with older Safari because it does not use lookbehind.

Bugs (1)

1. 🟠 Exclude showLineNumbers inside delimited annotations

Location: packages/core/src/index.ts:433

const showLineNumbersMatch = meta.match(
  /(?:^|\s)showLineNumbers(?:\{(\d+)\})?(?=\s|$)/,
);

Whitespace boundaries do not prove that the match is outside a slash- or quote-delimited annotation. Multi-word character highlights may legitimately contain whitespace.

Failure scenario: With metadata /foo showLineNumbers bar/, the text is highlighted correctly but the resulting <code> also receives data-line-numbers.

Fix: Tokenize the metadata or reject matches whose indices fall inside the existing parsed annotation spans. Keep the solution lookbehind-free for pre-Safari 16.4 compatibility, and add this multi-word case to the fixture.

Docs (1)

1. 🟠 Add a patch changeset

Location: packages/core/src/index.ts:429

// Detect `showLineNumbers` ... as a standalone meta token

This fixes public metadata behavior but currently produces no package version bump.

Failure scenario: The fix merges without being included in the next published version.

Fix: Add a changeset declaring "rehype-pretty-code": patch.

Verdict

Approve after nits - the remaining false positive is narrow but should be corrected before merging when practical.


🤖 Review generated with Codex

Whitespace boundaries alone do not prove the token is an option: a
character highlight may legitimately span whitespace, so
`/foo showLineNumbers bar/` highlighted the text correctly but also gave
the `<code>` a spurious `data-line-numbers`.

Move the detection into `getShowLineNumbers`, which skips matches
overlapping the spans already claimed by the parsed `charsMatches`
annotations. Only lookahead is used, so this stays parseable on
Safari < 16.4.

Fixture gains the multi-word case both on its own and alongside a real
`showLineNumbers` option; the latter keeps line numbers and highlights
both occurrences.

Also adds the patch changeset.
@atomiks

atomiks commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

PR review

The detection rewrite is correct and the annotation-exclusion follow-up closes the gap raised in the previous round; nothing here is merge-blocking. Two narrower metadata forms that used to enable line numbers silently stop doing so, and that tightening is not mentioned in the changeset.

Docs (1)

1. 🟠 Two previously-accepted metadata forms silently stop enabling line numbers

Location: packages/core/src/utils.ts:117

const showLineNumbersRegex = /(?:^|\s)(showLineNumbers(?:\{(\d+)\})?)(?=\s|$)/g;

The old reversed-string regex matched srebmuNeniLwohs as a bare substring with no token boundary, so any metadata string containing the word — glued to other characters — enabled line numbers. The new regex requires whitespace or string boundaries on both sides, which is the right contract, but it is a behaviour change beyond the ordering fix the changeset describes.

Rendering the same fixture on master and on this branch, the only differences are:

meta master this branch
js /const/ showLineNumbers no line numbers line numbers (the fix)
js /a/ /b/ showLineNumbers{3} no line numbers line numbers, start 3 (the fix)
js showLineNumbersFoo line numbers none
js showLineNumbers{5}extra line numbers, start 5 none

The last two rows are the tightening. Both were almost certainly unintended on master, so this is a reasonable change, but a consumer with showLineNumbers{5}extra in a code fence loses line numbers on a patch bump with nothing in the release notes explaining why.

Failure scenario: A user upgrades a patch version, line numbers disappear from a code block, and the changeset only mentions detecting showLineNumbers in more positions — so the release notes point away from the actual cause.

Fix: Add a sentence to .changeset/fair-ads-begin.md noting that showLineNumbers is now only recognised as a standalone whitespace-delimited token, so forms like showLineNumbersFoo or showLineNumbers{5}extra no longer enable it.

Tests (1)

1. 🟡 The exclusion fixture only covers slash-delimited annotations

Location: packages/core/test/fixtures/showLineNumbersAfterHighlight.md:26

```js /foo showLineNumbers bar/
const value = 'foo showLineNumbers bar';
```

takenSpans is built from charsMatches, whose delimiter group is ["/] — quote-delimited annotations produce spans too, and charsIdAndOrRange (\S*) extends a span past the closing delimiter. Neither is exercised. Both work today; nothing pins them.

Failure scenario: A future change to the charsMatches regex or to the span arithmetic in index.ts breaks "foo showLineNumbers bar" or /foo showLineNumbers bar/1,2 and the fixture suite stays green.

Fix: Add two blocks to the fixture: one using " as the delimiter, and one with a range or id suffix on a multi-word annotation containing the word.

Verdict

Approve after nits - the regex and span arithmetic are correct and lookbehind-free; only the undocumented tightening and two fixture gaps remain.


🤖 Review generated with Claude Code

@pablofdezr

Copy link
Copy Markdown
Contributor Author

Addressed the review.

Detection moved into getShowLineNumbers() in utils.ts, which skips any match overlapping the spans already claimed by the parsed charsMatches annotations — so /foo showLineNumbers bar/ highlights the text without the <code> picking up data-line-numbers. Only lookahead is used, no lookbehind, so it stays parseable on Safari < 16.4.

The fixture gains the multi-word case, plus /foo showLineNumbers bar/ showLineNumbers to pin down that the word still works as an option when it also appears inside a highlight. Without the fix all five blocks in that fixture get data-line-numbers; now four do.

Patch changeset added.

@atomiks atomiks left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

@atomiks
atomiks merged commit 012acb6 into rehype-pretty:master Jul 24, 2026
3 checks passed
@pablofdezr
pablofdezr deleted the fix/show-line-numbers-meta-position branch July 24, 2026 23:26
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.

showLineNumber dosent work when placed at the end of the meta tags.

2 participants