Permissions¶
All the permissions settings are handled through these two functions:
sp-pair
for setting global pair properties,sp-local-pair
for setting local pair properties.
Each pair has opening and closing delimiter and a number of additional simple or list properties. Simple properties are simple scalars like a string or a number. List properties are lists of functions, predicates or constants and determine various behaviours of the pair. You can specify all the properties in one call. The distinction between simple and list properties is important when it comes to property inheritance.
There are additional properties you can set that are not handled by this
system. See M-x customize-group smartparens
for available options.
This mostly includes enabling and disabling various heuristics to handle
special cases.
List property inheritance¶
Note
If this section is not clear, consider looking at the Actions or Filters sections first to familiarize yourself with the concept of list properties.
When specifying a list of properties for a local pair, you can use special forms to inherit the global values and add or remove some of them locally. To do this, you specify the list with the special structure:
- if the list is a normal list, the global definition is ignored and
this list is used locally. This also means that specifying
nil
will simply remove all the values from the list. - if the first item in the list is keyword
:add
all the items in the list will be added to the global value - if the first item in the list is keyword
:rem
all the items in the list will be removed from the global value - if the list is of the form
((:add ...) (:rem ...))
the items in the “:add
list” are added and then the items in the “:rem
list” are removed from the global value.
Therefore, if you first add a global filter and then wish to remove it locally in a specific major mode, you can do it like this:
;; add a global filter
(sp-pair "'" nil :unless '(sp-point-after-word-p))
;; then remove it in c-mode
(sp-local-pair 'c-mode "'" nil :unless '(:rem sp-point-after-word-p))
;; in case there is only one filter the same result would be achieved with
(sp-local-pair 'c-mode "'" nil :unless nil)
By default all the properties are inherited from the global definition if one exist.
Actions¶
Each pair has with it associated a list of actions that it can perform. The supported actions are:
- insert - Autoinsert the closing pair when opening pair is typed.
- wrap - Wrap an active region with this pair if its opening delimiter is typed while region is active.
- autoskip - If the point is before the closing delimiter and you start
typing it, smartparens will skip over it in order to not create
unbalanced expression. However, this is also controlled by various
other settings, see
M-x customize-group RET smartparens RET
, and search forautoskip
. Settingautoskip
action will only tell smartparens it is possible (not necessary) to do the skipping. Therefore, to completely disable autoskip for a pair, you should remove this action. - navigate - Enable this pair for navigation/highlight and strictness checks.
- escape - Allow autoescaping of this delimiter in string contexts.
All of these actions are enabled for each pair by default. If you want
to only use pair for a subset of actions, you can specify the explicit
actions set by using the keyword argument (just argument from now on)
:actions
:
(sp-pair "'" "'" :actions '(wrap)) ;; only use '' pair for wrapping
(sp-pair "%" "%" :actions '(insert)) ;; only use %% pair for auto insertion, never for wrapping
(sp-pair "(" ")" :actions '(wrap insert)) ;; use () pair for both actions.
To disable some action only in a specific major mode you add the local
property for the pair. This is done using the function
sp-local-pair
. To completely disable a pair in a specific mode, you
can use nil
as the list of actions, then no actions will be
performed.
(sp-local-pair 'emacs-lisp-mode "'" nil :actions nil) ;; no '' pair in emacs-lisp-mode
(sp-local-pair 'markdown-mode "`" nil :actions '(insert)) ;; only use ` for auto insertion in markdown-mode
(sp-local-pair 'latex-mode "\\"" nil :actions '(:rem insert)) ;; do not use \" for insert action but use it for any other action
Note
You have to specify some value for the 3rd argument (closing delimiter) in sp-local-pair
. If you specify nil, the old value is preserved. If you specify a string, this will locally override the definiton of the closing pair.
Warning
You must specify the action lists together with all other properties in the same call with which you define the pair. Calling sp-local-pair
twice will ignore the previous call and reapply the current properties on top of the global definitions.
Filters¶
Each pair has with it associated two lists of predicates that decide if the action should be performed or not. These are the when list and unless list.
If the when list is not empty, at least one predicate on this list has to return non-nil in order for the action to be performed.
If the unless list is not empty, all of the predicates must return nil in order for the action to be performed.
You can use both lists, in which case the formula is:
(and (test-predicates when-list)
(not (test-predicates unless-list)))
This expression has to return true in order for the action to be performed.
To specify the filter lists for the pair, you use the arguments
:when
and :unless
:
;; The '' pair will autopair UNLESS the point is right after a word,
;; in which case you want to insert a single apostrophe.
(sp-pair "'" nil :unless '(sp-point-after-word-p))
;; You can also define local filters. Only pair the `' pair inside
;; emacs-lisp-mode WHEN point is inside a string. In other modes, the
;; global definition is used.
(sp-local-pair 'emacs-lisp-mode "`" nil :when '(sp-in-string-p)))
The built-in predicates are:
sp-in-string-p
- non-nil if point is inside a stringsp-in-docstring-p
- non-nil if point is inside an elisp docstringsp-in-code-p
- non-nil if point is inside codesp-in-comment-p
- non-nil if point is inside commentsp-in-math-p
- non-nil if point is inside a LaTeX math blocksp-point-in-empty-line-p
- non-nil if point is on an empty line (line filled only with whitespace)
Following predicates are only tested for the insert
action:
sp-point-before-eol-p
- non-nil if point is before whitespace followed by newlinesp-point-after-bol-p
- non-nil if point is after beginning of line followed by optional whitespacesp-point-at-bol-p
- non-nil if point is exactly at the beginning of linesp-point-before-symbol-p
- non-nil if point is before symbolsp-point-before-word-p
- non-nil if point is before word, where word is eitherw
or_
syntax classsp-point-after-word-p
- non nil if point is after wordsp-point-before-same-p
- non-nil if point is before an opening pair same as the currently inserted one
You can supply any number of your own predicates. These predicates should accept three arguments:
- id of the pair, which is the opening delimiter.
- action - See Actions.
- context - currently
'string
,'code
or'comment
. Note that the string/code distinction only makes sense in programming modes and modes that define what a “string” is.
Here’s a definition of the built-in predicate sp-point-after-word-p
for your inspiration:
(defun sp-point-after-word-p (id action context)
"Return t if point is after a word, nil otherwise. This
predicate is only tested on \"insert\" action."
(when (eq action 'insert)
(save-excursion
(backward-char 1)
(looking-back "\\sw\\|\\s_"))))
Delayed insertion¶
When using pairs not made of punctuation but of words, such as Ruby’s
def
and end
pair, we usually don’t want to expand def
in
indefinable
. To solve this issue, we can setup a “delayed
insertion”. This will tell smartparens to wait and not insert the
closing pair right away, but see what the next action might
be. Usually we want to pair when the next action is a SPC
or
RET
otherwise we don’t.
Setting up delayed insertion is very simple. Instead of the usual
predicate you add a list of triggers to the :when
filter.
These triggers will be tested after the next command is run (next
command means next interactive function in Emacs).
While you can add this special form to a list of regular tests, if it
is present in the :when
argument it will always take precedence
and the insertion will always be delayed.
Here’s a shortened example from smartparens-ruby.el
:
(sp-local-pair 'ruby-mode "def" "end"
:when '(("SPC" "RET" "<evil-ret>"))
)
This will set up a delayed insertion and insert the closing pair only
if the next action was invoked by SPC
, RET
or <evil-ret>
key. In addition to key codes, you can supply any number of names of
the commands (such as 'newline
) or regular :when
predicates
which will be tested after the next command finished.
The predicates on :unless
list are still considered and if they fail
the delayed insertion is not set up.
Make sure to also read the built-in function documentation for
sp-pair
inside Emacs by invoking C-h f sp-pair RET
.
Pre and post action hooks¶
Each pair has with it associated two lists of functions that are executed before and after an action is performed with this pair. These lists are:
- pre-handlers - functions that run before the action is executed.
- post-handlers - functions that run after the action is executed.
These lists are specified by using argument :pre-handlers
and
:post-handlers
respectivelly.
These actions are supported:
insert
- run insp-insert-pair
before/after closing delimiter is insertedwrap
- run after region is wrapped with a pair
Additionally, special actions for various navigation or sexp
manipulation commands are added to allow users to do some additional
formatting. These actions trigger both for :pre-handlers
and
:post-handlers
slurp-forward
- run in forward-slurp before/after the closing delimiter is movedslurp-backward
- same, but for backward slurpbarf-forward
- run in forward-barf before the closing delimiter is movedbarf-backward
- same, but for backward barf.split-sexp
- forsp-split-sexp
These actions only run in :post-handlers
:
beginning-of-sexp
- run aftersp-beginning-of-sexp
end-of-sexp
- run aftersp-end-of-sexp
indent-adjust-sexp
- run aftersp-indent-adjust-sexp
dedent-adjust-sexp
- run aftersp-dedent-adjust-sexp
skip-closing-pair
- run aftersp-skip-closing-pair
rewrap-sexp
- run aftersp-rewrap-sexp
You should always test for the type of action in your hooks and only run the code when apropriate. One can either create one callback with a “dispatch table” that then calls the relevant code, or supply many smaller functions that each test the action separately.
Each handler should be either a lambda, a symbol referring to a named function or an insertion specification. If a function, it should take these threes arguments (same as for the filter predicates):
id
- the id of the pair,action
- the action that triggered the handler,context
- context in which the action was triggered.
You can also provide a special form (function conditions...)
as a
handler. The condition can be either a name of command or a string
describing an event (in the format of single-key-description
). If
the last command or the event matches any on the conditions, the hook
will be executed. This means these hooks are run not after the
insertion, but after the next command is executed. This handler is
only executed after a command following an insertion action.
With these handlers (or hooks, using standard Emacs terminology) you can
perform various actions after the autoinsertions or wrappings. For
example, a simple function might be used to automatically add newlines
and position the point inside a {}
block in c-mode
:
(defun my-open-block-c-mode (id action context)
(when (eq action 'insert)
(newline)
(newline)
(indent-according-to-mode)
(previous-line)
(indent-according-to-mode)))
To add this function to the {}
pair post-handlers:
;; we use :add to keep any global handlers. If you want to replace
;; them, simply specify the "bare list" as an argument:
;; '(my-open-block-c-mode)
(sp-local-pair 'c-mode "{" nil :post-handlers '(:add my-open-block-c-mode))
Another useful hook might be to add a space after a pair if it is
directly followed by a word or another pair. An example for the ()
in emacs lisp:
(sp-local-pair 'emacs-lisp-mode "(" nil :post-handlers '(:add my-add-space-after-sexp-insertion))
(defun my-add-space-after-sexp-insertion (id action _context)
(when (eq action 'insert)
(save-excursion
(forward-char (length (plist-get (sp-get-pair id) :close)))
(when (or (eq (char-syntax (following-char)) ?w)
(looking-at (sp--get-opening-regexp)))
(insert " ")))))
Finally, an example of a special delayed action form. This will run the
my-create-newline-and-enter-sexp
function after you’ve inserted
{}
pair and immediately after hit RET
.
(sp-local-pair 'c++-mode "{" nil :post-handlers '((my-create-newline-and-enter-sexp "RET")))
(defun my-create-newline-and-enter-sexp (&rest _ignored)
"Open a new brace or bracket expression, with relevant newlines and indent. "
(newline)
(indent-according-to-mode)
(forward-line -1)
(indent-according-to-mode))
Of course, you can perform tasks of any complexity in these hooks, but it’s a good idea to keep them as simple as possible so the “editing flow” won’t be disrupted (e.g.: connecting to wikipedia and fetching 100 pages in a post-handler is not a good idea :)).
Insertion specification¶
Because it is very common to insert text inside a pair after this is inserted, smartparens provides a simple specification “language” you can use to make this process easier. Let us first define the language in a semi-formal way, then we will list some examples.
- character
|
means “save-excursion”, that is, all actions after this character will be carried out and then the point returns here. - sequence
||
means the same as above, but after all the instructions are executed,indent-according-to-mode
is called here as well. - a square bracket block
[...]
inserts a special directive/command. Right now, these are supported:i
- callindent-according-to-mode
at this point.d#
- calldelete-char
with the specified number (#
stands for an integer) as argument.
- to insert special characters
|
or[
put a backslash\
before it. You don’t need to escape]
. - all other non-special chatacters are inserted literally.
You can use this in :pre-handlers
and :post-handlers
instead of
a symbol specifying a function—both in the immediate and delayed
hooks.
The specification string is actually translated into a small lisp
program which is then evaluated with point inside the newly inserted
pair. Here we provide couple examples, for a more comprehensive list you
can look into the test suite
here
(look for test sp-test-insertion-specification-parser
).
"ab" => (progn (insert "ab"))
"a|b" =>
(progn
(insert "a")
(save-excursion
(insert "b")))
"||\n[i]" => ;; this is useful as a delayed RET action for {} pair in C-like languages
(progn ;; cf. my-create-newline-and-enter-sexp above
(save-excursion
(insert "\n")
(indent-according-to-mode))
(indent-according-to-mode))
"* ||\n[i]" => ;; pretty-formats /**/ style comments after RET
(progn
(insert "* ")
(save-excursion
(insert "\n")
(indent-according-to-mode))
(indent-according-to-mode))
;; like so
;; /*|*/ =>
;; /*
;; * |
;; */
Here are some examples replicating some of the above handlers that use function callbacks.
(sp-local-pair 'c++-mode "{" nil :post-handlers '((my-create-newline-and-enter-sexp "RET")))
;; =>
(sp-local-pair 'c++-mode "{" nil :post-handlers '(("||\n[i]" "RET")))
;; insert space, remember position, insert space
(sp-local-pair 'emacs-lisp-mode "(" nil :post-handlers '(:add " | "))
;; so that typing `(' results into `( | )', where | is the point.
Hooks for wrapping actions¶
Due to the nature of the wrapping action the pre-handlers are NOT
executed for this action. If you wish to query for information about
the last wrap in the post handler you can use the
sp-last-wrapped-region
variable to do so.
If you change the positions of the opening and closing delimiters (for
example by opening new lines or inserting text) you should also update
the sp-last-wrapped-region
variable to reflect these changes,
otherwise some functions might not work correctly (repeated wrapping
and last wrap deletion for example). See the documentation for this
variable for details.