Skip to content

ci: Document with a patched roxygen2 that keeps the sentence gap - #610

Open
krlmlr wants to merge 3 commits into
claude/readme-rmdfrom
claude/roxygen2-fork
Open

krlmlr wants to merge 3 commits into
claude/readme-rmdfrom
claude/roxygen2-fork

Conversation

@krlmlr

@krlmlr krlmlr commented Sep 13, 2026

Copy link
Copy Markdown
Member

Split out of #606 so it can be decided on its own.

The problem

commonmark discards the whitespace a line break stands for. roxygen2 turns every softbreak into a bare newline, so the .Rd carries a line break with nothing after it and Rd2txt() renders it as a single space. Prose written one sentence per line therefore comes out single-spaced, while the same words wrapped to a fixed width with two spaces after the period keep the gap.

That makes the two styles mutually exclusive: splitting a sentence pair separated by two spaces silently drops one of them. That is what forced the very long single roxygen lines this package used to carry, because joining was the only way to keep the gap.

Only the text renderer is affected. Rd2HTML() and Rd2latex() differ by whitespace that HTML and TeX collapse — and LaTeX applies its own sentence spacing regardless. But text is what ?topic shows, so it is the version most people read.

The change

A new composite action, .github/workflows/roxygen2-fork, installs roxygen2 from upstream with the R/ part of krlmlr/roxygen2@f-sentence-spacing applied on top, and R-CMD-check.yaml runs it immediately before the Roxygenize step.

It shallow-clones upstream, fetches the branch, computes the diff against their merge base restricted to R/, and applies it with git apply --3way. Restricting to R/ means an unrelated upstream change to a test fixture cannot fail the documentation step. It aborts if the patch is empty or does not apply, rather than silently installing an unpatched build, and afterwards asserts that what it installed really carries the patch.

Config/roxygen2/version becomes 8.1.0.9100. The .9100 suffix distinguishes a patched build from upstream's own .9000 development builds; if upstream moves, the x.y.z part follows it and the suffix stays. DESCRIPTION is DCF and cannot carry a comment line — read.dcf() rejects one as malformed — so the explanation lives in a Config/cynkra/roxygen2 field.

Why it is a separate pull request

Taking it changes rendered help across the package; leaving it means the reformatting below renders exactly as the package renders today, with one space between sentences. Neither half needs the other to be correct, so they should be able to be decided separately.

man/ is not edited by hand here. The .Rd files are generated output, so CI roxygenises and commits them itself — they arrive on this branch as an automated commit rather than in the hand-written diff. That commit has already landed: 10 regenerated .Rd files are part of this pull request's diff, and the hand-written half stays reviewable on its own.

Upstream

The roxygen2 side is krlmlr/roxygen2#8, on the fork, not yet filed with r-lib. It is deliberately narrow: mid-sentence breaks are untouched, links are unaffected, and two guards keep it from firing wrongly — an abbreviation list so a line ending in e.g. or et al. does not gain a mid-sentence gap, and a fix for a determinism bug where a line starting with \doi{} or \code{} gained or lost its gap at random, because the test was reading roxygen2's own random placeholder for protected Rd tags.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WWhverMTZZKgEpUuTK117m


Generated by Claude Code

@krlmlr
krlmlr force-pushed the claude/roxygen2-fork branch from 8bc32bc to 25b1da5 Compare September 13, 2026 20:30
@krlmlr
krlmlr added this pull request to stack #611 September 13, 2026 21:05
@krlmlr
krlmlr force-pushed the claude/roxygen2-fork branch from 25b1da5 to 783171a Compare September 13, 2026 21:06
@krlmlr
krlmlr force-pushed the claude/roxygen2-fork branch from 783171a to 2f3b860 Compare September 13, 2026 21:42
@krlmlr
krlmlr force-pushed the claude/roxygen2-fork branch from cc34665 to 37a3d78 Compare September 14, 2026 05:46
krlmlr and others added 3 commits September 14, 2026 16:37
commonmark discards the whitespace a line break stands for, so roxygen prose written
one sentence per line loses the gap between sentences in the rendered help.
Only the text renderer is affected, which is what `?topic` shows.

A new composite action installs roxygen2 from upstream with the R/ part of
krlmlr/roxygen2@f-sentence-spacing applied on top, and runs just before the
Roxygenize step. It shallow-clones upstream, fetches the branch, applies the diff
restricted to R/ so conflicts in the test files cannot fail it, and aborts rather
than silently installing an unpatched build. It then asserts that what it installed
really carries the patch.

Config/roxygen2/version becomes 8.1.0.9100. The .9100 suffix distinguishes a patched
build from upstream's own .9000 development builds; if upstream moves, the x.y.z part
follows it and the suffix stays. DESCRIPTION is DCF and cannot carry a comment, so the
explanation lives in a Config/cynkra/roxygen2 field.

This is a separate decision from the line-break reformatting below it, and is kept in
its own pull request so it can be taken or left on its own. Without it, the
reformatting simply renders as it does today, with one space between sentences.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WWhverMTZZKgEpUuTK117m
…l escape

The version stamp passed `"\1"` as the replacement in the `sub()` call that derives the `.9100` build number.
R parses `"\1"` in a string literal as the octal escape for `\001`, not as a regex backreference,
so `DESCRIPTION` received a control character where the `x.y.z` prefix belonged,
and `R CMD INSTALL` aborted with "Malformed package version".
The replacement is now `"\\1"`, which reaches the regex engine as a backreference and stamps `8.1.0.9000` to `8.1.0.9100`.

The post-install guard asserted only the `.9100` suffix, which the corrupt `"\001.9100"` also satisfies.
That is why the bug survived the very check that was meant to catch it.
The guard now asserts the whole `x.y.z.9100` shape, and passes `inherits = FALSE` to `exists()`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WWhverMTZZKgEpUuTK117m
@krlmlr
krlmlr force-pushed the claude/roxygen2-fork branch from 37a3d78 to 1226d40 Compare September 14, 2026 14:37
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.

2 participants