Function: combobulate-envelope-expand-instructions

combobulate-envelope-expand-instructions is a natively compiled function defined in combobulate-envelope.el.

Signature

(combobulate-envelope-expand-instructions INSTRUCTIONS &optional REGISTERS)

Documentation

Expand an envelope of INSTRUCTIONS at point.

Combobulate envelopes work in much the same way as Tempo or Skeletons, but Combobulate's envelopes are more powerful.

Here are some of the differences:

1. Prompts are executed in the minibuffer, much like the
   aforementioned tools, but they also update interactively as
   you type. You can have transformers that alter some or all of
   the prompts and fields you use.

2. Regions are now stored in a register, and thus r, r> and
   so on are in effect just using those.

3. The indentation algorithm now "understands" block-based,
whitespace-sensitive languages like Python better.

   This follows on from point #2: special care is made in
   whitespace-sensitive languages like Python. Combobulate will
   attempt to ensure the indentation is correct for the block you
   wish to insert the code into. Combobulate can only do this if
   the underlying Python mode's indentation engine is capable of
   determining the correct indentation for a given line. If it is
   not, then Combobulate will not be able to determine the
   correct indentation either.

4. Remembering a previous line's indentation is very difficult
   with other templating tools. Combobulate simplifies this with
   the save-column form. When Combobulate enters a
   save-column form it saves the column offset (but not point!)
   and restores the column on exit. That makes it possible to
   have nested sequences of code and be assured that the column
   is reset correctly when you exit the block. This is only of
   importance in whitespace-sensitive languages where Combobulate
   cannot safely indent the whole region.

5. You can now explicitly place point with @. Multiple
   instances of @ are remembered and presented to you at the
   end of the expansion so that you can choose which one to place
   your point at.

7. Repetition (also a feature in Skeleton, but not Tempo) is also
   possible with repeat and repeat-1. Note that indentation
   in whitespace-sensitive languages can be difficult to control
   with these forms. Keep them simple if you can.

If there is an active REGION, then everything between point and mark is extracted and deleted and made available to INSTRUCTIONS through the register system. It is accessed implicitly by calling r or r> without a form; or explicitly with (r REGISTER [DEFAULT]) where REGISTER is region.

