Files
klammertext/doc/edit/emacs

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:

(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 — 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):

(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@codecode@ — 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:

(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.