Add an elisp REPL via doom's CLI

This commit is contained in:
TEC 2021-09-11 17:39:54 +08:00
parent 2747094cfa
commit ac635e8df6
Signed by: tec
GPG Key ID: 779591AFDB81F06C
1 changed files with 99 additions and 0 deletions

View File

@ -851,6 +851,105 @@ avoid polluting the output.
(advice-add 'org-babel-execute-src-block :around #'doom-shut-up-a)
#+end_src
*** Elisp REPL
I think an elisp REPL sounds like a fun idea, even if not a particularly useful
one 😛. We can do this by adding a new command in =cli.el=.
#+begin_src emacs-lisp :tangle cli.el
(defcli! repl ()
"Start an elisp REPL."
(doom-initialize-packages)
(require 'engrave-faces-ansi)
(setq engrave-faces-ansi-color-mode '3-bit)
;; For some reason (require 'parent-mode) doesn't work :(
(defun parent-mode-list (mode)
"Return a list of MODE and all its parent modes.
The returned list starts with the parent-most mode and ends with MODE."
(let ((result ()))
(parent-mode--worker mode (lambda (mode)
(push mode result)))
result))
(defun parent-mode--worker (mode func)
"For MODE and all its parent modes, call FUNC.
FUNC is first called for MODE, then for its parent, then for the parent's
parent, and so on.
MODE shall be a symbol referring to a function.
FUNC shall be a function taking one argument."
(funcall func mode)
(when (not (fboundp mode))
(signal 'void-function (list mode)))
(let ((modefunc (symbol-function mode)))
(if (symbolp modefunc)
;; Hande all the modes that use (defalias 'foo-parent-mode (stuff)) as
;; their parent
(parent-mode--worker modefunc func)
(let ((parentmode (get mode 'derived-mode-parent)))
(when parentmode
(parent-mode--worker parentmode func))))))
(provide 'parent-mode)
;; Some extra highlighting (needs parent-mode)
(require 'rainbow-delimiters)
(require 'highlight-quoted)
(require 'highlight-numbers)
(setq emacs-lisp-mode-hook '(rainbow-delimiters-mode
highlight-quoted-mode
highlight-numbers-mode))
;; Pretty print
(defun pp-sexp (sexp)
(with-temp-buffer
(cl-prettyprint sexp)
(emacs-lisp-mode)
(font-lock-ensure)
(with-current-buffer (engrave-faces-ansi-buffer)
(princ (string-trim (buffer-string)))
(kill-buffer (current-buffer)))))
;; Now do the REPL
(defvar accumulated-input nil)
(while t
(condition-case nil
(let ((input (if accumulated-input
(read-string "\e[31m .\e[0m ")
(read-string "\e[31mλ:\e[0m "))))
(setq input (concat accumulated-input
(when accumulated-input "\n")
input))
(cond
((string-match-p "\\`[[:space:]]*\\'" input)
nil)
((string= input "exit")
(princ "\n") (kill-emacs 0))
(t
(condition-case err
(let ((input-sexp (car (read-from-string input))))
(setq accumulated-input nil)
(pp-sexp (eval input-sexp))
(princ "\n"))
;; Caused when sexp in unbalanced
(end-of-file (setq accumulated-input input))
(error
(cl-destructuring-bind (backtrace &optional type data . _)
(cons (doom-cli--backtrace) err)
(princ (concat "\e[1;31mERROR:\e[0m " (get type 'error-message)))
(princ "\n ")
(pp-sexp (cons type data))
(when backtrace
(print! (bold "Backtrace:"))
(print-group!
(dolist (frame (seq-take backtrace 10))
(print!
"%0.74s" (replace-regexp-in-string
"[\n\r]" "\\\\n"
(format "%S" frame))))))
(princ "\n")))))))
;; C-d causes an end-of-file error
(end-of-file (princ "exit\n") (kill-emacs 0)))
(unless accumulated-input (princ "\n"))))
#+end_src
*** Asynchronous config tangling
Doom adds an =org-mode= hook ~+literate-enable-recompile-h~. This is a nice idea,