| Title: | Generate Markdown Reference Documentation for R Packages |
| Version: | 0.1 |
| Description: | Generates plain Markdown reference documentation for any R package, installed or a local development source tree, optimized for rendering in the 'GitHub'/'Gitea' browser UI and for use as LLM context (e.g. 'Claude Code'). Reads Rd documentation via tools::Rd_db() (installed packages) or directly from man/*.Rd (development packages, no installation required) and renders one file per topic plus a navigable index. |
| License: | MIT + file LICENSE |
| URL: | https://github.com/peterczerner/pkgmd |
| BugReports: | https://github.com/peterczerner/pkgmd/issues |
| Encoding: | UTF-8 |
| RoxygenNote: | 7.3.2 |
| Depends: | R (≥ 4.1.0) |
| Imports: | purrr (≥ 1.0.0), glue (≥ 1.6.0), cli (≥ 3.0.0), fs (≥ 1.6.0) |
| Suggests: | testthat (≥ 3.0.0), dplyr, withr, roxygen2 (≥ 7.0.0) |
| Config/testthat/edition: | 3 |
| NeedsCompilation: | no |
| Packaged: | 2026-07-29 19:41:54 UTC; peter |
| Author: | Peter Czerner [aut, cre] |
| Maintainer: | Peter Czerner <peterretep@posteo.de> |
| Repository: | CRAN |
| Date/Publication: | 2026-08-07 16:10:10 UTC |
Parse every entry of an Rd database into topic lists
Description
Topics without a name (e.g. purely internal Rd fragments) are dropped;
the result is sorted alphabetically by name so that the index and
_full_reference.md have a stable order.
Usage
.build_topics(db, package, exported_names = NULL)
Arguments
db |
Result of |
package |
Package name. |
exported_names |
Character vector of the package's exported
names, or |
Value
List of parsed topic lists, sorted by name.
Determine which of a topic's name/aliases actually look callable
Description
A name/alias "looks callable" if it appears followed by ( in the
topic's \\usage{} text - e.g. mutate.Rd has usage
mutate(.data, ...), so "mutate" qualifies. Infix operators (e.g.
%||%, %>%) are never followed by ( in \\usage{} - they're
invoked as x %op% y, not %op%(x, y) - so they get a second
check: an operator-shaped name (^%.+%$) qualifies if it appears
anywhere in a non-empty usage, which is safe precisely because that
syntax is unambiguous (never a dataset or concept-page name). Datasets
and concept pages (\\usage{} is just a bare name, or empty) never
qualify either way. A single Rd topic can document several callables
at once (e.g. lead-lag.Rd documents both lead() and lag(), with
topic name "lead-lag" matching neither) - hence this returns a
vector, not a single yes/no. Fixed-string matching (not regex) is used
for the name( check deliberately: Rd names can contain regex
metacharacters (%>%, [<-, ...).
Usage
.callable_candidates(name, aliases, usage)
Arguments
name |
Topic name. |
aliases |
Character vector of the topic's aliases (possibly empty). |
usage |
The topic's |
Details
Shared by .render_topic_heading() in render_md.R (decides whether
to append () to the heading) and .compute_is_private() in
parse_rd.R (a topic whose usage is a bare name - a dataset - is never
flagged private just because datasets aren't in NAMESPACE's
export() list the way functions are; an operator, unlike a dataset,
still needs to be checkable against NAMESPACE, which is exactly what
this second branch enables).
Value
Character vector of callable names (possibly empty).
Determine which of a topic's name/aliases are actually callable
Description
A topic is "callable" for heading purposes if its own name (or one of
its aliases) literally appears followed by ( in its \\usage{} block
e.g.
mutate.Rdhas usagemutate(.data, ...), so"mutate"qualifies. Datasets and concept pages (\\usage{}is just a bare name, or empty) never qualify. A single Rd topic can document several callables at once (e.g.lead-lag.Rddocuments bothlead()andlag(), with topic name"lead-lag"matching neither) - hence this returns a vector, not a single yes/no.
Usage
.callable_names(topic)
Arguments
topic |
Parsed topic list. |
Value
Character vector of callable names (possibly empty).
Abort if output_dir already contains .md files
Description
Abort if output_dir already contains .md files
Usage
.check_existing_output(output_dir, overwrite)
Arguments
output_dir |
Target directory. |
overwrite |
Whether existing files may be overwritten. |
Value
NULL, invisibly. Aborts via cli::cli_abort() if needed.
Abort if two topics would collide on the filesystem
Description
Two distinct failure modes: (1) a topic whose name is README or
_full_reference (case-insensitive) would silently overwrite one of
pkgmd's own generated meta files; (2) two topics whose names differ
only in letter case (e.g. Foo/foo) write to the same path on a
case-insensitive filesystem (macOS/Windows default), so the second
write silently clobbers the first. Both are rare but corrupt the output
silently if left unchecked, so they abort the whole build with a clear
message rather than producing an incomplete reference.
Usage
.check_topic_name_collisions(topics)
Arguments
topics |
List of parsed topic lists. |
Value
NULL, invisibly. Aborts via cli::cli_abort() if needed.
Remove every .md file already in output_dir
Description
Called before (re-)writing a build's output when overwrite = TRUE, so
that a topic renamed or removed from the package since the last build
doesn't leave a stale, orphaned .md file behind - without this, the
directory only ever grows and drifts from what the current package
actually documents.
Usage
.clear_generated_output(output_dir)
Arguments
output_dir |
Target directory. |
Value
NULL, invisibly.
Pick a backtick fence long enough to safely wrap some code
Description
A fixed ``` fence breaks if the code itself contains a run of 3+
backticks (e.g. code that documents Markdown syntax, or nested code
blocks) - the fence closes prematurely at the first embedded run. Per
CommonMark, a fence must be longer than the longest backtick run
appearing in its content.
Usage
.code_fence(code)
Arguments
code |
The code the fence will wrap. |
Value
A string of backticks, at least 3 and at least one longer than
the longest backtick run found in code.
Collapse a prose string to a single line
Description
Rd source hard-wraps prose (\\description{}, \\item{}{} descriptions,
etc.) at some column width for source readability - those line breaks
aren't semantically meaningful. Left in place, they break Markdown
tables, since a table cell can't contain a literal newline: text after
the break renders as plain content outside the table and everything
below it in the same table is broken too. Use this on prose fields
only - never on \\usage{}/\\examples{} content, where newlines are
real code line breaks that must be preserved.
Usage
.collapse_whitespace(text)
Arguments
text |
A string, typically already passed through |
Value
text with all runs of whitespace (including newlines)
collapsed to a single space, trimmed.
Decide whether a topic counts as "private" (not part of the public API)
Description
A topic is private when it has at least one callable name (see
.callable_candidates() - this excludes datasets/concept pages, which
aren't in NAMESPACE's export() list either but aren't private
helpers) and none of its callable names are in exported_names. When
exported_names is NULL (export status couldn't be determined - see
.read_exported_names()), every topic is treated as public, matching
behavior from before this feature existed.
Usage
.compute_is_private(name, aliases, usage, exported_names)
Arguments
name |
Topic name. |
aliases |
Character vector of the topic's aliases. |
usage |
The topic's |
exported_names |
Character vector of the package's exported
names, or |
Value
TRUE/FALSE.
Decode the handful of HTML entities that show up in Rd \\out{} blocks
Description
Not a general HTML-entity decoder - just the small set that appears in
practice (roxygen2/pandoc-generated #> output markers and escaped
quotes/ampersands). & is decoded last so a literal &lt; in
the source doesn't get double-decoded into <.
Usage
.decode_html_entities(text)
Arguments
text |
A string. |
Value
text with entities decoded.
Detect a class="sourceCode <lang>" marker in raw HTML, if present
Description
Detect a class="sourceCode <lang>" marker in raw HTML, if present
Usage
.detect_source_code_lang(html)
Arguments
html |
Raw HTML string. |
Value
The language string (e.g. "r"), or NULL if not found.
Escape literal pipe characters for use in a GFM table cell
Description
A raw | inside a table cell terminates the cell early and shifts every
column after it - breaks the whole row (and often the rows after it, if
the shift produces an odd number of remaining |s). Rd prose can
contain a literal | (e.g. documenting an OR condition or \\tabular{}
cell content), so cell-rendering call sites must escape it before
interpolating into a | cell | cell | row.
Usage
.escape_table_pipe(text)
Arguments
text |
A string destined for a table cell. |
Value
text with every | replaced by \\|.
Get the children of the first Rd node matching a tag
Description
A convenience wrapper around .get_rd_tag() for the common case of a
singleton top-level tag (e.g. a topic's one \\description{} block):
unwraps the match and returns its children directly, ready to hand to
a renderer like .rd_to_block_md(). Contrast with .get_rd_tag()
itself, which returns the matching node(s), not their contents.
Usage
.first_rd_tag_children(rd, tag)
Arguments
rd |
An |
tag |
Tag name including the leading backslash, e.g. |
Value
The children of the first match, or an empty list if there is no match.
Extract all child tags of a given type from an Rd object
Description
tools::Rd_db() (and tools::parse_Rd()) return objects of class Rd
whose elements carry a Rd_tag attribute (e.g. "\\arguments",
"\\description"). This helper will filter an Rd object (or a list of
Rd fragments) down to the direct children matching a given tag.
Usage
.get_rd_tag(rd, tag)
Arguments
rd |
An |
tag |
Tag name including the leading backslash, e.g. |
Value
List of matching Rd fragments (possibly empty).
Detect a block-level \\if{html}{\out{...}} HTML code block, if x is one
Description
\\if{html}{...} / \\ifelse{html}{...}{...} are usually simple inline
conditionals (handled by .rd_inline_piece()), but roxygen2's Markdown
support compiles fenced code blocks (```r) into
\\if{html}{\out{<div class="sourceCode r">...}} - a real multi-line
code block authored as raw HTML passthrough, which needs block-level
handling (a fenced Markdown code block), not inline text. This detects
that specific shape: an \\if/\\ifelse node whose html-condition
branch has an \\out{} node as a direct child.
Usage
.if_out_html_block(x)
Arguments
x |
An Rd node (only |
Value
The rendered fenced code block (a string), or NULL if x
isn't this specific shape (the caller should treat it as ordinary
inline/other content instead).
Wrap text in an inline code span, widening delimiters to avoid collision
Description
A single backtick pair (`x`) breaks if x itself contains a
backtick - e.g. base's Quotes topic documents the backtick character
itself as an alias, and naively wrapping it as ' produces a
bare 3-backtick run that reads as a fence, not a code span (the same
failure mode .code_fence() guards against for fenced blocks). Per
CommonMark, an inline code span's delimiter must be longer than the
longest backtick run in its content, and content starting or ending
with a backtick needs a single space of padding on that side so the
delimiter doesn't visually fuse with it.
Usage
.inline_code_span(text)
Arguments
text |
The text to wrap (typically an alias, name, or other short
Rd-sourced token, not a full code block - see |
Value
A single string: text wrapped in backticks.
Detect whether package refers to a local package source directory
Description
Used to dispatch between the installed-package path (tools::Rd_db())
and the development-package path (reading man/*.Rd directly, or
falling back to a live roxygen2::parse_package() parse). A path is
treated as a package source directory if it exists on disk and contains
a DESCRIPTION file.
Usage
.is_source_path(package)
Arguments
package |
Value passed by the caller to |
Value
TRUE if package is a local source directory, FALSE
otherwise (i.e. it should be treated as an installed package name).
Render one Rd child that may be a bare leaf or a list, as raw text
Description
Render one Rd child that may be a bare leaf or a list, as raw text
Usage
.leaf_or_list_raw_text(part)
Arguments
part |
An Rd child - a bare character leaf or a list. |
Value
A string.
Render one Rd child that may be a bare leaf or a list, as inline text
Description
Render one Rd child that may be a bare leaf or a list, as inline text
Usage
.leaf_or_list_text(part)
Arguments
part |
An Rd child - a bare character leaf (e.g. one argument of
|
Value
A string.
Coerce a single (non-list) Rd node to a scalar string
Description
Almost always as.character(x) already returns a length-1 string. The
exception is "USERMACRO"-tagged nodes (from Rd macros like \\I{}),
whose value is a placeholder token ("#1") followed by the macro's
substituted argument (e.g. c("#1", "bar")). tools::parse_Rd()
already duplicates that substituted text into the following sibling
TEXT node as a fallback for consumers that don't understand
USERMACRO (verified against tools::Rd2txt()'s own output) - so a
USERMACRO node contributes nothing here to avoid rendering the text
twice. Any other, unanticipated multi-value leaf is collapsed rather
than erroring.
Usage
.leaf_rd_text(x)
Arguments
x |
A non-list Rd leaf node. |
Value
A single string.
Decide whether one name looks callable given a topic's usage text
Description
Decide whether one name looks callable given a topic's usage text
Usage
.looks_callable(name, usage)
Arguments
name |
A single candidate name. |
usage |
The topic's non-empty |
Value
TRUE/FALSE.
Collapse a block of prose to paragraphs, preserving paragraph breaks
Description
Rd source hard-wraps prose at some column width for source readability
(see .collapse_whitespace() for the single-line equivalent used in
table cells) - those line breaks aren't semantically meaningful and are
joined back into flowing text. A blank line in the source (2+
consecutive newlines), however, is a meaningful paragraph break and is
preserved as exactly one blank line.
Usage
.normalize_block_whitespace(text)
Arguments
text |
A string, typically already passed through |
Value
text with hard-wrapped single newlines joined into spaces and
paragraph breaks normalized to a single blank line, trimmed.
Parse export(...) directives out of a NAMESPACE file
Description
Parse export(...) directives out of a NAMESPACE file
Usage
.parse_namespace_exports(ns_file)
Arguments
ns_file |
Path to a |
Value
Character vector of exported names (possibly empty), with
surrounding quotes/backticks stripped (export("[<-.foo"),
export(`%>%`)).
Extract all \\alias{} entries of an Rd object
Description
Extract all \\alias{} entries of an Rd object
Usage
.parse_rd_aliases(rd)
Arguments
rd |
An |
Value
Character vector of aliases.
Extract \\arguments{} as a list of name/description pairs
Description
Each \\item{name}{description} inside \\arguments{} becomes one row
in the rendered arguments table. A well-formed \\item always has 2
children (name, description); one with fewer is malformed Rd, but
skipped rather than erroring out the whole topic - a rendering gap for
one argument shouldn't take down documentation for everything else.
Usage
.parse_rd_arguments(rd)
Arguments
rd |
An |
Value
List of lists with fields name, description.
Extract the \\examples{} block as a code string
Description
Uses .rd_to_raw_text() (Tier 1), not .rd_to_text() - examples are R
code, so exact spacing/indentation must be preserved verbatim rather
than collapsed.
Usage
.parse_rd_examples(rd)
Arguments
rd |
An |
Value
String with the example code (no backtick fence), empty if no examples are present.
Parse one in-memory roxygen2 RdFile topic into an Rd object
Description
format(topic) renders the RdFile back to .Rd source text, which
tools::parse_Rd() only accepts from a connection, not a plain string -
hence the textConnection() round-trip. The connection is explicitly
closed afterwards; left open, each call leaks one and eventually
surfaces as an R "closing unused connection" warning once the
finalizer runs.
Usage
.parse_rd_from_roxygen_topic(topic)
Arguments
topic |
A single roxygen2 |
Value
An Rd object, as tools::parse_Rd() would return for a file
on disk.
Resolve a topic's name, preferring its \\name{} tag
Description
The Rd database's list name (rd_name, typically "function_name.Rd")
is usually identical to the topic's own \\name{} tag, but not always -
e.g. dplyr's group_by_prepare.Rd declares \\name{distinct_prepare}.
The \\name{} tag is what ?topic and cross-references actually
resolve against, so it takes priority; the filename-derived name is only
a fallback for the (invalid, but not worth hard-failing on) case where
\\name{} is missing.
Usage
.parse_rd_name(rd, rd_name)
Arguments
rd |
An |
rd_name |
Name of the Rd entry (list name from the Rd database). |
Value
A single string.
Extract all \\section{title}{body} blocks of an Rd object
Description
Unlike the other top-level tags (\\details{}, \\value{}, ...),
\\section{} can appear multiple times per topic, each with its own
title - e.g. dplyr's mutate.Rd has separate "Useful mutate functions",
"Grouped tibbles", and "Methods" sections.
Usage
.parse_rd_sections(rd)
Arguments
rd |
An |
Value
A list of list(title, body) pairs, body rendered as Tier 3
Markdown via .rd_to_block_md().
Parse a single Rd object into an internal list
Description
Converts an Rd object (from either tools::Rd_db() or
tools::parse_Rd()) into a flat list consumable by render_md.R.
title/arguments[].name/arguments[].description/value/seealso
are single-line-safe text via .rd_to_text() (Tier 2 - table cells and
the page title can't contain a literal newline). description/
details/format/note/source/references/author/each section's
body are full multi-paragraph Markdown via .rd_to_block_md() (Tier 3),
since they commonly contain \\itemize{}, \\describe{},
\\subsection{}{}, etc. that Tier 2 would flatten into unreadable prose.
Usage
.parse_rd_topic(rd, rd_name, package, exported_names = NULL)
Arguments
rd |
A single |
rd_name |
Name of the Rd entry (list name from the Rd database,
typically |
package |
Name of the package this topic belongs to. |
exported_names |
Character vector of the package's exported
names (see |
Value
A list with fields name, package, aliases, is_private,
title, description, usage, arguments, details, sections,
format, value, examples, seealso, note, source,
references, author.
Extract the \\usage{} block as preformatted code
Description
Uses .rd_to_raw_text() (Tier 1), not .rd_to_text() - usage is R
code, so exact spacing/indentation (e.g. across multi-line signatures)
must be preserved verbatim rather than collapsed.
Usage
.parse_rd_usage(rd)
Arguments
rd |
An |
Value
String with the usage code (no backtick fence).
Render one Rd child node as an inline Markdown piece
Description
Dispatches to .leaf_rd_text() for non-list (leaf) nodes; special-cases
\\method{generic}{class} (rendered as generic.class, the convention
used in \\usage{} for S3 method signatures) and the common inline
formatting tags (\\code, \\emph, \\href, ...). Nodes that are
lists but not a recognized tag (e.g. \\item, or an unnamed wrapper
list) are treated as transparent containers via a plain recursive
.rd_to_text() call.
Usage
.rd_inline_piece(x, in_code = FALSE)
Arguments
x |
A single Rd child node (leaf or list). |
in_code |
Whether |
Value
A string fragment.
Render one Rd child node as raw text (no whitespace/inline formatting)
Description
Render one Rd child node as raw text (no whitespace/inline formatting)
Usage
.rd_raw_piece(x)
Arguments
x |
A single Rd child node (leaf or list). |
Value
A string fragment.
Render a list of Rd block-level children as multi-paragraph Markdown
Description
This is "Tier 3" of pkgmd's text extraction (see .rd_to_text() for
Tier 2 and .rd_to_raw_text() for Tier 1): unlike those, which produce
single-line-safe text, .rd_to_block_md() walks Rd content and produces
real multi-line Markdown — paragraphs, bullet/numbered lists, nested
subsection headings, and fenced code blocks — instead of flattening
everything to a single run of prose. Use it for \\description{} and
\\details{}, which commonly contain \\itemize{}, \\enumerate{},
\\subsection{}{}, and \\preformatted{}.
Usage
.rd_to_block_md(rd, heading_level = 3)
Arguments
rd |
A list of Rd fragments. |
heading_level |
Heading depth for any |
Details
rd is expected to already be a plain list of block-level children
(e.g. from .first_rd_tag_children() or, for a \\subsection{}{}
body, subsection_node[[2]] directly) — not wrapped in an outer
singleton list the way .get_rd_tag() returns matches.
Value
A single Markdown string with blank lines between block segments.
Flatten an Rd fragment to raw, unnormalized text
Description
Like .rd_to_text(), but performs no whitespace normalization
(beyond the .leaf_rd_text() USERMACRO handling) and applies no
inline Markdown formatting. This is "Tier 1" of pkgmd's text
extraction: use it for code content (\\usage{}, \\examples{},
\\preformatted{}) where exact spacing and indentation must survive
verbatim. Callers should trimws() the result themselves to drop
leading/trailing edges without touching internal formatting.
Usage
.rd_to_raw_text(rd)
Arguments
rd |
An Rd fragment or a list of Rd fragments. |
Details
Also special-cases \\method{generic}{class} the same way
.rd_inline_piece() does (rendered as generic.class) - \\usage{}
is the main place this construct appears, and it's Tier 1 content.
Value
A single string with internal whitespace untouched.
Flatten an Rd text fragment into a single string, with inline Markdown
Description
Rd fragments are themselves nested lists of text and tag nodes (e.g. for
\\code{} or \\link{}). .rd_to_text() recursively reduces a fragment
to a single-line-safe string, normalizing horizontal whitespace, and
renders common inline Rd tags as their Markdown equivalent (\\code{}
-> `x`, \\emph{} -> _x_, \\href{}{} -> [text](url), etc. -
see .rd_inline_piece()) rather than silently stripping them to plain
text.
Usage
.rd_to_text(rd, in_code = FALSE)
Arguments
rd |
An Rd fragment or a list of Rd fragments. |
in_code |
Whether this call is already nested inside a code-styled
wrapper ( |
Details
Some Rd macros (e.g. \\I{}) are parsed into a leaf node tagged
"USERMACRO" whose value is a character vector like c("#1", "bar") -
a #N placeholder token followed by the actual substituted text, rather
than a plain scalar. Placeholder tokens are dropped so the substituted
text is used (see .leaf_rd_text()).
This is "Tier 2" of pkgmd's text extraction (single-line inline). Use
.rd_to_raw_text() ("Tier 1") instead for code content
(\\usage{}/\\examples{}/\\preformatted{}) where exact whitespace
must survive, and .rd_to_block_md() ("Tier 3", in render_rd_block.R)
for rich multi-paragraph content (\\description{}/\\details{}) that
may contain lists, subsections, or code blocks.
Value
A single, trimmed string.
Determine a package's exported names, for flagging private topics
Description
Dispatches based on .is_source_path(), mirroring .read_rd_db().
Usage
.read_exported_names(package)
Arguments
package |
Name of an installed package, or a path to a local package source directory. |
Value
Character vector of exported names, or NULL if export status
couldn't be determined (see the installed/source variants for when
that happens) - callers should treat NULL as "unknown", not as "no
exports".
Determine an installed package's exported names via its namespace
Description
getNamespaceExports() is the only fully correct way to get this list -
it's what R itself resolves export()/exportPattern()/etc. down to,
so hand-parsing the NAMESPACE file here would mean reimplementing
that resolution (including regex exportPattern() matching against the
namespace's actual contents). The cost is that it loads the package's
namespace if not already loaded - unlike .read_rd_db_installed()'s
existence check, this is unavoidable here since the actual export list
can only come from the loaded namespace itself. Wrapped in tryCatch()
so a package that fails to load degrades to "unknown" (NULL) rather
than aborting the whole reference build - include_private simply has
nothing to filter on in that case, same as before this feature existed.
Usage
.read_exported_names_installed(package)
Arguments
package |
Name of an installed package. |
Value
Character vector of exported names, or NULL if the namespace
couldn't be loaded.
Determine a source package's exported names from its NAMESPACE file
Description
Parses explicit export(name) directives (line-per-symbol, the
convention roxygen2::document() writes) rather than loading the
package - a dev package is often not in a loadable state. Doesn't
resolve exportPattern() regexes (rare in roxygen2-generated
NAMESPACE files; common only in hand-written classic ones) - a topic
exported only via such a pattern would be misflagged as private, a
known, minor limitation.
Usage
.read_exported_names_source(path)
Arguments
path |
Path to a local package source directory. |
Value
Character vector of exported names, or NULL if no
NAMESPACE file exists yet (e.g. a dev package that has never run
devtools::document()) - export status is simply unknown then, not
"nothing is exported".
Resolve package name and version, for either an installed or a source package
Description
For an installed package, name/version come from utils::packageVersion().
For a development package, they are read from the DESCRIPTION file in
the source directory via read.dcf() - this deliberately avoids
requiring installation.
Usage
.read_package_metadata(package)
Arguments
package |
Name of an installed package, or a path to a local package source directory. |
Value
A list with fields name, version.
Read the Rd documentation of a package, installed or in development
Description
Dispatches based on .is_source_path(): an installed package name goes
through .read_rd_db_installed(), while a path to a local package
source directory goes through .read_rd_db_source().
Usage
.read_rd_db(package)
Arguments
package |
Name of an installed package, or a path to a local
package source directory (detected via a |
Value
A named list of Rd objects (one entry per documented topic).
Read the Rd database of an installed package
Description
Thin wrapper around tools::Rd_db() that checks the package is actually
installed first and raises a helpful error otherwise.
Usage
.read_rd_db_installed(package)
Arguments
package |
Name of an installed package, e.g. |
Value
A named list of Rd objects, as returned by tools::Rd_db().
Read the Rd documentation of a package straight from its source tree
Description
Used for development packages that have not been installed. Reads every
man/*.Rd file in path via tools::parse_Rd(). If man/ is missing
or empty, falls back to a live, in-memory parse via
roxygen2::parse_package(path) — only attempted if the roxygen2
namespace is available; otherwise raises an error pointing the user at
devtools::document().
Usage
.read_rd_db_source(path)
Arguments
path |
Path to a local package source directory (contains a
|
Value
A named list of Rd objects, in the same shape as
tools::Rd_db() would return for an installed package.
Render a compact "Aliases:" line for aliases not already in the heading
Description
topic$aliases commonly includes the topic's own name plus every name
already surfaced via .render_topic_heading() (e.g. S3 method aliases,
or the callable names of a multi-function topic) - repeating those here
would be redundant. Only genuinely additional aliases are listed, so
e.g. lag() stays findable even though the topic file itself is named
lead-lag.md.
Usage
.render_aliases_line(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the arguments table, if present
Description
Cell content is escaped via .escape_table_pipe() - argument
descriptions are free-form prose that may legitimately contain a
literal | (e.g. "TRUE or FALSE"), which would otherwise terminate the
cell early and break the rest of the table.
Usage
.render_arguments_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the author section, if present
Description
Render the author section, if present
Usage
.render_author_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the description section, if present
Description
Render the description section, if present
Usage
.render_description_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the details section, if present
Description
topic$details is already multi-paragraph Markdown produced by
.rd_to_block_md() (lists, subsections, code blocks may all be
present), so it's interpolated as-is rather than wrapped as a single
line.
Usage
.render_details_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the examples section as a <details> block, if present
Description
Render the examples section as a <details> block, if present
Usage
.render_examples_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the format section, if present
Description
\\format{} is where dataset topics (e.g. starwars, storms)
describe their variables - almost always via \\describe{}, handled as
Tier 3 Markdown like \\details{}.
Usage
.render_format_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render every topic into a single Markdown string
Description
The shared core of _full_reference.md (written by
.write_full_reference_file()) and get_documentation() (returned
directly, no file written) - both want the exact same content, one to
disk and one in memory.
Usage
.render_full_reference_md(topics, package, version)
Arguments
topics |
List of parsed topic lists. |
package |
Package name. |
version |
Package version. |
Value
A single Markdown string.
Render the README.md index file for a reference output directory
Description
Each row's "Topic" cell uses the same heading identifier as the topic's
own file (see .render_topic_heading()) - callable names get (),
datasets/concept pages don't - plus the same _(private)_ marker (see
.render_private_badge()) if applicable. Aliases beyond that (e.g.
lag() alongside a lead-lag topic named after both) get their own
column so they stay findable even though they don't have their own
file/row - see .render_aliases_line() for the same logic applied to
the topic file itself. Title and alias cells are pipe-escaped (see
.escape_table_pipe()) since both are free-form text that could
contain a literal |.
Usage
.render_index_md(topics, package, version)
Arguments
topics |
List of parsed topic lists. |
package |
Package name. |
version |
Package version. |
Value
A single Markdown string (contents of README.md).
Render the note section, if present
Description
Render the note section, if present
Usage
.render_note_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render a _(private)_ marker for topics outside the public API
Description
Render a _(private)_ marker for topics outside the public API
Usage
.render_private_badge(topic)
Arguments
topic |
Parsed topic list. |
Value
" _(private)_", or "" if topic$is_private isn't TRUE.
Render a \\describe{} node as a Markdown definition-style bullet list
Description
Unlike \\itemize{}/\\enumerate{}, whose \\item is a bare marker,
\\describe{}'s \\item{term}{description} is a proper 2-child
container - the same shape as \\arguments{}'s \\item{name}{desc}.
Markdown has no widely-supported definition-list syntax on GitHub/Gitea,
so each entry is rendered as - **term**: description.
Usage
.render_rd_describe(x)
Arguments
x |
A |
Value
A Markdown string, one - **term**: description line per item.
Render an \\enumerate{} node as a Markdown numbered list
Description
Each item is collapsed to a single line - see .render_rd_itemize()
for why.
Usage
.render_rd_enumerate(x)
Arguments
x |
An |
Value
A Markdown string, one N. item line per item.
Render one \\figure{file}{options} node as its alt text, if any
Description
We can't usefully embed a remote image in Markdown output meant to
render in GitHub/Gitea, so \\figure{} is reduced to whatever alt text
its options argument specifies (options: alt='...'), or dropped
entirely if there is none. In practice \\figure{} is almost always
reached through \\ifelse{html}{\figure{...}}{...} (e.g. lifecycle
badges), where the "no" branch already supplies a clean plain-text
fallback and this function is never called - it exists for the rarer
case of a bare \\figure{} with no such alternative.
Usage
.render_rd_figure_alt(x)
Arguments
x |
A |
Value
The alt text, or "".
Render an \\itemize{} node as a Markdown bullet list
Description
Each item is collapsed to a single line via .collapse_whitespace(),
same as a table cell. Rd source hard-wraps item text across lines for
readability; left as raw newlines, a continuation line isn't reliably
recognized as part of the same list item across Markdown renderers (and
can be actively misread as a new block - a line that happens to start
with a digit and . , or contains something like <table>, is
ambiguous or gets parsed as literal HTML). GitHub/Gitea compatibility
requires each bullet to be self-contained on one line.
Usage
.render_rd_itemize(x)
Arguments
x |
An |
Value
A Markdown string, one - item line per item.
Render an \\out{} node's raw HTML as a fenced Markdown code block
Description
Strips HTML tags and decodes common entities rather than attempting to
actually parse the HTML - good enough to turn e.g.
<div class="sourceCode r">mutate(x) #> result</div> into readable
```r mutate(x) #> result ``` , not pixel-perfect HTML rendering (which Markdown can't do
anyway).
Usage
.render_rd_out_as_code(out_node)
Arguments
out_node |
An |
Details
roxygen2's Markdown fenced-code support compiles a single fenced block
into three separate top-level nodes: an opening
\\if{html}{\out{<div class="sourceCode r">}}, the actual code as a
plain \\preformatted{} (portable across all output formats, already
handled by .render_rd_preformatted()), and a closing
\\if{html}{\out{</div>}}. The opening/closing wrapper nodes have no
real content once their tags are stripped - returning NULL instead of
an empty fence for those lets the caller skip them entirely rather than
emitting spurious empty ``` blocks around the real one.
Value
A fenced Markdown code block, or NULL if it has no content
once HTML tags are stripped (a pure wrapper tag, not real code).
Render a \\preformatted{} node as a fenced Markdown code block
Description
Uses .rd_to_raw_text() (Tier 1) rather than .rd_to_text(), since
preformatted content's exact spacing/indentation must be preserved
verbatim. No language tag is added to the fence, since preformatted
content isn't necessarily R code.
Usage
.render_rd_preformatted(x)
Arguments
x |
A |
Value
A fenced (```) Markdown code block.
Render a \\subsection{title}{body} node as a Markdown heading + body
Description
The title is collapsed to a single line via .collapse_whitespace() -
an ATX heading (### Title) can't contain a literal newline either.
Usage
.render_rd_subsection(x, heading_level)
Arguments
x |
A |
heading_level |
Heading depth for this subsection's own title. |
Value
A Markdown string: the heading, a blank line, then the
recursively rendered body (nested subsections increase
heading_level by 1).
Render a \\tabular{spec}{body} node as a Markdown table
Description
spec is a column-alignment string like "ll" (2 left-aligned
columns) or "lcr" (left, center, right) - l/c/r map to the
corresponding Markdown table alignment marker; anything else defaults
to left. spec is filtered down to [lcr] characters before use, since
the raw Rd source can carry stray whitespace/newlines around the spec
that would otherwise shift the character-to-column mapping. Cell text
is also pipe-escaped (see .escape_table_pipe()) - a literal | in
cell content would otherwise break the row. Rows that are entirely
blank once collapsed (e.g. a spacer
\\cr with nothing between it and the next, or trailing whitespace
after the last real row) are dropped. The first surviving row becomes
the Markdown table's header, since GFM tables require one and Rd's
\\tabular{} doesn't distinguish a header row - this matches how such
tables are typically authored in practice (a label row followed by
data rows).
Usage
.render_rd_tabular(x)
Arguments
x |
A |
Value
A Markdown table string, or "" if the table has no non-blank
rows.
Render the references section, if present
Description
Render the references section, if present
Usage
.render_references_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render every \\section{title}{body} block, if any
Description
A topic can have any number of these (see .parse_rd_sections()), each
with its own title - e.g. dplyr's mutate.Rd has "Useful mutate
functions", "Grouped tibbles", and "Methods". Each renders as its own
## Title heading, in source order.
Usage
.render_sections(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the related-functions section, if present
Description
Render the related-functions section, if present
Usage
.render_seealso_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the source section, if present
Description
Render the source section, if present
Usage
.render_source_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render a topic's heading identifier (the part before " - Title")
Description
Renders callable names as `name()` (joined with a middle dot when a
topic documents more than one, e.g. lead() / lag()), or the bare
topic name with no parentheses for non-callable topics (datasets,
concept/package-overview pages) - see .callable_names().
Usage
.render_topic_heading(topic)
Arguments
topic |
Parsed topic list. |
Value
A Markdown string fragment.
Render a parsed Rd topic as a Markdown string
Description
Produces the full contents of a single function's .md file, following
the output format defined in CLAUDE.md (title line, arguments table,
return value, collapsible examples, related functions, footer). Both the
R code (comments, roxygen docs) and the rendered Markdown output are in
English, per project convention.
Usage
.render_topic_md(topic, version, footer = TRUE)
Arguments
topic |
A parsed topic list, as produced by |
version |
Version string of the documented package. |
footer |
Whether to append the "Generated by pkgmd" footer with its
relative links. Set to |
Value
A single Markdown string (the complete file contents).
Render the usage section, if present
Description
Render the usage section, if present
Usage
.render_usage_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Render the return-value section, if present
Description
Render the return-value section, if present
Usage
.render_value_section(topic)
Arguments
topic |
Parsed topic list. |
Value
Markdown string, possibly empty.
Resolve output_dir, applying the source-package-only default
Description
"md-docs" is a reasonable default location next to a package's own
source (the common build_reference(".") workflow), but there's no
similarly obvious spot for an installed package's output - defaulting
there too could scatter generated docs into an unexpected/unwanted
location relative to whatever directory the user happened to be in.
So the default only applies when package resolves to a local source
directory; otherwise a missing output_dir is a clear, actionable
error rather than a surprising write location.
Usage
.resolve_output_dir(package, output_dir)
Arguments
package |
As passed to |
output_dir |
As passed to |
Value
The resolved output_dir string.
Make a function/topic name safe for use as a filename
Description
Replaces characters that are problematic in filenames (e.g. %, <-,
/) with URL encoding. A leading . - the standard R convention for
internal helpers (.get_rd_tag, .rd_to_text, ...), and exactly what
include_private surfaces - gets a dot- prefix instead (mirroring
roxygen2::document()'s own man/dot-*.Rd convention): a literal
leading dot makes the file invisible to list.files() (default
all.files = FALSE), Finder, and plain ls, which would make a
documented-and-correctly-flagged-private topic file look undocumented
even though it exists on disk. Otherwise-plain alphanumeric names
(including any remaining internal ./_) are returned unchanged.
Usage
.safe_filename(name)
Arguments
name |
Topic or function name. |
Value
Filesystem-safe string without file extension.
Split the children of an \\itemize{}/\\enumerate{} node into items
Description
Unlike \\arguments{}'s \\item{name}{description} (a proper 2-child
container), \\itemize{}/\\enumerate{}'s \\item is a bare marker
with no children of its own — an item's actual content is its
following sibling nodes, up to the next \\item marker (or the end
of the list). Content before the first \\item (leading whitespace) is
discarded.
Usage
.split_rd_items(children)
Arguments
children |
The children of an |
Value
A list of items, each itself a list of Rd fragments.
Split one table row's children into cells
Description
Split one table row's children into cells
Usage
.split_rd_table_cells(row_children)
Arguments
row_children |
One row from |
Value
A list of cells, each itself a list of Rd fragments.
Split the children of a \\tabular{}{} node's body into rows
Description
Like .split_rd_items() for \\itemize{}, \\cr is a bare row-separator
marker (0 children of its own) rather than a container. Unlike
\\item, content before the first \\cr is significant (it's the
first row's content, not discardable leading whitespace) - \\tabular{}
has no separate per-row start marker the way \\itemize{}'s \\item
doubles as both.
Usage
.split_rd_table_rows(children)
Arguments
children |
The children of a |
Value
A list of rows, each itself a list of Rd fragments (to be
further split into cells by .split_rd_table_cells()).
Strip HTML tags from a string
Description
Strip HTML tags from a string
Usage
.strip_html_tags(html)
Arguments
html |
Raw HTML string. |
Value
html with every <...> tag removed.
Write _full_reference.md (every topic in a single file)
Description
Meant for the Claude Code inject via
@docs/reference/_full_reference.md. The underscore prefix sorts the
file alphabetically to the top of the directory and signals that it is
a generated meta file. Each topic section is rendered with
footer = FALSE - the per-topic "Generated by pkgmd / Back to index"
footer and its relative links only make sense in a standalone file, not
repeated after every one of potentially hundreds of sections here.
Usage
.write_full_reference_file(topics, package, version, output_dir)
Arguments
topics |
List of parsed topic lists. |
package |
Package name. |
version |
Package version. |
output_dir |
Target directory. |
Value
Path to the written file, invisibly.
Write the README.md index file
Description
Write the README.md index file
Usage
.write_index_file(topics, package, version, output_dir)
Arguments
topics |
List of parsed topic lists. |
package |
Package name. |
version |
Package version. |
output_dir |
Target directory. |
Value
Path to the written file, invisibly.
Write the Markdown file for a single topic
Description
Write the Markdown file for a single topic
Usage
.write_topic_file(topic, version, output_dir)
Arguments
topic |
Parsed topic list. |
version |
Package version. |
output_dir |
Target directory. |
Value
Path to the written file, invisibly.
Write text to a file as UTF-8, regardless of the platform's locale
Description
Plain writeLines(x, path, useBytes = TRUE) relies on every string in
x already carrying a UTF-8 marking that R's useBytes path preserves
as-is - true on macOS/Linux where the native encoding already is UTF-8,
but fragile on Windows locales where a non-UTF-8 native encoding can
cause silent mis-encoding. Opening the connection in raw binary mode
("wb") and explicitly marking the text as UTF-8 first
(enc2utf8()) writes the intended bytes unconditionally, independent
of the platform's native encoding, and also sidesteps any newline
translation "w" mode would otherwise apply.
Usage
.write_utf8_lines(text, path)
Arguments
text |
Character vector to write, one file line per element. |
path |
Destination file path. |
Value
path, invisibly.
Generate Markdown reference documentation for a package
Description
Reads the Rd documentation of a package - either installed, or a local
development package that has not been installed - renders it as
Markdown, and writes one .md file per function into output_dir.
Also produces a README.md (index table of all functions) and a
_full_reference.md (every function in one file, meant to be injected
as LLM context, e.g. via @docs/reference/_full_reference.md in Claude
Code).
Usage
build_reference(
package = ".",
output_dir = NULL,
overwrite = TRUE,
include_private = TRUE
)
Arguments
package |
Name of an installed package, or a path to a local
package source directory. Defaults to |
output_dir |
Target directory for the generated |
overwrite |
If |
include_private |
If |
Details
package accepts two kinds of input:
the name of an installed package, e.g.
"dplyr"- Rd data is read viatools::Rd_db();a path to a local package source directory, e.g.
"."or"../myotherpkg"(detected by the presence of aDESCRIPTIONfile at that path) - Rd data is read directly fromman/*.Rd, falling back to a liveroxygen2::parse_package()parse ifman/is missing or empty. This mode does not require the package to be installed.
Value
Invisibly, the path to output_dir.
Examples
## Not run:
# Installed package:
pkgmd::build_reference("dplyr", output_dir = "docs/reference")
# Development package, not installed - "." and "md-docs" are both
# defaults, so this is equivalent to just build_reference():
pkgmd::build_reference(".", output_dir = "md-docs")
# Public API only, no internal helpers:
pkgmd::build_reference("dplyr", output_dir = "docs/reference", include_private = FALSE)
## End(Not run)
Get a package's full reference documentation as a single string
Description
Returns the same Markdown content that build_reference() writes to
_full_reference.md - every topic rendered and concatenated into one
string - without writing anything to disk. Meant for programmatic
consumption, e.g. an LLM agent that wants a package's complete
reference injected directly into its context in one call.
Usage
get_documentation(package = ".", include_private = FALSE)
Arguments
package |
As in |
include_private |
If |
Value
A single string: the package's full reference documentation in Markdown.
Examples
## Not run:
# Installed package:
docs <- pkgmd::get_documentation("dplyr")
# Development package, not installed:
docs <- pkgmd::get_documentation(".")
## End(Not run)
Default for NULL values
Description
Default for NULL values
Usage
x %||% y
Arguments
x |
Value to check. |
y |
Fallback used when |
Value
x if not NULL, otherwise y.