169 lines
6.9 KiB
Org Mode
169 lines
6.9 KiB
Org Mode
|
#+TITLE: Emacs Lisp Configuration
|
|||
|
#+AUTHOR: Howard X. Abrams
|
|||
|
#+DATE: 2022-05-11
|
|||
|
#+FILETAGS: :emacs:
|
|||
|
|
|||
|
A literate programming file for configuring Emacs for Lisp programming.
|
|||
|
|
|||
|
#+BEGIN_SRC emacs-lisp :exports none
|
|||
|
;;; ha-lisp --- configuring Emacs for Lisp programming. -*- lexical-binding: t; -*-
|
|||
|
;;
|
|||
|
;; © 2022 Howard X. Abrams
|
|||
|
;; This work is licensed under a Creative Commons Attribution 4.0 International License.
|
|||
|
;; See http://creativecommons.org/licenses/by/4.0/
|
|||
|
;;
|
|||
|
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
|
|||
|
;; Maintainer: Howard X. Abrams
|
|||
|
;; Created: May 11, 2022
|
|||
|
;;
|
|||
|
;; This file is not part of GNU Emacs.
|
|||
|
;;
|
|||
|
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
|
|||
|
;; /Users/howard.abrams/other/hamacs/ha-lisp.org
|
|||
|
;; And tangle the file to recreate this one.
|
|||
|
;;
|
|||
|
;;; Code:
|
|||
|
#+END_SRC
|
|||
|
* Introduction
|
|||
|
While I program in a lot of languages, I seem to be writing all my helper tools and scripts in … Emacs Lisp. So I’m cranking this up to 11.
|
|||
|
|
|||
|
New, /non-literal/ source code comes from [[file:templates/emacs-lisp-mode.el][emacs-lisp-mode template]]:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(ha-auto-insert-file (rx ".el" eol) "emacs-lisp-mode.el")
|
|||
|
#+END_SRC
|
|||
|
* Syntax Display
|
|||
|
** Dim those Parenthesis
|
|||
|
The [[https://github.com/tarsius/paren-face][paren-face]] project lowers the color level of parenthesis which I find better.
|
|||
|
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(use-package paren-face
|
|||
|
:hook (emacs-lisp-mode . paren-face-mode))
|
|||
|
#+END_SRC
|
|||
|
I'm going to play with the [[https://github.com/DogLooksGood/parinfer-mode][parinfer]] package.
|
|||
|
|
|||
|
* Navigation and Editing
|
|||
|
** Clever Parenthesis
|
|||
|
We need to make sure we keep the [[https://github.com/Fuco1/smartparens][smartparens]] project always in /strict mode/, because who wants to worry about paren-matching:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(use-package smartparens
|
|||
|
:custom
|
|||
|
(smartparens-global-strict-mode t)
|
|||
|
:hook
|
|||
|
(prog-mode . smartparens-strict-mode))
|
|||
|
#+END_SRC
|
|||
|
|
|||
|
The [[https://github.com/luxbock/evil-cleverparens][evil-cleverparens]] solves having me create keybindings to the [[https://github.com/Fuco1/smartparens][smartparens]] project by updating the evil states with Lisp-specific bindings.
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(use-package evil-cleverparens
|
|||
|
:after smartparens
|
|||
|
:custom
|
|||
|
(evil-cleverparens-use-additional-bindings t)
|
|||
|
(evil-cleverparens-use-additional-movement-keys t)
|
|||
|
(evil-cleverparens-use-s-and-S t)
|
|||
|
|
|||
|
:init
|
|||
|
(require 'evil-cleverparens-text-objects)
|
|||
|
|
|||
|
:hook
|
|||
|
(prog-mode . evil-cleverparens-mode)) ;; All the languages!
|
|||
|
;; Otherwise: (emacs-lisp-mode . evil-cleverparens-mode)
|
|||
|
#+END_SRC
|
|||
|
|
|||
|
The /trick/ to being effective with the [[https://www.emacswiki.org/emacs/ParEdit][paredit-family]] of extensions is learning the keys. The killer “app” is the slurp/barf sequence. Use the ~<~ key, in normal mode, to barf (or jettison)… in other words, /move/ the paren closer to the point. For instance:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(+ 41 (* ‖1 3)) ⟹ (+ 41 (* ‖1) 3)
|
|||
|
#+END_SRC
|
|||
|
Use the ~>~ key to /slurp/ in outside objects into the current expression… in other words, move the paren away from the point. For instance:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(+ 41 (* ‖1) 3) ⟹ (+ 41 (* ‖1 3))
|
|||
|
#+END_SRC
|
|||
|
|
|||
|
*Opening Parens.* Those two keys seem straight-forward, but they behave differently when the are on the opening parens.
|
|||
|
When the point (symbolized by ~‖~) is /on/ the opening paren, ~<~ moves the paren to the left. For instance:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(+ 41 ‖(* 1 3)) ⟹ (+ ‖(41 * 1 3))
|
|||
|
#+END_SRC
|
|||
|
And the ~>~ moves the paren to the right. For instance:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(+ 41 ‖(* 1 3)) ⟹ (+ 41 * ‖(1 3))
|
|||
|
#+END_SRC
|
|||
|
|
|||
|
I would like to have a list of what keybindings that work in =normal= mode:
|
|||
|
- ~M-h~ / ~M-l~ move back/forward by functions
|
|||
|
- ~H~ / ~L~ move back/forward by s-expression
|
|||
|
- ~M-i~ insert at the beginning of the form
|
|||
|
- ~M-a~ appends at the end of the form
|
|||
|
- ~M-o~ new form after the current sexp
|
|||
|
- ~M-O~ new form /before/ the current sexp
|
|||
|
- ~M-j~ / ~M-k~ drags /thing at point/ and back and forth in the form
|
|||
|
- ~>~ slurp forward if at the end of form, at beginning, it barfs backwards
|
|||
|
- ~<~ slurp backward if at start of form, at the end, it barfs forwards
|
|||
|
- ~M-(~ / ~M-)~ wraps next/previous form in parens (braces and brackets work too)
|
|||
|
- ~x~ unwraps if the point is on the =(= of an expression.
|
|||
|
- ~D~ deletes an entire s-expression, but this can depend on the position of the point.
|
|||
|
|
|||
|
The other advantage is moving around by s-expressions. This takes a little getting used to, for instance:
|
|||
|
- ~[~ and ~]~ move from paren to paren, essentially, from s-expression.
|
|||
|
- ~H~ and ~L~ act similarly to the above.
|
|||
|
- ~(~ and ~)~ move up to the parent s-expression
|
|||
|
|
|||
|
We need a real-world example. Let’s suppose we entered this:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(format "The sum of %d %d is %d" a b (+ a b))
|
|||
|
#+END_SRC
|
|||
|
But we forgot to define the =a= and =b= variables. One approach, after Escaping into the normal state, is to hit ~(~ to just to the beginning of the s-expression, and then type, ~M-(~ to wrap the expression, and type ~i~ to go into insert mode:
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(‖ (format "The sum of %d %d is %d" a b (+ a b)))
|
|||
|
#+END_SRC
|
|||
|
And now we can enter the =let= expression.
|
|||
|
|
|||
|
Other nifty keybindings that I need to commit to muscle memory include:
|
|||
|
| ~M-q~ | =sp-indent-defun= |
|
|||
|
| ~M-J~ | =sp-join-sexp= |
|
|||
|
| ~M-s~ | =sp-splice-sexp= |
|
|||
|
| ~M-S~ | =sp-split-sexp= |
|
|||
|
| ~M-t~ | =sp-transpose-sexp= |
|
|||
|
| ~M-v~ | =sp-convolute-sexp= |
|
|||
|
| ~M-r~ | =sp-raise-sexp= |
|
|||
|
|
|||
|
** Eval Current Expression
|
|||
|
|
|||
|
A feature I enjoyed from Spacemacs is the ability to evaluate the s-expression currently containing the point. Not sure how how they made it, but cleverparens can help:
|
|||
|
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(defun ha-eval-current-expression ()
|
|||
|
"Evaluates the expression the point is currently 'in'.
|
|||
|
It does this, by jumping to the end of the current
|
|||
|
expression (using evil-cleverparens), and evaluating what it
|
|||
|
finds at that point."
|
|||
|
(interactive)
|
|||
|
(save-excursion
|
|||
|
(evil-cp-next-closing)
|
|||
|
(evil-cp-forward-sexp)
|
|||
|
(call-interactively 'eval-last-sexp)))
|
|||
|
#+END_SRC
|
|||
|
|
|||
|
And we just need to bind it.
|
|||
|
#+BEGIN_SRC emacs-lisp
|
|||
|
(ha-prog-leader
|
|||
|
"e c" '("current" . ha-eval-current-expression))
|
|||
|
#+END_SRC
|
|||
|
* Technical Artifacts :noexport:
|
|||
|
|
|||
|
Let's =provide= a name so we can =require= this file:
|
|||
|
|
|||
|
#+BEGIN_SRC emacs-lisp :exports none
|
|||
|
(provide 'ha-programming-elisp)
|
|||
|
;;; ha-programming-elisp.el ends here
|
|||
|
#+END_SRC
|
|||
|
|
|||
|
#+DESCRIPTION: configuring Emacs for Lisp programming.
|
|||
|
|
|||
|
#+PROPERTY: header-args:sh :tangle no
|
|||
|
#+PROPERTY: header-args:emacs-lisp :tangle yes
|
|||
|
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
|
|||
|
|
|||
|
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
|
|||
|
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
|
|||
|
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
|