%YAML 1.2
---
# Klammertext.sublime-syntax
#
# Sublime Text syntax highlighting for Klammertext (.kt and .k files).
# A port of the Emacs major mode doc/edit/emacs/klammertext-mode.el.
#
# ---------------------------------------------------------------------------
# What it highlights (mirrors the Emacs mode's eight token classes):
#
#   Text removal (#):
#     #  ...            remove to end of line   (marker + removed text)
#     ## ...            remove to end of file    (marker + removed text)
#     #[ ... ]#         remove enclosed text, nestable (markers + removed text)
#     #- #+ #/          whitespace operators: NOT removals, left unhighlighted
#                       (matched only so the '#' above does not eat the line)
#
#   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, so it
#             is consumed and NOT treated as a delimiter.  Left unscoped, to
#             match the Emacs mode, which shows escaped characters as ordinary
#             text.  (A run of carets pairs left-to-right: ^^ is a literal
#             caret, a leftover single ^ escapes the following character —
#             the '\^.' rule reproduces exactly that parity.)
#
#   Literal klammers:  @code ... code@  and  @c ... c@   interior is verbatim (no # or @
#             interpreted).  To add another literal klammer 'foo', copy the
#             '@code' rule and the 'literal_code' context below, replacing
#             code -> foo.
#
#             SYNC: the literal-klammer set's source of truth is
#             LITERAL_KLAMMERS in doc/edit/shared/klammertext_edit.py (the
#             shared core all the Python-side integrations import).  A static
#             syntax file cannot read it, so when you add or remove one,
#             mirror it in the per-editor artifacts:
#               * klammertext-literal-klammers in
#                 doc/edit/emacs/klammertext-mode.el
#               * the @NAME rule + literal_NAME context here
#               * the @NAME verbatim region in doc/edit/vim/syntax/klammertext.vim
#               * the @NAME rule in
#                 doc/edit/vscode/syntaxes/klammertext.tmLanguage.json
#             All are currently seeded with 'code' and 'c'.
#
# ---------------------------------------------------------------------------
# How open vs. close is decided (the same rule the Emacs scanner uses):
#   * a delimiter whose NAME follows the @-run (@name) is an OPENING;
#   * a bare @-run, or one whose NAME precedes it (name@), is a CLOSING.
# Because this tokenizer runs left-to-right, an opening consumes "@name" as one
# unit, so a trailing bare @ in the compact form @name@ is naturally a close.
# The (?![A-Za-z0-9_@]) look-ahead on every closing keeps "foo@bar" correct:
# @ is followed by a name, so it opens @bar and 'foo' stays plain text.
#
# ---------------------------------------------------------------------------
# Scope -> color.  Colors live in the color scheme, not here.  The package ships
# additive .sublime-color-scheme overrides for all five of Sublime's built-in
# schemes (Breakers, Celeste, Mariana, Monokai, Sixteen); each merges onto its
# scheme by filename and recolors only .klammertext scopes.  They use one hue
# system — application blue, definition green, system orange, each opening bright
# and its close the same hue darker — shown at full intensity on dark grounds and
# scaled down on light grounds.  Exact values are in each override's header.
#
# Without a matching override (e.g. a third-party scheme) a stock scheme still
# gives a meaningful default from these scope names: three klammer-family colors
# (function / storage / keyword), muted removed text (comment), plain escapes.
# To get the full palette on another scheme, copy one of the shipped overrides
# to <SchemeName>.sublime-color-scheme.
#
# ---------------------------------------------------------------------------
# Install:  put this file — together with its companions Klammertext.py,
#   Default.sublime-keymap and Comments.tmPreferences — in a dedicated package
#   folder named 'Klammertext' under Packages/ (Preferences -> Browse Packages
#   opens Packages/):
#     ~/.config/sublime-text/Packages/Klammertext/         (Linux)
#     ~/Library/Application Support/Sublime Text/Packages/Klammertext/  (macOS)
#   A dedicated folder (not Packages/User/) keeps the bundled keymap from
#   merging into your personal one.  Sublime picks it all up live and applies
#   the syntax to .kt and .k files.  (The syntax file alone also works from
#   Packages/User/ if you only want highlighting.)
#
# ---------------------------------------------------------------------------
# Known differences from the Emacs mode (deliberate, matching its own limits):
#   * @@ and @@@ definition BODIES are highlighted as ordinary Klammertext,
#     not treated specially — same as the Emacs mode.
#   * Delimiter MATCHING (jump + live highlight) is not in this syntax file —
#     Sublime's built-in bracket matching needs fixed character pairs, which @
#     (both open and close, decided by context) cannot provide.  It lives in
#     the companion Klammertext.py instead: klammertext_jump_to_match (C-c C-j
#     equivalent) and a ViewEventListener that highlights the matching
#     delimiter as the caret moves (show-paren equivalent), both reusing one
#     context-sensitive matcher.  This is a plugin concern, not a tokenizer one.
#   * Comment toggling is provided by the companion Comments.tmPreferences:
#     Ctrl-/ inserts '# ' (line removal), Ctrl-Shift-/ wraps in '#[ ... ]#'
#     (block removal).

