Function: define-combobulate-language

define-combobulate-language is a macro defined in combobulate-setup.el.

Signature

(define-combobulate-language &key NAME MAJOR-MODES (CUSTOM nil) SETUP-FN (KEYMAP-VAR nil) (EXTRA-DEFCUSTOMS nil) (EXTRA-DEFVARS nil))

Documentation

Define a new language for Combobulate.

NAME is the name of the language as it'll be known to Combobulate and tree-sitter, and MAJOR-MODES is a list of major modes that should be set up for this language.

Source Code

;; Defined in /nix/store/b5fvrwzi3zvkabyzx0in6gw1cj963z46-emacs-packages-deps/share/emacs/site-lisp/combobulate-setup.el
(cl-defmacro define-combobulate-language (&key name
                                               major-modes (custom nil)
                                               setup-fn
                                               (keymap-var nil)
                                               (extra-defcustoms nil)
                                               (extra-defvars nil))
  "Define a new language for Combobulate.

NAME is the name of the language as it'll be known to
Combobulate and tree-sitter, and MAJOR-MODES is a list of major
modes that should be set up for this language."
  (cl-assert (symbolp name) t "NAME must be a symbol")
  (let ((group-name (intern (format "combobulate-language-%s" name))))
    ;; Create a customize group for the language
    (let ((defvars
           ;; ((SHORTHAND DOCSTRING DEFAULT-VALUE) ... )
           (append
            combobulate-default-definitions
            (and (boundp extra-defvars) (symbol-value extra-defvars))))
          (defcustoms
           (append
            `((major-modes
               "List of major modes where this language applies.

Combobulate will set up tree-sitter to use that language as the
primary buffer language if the major mode does not do this
itself. This is also useful for modes that lack tree-sitter
support where you still want to use Combobulate's features."
               ',major-modes
               :type '(repeat symbol)))
            (and (boundp extra-defcustoms) (symbol-value extra-defcustoms))))
          (known-variable-shorthands)
          (decls))
      (cl-flet*
          ((intern-var (lang-name variable)
             (combobulate-get variable lang-name))
           (intern-lang-var (variable)
             (push (if (stringp variable)
                       (intern variable)
                     variable)
                   known-variable-shorthands)
             (intern-var name variable)))
        (push `(custom-declare-group
                ',group-name nil ,(format "Language settings for `%s'" name)
                :group 'combobulate)
              decls)
        ;; Appease the byte compiler.
        (push '(declare-function combobulate-baseline-indentation-default "combobulate-manipulation") decls)
        (push '(defvar combobulate-key-map) decls)
        (push '(defvar combobulate-options-key-map) decls)
        ;; Create the `defcustom' forms.
        (pcase-dolist (`(,shorthand-var ,docstring ,default . ,rest) defcustoms)
          ;; (push shorthand-var known-variable-shorthands)
          (push `(defcustom ,(intern-lang-var shorthand-var) ,default ,docstring
                   ,@rest
                   :group ',group-name)
                decls))
        ;; Create the `defvar' forms.
        (pcase-dolist (`(,shorthand-var ,docstring ,default) defvars)
          ;; (push shorthand-var known-variable-shorthands)
          (push `(defvar ,(intern-lang-var shorthand-var)
                   ;; We must check that we have an association record
                   ;; explicitly (and not just the value) as it could
                   ;; be nil, which would then roll over to default,
                   ;; which is not what we want.
                   ,(if-let ((v (assoc shorthand-var (symbol-value custom))))
                        (cadr v)
                      default)
                   ,docstring)
                decls))
        ;; Create (or reuse) a key map and create a minor mode for
        ;; LANGUAGE.
        (let ((language-keymap (or keymap-var (intern-lang-var "map")))
              (envelope-keymap (or keymap-var (intern-lang-var "envelope-map")))
              (minor-mode-fn (intern-lang-var "minor-mode")))
          ;; General key map for this language
          (push `(makunbound ',language-keymap) decls)
          (push `(defvar-keymap ,language-keymap
                   :doc ,(format "Keymap for Combobulate language for `%s'." name)
                   :full nil
                   :parent combobulate-key-map)
                decls)

          ;; This is the envelope-specific keymap
          (push `(defvar-keymap ,envelope-keymap
                   :doc ,(format "Keymap for Combobulate envelopes for `%s'." name)
                   :full nil)
                decls)
          ;; This maps the `e' key to the envelope keymap.
          (push `(define-key ,language-keymap (kbd ,(format "%s e" combobulate-key-prefix))
                             ,envelope-keymap)
                decls)
          ;; Create a minor mode for this language.
          (push `(defvar-local ,minor-mode-fn nil
                   ,(format "Combobulate minor mode for the `%s' tree-sitter language." name))
                decls)
          (push `(define-minor-mode ,minor-mode-fn
                   ,(format "Combobulate minor mode for the `%s' tree-sitter language." name)
                   :init-value nil
                   :lighter "©"
                   :keymap ,language-keymap
                   ;; Recycle `combobulate-mode' as the variable used
                   ;; to store the toggle state of this minor mode
                   ;; even though it is effectively 'shared' across
                   ;; all the various minor modes we may create when
                   ;; this macro is called. This has two beneficial
                   ;; advantages:
                   ;;
                   ;;  1. `M-x combobulate-mode' is the default
                   ;;  entrypoint for the package, and people expect
                   ;;  the "mode" variable to match the command name.
                   ;;
                   ;;  2. It is not possible to engage more than one
                   ;;  minor mode in the same buffer.
                   :variable ,minor-mode-fn
                   ;; Hm... leaving this to nil is probably for the
                   ;; best. We do not want to encourage people to go
                   ;; around experimenting with the minor mode as that
                   ;; could circumvent other setup processes.
                   :interactive nil
                   ;; This is the generic setup function that is
                   ;; always run.
                   (combobulate-setup)
                   ;; If a language has a custom setup function, we
                   ;; run it with the language we are being
                   ;; triggered in.
                   ,(when setup-fn
                      `(,setup-fn ',name)))
                decls)
          (push `(combobulate-register-language ',name ',major-modes #',minor-mode-fn)
                decls)
          (push `(defconst ,(intern-lang-var "defined-variables")
                   ',known-variable-shorthands
                   ,(format "List of variable shorthands known to `%s'.

Each shorthand is a symbol referencing a variable belonging to
that language. They are best accessed with the `combobulate-read'
macro which optionally takes a language argument to retrieve that
language's setting." name))
                decls)))
      `(progn ,@(nreverse decls)))))