commit 0117b1c54d48361e3e2e8abd4b3c5f722462ee72
parent 9bf5203393af4247c31e61c106a60d9202b56a96
Author: andsy10 <andsy1016@gmail.com>
Date: Thu, 15 Oct 2026 05:28:00 +0800
Centralize elisp tools into init-elisp.el
Diffstat:
7 files changed, 158 insertions(+), 74 deletions(-)
diff --git a/init.el b/init.el
@@ -40,6 +40,7 @@
(require 'init-package)
(require 'init-org)
(require 'init-prog)
+ (require 'init-elisp)
(require 'init-window)
(require 'init-exwm)
(require 'init-dired)
diff --git a/lisp/init-base.el b/lisp/init-base.el
@@ -143,7 +143,9 @@
(mode . inferior-emacs-lisp-mode)
(mode . debugger-mode)
(mode . messages-buffer-mode)
- (mode . backtrace-mode)))
+ (mode . backtrace-mode)
+ (mode . eldoc-mode)
+ (mode . my-elisp-macroexpand-mode)))
("Scheme" (or
(mode . scheme-mode)))
("Geiser" (or
diff --git a/lisp/init-elisp.el b/lisp/init-elisp.el
@@ -0,0 +1,144 @@
+;;; init-elisp.el --- elisp tools -*- lexical-binding: t -*-
+
+;;; Commentary:
+
+;;; Code:
+
+;; from Purcell's Emacs configuration
+;; enable some features for lisp files
+(defun my-maybe-set-readonly ()
+ "If this elisp appears to be part of Emacs, then disallow editing."
+ (when-let* ((file (buffer-file-name))
+ (part-of-emacs-p (or (string-match-p "\\.el\\.gz\\'" file)
+ (string-match-p find-function-C-source-directory file))))
+ (read-only-mode 1)))
+(add-hook 'find-file-hook #'my-maybe-set-readonly)
+
+(defun my-enable-check-parens-on-save ()
+ "Run `check-parens' when the current buffer is saved."
+ (add-hook 'after-save-hook #'check-parens nil t))
+
+(defvar my-lispy-modes-hook
+ '(my-enable-check-parens-on-save)
+ "Hook run in all Lisp modes.")
+
+(defun my-lisp-setup ()
+ "Enable features useful in any Lisp mode."
+ (run-hooks 'my-lispy-modes-hook))
+
+(dolist (hook '(emacs-lisp-mode-hook
+ ielm-mode-hook lisp-mode-hook
+ inferior-lisp-mode-hook lisp-interaction-mode-hook))
+ (add-hook hook 'my-lisp-setup))
+(when (boundp 'eval-expression-minibuffer-setup-hook)
+ (add-hook 'eval-expression-minibuffer-setup-hook #'eldoc-mode))
+
+;; from Purcell's Emacs configuration
+(my-use-package ielm
+ :straight nil
+ :commands my-switch-to-ielm my-repl-switch-back
+ :config
+ ;; jump between ielm and elisp file
+ (defvar-local my-repl-original-buffer nil
+ "Buffer from which we jumped to this REPL.")
+
+ (defvar my-repl-switch-function 'switch-to-buffer)
+
+ (defun my-switch-to-ielm ()
+ (interactive)
+ (let ((orig-buffer (current-buffer)))
+ (if (get-buffer "*ielm*")
+ (funcall my-repl-switch-function "*ielm*")
+ (ielm))
+ (setq my-repl-original-buffer orig-buffer)))
+
+ (defun my-repl-switch-back ()
+ "Switch back to the buffer from which we reached this REPL."
+ (interactive)
+ (if my-repl-original-buffer
+ (funcall my-repl-switch-function my-repl-original-buffer)
+ (error "No original buffer"))))
+
+;; from Purcell's Emacs configuration
+(defun my-elisp-headerise ()
+ "Add minimal header and footer to an elisp buffer in order to placate flycheck."
+ (interactive)
+ (let ((fname (if (buffer-file-name)
+ (file-name-nondirectory (buffer-file-name))
+ (error "This buffer is not visiting a file"))))
+ (save-excursion
+ (goto-char (point-min))
+ (insert ";;; " fname " --- Insert description here -*- lexical-binding: t -*-\n"
+ ";;; Commentary:\n"
+ ";;; Code:\n\n")
+ (goto-char (point-max))
+ (insert ";;; " fname " ends here\n"))))
+
+(defun my-elisp-load-this-file ()
+ "Load the current file or buffer.
+The current directory is temporarily added to `load-path'. When there
+is no current file, eval the current buffer."
+ (interactive)
+ (let ((load-path (cons default-directory load-path))
+ (file (buffer-file-name)))
+ (if file
+ (progn
+ (let ((buf (current-buffer)))
+ (save-some-buffers
+ nil
+ (lambda () (eq (current-buffer) buf))))
+ (load-file (buffer-file-name))
+ (message "Loaded %s" file))
+ (eval-buffer)
+ (message "Evaluated %s" (current-buffer)))))
+
+;; from Protesilaos's Emacs configuration
+(defun my-elisp-eval-and-print-last-sexp ()
+ "Evaluate and print expression before point like `eval-print-last-sexp'.
+Prepend a comment to the return value. Also copy the return value to
+the `kill-ring' and set the mark to where point was before inserting the
+return value."
+ (declare (interactive-only t))
+ (interactive)
+ (if-let* ((string (thing-at-point 'sexp :no-properties))
+ (_ (not (string-prefix-p ";" string)))
+ (expression (read string)))
+ (let ((return-value (eval expression)))
+ (kill-new (format "%S" return-value))
+ (message "Copied: `%S'" return-value)
+ (push-mark (point))
+ (insert (format "\n%S\n" return-value))
+ (string-insert-rectangle (+ (mark) 1) (- (point) 1) ";; => "))
+ (user-error "No expression at point")))
+
+(defvar-keymap my-elisp-macroexpand-mode-map
+ :doc "keymap for `my-elisp-macroexpand-mode'."
+ :parent special-mode-map
+ "C-c C-l p" #'my-elisp-pp-macroexpand-last-sexp)
+
+(define-derived-mode my-elisp-macroexpand-mode emacs-lisp-mode "MacroExpand"
+ "Like `emacs-lisp-mode' but for macroexpanded forms."
+ :interactive nil
+ (read-only-mode 1)
+ (display-line-numbers-mode 1))
+
+(defun my-elisp-pp-macroexpand-last-sexp ()
+ "Like `pp-macroexpand-last-sexp' but with a generic `display-buffer'.
+Now use `display-buffer-alist' like the Lisp gods intended."
+ (declare (interactive-only t))
+ (interactive)
+ (if-let* ((thing (thing-at-point 'sexp :no-properties))
+ (expression (read thing))
+ (buffer (get-buffer-create "*my-elisp-macroexpand*"))
+ (inhibit-read-only t))
+ (progn
+ (with-current-buffer buffer
+ (erase-buffer)
+ (insert (format "%S" (macroexpand-1 expression)))
+ (my-elisp-macroexpand-mode)
+ (pp-buffer))
+ (display-buffer buffer))
+ (user-error "No expression to macroexpand")))
+
+(provide 'init-elisp)
+;;; init-elisp.el ends here
diff --git a/lisp/init-kbd-func.el b/lisp/init-kbd-func.el
@@ -75,21 +75,6 @@ there's a region, all lines that region covers will be duplicated."
(skip-syntax-forward " " (line-end-position))
(backward-prefix-chars))
-;; from Purcell's Emacs configuration
-(defun my-headerise-elisp ()
- "Add minimal header and footer to an elisp buffer in order to placate flycheck."
- (interactive)
- (let ((fname (if (buffer-file-name)
- (file-name-nondirectory (buffer-file-name))
- (error "This buffer is not visiting a file"))))
- (save-excursion
- (goto-char (point-min))
- (insert ";;; " fname " --- Insert description here -*- lexical-binding: t -*-\n"
- ";;; Commentary:\n"
- ";;; Code:\n\n")
- (goto-char (point-max))
- (insert ";;; " fname " ends here\n"))))
-
(defun my-straight-packages-count ()
"Count how many straight.el package are currently installed."
(interactive)
diff --git a/lisp/init-kbd.el b/lisp/init-kbd.el
@@ -80,6 +80,7 @@
"M-O" #'other-window-backward
;; other
+ [remap eval-expression] #'pp-eval-expression
"C-h h" #'hyperbole
"C-x r b" #'consult-bookmark
"C-;" #'embark-act
@@ -143,9 +144,15 @@
"v" #'my-elfeed-search-preview-other-window
"TAB" #'my-elfeed-search-preview-other-window)
-;; emacs-lisp-mode
-(general-def emacs-lisp-mode-map
+;; elisp
+(general-def (emacs-lisp-mode-map lisp-interaction-mode-map)
+ "C-c C-l l" #'my-elisp-load-this-file
+ "C-c C-l j" #'my-elisp-eval-and-print-last-sexp
+ "C-c C-l p" #'my-elisp-pp-macroexpand-last-sexp
+ "C-c C-l e" #'pp-eval-last-sexp
"C-c C-z" #'my-switch-to-ielm)
+(general-def lisp-interaction-mode-map
+ "C-j" #'my-elisp-eval-and-print-last-sexp)
;; embark
(general-def embark-file-map
@@ -236,10 +243,6 @@
(general-def Info-mode-map
"M-n" #'forward-paragraph)
-;; lisp-interaction-mode
-(general-def lisp-interaction-mode-map
- "C-c C-z" #'my-switch-to-ielm)
-
;; magit
(general-def (magit-mode-map tokei-mode-map)
"C-<tab>" #'my-magit-toggle-parent-section)
diff --git a/lisp/init-prog.el b/lisp/init-prog.el
@@ -5,58 +5,6 @@
;;; Code:
-;; from Purcell's Emacs configuration
-;; enable some features for lisp files
-(defun my-maybe-set-bundled-elisp-readonly ()
- "If this elisp appears to be part of Emacs, then disallow editing."
- (when (and (buffer-file-name)
- (string-match-p "\\.el\\.gz\\'" (buffer-file-name)))
- (read-only-mode 1)))
-(add-hook 'emacs-lisp-mode-hook 'my-maybe-set-bundled-elisp-readonly)
-
-(defun my-enable-check-parens-on-save ()
- "Run `check-parens' when the current buffer is saved."
- (add-hook 'after-save-hook #'check-parens nil t))
-
-(defvar my-lispy-modes-hook
- '(my-enable-check-parens-on-save)
- "Hook run in all Lisp modes.")
-
-(defun my-lisp-setup ()
- "Enable features useful in any Lisp mode."
- (run-hooks 'my-lispy-modes-hook))
-
-(require 'derived)
-(dolist (mode '(emacs-lisp-mode ielm-mode lisp-mode inferior-lisp-mode lisp-interaction-mode))
- (add-hook (derived-mode-hook-name mode) 'my-lisp-setup))
-(when (boundp 'eval-expression-minibuffer-setup-hook)
- (add-hook 'eval-expression-minibuffer-setup-hook #'eldoc-mode))
-
-(my-use-package ielm
- :straight nil
- :commands my-switch-to-ielm my-repl-switch-back
- :config
- ;; jump between ielm and elisp file
- (defvar-local my-repl-original-buffer nil
- "Buffer from which we jumped to this REPL.")
-
- (defvar my-repl-switch-function 'switch-to-buffer)
-
- (defun my-switch-to-ielm ()
- (interactive)
- (let ((orig-buffer (current-buffer)))
- (if (get-buffer "*ielm*")
- (funcall my-repl-switch-function "*ielm*")
- (ielm))
- (setq my-repl-original-buffer orig-buffer)))
-
- (defun my-repl-switch-back ()
- "Switch back to the buffer from which we reached this REPL."
- (interactive)
- (if my-repl-original-buffer
- (funcall my-repl-switch-function my-repl-original-buffer)
- (error "No original buffer"))))
-
;; comint
(my-use-package comint
:straight nil
diff --git a/lisp/init-window.el b/lisp/init-window.el
@@ -60,6 +60,7 @@
process-menu-mode list-environment-mode cargo-process-mode
youdao-dictionary-mode osx-dictionary-mode fanyi-mode
ibuffer-mode
+ my-elisp-macroexpand-mode
"^\\*eshell.*\\*.*$" eshell-mode
".*eshell\\*$"