2026-07-18 18:48:23 +02:00
|
|
|
|
# Emacs mode for Klammertext
|
|
|
|
|
|
|
|
|
|
|
|
`klammertext-mode.el` is an Emacs major mode for editing Klammertext files. It
|
|
|
|
|
|
helps Klammertext authors see the structure of klammer application through
|
|
|
|
|
|
syntax highlighting.
|
|
|
|
|
|
|
|
|
|
|
|
## Install
|
|
|
|
|
|
|
|
|
|
|
|
Put the `emacs` directory somewhere on your system, then tell Emacs where it is
|
|
|
|
|
|
and load the mode. Add to `~/.emacs.d/init.el`:
|
|
|
|
|
|
|
|
|
|
|
|
```elisp
|
|
|
|
|
|
(add-to-list 'load-path "full-pathname-of-the-emacs-directory")
|
|
|
|
|
|
(require 'klammertext-mode)
|
2026-07-27 00:49:08 +02:00
|
|
|
|
(require 'klammertext-indent) ; optional, experimental: TAB indentation
|
2026-07-27 01:21:58 +02:00
|
|
|
|
(require 'klammertext-align) ; optional, experimental: table alignment
|
2026-07-18 18:48:23 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Replace `full-pathname-of-the-emacs-directory` with the full path to the
|
2026-07-27 01:21:58 +02:00
|
|
|
|
directory that contains `klammertext-mode.el`. The last two requires load
|
|
|
|
|
|
the experimental indentation and table-alignment support (see their sections
|
|
|
|
|
|
below); each is a separate unit — comment its line out to disable it
|
|
|
|
|
|
entirely.
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
|
|
The mode auto-activates for `.kt` and `.k` files. (The `.k` / `.kt` distinction
|
|
|
|
|
|
is a filing convention, not a lexical one — the same mode serves both.) You can
|
|
|
|
|
|
also switch to it manually with `M-x klammertext-mode`.
|
|
|
|
|
|
|
|
|
|
|
|
## What it highlights
|
|
|
|
|
|
|
|
|
|
|
|
**Text-removal ("ignore") constructs** — in two independently chosen colors,
|
|
|
|
|
|
one for the *removed content*, one for the *marker characters*:
|
|
|
|
|
|
|
|
|
|
|
|
| Construct | Meaning |
|
|
|
|
|
|
|--------------|----------------------------------|
|
|
|
|
|
|
| `#` ... | remove to end of line |
|
|
|
|
|
|
| `##` ... | remove to end of buffer |
|
|
|
|
|
|
| `#[ ... ]#` | remove enclosed text (nestable) |
|
|
|
|
|
|
|
|
|
|
|
|
**Klammer applications** — in two independent colors: one for *opening* a
|
|
|
|
|
|
klammer, one for *closing* it. The `@` and the name of an opening are one
|
|
|
|
|
|
syntactic unit and share the opening color; the close (named or bare) gets the
|
|
|
|
|
|
closing color, so you always have visual confirmation of where a klammer ends:
|
|
|
|
|
|
|
|
|
|
|
|
| Form | Color | Meaning |
|
|
|
|
|
|
|-----------|---------|----------------------------------|
|
|
|
|
|
|
| `@name` | opening | opening `@` + name (one unit) |
|
|
|
|
|
|
| `name@` | closing | named closing delimiter |
|
|
|
|
|
|
| `@` | closing | bare closing delimiter |
|
|
|
|
|
|
|
|
|
|
|
|
In the abbreviated form `@name-arg1-arg2` (equivalent to `@name arg1 | arg2 @`)
|
|
|
|
|
|
only the name is colored — the name ends at the first hyphen, and the
|
|
|
|
|
|
hyphen-separated arguments stay plain, just as `arg1`/`arg2` would be plain in
|
|
|
|
|
|
the long form.
|
|
|
|
|
|
|
|
|
|
|
|
Because a named closing carries the closing color across its name too, long
|
|
|
|
|
|
klammers that name their closing delimiter (`@document ... document@`) stand
|
|
|
|
|
|
out — which is exactly where naming the closing delimiter earns its keep
|
|
|
|
|
|
(accurate unmatched-delimiter error messages). Short bodies (`@i word @`) are
|
|
|
|
|
|
conventionally left with a bare `@` to keep the text uncluttered.
|
|
|
|
|
|
|
|
|
|
|
|
**Klammer definitions** (`@@`) are highlighted the same way, in their own pair
|
|
|
|
|
|
of colors — so definitions read as distinct from applications at a glance:
|
|
|
|
|
|
|
|
|
|
|
|
| Form | Color | Meaning |
|
|
|
|
|
|
|------------|---------|-----------------------------------|
|
|
|
|
|
|
| `@@name` | opening | opening `@@` + name (one unit) |
|
|
|
|
|
|
| `name@@` | closing | named closing delimiter |
|
|
|
|
|
|
| `@@` | closing | bare closing delimiter |
|
|
|
|
|
|
|
|
|
|
|
|
The name ends at the first non-name character, so a target suffix like
|
|
|
|
|
|
`@@name.html` colors only `@@name` and leaves `.html` plain. A definition's
|
|
|
|
|
|
body (between `@@name` and the closing `@@`) is highlighted like ordinary
|
|
|
|
|
|
Klammertext — e.g. an `@i … @` inside it shows as a normal application.
|
|
|
|
|
|
|
|
|
|
|
|
**System/target commands** (`@@@`) — `@@@target`, `@@@argtype`, `@@@state` — get
|
|
|
|
|
|
a third pair of colors, so the three `@`-levels (application, definition, system)
|
|
|
|
|
|
are visually distinct:
|
|
|
|
|
|
|
|
|
|
|
|
| Form | Color | Meaning |
|
|
|
|
|
|
|------------|---------|-----------------------------------|
|
|
|
|
|
|
| `@@@name` | opening | opening `@@@` + name (one unit) |
|
|
|
|
|
|
| `name@@@` | closing | named closing delimiter |
|
|
|
|
|
|
| `@@@` | closing | bare closing delimiter |
|
|
|
|
|
|
|
|
|
|
|
|
`@@@` commands do not nest, so each delimiter is colored independently; their
|
|
|
|
|
|
bodies (`| … |` option lists) are highlighted as ordinary Klammertext.
|
|
|
|
|
|
|
|
|
|
|
|
## The eight faces
|
|
|
|
|
|
|
|
|
|
|
|
All eight faces are defined by the `defconst klammertext--palette` at the
|
|
|
|
|
|
beginning of `klammertext-mode.el`.
|
|
|
|
|
|
|
|
|
|
|
|
| Face | Applies to |
|
|
|
|
|
|
|-----------------------------------|-------------------------------------|
|
|
|
|
|
|
| `klammertext-ignored-face` | removed content |
|
|
|
|
|
|
| `klammertext-marker-face` | `#`, `##`, `#[`, `]#` |
|
|
|
|
|
|
| `klammertext-klammer-open-face` | an application opening `@name` |
|
|
|
|
|
|
| `klammertext-klammer-close-face` | an application close `name@` or `@` |
|
|
|
|
|
|
| `klammertext-def-open-face` | a definition opening `@@name` |
|
|
|
|
|
|
| `klammertext-def-close-face` | a definition close `name@@` or `@@` |
|
|
|
|
|
|
| `klammertext-system-open-face` | a system opening `@@@name` |
|
|
|
|
|
|
| `klammertext-system-close-face` | a system close `name@@@` or `@@@` |
|
|
|
|
|
|
|
|
|
|
|
|
To experiment with a color (evaluate in `*scratch*`, or add to your init):
|
|
|
|
|
|
|
|
|
|
|
|
```elisp
|
|
|
|
|
|
(set-face-foreground 'klammertext-marker-face "cyan")
|
|
|
|
|
|
(set-face-foreground 'klammertext-system-close-face "chocolate4")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
or `M-x customize-face RET klammertext-marker-face RET`.
|
|
|
|
|
|
|
|
|
|
|
|
## Matching delimiters (show-paren)
|
|
|
|
|
|
|
|
|
|
|
|
With `show-paren-mode` on (the default in Emacs 28+), placing point on a klammer
|
|
|
|
|
|
**application** delimiter highlights its partner, in both directions: on an
|
|
|
|
|
|
opening `@name` it highlights the closing `@`/`name@`, and on a close it
|
|
|
|
|
|
highlights the opening `@name`. Nesting is respected — in `@a @b x @ @`, the
|
|
|
|
|
|
outer `@a` matches the last `@`, not the first.
|
|
|
|
|
|
|
|
|
|
|
|
Matching covers applications only (`@`), not `@@`/`@@@`, since that is where
|
|
|
|
|
|
paired delimiters matter most. The matcher steps over `@@`/`@@@`, removed text,
|
|
|
|
|
|
other literal spans and escaped `^@`; the abbreviated `@name-arg` form has no
|
|
|
|
|
|
closing delimiter, so nothing is highlighted on it.
|
|
|
|
|
|
|
|
|
|
|
|
**Literal klammers** (those in `klammertext-literal-klammers`, e.g. `@code`) are
|
|
|
|
|
|
closed with the full `NAME@` form because their content is verbatim. These are
|
|
|
|
|
|
matched *by name* — `@code` ↔ `code@` — with the content treated as opaque, so a
|
|
|
|
|
|
stray `@` inside (`@code x @ y code@`) doesn't confuse the match, in either
|
|
|
|
|
|
direction. Register any klammer that declares a `literal` argument with
|
|
|
|
|
|
`(klammertext-add-literal-klammer "name")` in your init file so both its
|
|
|
|
|
|
highlighting and its delimiter matching work.
|
|
|
|
|
|
|
|
|
|
|
|
If a **named** close disagrees with its opening — e.g. `@doc … foo@` (should be
|
|
|
|
|
|
`doc@`) — the mismatched delimiter is shown in **bright red** (bold), and a
|
|
|
|
|
|
message describing the mismatch appears in the minibuffer, e.g.
|
|
|
|
|
|
|
|
|
|
|
|
> `Klammertext: closing foo@ does not match opening @doc`
|
|
|
|
|
|
|
|
|
|
|
|
An unbalanced delimiter (an opening with no close, or vice versa) is flagged the
|
|
|
|
|
|
same way. This turns the naming convention into a live check: name a long
|
|
|
|
|
|
klammer's closing delimiter and a typo'd or unbalanced name lights up
|
|
|
|
|
|
immediately. The red comes from `klammertext-mismatch-face`, which is remapped
|
|
|
|
|
|
over `show-paren-mismatch` **only in Klammertext buffers** (your global
|
|
|
|
|
|
`show-paren-mismatch` face is left untouched); customize it to taste.
|
|
|
|
|
|
|
|
|
|
|
|
This is wired in automatically (`show-paren-data-function`); you only need
|
|
|
|
|
|
`show-paren-mode` enabled. It relies on nothing in the syntax table — Klammertext
|
|
|
|
|
|
delimiters can't be expressed there — so it does not interfere with other
|
|
|
|
|
|
`@`/`#` characters.
|
|
|
|
|
|
|
|
|
|
|
|
### Jumping between matches
|
|
|
|
|
|
|
|
|
|
|
|
`klammertext-jump-to-match`, bound to **`C-c C-j`**, moves point to the matching
|
|
|
|
|
|
delimiter: from an opening `@name` to its close, and from a close back to its
|
|
|
|
|
|
opening `@name`. It uses the same matcher as the highlighting. The starting
|
|
|
|
|
|
position is pushed to the mark ring, so `C-u C-SPC` jumps back. (Also available
|
|
|
|
|
|
as `M-x klammertext-jump-to-match`.)
|
|
|
|
|
|
|
2026-07-27 00:49:08 +02:00
|
|
|
|
## Indentation (experimental)
|
|
|
|
|
|
|
|
|
|
|
|
With `klammertext-indent.el` loaded (the optional require above), **TAB**
|
|
|
|
|
|
indents the current line — and `indent-region` a selection — to reflect the
|
|
|
|
|
|
klammer nesting, two spaces per level:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
@ol
|
|
|
|
|
|
Item one
|
|
|
|
|
|
| Item two
|
|
|
|
|
|
@ol
|
|
|
|
|
|
Embedded item one
|
|
|
|
|
|
| Embedded item two
|
|
|
|
|
|
@
|
|
|
|
|
|
| Item three
|
|
|
|
|
|
@
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The rule: a line indents to 2 × depth; a line *beginning* with a bar run
|
|
|
|
|
|
(`|`, `||`, …) or a closing delimiter sits one level less — at its owner's
|
|
|
|
|
|
opening column. So the bars and the close line up under the `@` of the list
|
|
|
|
|
|
they belong to, and `| ` (bar + space) puts item text exactly at the content
|
|
|
|
|
|
column. The bar rule is dimension-independent: `||` table rows drop to the
|
|
|
|
|
|
opener's column the same way. All three `@`-tiers indent uniformly.
|
|
|
|
|
|
|
|
|
|
|
|
Exceptions, all deliberate:
|
|
|
|
|
|
|
|
|
|
|
|
- Klammers in `klammertext-transparent-klammers` (default: `document`)
|
|
|
|
|
|
contribute no level, so a document's ordinary paragraphs stay at the left
|
|
|
|
|
|
margin.
|
|
|
|
|
|
- Lines inside a literal klammer's verbatim content (`@code ... code@`), and
|
|
|
|
|
|
inside the argument span of a klammer in `klammertext-code-klammers`
|
|
|
|
|
|
(default: `eval` — inline Python is indentation-sensitive), are never
|
|
|
|
|
|
touched. Neither are removed regions (`#[ ... ]#`, after `##`).
|
|
|
|
|
|
|
|
|
|
|
|
Nothing reformats automatically — whitespace is content in Klammertext, so
|
|
|
|
|
|
indentation happens only when you ask for it (TAB, `indent-region`). The
|
|
|
|
|
|
offset is `klammertext-indent-offset` (default 2); all three variables are
|
|
|
|
|
|
customizable in the `klammertext-indent` group.
|
|
|
|
|
|
|
2026-07-27 01:21:58 +02:00
|
|
|
|
## Table alignment (experimental)
|
|
|
|
|
|
|
|
|
|
|
|
With `klammertext-align.el` loaded (the optional require above), **`C-c C-a`**
|
|
|
|
|
|
with point anywhere inside a `@table` span pads the cells of its rows so the
|
|
|
|
|
|
`|` separators line up:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
@table
|
|
|
|
|
|
First item | Second | A third item that's longer ||
|
|
|
|
|
|
Row 2 | Text | Not as long ||
|
|
|
|
|
|
@
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
A row is one line ending with the row delimiter `||` (the customary trailing
|
|
|
|
|
|
delimiter — the parser strips one trailing delimiter, and it keeps every row
|
|
|
|
|
|
uniform, which also suits program-generated tables); the last row may omit
|
|
|
|
|
|
it. Alignment is for small data items, so a row is left untouched — and
|
|
|
|
|
|
contributes nothing to the column widths — when any of its cells is longer
|
|
|
|
|
|
than `klammertext-align-cell-max` (30) characters or the row spans lines.
|
|
|
|
|
|
If the aligned rows would exceed `klammertext-align-row-max` (100) columns,
|
|
|
|
|
|
nothing changes and the reason is reported.
|
|
|
|
|
|
|
|
|
|
|
|
The padding is semantically free: the SKS strips cell content, and no
|
|
|
|
|
|
whitespace is ever inserted inside a bar run (which would turn a `||` row
|
|
|
|
|
|
separator into an empty `| |` cell). Bars inside a nested klammer in a cell
|
|
|
|
|
|
(`@frac 1 | 2 @`) belong to that klammer, not the table, and are left alone.
|
|
|
|
|
|
Aligned rows adopt the leading whitespace of the first aligned row — run TAB
|
|
|
|
|
|
first if the rows disagree.
|
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
|
## Literal klammers
|
|
|
|
|
|
|
|
|
|
|
|
Inside a `literal` argument — for example the body of `@code ... code@` — `#`
|
|
|
|
|
|
and `@` are literal text, not Klammertext syntax. The mode highlights the
|
|
|
|
|
|
opening `@code` and closing `code@` but leaves the interior as normal text,
|
|
|
|
|
|
for any klammer registered in `klammertext-literal-klammers`. `@code` is
|
|
|
|
|
|
registered by default.
|
|
|
|
|
|
|
|
|
|
|
|
If you define your own klammer with a `literal` parameter (a relatively
|
|
|
|
|
|
advanced action — see the `literal` argument type in the project
|
|
|
|
|
|
documentation), register it in your init file:
|
|
|
|
|
|
|
|
|
|
|
|
```elisp
|
|
|
|
|
|
(klammertext-add-literal-klammer "myverbatim")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Because the list is consulted at fontification time, registering a klammer
|
|
|
|
|
|
while a buffer is already open takes effect after `M-x font-lock-update` (or
|
|
|
|
|
|
re-visiting the file).
|
|
|
|
|
|
|
|
|
|
|
|
## Known limitations (deliberate, for now)
|
|
|
|
|
|
|
|
|
|
|
|
- Unescaped `@` is always treated as a delimiter (as the Klammermachine does),
|
|
|
|
|
|
so an `@` in prose that is *not* meant as a klammer — e.g. an email address
|
|
|
|
|
|
written `foo@bar` instead of `foo^@bar` — will be highlighted. This reflects
|
|
|
|
|
|
what the machine actually sees.
|
|
|
|
|
|
- Very large multiline blocks or literal spans edited far from their opening
|
|
|
|
|
|
may occasionally need `M-x font-lock-update` to re-highlight correctly.
|