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)))