hamacs/ha-programming-elisp.org
Howard Abrams 241d72bc0e Add elisp-demos and elisp-def
As these two packages make Emacs Lisp programming much nicer.
2022-05-11 14:34:43 -07:00

8 KiB
Raw Blame History

Emacs Lisp Configuration

A literate programming file for configuring Emacs for Lisp programming.

Introduction

While I program in a lot of languages, I seem to be writing all my helper tools and scripts in … Emacs Lisp. So Im cranking this up to 11.

New, non-literal source code comes from emacs-lisp-mode template:

  (ha-auto-insert-file (rx ".el" eol) "emacs-lisp-mode.el")

Syntax Display

Dim those Parenthesis

The paren-face project lowers the color level of parenthesis which I find better.

  (use-package paren-face
    :hook (emacs-lisp-mode . paren-face-mode))

Show code examples with the elisp-demos package. This is really helpful.

  (use-package elisp-demos
    :config
    (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1))

Navigation and Editing

Goto Definitions

Fs elisp-def project does a better job at jumping to the definition of a symbol at the point, so:

  (use-package elisp-def
    :hook (emacs-lisp-mode . elisp-def-mode))

This should work with evil-goto-defintion, as that calls this list from evil-goto-definition-functions:

I love packages that add functionality but I dont have to learn anything.

Clever Parenthesis

We need to make sure we keep the smartparens project always in strict mode, because who wants to worry about paren-matching:

  (use-package smartparens
    :custom
    (smartparens-global-strict-mode t)
    :hook
    (prog-mode . smartparens-strict-mode))

The evil-cleverparens solves having me create keybindings to the smartparens project by updating the evil states with Lisp-specific bindings.

  (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)

The trick to being effective with the 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:

  (+ 41 (* 1 3))    (+ 41 (* 1) 3)

Use the > key to slurp in outside objects into the current expression… in other words, move the paren away from the point. For instance:

  (+ 41 (* 1) 3)    (+ 41 (* 1 3))

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:

  (+ 41 (* 1 3))    (+ (41 * 1 3))

And the > moves the paren to the right. For instance:

  (+ 41 (* 1 3))   (+ 41 * (1 3))

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. Lets suppose we entered this:

(format "The sum of %d %d is %d" a b (+ a b))

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:

  ( (format "The sum of %d %d is %d" a b (+ a b)))

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 they made it, but evil-cp-next-closing from cleverparens can help:

(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)))

And we just need to bind it.

  (ha-prog-leader
    "e c" '("current" . ha-eval-current-expression))