Editor support generalized: shared core, language server, Vim and VS Code (from dev eb5baf9cbe59)

doc/edit/ now holds a shared Python implementation of the language's
structural layer (klammertext_edit.py) and a dependency-free language
server (klammertext_ls.py), with integrations for Emacs, Sublime Text,
Vim, and Visual Studio Code.  The editor test suite in tst/ covers the
core's API and CLI, the language server protocol, the VS Code
extension, headless Vim, and Emacs byte-equality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 15:01:49 +02:00
parent 73ed7f3d5d
commit f855c5ccae
27 changed files with 3918 additions and 1170 deletions

117
doc/edit/vim/README.md Normal file
View File

@@ -0,0 +1,117 @@
# Klammertext support for Vim
Vim support for editing Klammertext files (`.kt` documents and `.k` klammer
definitions): syntax highlighting, delimiter matching and jumping, structural
reindentation, table alignment, and a delimiter checker.
The structure-aware features are thin wrappers around the **shared editor
core** (`klammertext_edit.py`) — the single Python implementation of
Klammertext's structural layer used by the Sublime Text and VS Code
integrations and by the Klammertext language server. The Vim plugin runs it
through `python3`; there is no Vim-specific reimplementation to drift out of
sync.
## Requirements
- Vim 8+ (or Neovim) with `+eval` — any normal Vim; only `vim-tiny` lacks it.
- `python3` on `PATH` (Klammertext itself already requires Python).
- The shared core, found automatically in this order:
1. `g:klammertext_edit_py`, if you set it (a full path to
`klammertext_edit.py`);
2. a `klammertext_edit.py` vendored at this plugin's root (the layout the
Klammertext editing zip ships);
3. `../shared/klammertext_edit.py` relative to the plugin directory (the
layout of the Klammertext repository — using the plugin straight from a
checkout just works);
4. `$KLAMMERTEXT_HOME/doc/edit/shared/klammertext_edit.py`.
## Install
Copy (or symlink) this `vim/` directory into a Vim native package path,
renaming it as you like:
mkdir -p ~/.vim/pack/klammertext/start
cp -R vim ~/.vim/pack/klammertext/start/klammertext
(Neovim: `~/.local/share/nvim/site/pack/klammertext/start/klammertext`.)
If you copy the directory out of the Klammertext tree, also copy
`shared/klammertext_edit.py` into the copied folder's root — or set
`g:klammertext_edit_py`. The editing zip from the Klammertext website ships
the vendored copy already in place.
`.kt` and `.k` files then get the `klammertext` filetype. **Note:** `.kt`
is also Kotlin's extension, and stock Vim maps it to Kotlin; this plugin's
`ftdetect` overrides that unconditionally. If you edit both languages, see
the comment in `ftdetect/klammertext.vim`.
## What you get
**Syntax highlighting** — the same token classes and palette as the Emacs
and Sublime Text support: text removal (`#`, `##`, nestable `#[ ... ]#`),
the three `@`-tiers — application (`@`, blue), definition (`@@`, green),
system (`@@@`, orange) — each as an opening (`@name`) or a close (`name@`,
bare `@`), opens bright and closes the same hue darker; `^`-escapes;
verbatim `@code ... code@` interiors. All groups are `hi def` — override
them with `:highlight` in your vimrc.
**Commands** (buffer-local; default mappings below):
| Command | Does |
|---|---|
| `:[range]KlammertextReindent` | reindent the range (default: whole buffer) |
| `:KlammertextAlign` | align the `@table` enclosing the cursor |
| `:KlammertextJumpToMatch` | jump between a klammer application's opening and closing delimiter |
| `:KlammertextCheck` | list unclosed/mismatched delimiters in the location list |
Default mappings (buffer-local; suppress them all with
`let g:klammertext_no_mappings = 1`): `<LocalLeader>i` reindents the current
line (in visual mode, the selection), `<LocalLeader>a` aligns the enclosing
table, `<LocalLeader>j` jumps to the matching delimiter. `LocalLeader`
defaults to backslash; set `maplocalleader` to taste.
Reindentation is **explicit-only**: whitespace is content in Klammertext,
so nothing reformats as a side effect of typing (`indentkeys` is emptied).
Alignment follows the shared rules: rows end with `||`; a row with a cell
over 30 characters or spanning lines is left untouched; beyond 100 columns
the command declines; bars inside a nested klammer are not separators; no
whitespace is ever inserted inside a bar run.
**With `+python3`** (check `:echo has('python3')`) the shared core also runs
in-process: the `=` operator reindents through `'indentexpr'` (`==`, `gg=G`),
and the matching delimiter is highlighted live as the cursor sits on one —
the show-paren equivalent, with a mismatched or unbalanced delimiter shown
in red plus a message. Without `+python3` the commands above still work;
they shell out to `python3`.
**Comment toggling**`'commentstring'` is set to `# %s`, so Vim 9.1's
built-in commenting and Neovim's `gcc`/`gc` (or the commentary plugin)
toggle `#` line removal.
## Configuration
| Variable | Meaning (default) |
|---|---|
| `g:klammertext_edit_py` | full path to `klammertext_edit.py` (auto-detected) |
| `g:klammertext_python` | Python interpreter (`python3`) |
| `g:klammertext_no_mappings` | set to define no default mappings |
## Neovim and the language server
Neovim users can additionally attach Neovim's built-in LSP client to the
Klammertext language server (`klammertext_ls.py`, next to the shared core)
for diagnostics as you type, `gq`/format-document reindentation, and
cursor-hold match highlighting:
```lua
vim.api.nvim_create_autocmd('FileType', {
pattern = 'klammertext',
callback = function()
vim.lsp.start {
name = 'klammertext-ls',
cmd = { 'python3', '/path/to/doc/edit/shared/klammertext_ls.py' },
}
end,
})
```
The plugin's own commands work the same with or without it.

