                             ━━━━━━━━━━━━━━
                              LISP-TS-MODE
                             ━━━━━━━━━━━━━━


A tree-sitter alternative to `lisp-mode' which uses [this grammar].

Table of Contents
─────────────────

1. Why?
2. Getting started
3. `FORMAT' directive indentation
4. Font lock


[this grammar] <https://codeberg.org/zshaftel/tree-sitter-cl-syntax>


1 Why?
══════

  The killer feature of this mode is the `FORMAT' string support. Using
  a specialized embedded grammar for format strings enables format
  directive font-lock and indentation. Trust me, the font-lock helps *a
  lot* when writing intricate format strings. The indentation is more
  niche and can be customized or simply disabled, see 3.

  A sample, showing off both indentation and font-lock:

  <file:format-screenshot.png>

  If this makes your eyes bleed, you can customize all those faces to
  look as boring as you'd like.

  The highlighted `~​{​~​}' directive after the cursor is from
  `show-paren-mode', because `lisp-ts-mode''s
  `syntax-propertize-function' adds delimiter syntax to format
  directives. This also means `forward-sexp' moves across those
  directives like it does on regular lists.

  Aside from that, the truth is tree-sitter doesn't add nearly as much
  to most Lisp languages *on its own* compared to other
  languages. Emacs' syntax system is (unsurprisingly) extremely well
  suited to handling Lisp syntax (notably, it even has first class
  support for nested comments unlike tree-sitter). *But* the CST
  generated by tree-sitter enables some /very/ fancy semantic font-lock,
  see the sister package [gaudy-cl].


[gaudy-cl] <https://codeberg.org/zshaftel/gaudy-cl>


2 Getting started
═════════════════

  This package is available on GNU ELPA, so here are some examples of
  how to activate:
  ┌────
  │ (use-package lisp-ts-mode
  │   :ensure t)
  └────
  If you use [elpaca], you can use the following recipe:
  ┌────
  │ (elpaca lisp-ts-mode)
  └────
  For [straight.el]:
  ┌────
  │ (straight-use-package 'lisp-ts-mode)
  └────
  To automatically activate `lisp-ts-mode' wherever `lisp-mode' normally
  activates, use
  ┌────
  │ (setf (alist-get 'lisp-mode major-mode-remap-alist) 'lisp-ts-mode)
  └────
  You can add this to the `:init' section of `use-package'.

  I also recommend this snippet:
  ┌────
  │ (setf (alist-get 'lisp-ts-mode font-lock-ignore)
  │       lisp-ts-mode-font-lock-ignore-keywords)
  └────
  You can place this in the `:config' section of `use-package'. This
  disables font-lock keywords which are already handled by the
  tree-sitter based font-lock rules.

  Lastly, the `FORMAT' string support is a separate minor mode,
  `lisp-ts-format-support-mode'. You can enable it with
  ┌────
  │ (add-hook 'lisp-ts-mode-hook #'lisp-ts-format-support-mode)
  └────
  But if you use [gaudy-cl], it will apply the grammar on its own (and
  much more).


[elpaca] <https://github.com/progfolio/elpaca>

[straight.el] <https://github.com/radian-software/straight.el>

[gaudy-cl] <https://codeberg.org/zshaftel/gaudy-cl>


3 `FORMAT' directive indentation
════════════════════════════════

  There is optional (but currently enabled by default) auto-indentation
  of format directives. Still somewhat experimental. This is mainly to
  indent relative to the paired directives (`~[', `~{'​, `~(' and
  `~<'). Example:
  ┌────
  │ "~<
  │ ~A
  │  ~:>"
  └────
  After pressing `TAB' on the second line:
  ┌────
  │ "~<~@
  │    ~A
  │  ~:>"
  └────
  The directive is indented to the column after the `~<', and the
  newline is converted to a `~@<newline>' directive so that the
  indentation of the output isn't affected. The behavior depends on a
  few customizable variables:
  `lisp-ts-mode-format-indent-predicate'
        A predicate for `treesit-node-match-p' (usually a function or
        regexp matching a treesit node's type) to determine which nodes
        should have their /contents/ indented. By default it matches the
        four paired directives, but you can also set it to indent
        relative to the start of the entire format string. *To disable
        format indentation entirely, set it to nil.*
  `lisp-ts-mode-format-indent-auto-escape-eol'
        Controls the behavior of the newline directive. It can be set to
        nil (don't indent if there isn't already a `~<newline>'), t
        (don't add it but indent anyway), a string specifying the
        directive prefix to add, or a cons pair `(LOGICAL-BLOCK
        . DEFAULT)' to specify a different string (like `~:@_~') to use
        within `~<​~:>'.
  `lisp-ts-mode-format-indent-tilde-relative'
        Determines which column is used as an anchor for calculating
        indentation (using the following two variables), and the
        indentation of the end of a paired directive when it begins a
        line. If nil (the default), the directive characters themselves
        are aligned, like
        ┌────
        │ "~:@{
        │      ~A
        │    ~}"
        └────
        If set to non-nil, the ~s are aligned:
        ┌────
        │ "~:@{
        │   ~A
        │  ~}"
        └────
  `lisp-ts-mode-format-group-indent-offset'
        Number of columns added to indentation relative to the start of
        a paired directive.
  `lisp-ts-mode-format-string-indent-offset'
        Like the above but controls indentation relative to the start of
        the string, if that's enabled by
        `lisp-ts-mode-format-indent-predicate'.
  `lisp-ts-mode-format-indent-function'
        This variable isn't customizable because it's meant to be used
        with `add-function'. This is the function called to perform the
        indentation, so you can wrap it to control if, when or how the
        indentation is performed.

  For more details see each variable's documentation.


4 Font lock
═══════════

  There are distinct faces for every component of format directives I
  could think of: `~', numeric, character, `V' and `#' parameters, the
  `,' separator between parameters, `:' and `@' modifiers and the
  directive character itself all have specific faces. The paired
  directives (`~[', `~{'​, `~(' and `~<') have a separate face from other
  directives, and you can customize
  `lisp-ts-mode-format-rainbow-delimiters' to use [rainbow-delimiters]
  faces to highlight those directives based on nesting level. There are
  also three faces for each level of `#||#' comment nesting (the top
  level uses `font-lock-comment-face'). See `M-x customize-group RET
  lisp-ts-mode'.


[rainbow-delimiters] <https://github.com/Fanael/rainbow-delimiters>