The following instructions are supported by Combobulate's envelope expansion:

 >

   Call indent-according-to-mode at point.

 (r REGISTER [DEFAULT])
 (r> REGISTER [DEFAULT])
 r
 r>

   Insert REGISTER at point. Where REGISTER defaults to
   region (when envelope-indent-region-function
   is non-nil) or region-indented when it is nil.

   Both register names hold the marked region (which is likely
   the triggering node, if the user did not mark a region
   themselves) when the envelope is activated. The value is
   stored in combobulate-envelope-registers.

   The DEFAULT is a fallback value in case the REGISTER does not
   exist.

   Instructions ending with > also indent the inserted text
   according to the major mode's indentation preferences. It uses
   indent-region for languages that are not
   whitespace-sensitive.

   However, if envelope-indent-region-function is
   nil (as it is in the likes of python-mode) then a
   specialized indentation system is used instead. The relative
   indentation at the point of envelope invocation is preserved
   and used to indent the inserted register according to its new
   column offset when it is inserted and indented by r>.

 (b BLOCK)

   Execute the BLOCK of instructions and, when exiting, action
   all the user actions that require user input:

     - repeat instructions are executed first;
     - Then, choice prompts are executed;
     - Then, prompts are executed.

   All envelopes are wrapped an implicit b block. You really
   only need this construct if you're doing something very
   specific, such as multiple distinct choice groupings.

 (choice BLOCK)
 (choice* :name NAME :missing MISSING-BLOCK :rest BLOCK)

   Collect all choice instructions in the current b block and
   present them to the user to choose one. The BLOCK is a list of
   instructions that are executed when the user picks that
   choice.

   The choice* form is a variant that allows you to specify a
   name for the choice, a block to execute if the choice is
   *not* picked, and a block to execute if the choice is picked.

   Warning: If you are using multiple choice* instructions with
   :missing properties set in a row, you may run into expansion
   problems.

 (prompt TAG PROMPT [TRANSFORMER-FN])
 (p TAG PROMPT [TRANSFORMER-FN])

   Place a prompt named TAG at point and interactively query the
   user with PROMPT. Any input is optionally run through
   TRANSFORMER-FN which takes one function argument: the input
   string.

   Prompts are commonly matched with fields, which replicate the
   input of a prompt.

   Prompt values are stored in
   combobulate-envelope-registers. If there is already an entry
   for TAG in that variable, then no interactive prompt is
   presented, and the stored value is used instead.

 (field TAG [TRANSFORMER-FN])
 (f TAG [TRANSFORMER-FN])

   Like a prompt, but only inserts the text belonging to its
   prompt named TAG.

 @
   Insert a point marker at point. Point markers move with the
   text being inserted, which is probably what you want. After
   expansion, your point is placed at this location. If there is
   more than one, then you are asked to pick the one to jump to.

 @@

   Track the absolute position of point. This is useful if you
   want to place point at a specific location after expansion,
   and because @ does not put your point where you want it to.

 n
 n>

   Call newline or newline followed by
   indent-according-to-mode.

   Indentation is done according to your major mode. Pay
   attention if you use an envelope in a whitespace-sensitive
   language like Python or YAML: you may need to use
   save-column to remember the indentation of the previous
   line, or < to remove one level of semantic indentation.

 <

   Remove one level of indentation from the current line. This
   operation only works in select, whitespace-sensitive modes
   like Python or YAML.

 (save-column BLOCK)

   Saves point's *column* -- but not point itself! -- when
   entering BLOCK. Use this to remember indentation offset from
   the beginning of the line.

   This is mostly of use in whitespace-sensitive languages like
   Python.

   You are strongly encouraged to place a singular n at the end
   of BLOCK: this will ensure your point is placed on a new line
   with the correct indentation.

 (repeat BLOCK)
 (repeat-1 BLOCK)

   Temporarily inserts BLOCK and then asks if you want to keep
   it. If you answer yes, the BLOCK is kept; if you answer no,
   it is removed and Combobulate exits the repeat instruction
   form and carries on.

   The instruction repeat-1 is identical to repeat except it
   only allows at most 1 entry.

 "STRING"

   Insert STRING at point.

Source Code

