Difference between revisions of "Lisp editing"
m (right title depth) |
m ((minor test)) |
||
(21 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Tips on how to edit lisp code efficiently. | Tips on how to edit lisp code efficiently. | ||
+ | |||
== Built-in modes == | == Built-in modes == | ||
− | == | + | The built-in mode depends on the language but probably it does syntax highlighting and has shortcuts to evaluate and compile code. |
+ | |||
+ | == Highlight parenthesis and other delimiters == | ||
+ | |||
+ | === Highlight pairs with different colors === | ||
+ | |||
+ | That's possible with [https://github.com/Fanael/rainbow-delimiters rainbow-delimiters]. It highlights delimiters such as parentheses, brackets or braces according to their depth. Each successive level is highlighted in a different color. | ||
+ | |||
+ | === Make parenthesis easier on the eyes (paren-face) === | ||
+ | |||
+ | We can also do the contrary of highlighting parenthesis and make them smoother on the eyes. The mode [https://github.com/tarsius/paren-face/ paren-face] renders them as a light grey by default. | ||
+ | |||
+ | "We lispers probably don't need to be constantly made aware of the existence of the parentheses. Dimming them might be even more useful for people new to lisp who have not yet learned to subconsciously blend out the parentheses." | ||
+ | |||
+ | == Managing parenthesis == | ||
+ | |||
+ | === adjust-parens - adjust parens given the indentation === | ||
+ | |||
+ | [https://elpa.gnu.org/packages/adjust-parens.html adjust-parens] (in ELPA) adjusts the parens of the sexp around point given its indentation. | ||
+ | |||
+ | Example: | indicates the position of point. | ||
+ | |||
+ | (let ((x 10) (y (some-func 20)))) | ||
+ | | | ||
+ | |||
+ | After one TAB: | ||
+ | |||
+ | (let ((x 10) (y (some-func 20))) | ||
+ | |) | ||
+ | |||
+ | After three more TAB: | ||
+ | |||
+ | (let ((x 10) (y (some-func 20 | ||
+ | |)))) | ||
+ | |||
+ | After two Shift-TAB to dedent: | ||
+ | |||
+ | (let ((x 10) (y (some-func 20)) | ||
+ | |)) | ||
+ | |||
+ | |||
+ | Note that lispy and smartparens also have this functionality. | ||
+ | |||
+ | === Parinfer - keep parens and indentation balanced === | ||
+ | |||
+ | Let's begin the serie with [https://shaunlebron.github.io/parinfer/ Parinfer]. It is the newest of the modes but yet the easiest to get going with (no learning gap) and still as feature-full. It helps to keep both indentation and parens balanced and it introduces to more advanced features similar to Paredit. Its Emacs plugin is in melpa. | ||
+ | |||
+ | It could be seen as a more advanced adjust-parens. | ||
+ | |||
+ | |||
+ | === Smartparens === | ||
+ | |||
+ | [https://github.com/Fuco1/smartparens Smartparens] is a minor mode for Emacs that deals with parens pairs and tries to be smart about it. It not only deals with parens but with everything that comes in pairs (html tags,…) and thus has features for non-lispy languages. | ||
+ | |||
+ | It is available in [[melpa]]. | ||
+ | |||
+ | === Paxedit === | ||
+ | |||
+ | [https://github.com/promethial/paxedit Paxedit] adds commands based on the context (in a symbol, a sexp,… ) and puts efforts on whitespace cleanup and context refactoring. It does more than Paredit. | ||
+ | |||
+ | It is an Emacs extension which eliminates the work, tedium, and mistakes involved with manual editing and refactoring LISP code. Paxedit allows the quick refactoring of symbols, symbolic expressions (explicit and implicit), and comments. Normally a unique command or set of commands would allow a user to delete, copy, or transpose symbols, symbolic expressions, or comments. Additionally, after executing some delete or general refactoring commands the user must clean up any extraneous whitespace, correct indentation, and make sure all their expressions are balanced. | ||
+ | |||
+ | Paxedit takes a departure from the above manual state of code editing through automation. Paxedit does away with multiple different commands. Paxedit knows when it’s in a symbol or a comment. Paxedit '''does the right thing in the right context'''. For example, Paxedit has one delete command which can be used to delete comments and symbolic expressions explicit and implicit. That is just one of many Paxedit’s context aware commands. Additionally, all Paxedit commands by default cleanup whitespace, fix indentation issues caused by refactoring, and expressions stay balanced. | ||
+ | |||
+ | |||
+ | * Conceptually consistent | ||
+ | * Parenthesis, brackets, quotes, etc. stay balanced | ||
+ | * Removes the need to select different commands for different contexts | ||
+ | * No need for manual whitespace cleanup | ||
+ | * Code stays correctly indented | ||
+ | * Understands implicit expressions | ||
+ | * Customizable: Can be extended to work for other languages, Can even be used for general text editing | ||
+ | |||
+ | ==== Example ==== | ||
+ | |||
+ | The sign "-!-" stands for the cursor: | ||
+ | |||
+ | <source lang="lisp"> | ||
+ | (if x | ||
+ | (message-!- "It's true!") | ||
+ | (message "It's false!")) | ||
+ | |||
+ | ;;; To delete the "(message "It's true!")" symbolic | ||
+ | ;;; expression from the containing if expression | ||
+ | ;;; consider the number of steps required. | ||
+ | |||
+ | ;;; 1. Move the cursor to the outside of the expression | ||
+ | |||
+ | (if x | ||
+ | -!-(message "It's true!") | ||
+ | (message "It's false!")) | ||
+ | |||
+ | ;;; 2. Delete the expression with a command that deletes the expression | ||
+ | |||
+ | (if x | ||
+ | -!- | ||
+ | (message "It's false!")) | ||
+ | |||
+ | ;;; 3. Clean up the remaining whitespace | ||
+ | |||
+ | (if x-!- | ||
+ | (message "It's false!")) | ||
+ | |||
+ | ;;; Paxedit can accomplish the above in one command. | ||
+ | ;;; Place the cursor inside the expression you want | ||
+ | ;;; to delete and run the command: | ||
+ | ;;; paxedit-delete | ||
+ | </source> | ||
+ | |||
+ | There are many more examples in the project's page. | ||
+ | |||
+ | ==== Installation ==== | ||
+ | |||
+ | It is available in [[melpa]]. | ||
=== Lispy, a vi-like paredit === | === Lispy, a vi-like paredit === | ||
− | [https://github.com/abo-abo/lispy Lispy] | + | [https://github.com/abo-abo/lispy Lispy] offers very short keybindings that work depending on the point position. For example, going to the next parenthesis is bound to '''j''', '''s''' moves down the current expression, '''e''' to eval and '''xe''' to edebug, '''xd''' turns current lambda into a defun, etc |
− | |||
− | |||
The price to pay is that the bindings are only active when: | The price to pay is that the bindings are only active when: | ||
Line 16: | Line 128: | ||
* the point is after a close paren: ), ] or } | * the point is after a close paren: ), ] or } | ||
* the region is active | * the region is active | ||
+ | |||
+ | Lispy claims to be vi-like but it doesn't integrate with evil-mode bindings, that's why we don't put it in the following part, "With evil-mode". | ||
==== Installation ==== | ==== Installation ==== | ||
Line 25: | Line 139: | ||
(you might want to '''M-x package-refresh-content''' beforehand). | (you might want to '''M-x package-refresh-content''' beforehand). | ||
+ | == With evil-mode == | ||
+ | |||
+ | === Indent an S-expression === | ||
+ | |||
+ | Evil-mode has the built-in '''=ip''' to indent the s-exp in front of the cursor (call it when point is just before an open parenthesis). | ||
+ | |||
+ | === Evil-smartparens === | ||
+ | |||
+ | [https://github.com/expez/evil-smartparens evil-smartparens] is a minor mode which makes [[evil]] play nicely with '''smartparens'''. It helps keeping expressions balanced when you type evil commands like '''dd'''. | ||
+ | |||
+ | It is available on [[Melpa]]. | ||
+ | |||
+ | Let's take the example of '''dd''', which in evil-mode means "delete the current line". Let's consider the following expression, where the cursor is represented by "|": | ||
+ | |||
+ | <source lang="lisp"> | ||
+ | (let| ((foo 1.01)) | ||
+ | (frobnicate foo)) | ||
+ | </source> | ||
+ | |||
+ | If we hit '''dd''' then nothing will happen. But If we move down a line and hit '''dd''' then we will see: | ||
+ | |||
+ | <source lang="lisp"> | ||
+ | (let ((foo 1.01)) | ||
+ | ) | ||
+ | </source> | ||
+ | |||
+ | The last parenthesis is preserved to keep our s-expression correct. You can see many more examples on the project's page. | ||
+ | |||
+ | === Evil-cleverparens === | ||
+ | |||
+ | [https://github.com/luxbock/evil-cleverparens Evil-cleverparens] has a lot in common with evil-smartparens but has some more features, like helpers for navigation, moving expressions around, slurping and barfing and wrapping text. | ||
=== Tips and tricks === | === Tips and tricks === | ||
Line 53: | Line 198: | ||
The [[emacs lisp]] page. | The [[emacs lisp]] page. | ||
+ | |||
+ | === Skip all closing parens at once === | ||
+ | |||
+ | Doable with | ||
+ | |||
+ | (skip-syntax-forward ")") | ||
+ | |||
+ | |||
+ | === Writing all closing parens at once === | ||
+ | |||
+ | If you are not using a package which inserts them automatically, see [http://acidwords.com/posts/2017-10-19-closing-all-parentheses-at-once.html this blog post]. | ||
+ | |||
+ | |||
+ | === Highlight quoted symbols === | ||
+ | |||
+ | The [https://github.com/Fanael/highlight-quoted highlight-quote] symbols highlights quoted symbol (by default, they have the same color of a function). Not in MELPA: clone this repo and load its elisp file ('''M-x load-file'''). | ||
+ | |||
+ | === agressive-indent: Keep a block indented === | ||
+ | |||
+ | [https://github.com/Malabarba/aggressive-indent-mode agressive-indent-mode] is a minor mode that keeps your code always indented. It reindents after every change, making it more reliable than electric-indent-mode. | ||
+ | |||
+ | [[File:Agressive-indent.gif]] | ||
[[Category:Lisp]] | [[Category:Lisp]] | ||
[[Category:Text Editing]] | [[Category:Text Editing]] |
Latest revision as of 15:14, 5 August 2020
Tips on how to edit lisp code efficiently.
Built-in modes
The built-in mode depends on the language but probably it does syntax highlighting and has shortcuts to evaluate and compile code.
Highlight parenthesis and other delimiters
Highlight pairs with different colors
That's possible with rainbow-delimiters. It highlights delimiters such as parentheses, brackets or braces according to their depth. Each successive level is highlighted in a different color.
Make parenthesis easier on the eyes (paren-face)
We can also do the contrary of highlighting parenthesis and make them smoother on the eyes. The mode paren-face renders them as a light grey by default.
"We lispers probably don't need to be constantly made aware of the existence of the parentheses. Dimming them might be even more useful for people new to lisp who have not yet learned to subconsciously blend out the parentheses."
Managing parenthesis
adjust-parens - adjust parens given the indentation
adjust-parens (in ELPA) adjusts the parens of the sexp around point given its indentation.
Example: | indicates the position of point.
(let ((x 10) (y (some-func 20)))) |
After one TAB:
(let ((x 10) (y (some-func 20))) |)
After three more TAB:
(let ((x 10) (y (some-func 20 |))))
After two Shift-TAB to dedent:
(let ((x 10) (y (some-func 20)) |))
Note that lispy and smartparens also have this functionality.
Parinfer - keep parens and indentation balanced
Let's begin the serie with Parinfer. It is the newest of the modes but yet the easiest to get going with (no learning gap) and still as feature-full. It helps to keep both indentation and parens balanced and it introduces to more advanced features similar to Paredit. Its Emacs plugin is in melpa.
It could be seen as a more advanced adjust-parens.
Smartparens
Smartparens is a minor mode for Emacs that deals with parens pairs and tries to be smart about it. It not only deals with parens but with everything that comes in pairs (html tags,…) and thus has features for non-lispy languages.
It is available in melpa.
Paxedit
Paxedit adds commands based on the context (in a symbol, a sexp,… ) and puts efforts on whitespace cleanup and context refactoring. It does more than Paredit.
It is an Emacs extension which eliminates the work, tedium, and mistakes involved with manual editing and refactoring LISP code. Paxedit allows the quick refactoring of symbols, symbolic expressions (explicit and implicit), and comments. Normally a unique command or set of commands would allow a user to delete, copy, or transpose symbols, symbolic expressions, or comments. Additionally, after executing some delete or general refactoring commands the user must clean up any extraneous whitespace, correct indentation, and make sure all their expressions are balanced.
Paxedit takes a departure from the above manual state of code editing through automation. Paxedit does away with multiple different commands. Paxedit knows when it’s in a symbol or a comment. Paxedit does the right thing in the right context. For example, Paxedit has one delete command which can be used to delete comments and symbolic expressions explicit and implicit. That is just one of many Paxedit’s context aware commands. Additionally, all Paxedit commands by default cleanup whitespace, fix indentation issues caused by refactoring, and expressions stay balanced.
- Conceptually consistent
- Parenthesis, brackets, quotes, etc. stay balanced
- Removes the need to select different commands for different contexts
- No need for manual whitespace cleanup
- Code stays correctly indented
- Understands implicit expressions
- Customizable: Can be extended to work for other languages, Can even be used for general text editing
Example
The sign "-!-" stands for the cursor:
(if x
(message-!- "It's true!")
(message "It's false!"))
;;; To delete the "(message "It's true!")" symbolic
;;; expression from the containing if expression
;;; consider the number of steps required.
;;; 1. Move the cursor to the outside of the expression
(if x
-!-(message "It's true!")
(message "It's false!"))
;;; 2. Delete the expression with a command that deletes the expression
(if x
-!-
(message "It's false!"))
;;; 3. Clean up the remaining whitespace
(if x-!-
(message "It's false!"))
;;; Paxedit can accomplish the above in one command.
;;; Place the cursor inside the expression you want
;;; to delete and run the command:
;;; paxedit-delete
There are many more examples in the project's page.
Installation
It is available in melpa.
Lispy, a vi-like paredit
Lispy offers very short keybindings that work depending on the point position. For example, going to the next parenthesis is bound to j, s moves down the current expression, e to eval and xe to edebug, xd turns current lambda into a defun, etc
The price to pay is that the bindings are only active when:
- the point is before an open paren: (, [ or {
- the point is after a close paren: ), ] or }
- the region is active
Lispy claims to be vi-like but it doesn't integrate with evil-mode bindings, that's why we don't put it in the following part, "With evil-mode".
Installation
Via MELPA:
M-x package-install RET lispy RET
(you might want to M-x package-refresh-content beforehand).
With evil-mode
Indent an S-expression
Evil-mode has the built-in =ip to indent the s-exp in front of the cursor (call it when point is just before an open parenthesis).
Evil-smartparens
evil-smartparens is a minor mode which makes evil play nicely with smartparens. It helps keeping expressions balanced when you type evil commands like dd.
It is available on Melpa.
Let's take the example of dd, which in evil-mode means "delete the current line". Let's consider the following expression, where the cursor is represented by "|":
(let| ((foo 1.01))
(frobnicate foo))
If we hit dd then nothing will happen. But If we move down a line and hit dd then we will see:
(let ((foo 1.01))
)
The last parenthesis is preserved to keep our s-expression correct. You can see many more examples on the project's page.
Evil-cleverparens
Evil-cleverparens has a lot in common with evil-smartparens but has some more features, like helpers for navigation, moving expressions around, slurping and barfing and wrapping text.
Tips and tricks
Integrate expand-region
expand-region is handy because it permits to expand the region by semantic units. For example, in the string (hello "foo | oo"), calling it the first will select "foo", then "\"foo\"", then "hello \"foooo\"", then the whole expression with the parenthesis.
The following snippet binds "v" to expand-region when the region is active.
;; https://emacs.stackexchange.com/questions/16614/make-evil-mode-more-lisp-friendly
(defun evil-visual-char-or-expand-region ()
(interactive)
(if (region-active-p)
(call-interactively 'er/expand-region)
(evil-visual-char)))
(define-key evil-normal-state-map "v" 'evil-visual-char-or-expand-region)
(define-key evil-visual-state-map "v" 'evil-visual-char-or-expand-region)
(define-key evil-visual-state-map [escape] 'evil-visual-char)
Make "dd" delete an expression
di( or da( isn't specific to lisp, they are just evil text objects.
See also
The emacs lisp page.
Skip all closing parens at once
Doable with
(skip-syntax-forward ")")
Writing all closing parens at once
If you are not using a package which inserts them automatically, see this blog post.
Highlight quoted symbols
The highlight-quote symbols highlights quoted symbol (by default, they have the same color of a function). Not in MELPA: clone this repo and load its elisp file (M-x load-file).
agressive-indent: Keep a block indented
agressive-indent-mode is a minor mode that keeps your code always indented. It reindents after every change, making it more reliable than electric-indent-mode.