Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copilot Instructions

## Quarto rendering

Before requesting review on any change to a `.qmd` file:

1. **Always render the document locally** (`quarto render <file>` or `quarto preview`) and inspect the HTML output.
2. **Verify that math renders correctly** — open the rendered HTML and confirm MathJax typesetting is visible and correct.
3. **Do not push until rendering succeeds** and the output looks as intended.

This step is required because MathJax rendering failures are silent in R/knitr output and only visible in the browser.
65 changes: 65 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Quarto Preview

on:
pull_request:
types:
- opened
- reopened
- synchronize
- closed

jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
packages: |
any::knitr
any::rmarkdown
any::DT

- name: Render
uses: quarto-dev/quarto-actions/render@v2

- name: Deploy PR Preview
id: preview-step
uses: rossjrw/pr-preview-action@v1
with:
source-dir: ./_site/
action: ${{ github.event.action == 'closed' && 'remove' || 'deploy' }}
preview-branch: gh-pages
comment: "false"

- uses: marocchino/sticky-pull-request-comment@v2
if: steps.preview-step.outputs.deployment-action == 'deploy'
with:
header: pr-preview
recreate: true
message: |
[PR Preview Action](https://github.com/rossjrw/pr-preview-action) ${{ steps.preview-step.outputs.action-version }}
:---:
:rocket: View preview at ${{ steps.preview-step.outputs.preview-url }}

- uses: marocchino/sticky-pull-request-comment@v2
if: steps.preview-step.outputs.deployment-action == 'remove'
with:
header: pr-preview
recreate: true
message: |
[PR Preview Action](https://github.com/rossjrw/pr-preview-action) ${{ steps.preview-step.outputs.action-version }}
:---:
Preview removed because the pull request was closed.
39 changes: 39 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
on:
workflow_dispatch:
push:
branches: main

name: Quarto Publish

jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
packages: |
any::knitr
any::rmarkdown
any::DT

- name: Render
uses: quarto-dev/quarto-actions/render@v2

- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: _site/
clean-exclude: pr-preview/
force: false
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.quarto/
/_site/
/_freeze/
/*.deb
21 changes: 21 additions & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
project:
type: website

website:
title: "macros"
navbar:
left:
- href: index.qmd
text: Home
- href: macros-table.qmd
text: Macro Reference

format:
html:
theme: cosmo
toc: true

execute:
warning: false
message: false
freeze: auto
76 changes: 76 additions & 0 deletions index.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: "macros"
---

This repository contains LaTeX macros for mathematical notation used in statistical and regression modeling, originally from the [rme repository](https://github.com/d-morrison/rme).

## Contents

- `macros.qmd`: LaTeX macro definitions for use in Quarto documents
- [Macro Reference](macros-table.qmd): Searchable table of all macros

These macros provide convenient shorthand for common mathematical expressions used in:

- Probability and statistics
- Regression models (linear, logistic, Poisson, survival analysis)
- Maximum likelihood estimation
- Mathematical notation (vectors, matrices, derivatives, etc.)

## Using as a Git Submodule

### Adding the submodule to your project

From the root of your project repository, run:

```bash
git submodule add https://github.com/d-morrison/macros.git macros
git commit -m "Add macros submodule"
```

This will clone the `macros` repository into a `macros/` subdirectory and record it as a submodule.

### Cloning a project that already uses this submodule

When cloning a repository that includes this submodule, initialize and fetch the submodule content with:

```bash
git clone --recurse-submodules <your-repo-url>
```

Or, if you have already cloned the repository without `--recurse-submodules`:

```bash
git submodule update --init
```

### Updating the submodule

To pull the latest changes from this repository into your project:

```bash
git submodule update --remote macros
git commit -m "Update macros submodule"
```

### Including the macros in a Quarto document

In your Quarto document's YAML front matter, reference the `macros.qmd` file using the `include-in-header` or via `_quarto.yml`:

```yaml
---
include-in-header:
- macros/macros.qmd
---
```

Or, in a shared `_quarto.yml` configuration file:

```yaml
format:
html:
include-in-header:
- macros/macros.qmd
pdf:
include-in-header:
- macros/macros.qmd
```
163 changes: 163 additions & 0 deletions macros-table.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
title: "Macro Reference"
---

```{r}
#| label: embed-macros
#| echo: false
#| results: asis

# Embed macro definitions in a hidden Quarto fenced-div so pandoc processes
# the $$ block into a real MathJax display-math element (not raw HTML).
# Raw <div>...</div> blocks are passed through by pandoc without math processing.
lines <- readLines("macros.qmd", warn = FALSE)
lines <- lines[!grepl("^\\s*<!--", lines) & nzchar(trimws(lines))]

# MathJax v3 does NOT support \providecommand.
# Convert \providecommand{\name}[n]{body} to TeX primitive \def\name#1...#n{body},
# which always works in MathJax regardless of whether the command is already defined.
lines <- vapply(lines, function(line) {
m <- regmatches(
line,
regexec(
"^\\\\providecommand\\{\\\\([^}]+)\\}(?:\\[(\\d+)\\])?\\{(.*)\\}\\s*$",
line,
perl = TRUE
)
)[[1]]
if (length(m) == 4L && nzchar(m[1L])) {
nargs <- if (nzchar(m[3L])) as.integer(m[3L]) else 0L
args_str <- if (nargs > 0L) paste0("#", seq_len(nargs), collapse = "") else ""
return(paste0("\\def\\", m[2L], args_str, "{", m[4L], "}"))
}
line
}, character(1L), USE.NAMES = FALSE)

cat('::: {style="display:none"}\n$$\n')
cat(paste(lines, collapse = "\n"), "\n")
cat('$$\n:::\n')
```

This page provides a searchable table of all macros defined in `macros.qmd`.

```{r}
#| label: parse-macros
#| echo: false

parse_macros <- function(file_path = "macros.qmd") {
lines <- readLines(file_path, warn = FALSE)

# Remove HTML comments and blank lines
lines <- grep("^\\s*<!--", lines, invert = TRUE, value = TRUE)
lines <- grep("^\\s*$", lines, invert = TRUE, value = TRUE)

# Collect entries, joining multi-line definitions
entries <- character(0)
current <- ""
for (line in lines) {
if (grepl("^\\\\(def|providecommand|renewcommand)", line, perl = TRUE)) {
if (nzchar(current)) entries <- c(entries, current)
current <- line
} else {
current <- paste(current, trimws(line))
}
}
if (nzchar(current)) entries <- c(entries, current)

rows <- lapply(entries, function(entry) {
# Match \def\name{definition}
m <- regmatches(
entry,
regexec(
"^\\\\def\\\\([^{ \t]+)\\{(.*)\\}\\s*$",
entry,
perl = TRUE
)
)[[1]]
if (length(m) == 3 && nzchar(m[1])) {
return(data.frame(
name = paste0("\\", m[2]),
args = 0L,
definition = m[3],
stringsAsFactors = FALSE
))
}

# Match \providecommand or \renewcommand
m <- regmatches(
entry,
regexec(
paste0(
"^\\\\(?:providecommand|renewcommand)",
"\\{\\\\([^}]+)\\}(?:\\[(\\d+)\\])?\\{(.*)\\}\\s*$"
),
entry,
perl = TRUE
)
)[[1]]
if (length(m) == 4 && nzchar(m[1])) {
return(data.frame(
name = paste0("\\", m[2]),
args = if (nzchar(m[3])) as.integer(m[3]) else 0L,
definition = m[4],
stringsAsFactors = FALSE
))
}

NULL
})

do.call(rbind, Filter(Negate(is.null), rows))
}

macros <- parse_macros("macros.qmd")

# Build example usage strings based on argument count.
# 0-arg macros: \name | n-arg macros: \name{x}{y}...
arg_placeholders <- c("x", "y", "z", "w")
make_arg_str <- function(n) {
if (n == 0L) return("")
paste0("{", arg_placeholders[seq_len(min(n, 4L))], "}", collapse = "")
}
example_call <- paste0(
macros$name,
vapply(macros$args, make_arg_str, character(1L))
)

# Wrap text columns in <code> tags for monospace font (escape=FALSE lets them render as HTML)
code <- function(x) paste0("<code>", x, "</code>")
macros$name <- code(macros$name)
macros$definition <- code(macros$definition)

# Plain-text source column wrapped in <code>; escape=FALSE renders the tags
macros$example_source <- code(example_call)

# Rendered column: MathJax inline-math delimiters; DT renders with escape=FALSE
macros$example_rendered <- paste0("\\(", example_call, "\\)")
```

```{r}
#| label: macros-table
#| echo: false

DT::datatable(
macros[, c("name", "args", "definition", "example_source", "example_rendered")],
colnames = c("Macro", "# Args", "Definition", "Example Source", "Example"),
rownames = FALSE,
escape = FALSE,
filter = "top",
options = list(
pageLength = 25,
dom = "lftip",
# Re-typeset MathJax after each DataTables draw (pagination, search, etc.)
drawCallback = DT::JS(
"function(settings) {
if (window.MathJax && MathJax.typesetPromise) {
(MathJax.startup ? MathJax.startup.promise : Promise.resolve())
.then(function() { MathJax.typesetPromise(); });
}
}"
)
)
)
```
Loading