A character written in the ^...^ mnemonic form -- ^oe^, ^zz^, ^hello^ --
used to kill ktext outright: no message, no output, and an exit status
that looked like a crash because it was one. Only the forms that happen
to spell hexadecimal digits escaped, and they were right by luck rather
than by reading. Any caret form that names no character is now a located
error that quotes what was written.
A klammer may now sit inside square brackets and braces as it already
could inside parentheses, and may be followed by the punctuation that
ends a sentence: [@i word@], {@b word@}, @i word@'s. The rule is a fixed
list of characters, because none of them can occur inside a klammer name
and so none of them is ambiguous next to a delimiter. A letter still
cannot: "x@" is the named closing delimiter, so a letter before "@" has
two readings and no rule can separate them.
The abbreviations an author writes -- the hyphen form @i-word, a klammer
glued to its neighbouring punctuation -- are now recognised as author
conveniences only. They are not applied to @@ definitions or @@@ system
commands, which are written by someone who needs no such shorthand and
whose forms are already whole.
Also fixed, each of them silent: an exported shell function in the
environment broke every @eval in the document; a line break written with
@nl was destroyed when it passed through a klammer that generates a
table, printing as \textbackslash{}newline in the finished PDF; and
"kdesc --coverage" shortened its file column by comparing the spelling of
a path rather than the path, so a klammer set reached by a symbolic link
showed a full path where its neighbours showed a short one.
New in the distribution: ref/, a small klammerset that describes the
Klammermachine's own tables in Klammertext. It currently provides the
character tables -- the diacritics and the extended Latin letters -- as
klammers that render to any target, so the documentation of what can be
written cannot drift from what the machine accepts. It is early, and
will grow.
The Klammertext taxonomy now has a section on targets, describes the
@@@klammerset system command alongside the other three, and states the
precedence of "^" -- what a caret means before a letter, a digit, a
punctuation character, and a quote, in one ordered list.
(from dev 6594c7ff8c1f)
262 lines
12 KiB
Markdown
262 lines
12 KiB
Markdown
# 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.
|
||
|
||
Requires **Emacs 27.1 or newer** — the version Ubuntu 22.04 and Pop!_OS
|
||
22.04 ship, so it is the practical floor. Tested on 27.1 and 30.2.
|
||
|
||
## 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)
|
||
(require 'klammertext-indent) ; optional, experimental: TAB indentation
|
||
(require 'klammertext-align) ; optional, experimental: table alignment
|
||
```
|
||
|
||
Replace `full-pathname-of-the-emacs-directory` with the full path to the
|
||
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.
|
||
|
||
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`,
|
||
`@@@klammerset` — 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`.)
|
||
|
||
## 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.
|
||
|
||
## 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.
|
||
|
||
## 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.
|