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

186 lines
8 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+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 Im 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
Show code examples with the [[https://github.com/xuchunyang/elisp-demos][elisp-demos]] package. This is really helpful.
#+BEGIN_SRC emacs-lisp
(use-package elisp-demos
:config
(advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1))
#+END_SRC
* Navigation and Editing
** Goto Definitions
Fs [[https://github.com/Wilfred/elisp-def][elisp-def]] project does a better job at jumping to the definition of a symbol at the point, so:
#+BEGIN_SRC emacs-lisp
(use-package elisp-def
:hook (emacs-lisp-mode . elisp-def-mode))
#+END_SRC
This /should work/ with [[help:evil-goto-definition][evil-goto-defintion]], as that calls this list from [[help:evil-goto-definition-functions][evil-goto-definition-functions]]:
- [[help:evil-goto-definition-imenu][evil-goto-definition-imenu]]
- [[help:evil-goto-definition-semantic][evil-goto-definition-semantic]]
- [[help:evil-goto-definition-xref][evil-goto-definition-xref]] … and here is where this package will be called
- [[help:evil-goto-definition-search][evil-goto-definition-search]]
I love packages that add functionality but I dont have to learn anything.
** 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 :tangle no
(+ 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 :tangle no
(+ 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 :tangle no
(+ 41 (* 1 3)) (+ (41 * 1 3))
#+END_SRC
And the ~>~ moves the paren to the right. For instance:
#+BEGIN_SRC emacs-lisp :tangle no
(+ 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. Lets suppose we entered this:
#+BEGIN_SRC emacs-lisp :tangle no
(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 :tangle no
( (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 they made it, but [[help:evil-cp-next-closing ][evil-cp-next-closing]] from 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