Better Lisp Editing with major-mode-hydra

This commit is contained in:
Howard Abrams 2024-03-22 13:43:37 -07:00
parent 8857e1f6ef
commit 815f86fd87
3 changed files with 70 additions and 58 deletions

View file

@ -42,11 +42,11 @@ I'm not trying an experiment where specially-placed function keys on my fancy er
:keymaps 'override :keymaps 'override
:prefix "SPC" :prefix "SPC"
:non-normal-prefix "M-SPC" :non-normal-prefix "M-SPC"
:prefix "<f13>") :global-prefix "<f13>")
(general-create-definer ha-local-leader (general-create-definer ha-local-leader
:states '(normal visual motion) :states '(normal visual motion)
:global-prefix "<f17>") :prefix "<f17>")
(general-nmap "SPC m" (general-simulate-key "," :which-key "major mode"))) (general-nmap "SPC m" (general-simulate-key "," :which-key "major mode")))
#+end_src #+end_src

View file

@ -123,32 +123,35 @@ I like the idea of [[https://github.com/abo-abo/lispy][lispy]] for making a Lisp
My primary use-case is for its refactoring and other unique features. For instance, I love [[help:lispy-ace-paren][lispy-ace-paren]] that puts an /ace label/ on every parenthesis, allowing me to quickly jump to any s-expression. My primary use-case is for its refactoring and other unique features. For instance, I love [[help:lispy-ace-paren][lispy-ace-paren]] that puts an /ace label/ on every parenthesis, allowing me to quickly jump to any s-expression.
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package lispy (use-package lispy
:config :config
(when (fboundp 'evil-define-key) (when (fboundp 'evil-define-key)
(evil-define-key '(normal visual) lispyville-mode-map (evil-define-key '(normal visual) lispyville-mode-map
;; Jump to interesting places: ;; Jump to interesting places:
"gf" '("ace paren" . lispy-ace-paren) "gf" '("ace paren" . lispy-ace-paren)
"gF" '("ace symbol" . lispy-ace-symbol) "gF" '("ace symbol" . lispy-ace-symbol)
(kbd "M-v") '("mark s-exp" . lispy-mark))) ; Mark entire s-expression (kbd "M-v") '("mark s-exp" . lispy-mark))) ; Mark entire s-expression
(ha-local-leader :keymaps '(emacs-lisp-mode-map lisp-mode-map) (pretty-hydra-define lispy-debug nil
"r" '(:ignore t :which-key "refactor") ("Debug"
"r i" '("cond→if" . lispy-to-ifs) (("d" lispy-edebug "Start")
"r c" '("if→cond" . lispy-to-cond) ("j" lispy-debug-step-in "Jump in")
"r d" '("λ→𝑓" . lispy-to-defun) ("r" lispy-eval-and-replace "Eval/Replace"))
"r l" '("𝑓→λ" . lispy-to-lambda) "Instrument"
"r f" '("flatten" . lispy-flatten) (("f" (eval-defun t) "Function"))
"r b" '("bind var" . lispy-bind-variable) ))
"r u" '("unbind var" . lispy-unbind-variable)
"r >" '("to thread last" . lispy-toggle-thread-last)
"e d" '("edebug" . lispy-edebug) (pretty-hydra-define lisp-refactor nil
"e j" '("debug-step-in" . lispy-debug-step-in) ("To"
"e R" '("eval-and-replace" . lispy-eval-and-replace) (("i" lispy-to-ifs "cond→if")
("c" lispy-to-cond "if→cond")
"d d" '("describe" . lispy-describe) ("t" lispy-toggle-thread-last "to thread")
"t t" '("ert" . lispy-ert))) ("d" lispy-to-defun "λ→𝑓")
("l" lispy-to-lambda "𝑓→λ"))
"Convert"
(("F" lispy-flatten "flatten")
("b" lispy-bind-variable "bind var")
("B" lispy-unbind-variable "unbind var")))))
#+end_src #+end_src
** Lispyville ** Lispyville
I want an Evil version of [[Lispy]]. The [[https://github.com/noctuid/lispyville][lispyville project]] builds on it to make it Evil. From the README: I want an Evil version of [[Lispy]]. The [[https://github.com/noctuid/lispyville][lispyville project]] builds on it to make it Evil. From the README:
@ -273,21 +276,22 @@ These are all good, but the primary keys I need to figure out, are the s-express
** Refactoring ** Refactoring
Wilfreds [[https://github.com/Wilfred/emacs-refactor/tree/master#elisp][emacs-refactor]] package can be helpful if you turn on =context-menu-mode= and … Wilfreds [[https://github.com/Wilfred/emacs-refactor/tree/master#elisp][emacs-refactor]] package can be helpful if you turn on =context-menu-mode= and …
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package emacs-refactor (use-package emr
:general ;; :straight (:host github :repo "Wilfred/emacs-refactor")
(:states '(normal visual) :keymaps 'emacs-lisp-mode-map :config
;; Often know what functions are available: (pretty-hydra-define+ lisp-refactor nil
", r r" '("refactor menu" . emr-show-refactor-menu) ("To 𝛌"
;; These are my favorites ... (;; Often know what functions are available:
("a" emr-show-refactor-menu "all")
;; Extracts the current s-expression or region to function:
("f" emr-el-extract-function "to function")
("v" emr-el-extract-variable "to variable")
;; Converts the current let to a let*
("*" emr-el-toggle-let* "toggle let*")
;; asks for a variable, and extracts the code in a region
;; or the current s-expression, into the nearest let binding
("L" emr-el-extract-to-let "to let")))))
;; Extracts the current s-expression or region to function:
", r F" '("to function" . emr-el-extract-function)
", r V" '("to variable" . emr-el-extract-variable)
;; Converts the current let to a let*
", r *" '("toggle let*" . emr-el-toggle-let*)
;; asks for a variable, and extracts the code in a region
;; or the current s-expression, into the nearest let binding
", r l" '("to let" . emr-el-extract-to-let)))
#+end_src #+end_src
The idea of stealing some of Clojure Modes refactoring is brilliant (see [[https://isamert.net/2023/08/14/elisp-editing-development-tips.html#clojure-thread-lastfirst-all-from-https-github-com-clojure-emacs-clojure-mode-clojure-mode][the original idea]]), however, Im already using Lispys =toggle-thread-last=. The idea of stealing some of Clojure Modes refactoring is brilliant (see [[https://isamert.net/2023/08/14/elisp-editing-development-tips.html#clojure-thread-lastfirst-all-from-https-github-com-clojure-emacs-clojure-mode-clojure-mode][the original idea]]), however, Im already using Lispys =toggle-thread-last=.
@ -332,14 +336,22 @@ And we just need to bind it.
(ha-local-leader :keymaps '(emacs-lisp-mode-map lisp-mode-map) (ha-local-leader :keymaps '(emacs-lisp-mode-map lisp-mode-map)
"e e" '("current" . ha-eval-current-expression)) "e e" '("current" . ha-eval-current-expression))
#+end_src #+end_src
** Debugging * Minor Mode Hydra
The =edebug= debugger is built into Emacs, so all I need is an easier way to instrument a function: put it all together
#+begin_src emacs-lisp #+begin_src emacs-lisp
(ha-local-leader :keymaps '(emacs-lisp-mode-map lisp-mode-map) (use-package major-mode-hydra
"e D" '("set edebug" . (lambda () :config
(interactive) (major-mode-hydra-define emacs-lisp-mode nil
(setq current-prefix-arg '(4)) ; C-u ("Evaluating"
(call-interactively 'eval-defun)))) (("e" ha-eval-current-expression "Current")
("d" lispy-debug/body "Debugging")
("f" eval-defun "Function")
("b" eval-buffer "Buffer"))
"Editing"
(("r" lisp-refactor/body "Refactoring"))
"Documentation"
(("a" elisp-demos-add-demo "Add Demo")
("H" suggest "Suggestions")))))
#+end_src #+end_src
* Technical Artifacts :noexport: * Technical Artifacts :noexport:
Let's =provide= a name so we can =require= this file: Let's =provide= a name so we can =require= this file:

View file

@ -103,18 +103,18 @@ Im interested in using [[https://devdocs.io/][devdocs]] instead, which is sim
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package devdocs (use-package devdocs
:general (:states 'normal :general (:states 'normal
"gD" '("devdocs" . devdocs-lookup)) "gD" '("devdocs" . devdocs-lookup))
:config :config
(ha-local-leader :keymaps 'prog-mode-map (major-mode-hydra-define emacs-lisp-mode nil
"d" '(:ignore t :which-key "docs") ("Dev Docs"
"d e" '("eldoc" . eldoc) (("e" eldoc "eldoc")
"d d" '("open" . devdocs-lookup) ("d" devdocs-lookup "open")
"d p" '("peruse" . devdocs-peruse) ("p" devdocs-peruse "peruse")
"d i" '("install" . devdocs-install) ("i" devdocs-install "install")
"d u" '("update" . devdocs-update-all) ("u" devdocs-update-all "update")
"d x" '("uninstall" . devdocs-delete) ("x" devdocs-delete "uninstall")
"d s" '("search" . devdocs-search))) ("s" devdocs-search "search")))))
#+end_src #+end_src
The [[https://github.com/blahgeek/emacs-devdocs-browser][devdocs-browser]] project acts similar, but with slightly different command names. Its advantage is that it allows for downloading docs and having it available offline, in fact, you cant search for a function, until you download its pack. This is slightly faster because of this. The [[https://github.com/blahgeek/emacs-devdocs-browser][devdocs-browser]] project acts similar, but with slightly different command names. Its advantage is that it allows for downloading docs and having it available offline, in fact, you cant search for a function, until you download its pack. This is slightly faster because of this.
@ -689,7 +689,7 @@ I like =comment-dwim= (~M-;~), and I like =comment-box=, but I have an odd perso
(widen))) (widen)))
#+end_src #+end_src
And a keybinding: And a keybinding:
#+begin_src emacs-lisp #+begin_src emacs-lisp :tangle no
(ha-local-leader :keymaps 'prog-mode-map (ha-local-leader :keymaps 'prog-mode-map
"c" '(:ignore t :which-key "comment") "c" '(:ignore t :which-key "comment")
"c l" '("comment line" . ha-comment-line)) "c l" '("comment line" . ha-comment-line))