-
-
Notifications
You must be signed in to change notification settings - Fork 195
Permissions
All the permissions setting is done using the two functions: sp-pair
for setting global pair properties and sp-local-pair
for setting local pair properties.
Each pair has opening and closing delimiter and a number of additional "list properties". These properties are lists of functions, predicates or constants and determine various behaviours of the pair. You can specify all the list properties in one call.
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.
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))
;; 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.
Each pair has with it associated a list of actions that it can perform. The supported actions are:
- insert - Autoinsert the closing pair delimiter when opening pair delimiter 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.
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. This is default for each new pair
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.
You can specify the action lists (together with all other properties) in the same call with which you define the pair. This radically cuts down on the number of functions you need to call to completely define a new pair (in the old system, there were 15 specific functions for setting permissions!).
Note: these filters are used instead of the old sp-add/remove-ban/allow-...-[in-code/string] functions
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 true in order for the action to be performed.
If the "unless" list is not empty, none of the predicates must return true 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 string
- sp-in-code-p - non-nil if point is inside code
- sp-in-math-p - non-nil if point is inside a LaTeX math block
- sp-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 newline
- sp-point-after-bol-p - non-nil if point is after beginning of line followed by optional whitespace
- sp-point-at-bol-p - non-nil if point is exactly at the beginning of line
- sp-point-before-symbol-p - non-nil if point is before symbol
- sp-point-before-word-p - non-nil if point is before word, where word is either
w
or_
syntax class - sp-point-after-word-p - non nil if point is after word
- sp-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 - currently
'wrap
or'insert
. - context - currently
'string
or'code
. 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_"))))
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, when it is a SPC
or RET
character, we want to pair, otherwise we don't.
Setting up delayed insertion is very simple. Instead of the usual list of predicates, you set the :when
filter to a list of "triggers" that 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 a list is present in the :when
argument, it will always take precedence.
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.
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
.
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 in
sp-insert-pair
before/after closing delimiter is inserted - wrap - run after region is wrapped with a pair
Additionally, actions after 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 moved
- slurp-backward - same, but for backward slurp
- barf-forward - run in forward-barf before the closing delimiter is moved
- barf-backward - same, but for backward barf.
These actions only run in :post-handlers
:
- beginning-of-sexp - run after
sp-beginning-of-sexp
- end-of-sexp - run after
sp-end-of-sexp
- indent-adjust-sexp - run after
sp-indent-adjust-sexp
- dedent-adjust-sexp - run after
sp-dedent-adjust-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 (see below)
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 :)).
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.
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.