|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What this is |
| 6 | + |
| 7 | +`cli` is an R package for building command line interfaces: semantic elements (headings, lists, alerts, paragraphs), CSS-like theming, ANSI colors/styles, progress bars, rich error/warning messages, and pluralization. It has both an R layer and a C layer (`src/`), and is a foundational dependency for much of the R ecosystem, so backward compatibility and correctness matter a lot. |
| 8 | + |
| 9 | +## Development commands |
| 10 | + |
| 11 | +This package has compiled C code, so you must recompile after editing anything in `src/`. Use the `uncovr` helpers (they handle compilation + instrumentation): |
| 12 | + |
| 13 | +```r |
| 14 | +uncovr::reload() # compile C code and (re)load the package |
| 15 | +uncovr::test() # run the test suite (testthat, edition 3) |
| 16 | +uncovr::document() # regenerate roxygen2 docs (man/*.Rd and NAMESPACE) |
| 17 | +``` |
| 18 | + |
| 19 | +To run R CMD check (set `NOT_CRAN` so tests that are skipped on CRAN still run): |
| 20 | + |
| 21 | +```r |
| 22 | +withr::with_envvar(c(NOT_CRAN = "true"), rcmdcheck::rcmdcheck()) |
| 23 | +``` |
| 24 | + |
| 25 | +Running a single test file or a single test: |
| 26 | + |
| 27 | +```r |
| 28 | +uncovr::test(filter = "keypress") # run tests/testthat/test-keypress.R |
| 29 | +``` |
| 30 | + |
| 31 | +Code is formatted with [air](https://posit-dev.github.io/air/) (see `air.toml`). A GitHub Action suggests formatting fixes on PRs. |
| 32 | + |
| 33 | +## Architecture |
| 34 | + |
| 35 | +### R / C split |
| 36 | + |
| 37 | +The semantic CLI, theming, and most formatting logic live in R (`R/`). Performance-sensitive and OS-level primitives live in C (`src/`): |
| 38 | + |
| 39 | +- ANSI/UTF-8/string-width handling (`ansi.c`, `utf8.c`, `width.c`-related, `charwidth.h`) |
| 40 | +- the VT100 parser (`vt.c`, `vtparse*.c`) used to interpret/strip terminal control sequences |
| 41 | +- the progress bar engine (`progress.c`, `progress-altrep.c`) — progress state is shared with R via an ALTREP |
| 42 | +- keypress reading (`keypress*.c`, split into `keypress-unix.c` / `keypress-win.c`) |
| 43 | +- hashing (`md5.c`, `sha1.c`, `sha256.c`, `xxhash*.c`) and `diff.c`, `glue.c` |
| 44 | + |
| 45 | +C entry points are registered in `src/init.c` via `.Call`. `RCC(...)` registers functions that use the **cleancall** mechanism (`cleancall.c/.h`) for C-level resource cleanup; plain `R_CallMethodDef` entries (e.g. `cli_keypress`) are registered the normal way. When you add a C function callable from R, register it in `init.c`. Header `inst/include/cli/progress.h` is the public C API other packages link against — treat changes to it as part of the package's external contract. |
| 46 | + |
| 47 | +### The "app" model |
| 48 | + |
| 49 | +CLI output flows through a stack of **app** objects, not direct printing. `start_app()` / `stop_app()` / `default_app()` (in `R/app.R`) manage a global app stack in `cliappenv$stack`. An app (`R/cliapp.R`) is a closure-based object (via `new_class`) holding the active themes, container stack, and output connection. The user-facing `cli_*` functions (e.g. `cli_h1`, `cli_alert`, `cli_ul`) emit a *condition* (a `cliMessage`) that the default app formats and prints. The internal counterparts are named `clii_*` (app methods) and `clii__*` (lower-level helpers). |
| 50 | + |
| 51 | +`cli({ ... })` (in `R/cli.R`) records multiple `cli_*` calls and emits them as one combined message, using the `cli.record` option and the `cli_recorded` registry. Themes are CSS-like selector/style rules matched against the container tree (`R/themes.R`, `R/simple-theme.R`, `R/containers.R`). |
| 52 | + |
| 53 | +### Inline markup and glue |
| 54 | + |
| 55 | +cli text supports interpreted string literals via glue, plus inline classes like `{.url ...}`, `{.file ...}`, `{.emph ...}`. Inline span handling is in `R/inline.R`; glue integration in `R/glue.R`; pluralization (`{?s}`, `{qty()}`) in `R/pluralize.R`. |
| 56 | + |
| 57 | +### Loading & global state |
| 58 | + |
| 59 | +`R/onload.R` sets up package-level mutable state in the `clienv` environment (PID, timers, progress/status registries, load time). Note the `.onLoad` cursor-restore finalizer and task callback. Timing is configurable via env vars (`CLI_TICK_TIME`, `CLI_SPEED_TIME`, `R_CLI_HIDE_CURSOR`). |
| 60 | + |
| 61 | +## Testing conventions |
| 62 | + |
| 63 | +- testthat edition 3 with snapshot tests. Snapshots live in `tests/testthat/_snaps/`. After an intentional output change, review `testthat::snapshot_review()` / accept with `testthat::snapshot_accept()`. |
| 64 | +- `tests/testthat/setup.R` flushes gcov coverage data on teardown (`clic__gcov_flush`) and cleans `.gcda` files — this supports the coverage-instrumented test runs. |
| 65 | +- `tests/testthat/helper.R` defines capture helpers central to testing output: `capture_msgs()`, `capture_cli_messages()` (catches `cliMessage` conditions), `capt()`, and `local_cli_config()`. Use these rather than asserting on raw printed output. |
| 66 | +- `progresstest/` and `progresstestcpp/` are small embedded test packages exercising the C progress API from C and C++. |
| 67 | +- Many tests are environment-sensitive (terminal width, number of ANSI colors, UTF-8 support, TTY detection). Tests pin these via `local_cli_config()` / options so they are reproducible off a real terminal. |
| 68 | + |
| 69 | +## Documentation |
| 70 | + |
| 71 | +- Roxygen2 (version 8.0.0) generates `man/` and `NAMESPACE` — never edit those by hand; edit the roxygen comments and run `uncovr::document()`. |
| 72 | +- Many `.Rd` examples use **asciicast** ` ```{asciicast ...} ` code chunks (rendered to SVG for the website) rather than plain `\examples`. Match the surrounding style when adding examples. |
| 73 | +- `README.md` is generated from `README.Rmd` (via `make` / `Makefile`) — edit the `.Rmd`. |
| 74 | +- Update `NEWS.md` for user-facing changes. |
0 commit comments