View File

@@ -0,0 +1,299 @@
" autoload/klammertext.vim — implementation of the Klammertext commands.
"
" All the structure-aware work (indentation, table alignment, delimiter
" matching, diagnostics) is done by the shared Python core,
" klammertext_edit.py — the single implementation used by the Sublime,
" Vim, and VS Code integrations and by the language server. This file is
" glue: it locates the core, runs it (as a stdin/stdout filter, or
" in-process via +python3 where that is faster), and applies the results
" to the buffer.
"
" Configuration:
" g:klammertext_edit_py full path to klammertext_edit.py (overrides the
" search described below)
" g:klammertext_python the Python interpreter (default "python3")
" --- locating the shared core ---------------------------------------------
" Search order: an explicit g:klammertext_edit_py; a vendored copy at this
" plugin's root (the layout the editing zip installs); ../shared relative
" to the plugin root (the repository layout); $KLAMMERTEXT_HOME.
let s:plugin_root = expand('<sfile>:p:h:h')
function! s:EditScript() abort
if exists('g:klammertext_edit_py')
return g:klammertext_edit_py
endif
for candidate in [
\ s:plugin_root . '/klammertext_edit.py',
\ fnamemodify(s:plugin_root, ':h') . '/shared/klammertext_edit.py',
\ (empty($KLAMMERTEXT_HOME) ? '' :
\ $KLAMMERTEXT_HOME . '/doc/edit/shared/klammertext_edit.py')]
if !empty(candidate) && filereadable(candidate)
return candidate
endif
endfor
return ''
endfunction
function! s:Python() abort
return exists('g:klammertext_python') ? g:klammertext_python : 'python3'
endfunction
" Run the core CLI over the whole buffer. ARGS is the argument string
" (e.g. "indent 3-7"). Returns a dict {ok, lines, msg}: LINES is the
" transformed buffer (for the filter modes), MSG the stderr message.
function! s:RunCore(args) abort
let script = s:EditScript()
if empty(script)
return {'ok': 0, 'lines': [], 'msg':
\ 'Klammertext: cannot locate klammertext_edit.py'
\ . ' (set g:klammertext_edit_py)'}
endif
let errfile = tempname()
let cmd = s:Python() . ' ' . shellescape(script) . ' ' . a:args
\ . ' 2>' . shellescape(errfile)
let out = systemlist(cmd, getline(1, '$'))
let msg = filereadable(errfile) ? join(readfile(errfile), ' ') : ''
call delete(errfile)
if v:shell_error
return {'ok': 0, 'lines': [], 'msg':
\ empty(msg) ? 'Klammertext: the shared core failed' : msg}
endif
return {'ok': 1, 'lines': out, 'msg': msg}
endfunction
" Replace the buffer with LINES, touching only the lines that changed (so
" undo stays small and the cursor does not move). The transformations
" never add or remove lines; refuse if the count disagrees.
function! s:ApplyLines(lines) abort
if len(a:lines) != line('$')
echohl ErrorMsg
echo 'Klammertext: unexpected line count from the shared core'
echohl None
return 0
endif
let changed = 0
for i in range(1, line('$'))
if getline(i) !=# a:lines[i - 1]
call setline(i, a:lines[i - 1])
let changed += 1
endif
endfor
return changed
endfunction
function! s:CursorArgs() abort
let col = exists('*charcol') ? charcol('.') : col('.')
return line('.') . ' ' . col
endfunction
" --- the commands ----------------------------------------------------------
function! klammertext#Reindent(first, last) abort
let r = s:RunCore('indent ' . a:first . '-' . a:last)
if !r.ok
echohl ErrorMsg | echo r.msg | echohl None
return
endif
call s:ApplyLines(r.lines)
endfunction
function! klammertext#AlignTable() abort
let r = s:RunCore('align ' . s:CursorArgs())
if !r.ok
echohl ErrorMsg | echo r.msg | echohl None
return
endif
call s:ApplyLines(r.lines)
if !empty(r.msg)
echo 'Klammertext: ' . r.msg
endif
endfunction
function! klammertext#JumpToMatch() abort
let script = s:EditScript()
if empty(script)
echohl ErrorMsg
echo 'Klammertext: cannot locate klammertext_edit.py'
echohl None
return
endif
let out = systemlist(s:Python() . ' ' . shellescape(script)
\ . ' match ' . s:CursorArgs(), getline(1, '$'))
if v:shell_error || empty(out)
echohl ErrorMsg | echo 'Klammertext: the shared core failed' | echohl None
return
endif
let parts = split(out[0], ' ')
if parts[0] ==# 'none'
echo 'Klammertext: ' . join(parts[1:], ' ')
return
endif
" "match L C" or "mismatch L C message..."
let lnum = str2nr(parts[1])
let ccol = str2nr(parts[2])
if exists('*setcursorcharpos')
call setcursorcharpos(lnum, ccol)
else
call cursor(lnum, ccol)
endif
if parts[0] ==# 'mismatch'
echohl WarningMsg
echo 'Klammertext: ' . join(parts[3:], ' ')
echohl None
endif
endfunction
function! klammertext#Check() abort
let script = s:EditScript()
if empty(script)
echohl ErrorMsg
echo 'Klammertext: cannot locate klammertext_edit.py'
echohl None
return
endif
let out = systemlist(s:Python() . ' ' . shellescape(script) . ' check',
\ getline(1, '$'))
if v:shell_error
echohl ErrorMsg | echo 'Klammertext: the shared core failed' | echohl None
return
endif
let items = []
for line in out
let m = matchlist(line, '^\(\d\+\):\(\d\+\): \(.*\)$')
if !empty(m)
call add(items, {'bufnr': bufnr('%'), 'lnum': str2nr(m[1]),
\ 'col': str2nr(m[2]), 'text': m[3], 'type': 'E'})
endif
endfor
call setloclist(0, items, ' ')
call setloclist(0, [], 'a', {'title': 'Klammertext delimiter check'})
if empty(items)
lclose
echo 'Klammertext: no delimiter problems found'
else
lopen
endif
endfunction
" --- +python3: in-process indentexpr and live match highlighting ----------
" These load the shared core into Vim's embedded Python once, so the =
" operator and the per-cursor-move matcher run without spawning processes.
let s:py_ready = 0
function! s:PySetup() abort
if s:py_ready
return 1
endif
let script = s:EditScript()
if empty(script) || !has('python3')
return 0
endif
let g:klammertext_py_dir = fnamemodify(script, ':h')
py3 << EOF
import sys
import vim
_kt_dir = vim.eval('g:klammertext_py_dir')
if _kt_dir not in sys.path:
sys.path.insert(0, _kt_dir)
import klammertext_edit as _kt
def _kt_buffer_and_offset():
buf = vim.current.buffer
lines = buf[:]
s = '\n'.join(lines)
row, bytecol = vim.current.window.cursor # bytecol is 0-based bytes
line = lines[row - 1] if row <= len(lines) else ''
charcol = len(line.encode('utf-8')[:bytecol].decode('utf-8', 'replace'))
offset = sum(len(l) + 1 for l in lines[:row - 1]) + charcol
return s, lines, offset
def _kt_indent(lnum):
buf = vim.current.buffer
lines = buf[:]
s = '\n'.join(lines)
bol = sum(len(l) + 1 for l in lines[:lnum - 1])
col = _kt.target_column(s, bol)
return -1 if col is None else col
def _kt_pos(lines, offset):
"""(1-based line, 0-based char col, the line's text) for char OFFSET."""
line = 0
while line < len(lines) and offset > len(lines[line]):
offset -= len(lines[line]) + 1
line += 1
text = lines[line] if line < len(lines) else ''
return (line + 1, offset, text)
def _kt_match():
"""[] when not on a delimiter; else [mismatch, message,
l1, c1, len1, (l2, c2, len2)?] with byte columns for matchaddpos()."""
s, lines, offset = _kt_buffer_and_offset()
m = _kt.match_at(s, offset)
if m is None:
return []
out = [1 if m['mismatch'] else 0, m['message'] or '']
for tok in [m['token'], m['match_token']]:
if tok is None:
continue
line, ccol, text = _kt_pos(lines, tok[0])
bcol = len(text[:ccol].encode('utf-8')) + 1
blen = len(text[ccol:ccol + (tok[1] - tok[0])].encode('utf-8'))
out.extend([line, bcol, blen])
return out
EOF
let s:py_ready = 1
return 1
endfunction
function! klammertext#IndentExpr() abort
if !s:PySetup()
return -1
endif
return py3eval('_kt_indent(' . v:lnum . ')')
endfunction
function! s:ClearMatchHighlight() abort
if exists('w:klammertext_match_ids')
for id in w:klammertext_match_ids
silent! call matchdelete(id)
endfor
endif
let w:klammertext_match_ids = []
endfunction
function! s:UpdateMatchHighlight() abort
call s:ClearMatchHighlight()
let r = py3eval('_kt_match()')
if empty(r)
return
endif
let group = r[0] ? 'KlammertextMismatch' : 'KlammertextMatch'
let pos = []
let i = 2
while i + 3 <= len(r)
call add(pos, [r[i], r[i + 1], r[i + 2]])
let i += 3
endwhile
if !empty(pos)
call add(w:klammertext_match_ids, matchaddpos(group, pos))
endif
if r[0] && !empty(r[1])
echo 'Klammertext: ' . r[1]
endif
endfunction
function! klammertext#SetupMatchHighlight() abort
if !s:PySetup()
return
endif
hi def link KlammertextMatch MatchParen
hi def KlammertextMismatch guifg=#ff5555 gui=bold ctermfg=203 cterm=bold
augroup klammertextMatch
autocmd! * <buffer>
autocmd CursorMoved,CursorMovedI <buffer> call s:UpdateMatchHighlight()
autocmd BufLeave,WinLeave <buffer> call s:ClearMatchHighlight()
augroup END
endfunction

