PHPackages                             markup-carve/carve-grammars - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. markup-carve/carve-grammars

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

markup-carve/carve-grammars
===========================

Syntax-highlighting grammars for the Carve markup language (TextMate grammar for Torchlight / phiki, plus Prism and highlight.js definitions)

v0.1.3(2w ago)0823—5.7%MITJavaScriptPHP &gt;=8.1CI passing

Since Jul 12Pushed 1mo agoCompare

[ Source](https://github.com/markup-carve/carve-grammars)[ Packagist](https://packagist.org/packages/markup-carve/carve-grammars)[ Docs](https://github.com/markup-carve/carve-grammars)[ RSS](/packages/markup-carve-carve-grammars/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)DependenciesVersions (5)Used By (0)

Carve Grammars
==============

[](#carve-grammars)

Grammars for the [Carve](https://github.com/markup-carve/carve) markup language:

- a **Tiptap** integration (editor kit + serializer) that turns a Tiptap/ProseMirror document into Carve markup;
- **Prism** and **highlight.js** syntax-highlighting grammars for rendering Carve source on the web;
- a **TextMate** grammar (`textmate/carve.tmLanguage.json`) for TextMate-based highlighters such as Shiki (used by VitePress).

Modeled on [djot-grammars](https://github.com/php-collective/djot-grammars), adapted to Carve's syntax. The Tiptap mark mapping mirrors `carve-php`'s `HtmlToCarve` converter; the highlighting grammars mirror the canonical token set in [`carve/resources/grammar.ebnf`](https://github.com/markup-carve/carve) and the TextMate grammar in [vscode-carve](https://github.com/markup-carve/vscode-carve).

> **Status:** Tiptap integration, plus Prism, highlight.js and TextMate grammars. Sibling editor grammars live in their own repos: editor-bundled **TextMate** copies in [vscode-carve](https://github.com/markup-carve/vscode-carve) and [intellij-carve](https://github.com/markup-carve/intellij-carve); **Tree-sitter** in [tree-sitter-carve](https://github.com/markup-carve/tree-sitter-carve)and [zed-carve](https://github.com/markup-carve/zed-carve).

Install
-------

[](#install)

```
npm install @markup-carve/carve-grammars
```

All peer dependencies are optional - install only what you use: `@tiptap/core` + `@tiptap/starter-kit` (v2) for the editor, `prismjs` (v1) for Prism, `highlight.js` (v11) for highlight.js.

`CarveKit` also pulls in several standalone Tiptap marks/extensions (highlight, subscript, superscript, underline, link, image, table, task-list); install the `@tiptap/extension-*` packages you use, or disable them via `CarveKit.configure({ underline: false, ... })`.

Usage
-----

[](#usage)

```
import { Editor } from '@tiptap/core'
import { CarveKit, serializeToCarve } from '@markup-carve/carve-grammars/tiptap'

const editor = new Editor({
  element: document.getElementById('editor'),
  extensions: [CarveKit],
  onUpdate: ({ editor }) => {
    const carve = serializeToCarve(editor.getJSON())
    console.log(carve)
  },
})
```

### Individual extensions

[](#individual-extensions)

```
import StarterKit from '@tiptap/starter-kit'
import { CarveInsert, CarveDelete, CarveDiv, serializeToCarve } from '@markup-carve/carve-grammars/tiptap'

const editor = new Editor({
  extensions: [StarterKit, CarveInsert, CarveDelete, CarveDiv],
})
```

Mark mapping
------------

[](#mark-mapping)

Tiptap markCarve tokenRenders asbold`*text*` / `{*text*}```italic`/text/` / `{/text/}```underline`_text_` / `{_text_}```code``text````highlight`=text=` / `{=text=}```strike`~text~` / `{~text~}```subscript`{,text,}` (braced only)``superscript`{^text^}` (braced only)``insert`{+text+}```delete`{-text-}```link`[text](url)` / `[text](url "title")```image`![alt](src)` / `![alt](src "title")```span`[text]{.class}```abbreviation`[text]{abbr="..."}``` \*\*\*\*\*\* `[text]{abbr="..."}` renders a real `` only when carve's `SemanticSpanExtension` is enabled (the same opt-in extension also maps `{kbd}`-&gt; ``, `{dfn}` -&gt; ``, `{samp}` -&gt; ``, `{var}` -&gt; ``). Without it, the attribute stays literal: ``. The mark's `parseHTML` reads back the `` form.

The tokens target carve-php's **parser** (the contract: serialized Carve must parse back to the same elements). Carve's inline syntax differs notably from Djot's: emphasis is `/text/` (Djot uses `_`), `_text_` is underline, `~text~` is strikethrough, highlight is `=text=`, and subscript/superscript are the braced `{,text,}` / `{^text^}` only (a bare `,` or `^` is literal text since carve #259).

Each single-char delimiter has two equivalent forms: a **bare** form (`=text=`) and a **forced brace** form (`{=text=}`) that also works intraword; both parse to the same element. The two columns above list bare / forced. `serializeToCarve` emits the bare form for `* / _ ~` and the forced `{…}` form for `= , ^` (round-trip-safe — those delimiters are likelier to be inert bare); `{+…+}` / `{-…-}` (insert / delete) have only the brace form, since `+` / `-`are not emphasis delimiters.

### Escaping

[](#escaping)

To honor that round-trip contract, `serializeToCarve` escapes literal Carve syntax in plain text so it parses back as text rather than markup - inline code, links, footnotes, CriticMarkup, mentions/tags/emoji, and an emphasis delimiter appearing inside its own span. Escaping is **contextual**: Carve's flanking rules already make most lone delimiters inert (`price * 2`, intraword `x_1`, `comma,, two`, `C:\path`, `a@b.com`), so those stay clean. The same logic is exposed as `escapeCarve(text)`.

Block elements
--------------

[](#block-elements)

Headings (`#`), bullet / ordered / task lists, blockquotes (`>`), fenced code blocks (```` lang`), horizontal rules (`---`), tables (with `|=` header cells and `^` / `

hljs.highlightAll();
```

### Shiki / VitePress

[](#shiki--vitepress)

`@markup-carve/carve-grammars/shiki` is the shared kit every Carve docs site uses, so highlighting stays identical across them: the TextMate grammar, GitHub light/dark themes extended with Carve scope colors, and a transformer + CSS pair that bridges what Shiki's HTML emitter cannot express (strikethrough, sub/superscript positioning, highlight background).

```
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { carveMarkdown } from '@markup-carve/carve-grammars/shiki'

export default defineConfig({
  markdown: {
    ...carveMarkdown(),
    // carveMarkdown({ light, dark, languages }) to override base themes
    // or register extra grammars
  },
})
```

```
// .vitepress/theme/index.ts
import '@markup-carve/carve-grammars/shiki/carve.css'
```

Named exports for other setups: `carveGrammar`, `carveLightExtras` / `carveDarkExtras`, `carveLightTheme` / `carveDarkTheme`, `extendTheme`, `carveStylingTransformer`.

API
---

[](#api)

- `serializeToCarve(doc)` - serialize an `editor.getJSON()` document to Carve markup.
- `escapeCarve(text)` - contextually escape literal Carve syntax in a plain-text run so it round-trips as text (used internally by `serializeToCarve`).
- `CarveKit` - the bundled Tiptap extension set.
- Individual extensions: `CarveInsert`, `CarveDelete`, `CarveDiv`, `CarveSpan`, `CarveFootnote`, `CarveFootnoteDefinition`, `CarveMath`, `CarveEmbed`, `CarveAbbreviation`, `CarveDefinitionList`.

Attributes, math and footnotes
------------------------------

[](#attributes-math-and-footnotes)

- **Attributes** - spans, headings and images serialize an `id` and `class`(and any extra non-structural attrs) as a `{#id .class key="val"}` block, e.g. `[text]{#me .note}`, `![alt](src){.wide}`. Inline attrs trail their target; block attrs (headings) sit on the **preceding** line (strict djot), e.g. `{#slug}` then `# Title`.
- **Math** - `CarveMath` (inline atom) serializes to `$`x`$` and, with `display: true`, `$$`x`$$`.
- **Footnotes** - `CarveFootnote` is the inline `[^label]` reference; `CarveFootnoteDefinition` is the matching body block, serialized as `[^label]: body`.

Tests
-----

[](#tests)

```
npm test
```

The suite holds all three grammars to one source of truth: the shared corpus from the [`markup-carve/carve`](https://github.com/markup-carve/carve) spec, vendored as the `spec/` git submodule (`git submodule update --init`).

- `npm run test:coverage` - the coverage matrix. Each grammar (prism, highlightjs, tiptap) declares a covered-category set and a skip set (with a reason per skip); the test fails if the two do not partition every corpus category, so a new spec category forces a deliberate decision.
- `npm run test:snapshot` - golden token snapshots. Each covered `.crv` is tokenized with Prism's and highlight.js's own tokenizers and the token stream (type + text) is compared against a committed golden in `tests/snapshots/`. Refresh intended changes with `npm run snapshots:update`.
- `npm run test:roundtrip` - the Tiptap serializer round-trip. Each covered `.crv` runs `parse -> ProseMirror JSON -> serializeToCarve -> parse` and the two parsed ASTs must be identical, catching serializer drift. Categories the serializer cannot represent are skipped with a reason.

`npm test` runs all of the above plus the structural grammar and serializer unit tests. CI runs the same on Node 18, 20 and 22.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance94

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~14 days

Total

3

Last Release

15d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/39854?v=4)[Mark Scherer](/maintainers/dereuromark)[@dereuromark](https://github.com/dereuromark)

---

Top Contributors

[![dereuromark](https://avatars.githubusercontent.com/u/39854?v=4)](https://github.com/dereuromark "dereuromark (71 commits)")

---

Tags

carvemarkupgrammarsyntax-highlightingtorchlightcarvetextmatephiki

### Embed Badge

![Health badge](/badges/markup-carve-carve-grammars/health.svg)

```
[![Health](https://phpackages.com/badges/markup-carve-carve-grammars/health.svg)](https://phpackages.com/packages/markup-carve-carve-grammars)
```

###  Alternatives

[nlgen/nlgen

A library for creating recursive-descent natural language generators.

56208.9k](/packages/nlgen-nlgen)[torchlight/engine

The PHP-based Torchlight code annotation and rendering engine.

6620.7k9](/packages/torchlight-engine)[tomaj/prepositioner

Preposition replace library

108.3k](/packages/tomaj-prepositioner)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
