\
This package provides a powerful surrounding tool. It helps you to add, delete and change pairs. It is:
Powerful :: =isolate= can recognize complex inputs like html tags.
Straight forward :: Each action (add/delete/change) has two variants: =quick= and =long=. =quick= takes one key like =(= and =long= takes a longer input like ~
Easily extendable :: You can define new complex pairs easily by wrting regexp.
\
Credit: Inspired by [[https://github.com/tslilc/siege-mode/blob/master/siege-mode.el][siege-mode]].
[[./img/isolate.png]]
Table of Contents :TOC:
[[#why-you-want-it][Why you want it]]
[[#introduction][Introduction]]
[[#install][Install]]
[[#usage][Usage]]
[[#customizaion][Customizaion]]
[[#comparison-with-alternatives][Comparison with alternatives]]
[[#contribution][Contribution]]
Introduction
** How it works
** Recognize?
Basically, =isolate= provides three ways to regognize a pair:
This works for both adding and deleting. *** Add examples
**** An example for the first case
Command: =isolate-quick-add=
input: ( HELLO -> (HELLO)
input: ) HELLO -> ( HELLO )
[[./img/isolate-add-2.gif]]
**** A side node: segmentation
When you separate your input into segements on the left, isolate inverse the order of them on the right.
The separator is "," by default.
ABC -> 1,2,3ABC -> 123ABC321
**** An example for the second case
By inserting =
And it even recognizes a more complex input and completed the pair correctly.
Command: =isolate-long-add=
HELLO ->
HELLO ->
HELLO
HELLO ->
HELLO ->
HELLO
HELLO ->
,⮐,HELLO ->
HELLO
[[./img/isolate-add-1.gif]]
**** An example for the third case
Command: =isolate-long-add=
HELLO -> org-srcHELLO ->
=#+BEGIN_SRC=
=HELLO=
=#+END_SRC=
[[./img/isolate-add-3.gif]]
*** Delete examples
**** Above features also apply to change and delete commands
Command: =isolate-quick-delete=
(HELLO) -> HELLO
( HELLO ) -> HELLO
[[./img/isolate-delete-2.gif]]
**** The shortcuts are especially useful in deleting
Command: =isolate-long-delete=
[[./img/isolate-delete-1.gif]]
Command: =isolate-long-delete=
**** Featuring shortcuts appeared above
input: org-src
=#+BEGIN_SRC=
=HELLO=
=#+END_SRC=
-> HELLO
[[./img/isolate-delete-3.gif]]
** You can extend it!
All of these cool featurea are implemented by regexp matching (except segmentation). Therefore, you can extend these isolation magics by writing regexp rules! It's very easy!
Add [[https://melpa.org/#/getting-started][melpa]] to your package archives and =M-x package-install RET isolate RET=.
There are six commands avaliable:
| =isolate-quick-add= | =isolate-long-add= | | =isolate-quick-delete= | =isolate-long-delete= | | =isolate-quick-change= | =isolate-long-change= |
Quick commads asks for a key and add/delete/change the pair matches to it. Long commands allows you to make more complex edits and apply the change with =C-c C-c=
If you use evil, I suggest binding quick commands to =s= operators and long commands to =S= operators. i.e. =s=, =S=, =ds=, =dS=, =cs=, =cS=.
Note for non-evil users:
Recently I'm trying to switch to emacs keybindings and really missed the fast keybindings of isolate in evil. If you are like me, try this snippet and you can invoke isolate with one key stroke when region is active:
(defun activate-mark-hook@set-transient-map () (set-transient-map (let ((map (make-sparse-keymap))) (define-key map "s" #'isolate-quick-add) (define-key map "S" #'isolate-long-add) (define-key map "d" #'isolate-quick-delete) (define-key map "D" #'isolate-long-delete) (define-key map "c" #'isolate-quick-change) (define-key map "C" #'isolate-long-change) map)
(add-hook 'activate-mark-hook #'activate-mark-hook@set-transient-map)
Thanks to @xuchunyang for this snippet.
*** Long add
| C-c C-a | Go to beginning of left side | | C-c C-e | Go to end of left side | | C-c C-c | Finish edit | | C-c C-q | Abort edit |
*** Long delete
In minibuffer:
| C-p | Match outter pair | | C-n | Match inner pair | | RET | Finish edit | | C-g | Abort edit |
** Segmentation
You can segment your input with a special separator (default to ","). =isolate= inverses the order of segments on the right side:
1,2,3 -> 321
A very good use case is line surrounding:
(,RET -> RET)
which looks like:
( surrounded-text )
** Quick command shortcuts
=)=, =]=, =}= and =>= are translated to pair with space: =( surrounded-text )=
** [[https://github.com/emacs-evil/evil-surround][evil-surround]]
| | evil-surround | isolate | |---------------+---------------------------------+----------------------------------------------------------| | requires evil | yes | no | | text objects | yes | no (but that means straight forward!) | | extending | write hooks for each major mode | specify major mode (and other) condition(s) in rule list | | regexp | no | yes |
** [[https://github.com/cute-jumper/embrace.el][embrace]]
| | embrace | isolate | |-----------+-----------------------------+----------------------------------------------------------| | extending | embrace-language-minor-mode | specify major mode (and other) condition(s) in rule list | | regexp | no | yes |
** [[https://github.com/tslilc/siege-mode][siege-mode]]
| | siege | isolate | |-----------+--------------------------+----------------------------------------------------------| | extending | I'm not familiar with it | specify major mode (and other) condition(s) in rule list | | regexp | yes | yes | | abilities | add | add, change, delete |
The biggest part!
** Shortcuts for quick commands
The most useful rule list might be quick command shortcuts list. This is how "pair with space" are achieved.
When using quick commands you enter a key. But before isolate matches this single character to a pair, the string goes trhough a translator.
Basically, you can "translate" some predefined keys to longer strings, for example:
) -> "(, " (parans -> parens with space)
Personalize it
Since normally you don't surround anything with =a=, =c=, =x=, etc, you can bind your personal shortcuts to them!
How about binding =s= to =#+BEGIN_SRC= and =#+END_SRC=?
The rule list is =isolate-quick-shortcut-list=, its default value is:
(defvar isolate-quick-shortcut-list '((:from "]" :to "[, ") (:from ")" :to "(, ") (:from "}" :to "{, ") (:from ">" :to "<, ")) "Shortcuts for `isolate-quick-xxx' functions.
For example, by default \"]\" is mapped to \"[ \", etc.
Each element is an plist representing a shortcut. Each shortcut have three possible keys: :from, :to and :condition. :from and :to are strings (not regexp),
:condition is a function that takes user input as argument. :condition is optional. If :condition exists and returns nil, the shortcut will be ignored.")
** Rule list
The matching rule is in =isolate-pair-list=. =isolate= try to match user input whth a pair in this list.
How does isolate uses this rule list:
For add functions, isolates record user input (the left side), calculates the right side, insert right side and the end of region.
The calculating part is where the rule list apply. =isolate= uses the user input to match each "pair" in the rule list, and outputs a left and right side string.
There are three ways to match left side and gets a pair, as described in the documentation below.
If the user input doesn't match anything, =isolate= simply uses it as-is.
Here is the default value and documentation of it:
(defvar isolate-pair-list '((:to-left "`" :to-right "'" :no-regexp t :condition (lambda (_) (if (equal major-mode 'emacs-lisp-mode) t nil))) (:to-left "(" :to-right ")") (:to-left "[" :to-right "]" :no-regexp t) (:to-left "{" :to-right "}") (:to-left "<" :to-right ">") (:from "<\([^ ]+\).*>" :to-right (lambda (left) (format "</%s>" (match-string 1 left)))) (:to-left "\{begin}" :to-right "\{end}") (:from "org-src" :to-left "#+BEGIN_SRC\n" :to-right "#+END_SRC\n" :no-regexp t)) "Matching pairs. Each element is an plist with five possible keys: :from, :to-left, :to-right, :no-regexp and :condition. Only (:from or :to-left) and :to-right are required.
If only :to-left, and it equal to user input, and matches and condition passes, :to-left is used as left of pair, :to-right is used as right of pair.
If only :from, and the regexp of :from matches user input, user-input is used as left of pair and :to-right is used as right of pair.
If both :from and :to-left exists, :from as regexp is used to match user-input, if it matches, :to-left is used as left of pair and :to-right is used as right of pair.
In addition, :to-left and :to-right can be a function that takes user input as argument and return a string.
If they are functions, and you have a regexp :from, you can use (match-string num user-input) to get regexp matched groups.
:condition, if exist, should be a function that takes user input as argument and return a boolean. You can use it to check major modes, etc.
:no-regexp only affects delete commands, if you want to search the matched pair plainly by text rather than by regexp, add :no-regexp t.
This is especially important for pairs that contains regexp keywords such as [, \, +, etc.
A word of :from: \"^\" and \"$\" are added automatically to from before matching. Also don't forget regexp escapes.")
** Delete function's extended rule list.
There is also =isolate-delete-extended-pair-list=. This rule list is used by delete functions in addition to =isolate-pair-list=. So it's called "extended" list. The pairs in this list are tried first, then are that of =isolate-pair-list=.
How does delete function uses rule lists:
First, delete function asks for user input. Then it do the same thing as in add functions: Try to calculate out a pair.
When it gets a pair, or doesn't match anything and ends up with the original input, =isolate= uses the calculated (or not) left and right string to match text in buffer. If it can found the paired text, you can delete them.
Note that with =(match-string)= you can compose generic rules!
Here is the default value:
(defvar isolate-delete-extended-pair-list
'((:to-left "\" :to-right "\" :no-regexp t)
(:to-left "+" :to-right "+" :no-regexp t)
(:to-left "." :to-right "." :no-regexp t)
(:to-left "" :to-right "" :no-regexp t)
(:to-left "?" :to-right "?" :no-regexp t)
(:to-left "^" :to-right "^" :no-regexp t)
(:to-left "$" :to-right "$" :no-regexp t)
(:from "
Contribution is welcome! Especially matching rules. As you can see, right now there aren't much of them. For examples, there can be more latex pairs, but I don't use latex so I don't know any.