View File

@@ -0,0 +1,10 @@
" Klammertext filetype detection (.kt source files, .k klammer definitions).
"
" NOTE: .kt is also Kotlin's extension, and recent Vim/Neovim runtimes map
" *.kt to the kotlin filetype. This file overrides that unconditionally
" (`set filetype=`, not `setfiletype`, so it wins over the runtime's
" earlier detection). If you edit both Kotlin and Klammertext, replace the
" *.kt line with a content heuristic of your choice, or drop it and set the
" filetype per file with a modeline (# vim: ft=klammertext) or :set.
au BufRead,BufNewFile *.kt set filetype=klammertext
au BufRead,BufNewFile *.k set filetype=klammertext

View File

@@ -0,0 +1,63 @@
" Klammertext filetype plugin: comment format, the structural commands, and
" (when Vim has +python3) fast in-process indentation and live delimiter
" match highlighting. The implementations are in autoload/klammertext.vim;
" the algorithms themselves are the shared Python core
" (klammertext_edit.py — see doc/edit/README.md for how it is located).
"
" Commands (buffer-local):
" :[range]KlammertextReindent reindent the range (default: whole buffer)
" :KlammertextAlign align the @table enclosing the cursor
" :KlammertextJumpToMatch jump between a klammer application's
" opening and closing delimiter
" :KlammertextCheck unbalanced/mismatched delimiters -> the
" location list
"
" Default mappings (set g:klammertext_no_mappings to define none):
" <LocalLeader>i reindent the current line (visual: the selection)
" <LocalLeader>a align the enclosing table
" <LocalLeader>j jump to the matching delimiter
"
" Reindentation is EXPLICIT-ONLY: whitespace is content in Klammertext, so
" indentkeys is emptied and nothing reformats as a side effect of typing.
" With +python3 the ftplugin also sets 'indentexpr', so the = operator
" (e.g. ==, gg=G) reindents through the same shared implementation.
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
setlocal commentstring=#\ %s
setlocal comments=b:#
setlocal indentkeys=
command! -buffer -range=% KlammertextReindent
\ call klammertext#Reindent(<line1>, <line2>)
command! -buffer KlammertextAlign call klammertext#AlignTable()
command! -buffer KlammertextJumpToMatch call klammertext#JumpToMatch()
command! -buffer KlammertextCheck call klammertext#Check()
if !exists("g:klammertext_no_mappings")
nnoremap <buffer> <silent> <LocalLeader>i :.KlammertextReindent<CR>
xnoremap <buffer> <silent> <LocalLeader>i :KlammertextReindent<CR>
nnoremap <buffer> <silent> <LocalLeader>a :KlammertextAlign<CR>
nnoremap <buffer> <silent> <LocalLeader>j :KlammertextJumpToMatch<CR>
endif
let b:undo_ftplugin = "setlocal commentstring< comments< indentkeys<"
\ . " | delcommand KlammertextReindent"
\ . " | delcommand KlammertextAlign"
\ . " | delcommand KlammertextJumpToMatch"
\ . " | delcommand KlammertextCheck"
" With +python3 the shared core runs in-process: 'indentexpr' makes the =
" operator work, and the matching delimiter is highlighted live as the
" cursor sits on one (the show-paren equivalent; a mismatch shows in red
" with a message). Without +python3 the commands above still work — they
" shell out to python3 — and Neovim users can get live matching from the
" language server instead (see doc/edit/README.md).
if has('python3')
setlocal indentexpr=klammertext#IndentExpr()
let b:undo_ftplugin .= " | setlocal indentexpr<"
call klammertext#SetupMatchHighlight()
endif