;; Defined in /nix/store/b5fvrwzi3zvkabyzx0in6gw1cj963z46-emacs-packages-deps/share/emacs/site-lisp/combobulate-envelope.el
(defun combobulate-envelope-expand-instructions (instructions &optional registers)
  "Expand an envelope of INSTRUCTIONS at point.

Combobulate envelopes work in much the same way as Tempo or
Skeletons, but Combobulate's envelopes are more powerful.

Here are some of the differences:

1. Prompts are executed in the minibuffer, much like the
   aforementioned tools, but they also update interactively as
   you type. You can have transformers that alter some or all of
   the prompts and fields you use.

2. Regions are now stored in a register, and thus `r', `r>' and
   so on are in effect just using those.

3. The indentation algorithm now \"understands\" block-based,
whitespace-sensitive languages like Python better.

   This follows on from point #2: special care is made in
   whitespace-sensitive languages like Python. Combobulate will
   attempt to ensure the indentation is correct for the block you
   wish to insert the code into. Combobulate can only do this if
   the underlying Python mode's indentation engine is capable of
   determining the correct indentation for a given line. If it is
   not, then Combobulate will not be able to determine the
   correct indentation either.

4. Remembering a previous line's indentation is very difficult
   with other templating tools. Combobulate simplifies this with
   the `save-column' form. When Combobulate enters a
   `save-column' form it saves the column offset (but not point!)
   and restores the column on exit. That makes it possible to
   have nested sequences of code and be assured that the column
   is reset correctly when you exit the block. This is only of
   importance in whitespace-sensitive languages where Combobulate
   cannot safely indent the whole region.

5. You can now explicitly place point with `@'. Multiple
   instances of `@' are remembered and presented to you at the
   end of the expansion so that you can choose which one to place
   your point at.

7. Repetition (also a feature in Skeleton, but not Tempo) is also
   possible with `repeat' and `repeat-1'. Note that indentation
   in whitespace-sensitive languages can be difficult to control
   with these forms. Keep them simple if you can.

If there is an active REGION, then everything between `point' and
`mark' is extracted and deleted and made available to
INSTRUCTIONS through the register system. It is accessed
implicitly by calling `r' or `r>' without a form; or explicitly
with `(r REGISTER [DEFAULT])' where REGISTER is `region'.

The following instructions are supported by Combobulate's envelope
expansion:

 `>'

   Call `indent-according-to-mode' at point.

 `(r REGISTER [DEFAULT])'
 `(r> REGISTER [DEFAULT])'
 `r'
 `r>'

   Insert REGISTER at point. Where REGISTER defaults to
   `region' (when `envelope-indent-region-function'
   is non-nil) or `region-indented' when it is nil.

   Both register names hold the marked region (which is likely
   the triggering node, if the user did not mark a region
   themselves) when the envelope is activated. The value is
   stored in `combobulate-envelope-registers'.

   The DEFAULT is a fallback value in case the REGISTER does not
   exist.

   Instructions ending with `>' also indent the inserted text
   according to the major mode's indentation preferences. It uses
   `indent-region' for languages that are not
   whitespace-sensitive.

   However, if `envelope-indent-region-function' is
   nil (as it is in the likes of `python-mode') then a
   specialized indentation system is used instead. The relative
   indentation at the point of envelope invocation is preserved
   and used to indent the inserted register according to its new
   column offset when it is inserted and indented by `r>'.

 `(b BLOCK)'

   Execute the BLOCK of instructions and, when exiting, action
   all the user actions that require user input:

     - `repeat' instructions are executed first;
     - Then, `choice' prompts are executed;
     - Then, `prompt's are executed.

   All envelopes are wrapped an implicit `b' block. You really
   only need this construct if you're doing something very
   specific, such as multiple distinct choice groupings.

 `(choice BLOCK)'
 `(choice* :name NAME :missing MISSING-BLOCK :rest BLOCK)'

   Collect all choice instructions in the current `b' block and
   present them to the user to choose one. The BLOCK is a list of
   instructions that are executed when the user picks that
   choice.

   The `choice*' form is a variant that allows you to specify a
   name for the choice, a block to execute if the choice is
   *not* picked, and a block to execute if the choice is picked.

   Warning: If you are using multiple `choice*' instructions with
   `:missing' properties set in a row, you may run into expansion
   problems.

 `(prompt TAG PROMPT [TRANSFORMER-FN])'
 `(p TAG PROMPT [TRANSFORMER-FN])'

   Place a prompt named TAG at point and interactively query the
   user with PROMPT. Any input is optionally run through
   TRANSFORMER-FN which takes one function argument: the input
   string.

   Prompts are commonly matched with fields, which replicate the
   input of a prompt.

   Prompt values are stored in
   `combobulate-envelope-registers'. If there is already an entry
   for TAG in that variable, then no interactive prompt is
   presented, and the stored value is used instead.

 `(field TAG [TRANSFORMER-FN])'
 `(f TAG [TRANSFORMER-FN])'

   Like a prompt, but only inserts the text belonging to its
   prompt named TAG.

 `@'
   Insert a point marker at point. Point markers move with the
   text being inserted, which is probably what you want. After
   expansion, your point is placed at this location. If there is
   more than one, then you are asked to pick the one to jump to.

 `@@'

   Track the absolute position of point. This is useful if you
   want to place point at a specific location after expansion,
   and because `@' does not put your point where you want it to.

 `n'
 `n>'

   Call `newline' or `newline' followed by
   `indent-according-to-mode'.

   Indentation is done according to your major mode. Pay
   attention if you use an envelope in a whitespace-sensitive
   language like Python or YAML: you may need to use
   `save-column' to remember the indentation of the previous
   line, or `<' to remove one level of semantic indentation.

 `<'

   Remove one level of indentation from the current line. This
   operation only works in select, whitespace-sensitive modes
   like Python or YAML.

 `(save-column BLOCK)'

   Saves point's *column* -- but not point itself! -- when
   entering BLOCK. Use this to remember indentation offset from
   the beginning of the line.

   This is mostly of use in whitespace-sensitive languages like
   Python.

   You are strongly encouraged to place a singular `n' at the end
   of BLOCK: this will ensure your point is placed on a new line
   with the correct indentation.

 `(repeat BLOCK)'
 `(repeat-1 BLOCK)'

   Temporarily inserts BLOCK and then asks if you want to keep
   it.  If you answer yes, the BLOCK is kept; if you answer no,
   it is removed and Combobulate exits the repeat instruction
   form and carries on.

   The instruction `repeat-1' is identical to `repeat' except it
   only allows at most 1 entry.

 \"STRING\"

   Insert STRING at point."
  (when (and (use-region-p) (> (point) (mark))) (exchange-point-and-mark))
  (let ((start (point))
        (end (point-marker))
        (change-group (prepare-change-group))
        (state 'start)
        (combobulate-envelope--registers (append registers combobulate-envelope-registers))
        (combobulate-envelope-refactor-id (combobulate-refactor-setup))
        (selected-point (point)))
    (activate-change-group change-group)
    (if (use-region-p)
        (progn
          (indent-region (point) (mark) nil)
          (let ((col (current-indentation))
                (text (substring-no-properties (delete-and-extract-region (point) (mark)))))
            (push (cons 'region-indented (combobulate-indent-string-first-line text col))
                  combobulate-envelope--registers)
            (push (cons 'region text) combobulate-envelope--registers)
            ;; deactivate the mark as the region would otherwise interfere
            ;; with the expansion.
            (setq mark-active nil))))
    (cl-assert (eq state 'start))
    (combobulate-refactor (:id combobulate-envelope-refactor-id)
      (condition-case nil
          (progn
            (let ((ctx (combobulate-envelope-expand-instructions-1
                        ;; Build the base template for the envelope we are to
                        ;; expand. The base template is just `b*', which is a
                        ;; "super-block" that expands all user actions such as
                        ;; choice, repeat, prompt and -- for `b*' specifically --
                        ;; also `point'.
                        `((b* (repeat choice prompt point selected-point) ,@instructions)))))
              ;; The `point' category is special in that it is
              ;; executed only at the `b*' superblock stage. If there
              ;; is more than one `point', the user is asked to
              ;; choose: that choice is then put back into the context
              ;; as `selected-point'. This code will then move point
              ;; to that location.
              ;;
              ;; It's a little bit hacky, but we assume that as we
              ;; come out of this expansion of post-run instructions,
              ;; that where ever point is, is what we want to end the
              ;; envelope at.
              (combobulate-envelope-expand-post-run-instructions
               ctx
               '(selected-point))
              (setq selected-point (point))
              ;; Track the contextual end of the envelope.
              (set-marker end (combobulate-envelope-context-end ctx)))
            (commit)
            (setq state 'success))
        (quit (rollback) (setq state 'error))))
    ;; Amalgamate all the changes into one single change. If a user
    ;; accepts an envelope, but changes their mind, they won't have to
    ;; undo multiple times to return to the state they were in before
    ;; the envelope was expanded.
    (undo-amalgamate-change-group change-group)
    ;; Only when both `combobulate-envelope--undo-on-quit' and `state'
    ;; is `error' is set do we cancel the change group. The
    ;; `combobulate-envelope--undo-on-quit' variable is there to
    ;; prevent cancellations during proffer choices where the envelope
    ;; is previewed.
    (if (and (eq state 'error) combobulate-envelope--undo-on-quit)
        (cancel-change-group change-group)
      (accept-change-group change-group))
    ;; Throw a courtesy region indent call if we support such a
    ;; thing. (We do not in the likes of Python, where indenting a
    ;; region is dangerous.
    (when (combobulate-read envelope-indent-region-function)
      (apply (combobulate-read envelope-indent-region-function)
             (combobulate-extend-region-to-whole-lines start end)))
    (cons (cons start end) selected-point)))