.emacs.d/lisp/init-lisp.el
2024-09-06 11:42:11 +02:00

273 lines
8.7 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; 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