name: Klammertext
file_extensions:
  - kt
  - k
scope: text.klammertext
version: 2

variables:
  # A klammer name: letters, digits, underscore.  A hyphen is NOT a name char
  # (the abbreviated form @name-arg1-arg2 ends the name at the first hyphen).
  name: '[A-Za-z0-9_]+'
  # A closing delimiter must not be followed by a name char (that would be an
  # opening @name) or another @ (that would be a longer @-run).
  not_delim: '(?![A-Za-z0-9_@])'

contexts:
  main:
    # --- escapes: ^X makes X literal; consumed so # / @ are not delimiters ---
    - match: '\^.'

    # --- text removal (#) ---
    - match: '##'
      scope: punctuation.definition.comment.klammertext
      push: removal_file
    - match: '#\['
      scope: punctuation.definition.comment.klammertext
      push: removal_block
    # whitespace operators #- #+ #/ (with optional count): not removals.
    # Matched (and left unscoped) so the '#' line rule below does not consume
    # the rest of the line.  Add a scope here if you would rather color them.
    - match: '#[-+/]\d*'
    - match: '#'
      scope: punctuation.definition.comment.klammertext
      push: removal_line

    # --- literal klammers: interior is verbatim (seeded: @code and @c) ---
    - match: '@code(?![A-Za-z0-9_])'
      scope: entity.name.function.begin.klammertext
      push: literal_code
    - match: '@c(?![A-Za-z0-9_])'
      scope: entity.name.function.begin.klammertext
      push: literal_c

    # --- system / target commands  @@@ ---
    - match: '@@@{{name}}'
      scope: keyword.control.begin.klammertext        # @@@name  opening
    - match: '@@@{{not_delim}}'
      scope: keyword.control.end.klammertext          # bare @@@  close
    - match: '{{name}}@@@{{not_delim}}'
      scope: keyword.control.end.klammertext          # name@@@  named close

    # --- klammer definitions  @@ ---
    - match: '@@{{name}}'
      scope: storage.type.begin.klammertext           # @@name  opening
    - match: '@@{{not_delim}}'
      scope: storage.type.end.klammertext             # bare @@  close
    - match: '{{name}}@@{{not_delim}}'
      scope: storage.type.end.klammertext             # name@@  named close

    # --- klammer applications  @ ---
    - match: '@{{name}}'
      scope: entity.name.function.begin.klammertext   # @name  opening
    - match: '@{{not_delim}}'
      scope: entity.name.function.end.klammertext     # bare @  close
    - match: '{{name}}@{{not_delim}}'
      scope: entity.name.function.end.klammertext     # name@  named close

  # rest of line is removed
  removal_line:
    - meta_scope: comment.line.klammertext
    - match: '\n'
      pop: true

  # rest of file is removed (## never closes)
  removal_file:
    - meta_scope: comment.block.klammertext

  # #[ ... ]# removed, nestable
  removal_block:
    - meta_scope: comment.block.klammertext
    - match: '#\['
      scope: punctuation.definition.comment.klammertext
      push: removal_block
    - match: '\]#'
      scope: punctuation.definition.comment.klammertext
      pop: true

  # @code ... code@  — interior verbatim (unscoped), only the close ends it
  literal_code:
    - match: 'code@'
      scope: entity.name.function.end.klammertext
      pop: true

  # @c ... c@  — the inline form of @code, same verbatim interior.  The
  # lookbehind keeps a word ending in c ("basic@") from closing the span —
  # essential for a single-letter name.
  literal_c:
    - match: '(?<![A-Za-z0-9_])c@'
      scope: entity.name.function.end.klammertext
      pop: true