View File

@@ -0,0 +1,113 @@
" Vim syntax highlighting for Klammertext (.kt and .k files).
" The Vim counterpart of doc/edit/emacs/klammertext-mode.el's highlighting
" and doc/edit/sublime/Klammertext.sublime-syntax.
"
" What it highlights (the same token classes as the other editors):
"
" Text removal (#):
" # ... remove to end of line (marker + removed text)
" ## ... remove to end of file (marker + removed text)
" #[ ... ]# remove enclosed text, nestable (markers + removed)
" #- #+ #/ whitespace operators: NOT removals, left unhighlighted
"
" Klammer applications (@), definitions (@@), system commands (@@@):
" @name @@name @@@name opening (@ and name are one unit)
" name@ name@@ name@@@ named closing
" @ @@ @@@ bare closing
"
" Escapes: ^@ ^# ^| ^^ — the caret makes the next character literal; the
" two characters are consumed as one (unhighlighted) unit, so the escaped
" character is never read as a delimiter. A run of carets pairs
" left-to-right, reproducing the language's parity rule.
"
" Literal klammers: @code ... code@ — the interior is verbatim (no # or @
" interpreted). SYNC: the literal-klammer set's source of truth is
" LITERAL_KLAMMERS in doc/edit/shared/klammertext_edit.py; a static
" syntax file cannot read it, so when you add a literal klammer 'foo',
" copy the klammertextVerbatim region below with code -> foo (and mirror
" it in the Emacs, Sublime, and VS Code artifacts; all are seeded with
" just 'code').
"
" How open vs. close is decided (the same rule as every other integration):
" a delimiter whose NAME follows the @-run (@name) is an OPENING; a bare
" @-run, or one whose NAME precedes it (name@), is a CLOSING. The
" look-ahead \%(\w\|@\)\@! on every closing keeps 'foo@bar' correct: that @
" is followed by a name, so it opens @bar and 'foo' stays plain text. The
" abbreviated @name-arg form colors only @name (the name ends at the first
" hyphen), exactly like the other editors.
"
" Colors come from the shared Klammertext palette
" (notes/klammertext_palette.md in the development tree): application blue,
" definition green, system orange; each opening bright and its close the
" same hue darker; full intensity on dark backgrounds, deepened (0.60x) on
" light. All groups are `hi def`, so :highlight in your vimrc overrides.
" Delimiter matching (jump + live highlight) is not a tokenizer concern —
" it lives in the ftplugin/autoload files.
if exists("b:current_syntax")
finish
endif
" --- escapes: ^X makes X literal; consumed so # / @ are not delimiters ----
" (Defined first; it wins by the earlier-start rule, since the ^ precedes.)
syn match klammertextEscape /\^./
" --- text removal (#) -----------------------------------------------------
" Order matters: at the same start position, the LAST defined item wins.
syn match klammertextRemovedLine /#.*$/ contains=klammertextMarkerLine
syn match klammertextMarkerLine /#/ contained
" whitespace operators #- #+N #/N : not removals, left unhighlighted
syn match klammertextWhitespaceOp "#[-+/]\d*"
syn region klammertextRemovedBlock matchgroup=klammertextMarker start=/#\[/ end=/\]#/ contains=klammertextRemovedBlock
syn region klammertextRemovedFile matchgroup=klammertextMarker start=/##/ end=/\%$/
" --- system / target commands @@@ ----------------------------------------
syn match klammertextSysOpen /@\@1<!@@@\w\+/
syn match klammertextSysClose /@\@1<!@@@\%(\w\|@\)\@!/
syn match klammertextSysClose /@\@1<!\w\+@@@\%(\w\|@\)\@!/
" --- klammer definitions @@ ----------------------------------------------
syn match klammertextDefOpen /@\@1<!@@\w\+/
syn match klammertextDefClose /@\@1<!@@\%(\w\|@\)\@!/
syn match klammertextDefClose /@\@1<!\w\+@@\%(\w\|@\)\@!/
" --- klammer applications @ ----------------------------------------------
syn match klammertextAppOpen /@\@1<!@\w\+/
syn match klammertextAppClose /@\@1<!@\%(\w\|@\)\@!/
syn match klammertextAppClose /@\@1<!\w\+@\%(\w\|@\)\@!/
" --- literal klammer: interior verbatim (seeded default: @code) -----------
" Defined AFTER the @-tier matches: in Vim, when several items match at the
" same position the LAST defined wins, and this region must beat the plain
" klammertextAppOpen match at '@code'. (Sublime's tokenizer picks the FIRST
" listed rule — the opposite convention; don't copy that ordering here.)
syn region klammertextVerbatim matchgroup=klammertextAppOpen start=/@\@1<!@code\%(\w\)\@!/ matchgroup=klammertextAppClose end=/code@/
" --- colors ---------------------------------------------------------------
" The shared palette, dark and light values (see the header). cterm values
" are the nearest xterm-256 approximations.
if &background ==# 'light'
hi def klammertextAppOpen guifg=#528599 ctermfg=66
hi def klammertextAppClose guifg=#426a7a ctermfg=60
hi def klammertextDefOpen guifg=#758b55 ctermfg=101
hi def klammertextDefClose guifg=#5e7044 ctermfg=101
hi def klammertextSysOpen guifg=#996743 ctermfg=94
hi def klammertextSysClose guifg=#7a5236 ctermfg=94
hi def klammertextMarker guifg=#994040 ctermfg=131
hi def klammertextRemoved guifg=#9a9a9a ctermfg=247
else
hi def klammertextAppOpen guifg=#89ddff ctermfg=117
hi def klammertextAppClose guifg=#6eb1cc ctermfg=74
hi def klammertextDefOpen guifg=#c3e88d ctermfg=150
hi def klammertextDefClose guifg=#9cba71 ctermfg=107
hi def klammertextSysOpen guifg=#ffab70 ctermfg=216
hi def klammertextSysClose guifg=#cc895a ctermfg=173
hi def klammertextMarker guifg=#ff6b6b ctermfg=210
hi def klammertextRemoved guifg=#8a8272 ctermfg=101
endif
hi def link klammertextMarkerLine klammertextMarker
hi def link klammertextRemovedLine klammertextRemoved
hi def link klammertextRemovedBlock klammertextRemoved
hi def link klammertextRemovedFile klammertextRemoved
let b:current_syntax = "klammertext"