.emacs.d/lisp/init-lisp.el

274 lines
8.7 KiB
EmacsLisp
Raw Normal View History

2024-09-06 11:42:11 +02:00
;;; init-lisp.el --- Emacs lisp settings, and common config for other lisps -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:
(setq-default debugger-bury-or-kill 'kill)
(require-package 'elisp-slime-nav)
(dolist (hook '(emacs-lisp-mode-hook ielm-mode-hook))
(add-hook hook 'turn-on-elisp-slime-nav-mode))
(add-hook 'emacs-lisp-mode-hook (lambda () (setq mode-name "ELisp")))
(defun sanityinc/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"))))
;; Make C-x C-e run 'eval-region if the region is active
(defun sanityinc/eval-last-sexp-or-region (prefix)
"Eval region from BEG to END if active, otherwise the last sexp."
(interactive "P")
(if (and (mark) (use-region-p))
(eval-region (min (point) (mark)) (max (point) (mark)))
(pp-eval-last-sexp prefix)))
(global-set-key [remap eval-expression] 'pp-eval-expression)
(with-eval-after-load 'lisp-mode
(define-key emacs-lisp-mode-map (kbd "C-x C-e") 'sanityinc/eval-last-sexp-or-region)
(define-key emacs-lisp-mode-map (kbd "C-c C-e") 'pp-eval-expression))
(when (maybe-require-package 'ipretty)
(add-hook 'after-init-hook 'ipretty-mode))
(defun sanityinc/make-read-only (_expression out-buffer-name &rest _)
"Enable `view-mode' in the output buffer - if any - so it can be closed with `\"q\"."
(when (get-buffer out-buffer-name)
(with-current-buffer out-buffer-name
(view-mode 1))))
(advice-add 'pp-display-expression :after 'sanityinc/make-read-only)
(defun sanityinc/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
(save-some-buffers nil (apply-partially 'derived-mode-p 'emacs-lisp-mode))
(load-file (buffer-file-name))
(message "Loaded %s" file))
(eval-buffer)
(message "Evaluated %s" (current-buffer)))))
(with-eval-after-load 'lisp-mode
(define-key emacs-lisp-mode-map (kbd "C-c C-l") 'sanityinc/load-this-file))
(defun sanityinc/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)))
(setq buffer-read-only t)
(view-mode 1)))
(add-hook 'emacs-lisp-mode-hook 'sanityinc/maybe-set-bundled-elisp-readonly)
;; Use C-c C-z to toggle between elisp files and an ielm session
;; I might generalise this to ruby etc., or even just adopt the repl-toggle package.
(defvar-local sanityinc/repl-original-buffer nil
"Buffer from which we jumped to this REPL.")
(defvar sanityinc/repl-switch-function 'switch-to-buffer-other-window)
(defun sanityinc/switch-to-ielm ()
(interactive)
(let ((orig-buffer (current-buffer)))
(if (get-buffer "*ielm*")
(funcall sanityinc/repl-switch-function "*ielm*")
(ielm))
(setq sanityinc/repl-original-buffer orig-buffer)))
(defun sanityinc/repl-switch-back ()
"Switch back to the buffer from which we reached this REPL."
(interactive)
(if sanityinc/repl-original-buffer
(funcall sanityinc/repl-switch-function sanityinc/repl-original-buffer)
(error "No original buffer")))
(with-eval-after-load 'elisp-mode
(define-key emacs-lisp-mode-map (kbd "C-c C-z") 'sanityinc/switch-to-ielm))
(with-eval-after-load 'ielm
(define-key ielm-map (kbd "C-c C-z") 'sanityinc/repl-switch-back))
;; Hippie-expand
(defun set-up-hippie-expand-for-elisp ()
"Locally set `hippie-expand' completion functions for use with Emacs Lisp."
(make-local-variable 'hippie-expand-try-functions-list)
(add-to-list 'hippie-expand-try-functions-list 'try-complete-lisp-symbol t)
(add-to-list 'hippie-expand-try-functions-list 'try-complete-lisp-symbol-partially t))
;; Automatic byte compilation
(when (maybe-require-package 'auto-compile)
(setq auto-compile-delete-stray-dest nil)
(add-hook 'after-init-hook 'auto-compile-on-save-mode)
(add-hook 'after-init-hook 'auto-compile-on-load-mode))
;; Load .el if newer than corresponding .elc
(setq load-prefer-newer t)
(require-package 'immortal-scratch)
(add-hook 'after-init-hook 'immortal-scratch-mode)
;;; Support byte-compilation in a sub-process, as
;;; required by highlight-cl
(defun sanityinc/byte-compile-file-batch (filename)
"Byte-compile FILENAME in batch mode, ie. a clean sub-process."
(interactive "fFile to byte-compile in batch mode: ")
(let ((emacs (car command-line-args)))
(compile
(concat
emacs " "
(mapconcat
'shell-quote-argument
(list "-Q" "-batch" "-f" "batch-byte-compile" filename)
" ")))))
;; Enable desired features for all lisp modes
(defun sanityinc/enable-check-parens-on-save ()
"Run `check-parens' when the current buffer is saved."
(add-hook 'after-save-hook #'check-parens nil t))
(defvar sanityinc/lispy-modes-hook
'(enable-paredit-mode
sanityinc/enable-check-parens-on-save)
"Hook run in all Lisp modes.")
(when (maybe-require-package 'aggressive-indent)
(add-to-list 'sanityinc/lispy-modes-hook 'aggressive-indent-mode))
(defun sanityinc/lisp-setup ()
"Enable features useful in any Lisp mode."
(run-hooks 'sanityinc/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) 'sanityinc/lisp-setup))
(when (boundp 'eval-expression-minibuffer-setup-hook)
(add-hook 'eval-expression-minibuffer-setup-hook #'eldoc-mode))
(add-to-list 'auto-mode-alist '("\\.emacs-project\\'" . emacs-lisp-mode))
(add-to-list 'auto-mode-alist '("archive-contents\\'" . emacs-lisp-mode))
;; Delete .elc files when reverting the .el from VC or magit
;; When .el files are open, we can intercept when they are modified
;; by VC or magit in order to remove .elc files that are likely to
;; be out of sync.
;; This is handy while actively working on elisp files, though
;; obviously it doesn't ensure that unopened files will also have
;; their .elc counterparts removed - VC hooks would be necessary for
;; that.
(defvar sanityinc/vc-reverting nil
"Whether or not VC or Magit is currently reverting buffers.")
(defun sanityinc/maybe-remove-elc (&rest _)
"If reverting from VC, delete any .elc file that will now be out of sync."
(when sanityinc/vc-reverting
(when (and (eq 'emacs-lisp-mode major-mode)
buffer-file-name
(string= "el" (file-name-extension buffer-file-name)))
(let ((elc (concat buffer-file-name "c")))
(when (file-exists-p elc)
(message "Removing out-of-sync elc file %s" (file-name-nondirectory elc))
(delete-file elc))))))
(advice-add 'revert-buffer :after 'sanityinc/maybe-remove-elc)
(defun sanityinc/reverting (orig &rest args)
(let ((sanityinc/vc-reverting t))
(apply orig args)))
(advice-add 'magit-revert-buffers :around 'sanityinc/reverting)
(advice-add 'vc-revert-buffer-internal :around 'sanityinc/reverting)
(require-package 'macrostep)
(with-eval-after-load 'lisp-mode
(define-key emacs-lisp-mode-map (kbd "C-c x") 'macrostep-expand))
;; A quick way to jump to the definition of a function given its key binding
(global-set-key (kbd "C-h K") 'find-function-on-key)
;; Extras for theme editing
(when (maybe-require-package 'rainbow-mode)
(defun sanityinc/enable-rainbow-mode-if-theme ()
(when (and (buffer-file-name) (string-match-p "\\(color-theme-\\|-theme\\.el\\)" (buffer-file-name)))
(rainbow-mode)))
(add-hook 'emacs-lisp-mode-hook 'sanityinc/enable-rainbow-mode-if-theme)
(add-hook 'help-mode-hook 'rainbow-mode)
(with-eval-after-load 'rainbow-mode
(diminish 'rainbow-mode)))
(when (maybe-require-package 'highlight-quoted)
(add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode))
(when (maybe-require-package 'package-lint-flymake)
(add-hook 'emacs-lisp-mode-hook #'package-lint-flymake-setup))
;; ERT
(with-eval-after-load 'ert
(define-key ert-results-mode-map (kbd "g") 'ert-results-rerun-all-tests))
;;(maybe-require-package 'cl-libify)
(maybe-require-package 'flycheck-relint)
(maybe-require-package 'cask-mode)
(provide 'init-lisp)
;;; init-lisp.el ends here