130 lines
4.8 KiB
EmacsLisp
130 lines
4.8 KiB
EmacsLisp
;;; user-save.el --- Do things when explicitly saving files -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 2021--2022 Case Duckworth <acdw@acdw.net>
|
|
;; URL: ...
|
|
;; Version: 0.1.0
|
|
;; Package-Requires: ((emacs "24.3"))
|
|
;; Keywords: files
|
|
|
|
;;; Commentary:
|
|
|
|
;; Because `super-save-mode' automatically saves every time we move away from a
|
|
;; buffer, it tends to run a lot of `before-save-hook's that don't need to be
|
|
;; run that often. For that reason, I'm writing a mode where C-x C-s saves
|
|
;; /and/ runs all the "real" before-save-hooks, so that super-save won't
|
|
;; automatically do things like format the buffer all the time.
|
|
|
|
;;; Code:
|
|
|
|
(require 'cl-lib)
|
|
|
|
(defgroup user-save nil
|
|
"Group for `user-save-mode' customizations."
|
|
:group 'emacs
|
|
:prefix "user-save-")
|
|
|
|
(defcustom user-save-hook-into-kill-emacs nil
|
|
"Add a hook to perform `user-save' to `kill-emacs-hook'.
|
|
This option is only useful is `user-save-mode' is active when
|
|
Emacs is killed."
|
|
:type 'boolean)
|
|
|
|
(defcustom user-save-inhibit-modes '(special-mode)
|
|
"List of modes to inhibit `user-save-mode' from activation in."
|
|
:type '(repeat symbol))
|
|
|
|
(defcustom user-save-inhibit-predicates '(user-save-non-file-buffer-p)
|
|
"List of predicates to inhibit `user-save-mode' from activation.
|
|
Each predicate will be called with no arguments, and if it
|
|
returns t, will inhibit `user-save-mode' from activating."
|
|
:type '(repeat function))
|
|
|
|
(defvar user-save-hook nil
|
|
"Hook to run when the user, not Emacs, saves the buffer.")
|
|
|
|
(defvar user-save-mode-map (let ((map (make-sparse-keymap)))
|
|
(define-key map (kbd "C-x C-s") #'user-save-buffer)
|
|
(define-key map (kbd "C-x s") #'user-save-some-buffers)
|
|
map)
|
|
"Keymap for `user-save-mode'.
|
|
This map shadows the default map for `save-buffer'.")
|
|
|
|
(defun user-save-run-hooks (&rest _)
|
|
"Run the hooks in `user-save-hook'.
|
|
This does /not/ also save the buffer."
|
|
(with-demoted-errors "User-save-hook error: %S"
|
|
(run-hooks 'user-save-hook)))
|
|
|
|
(defun user-save-non-file-buffer-p (&optional buffer-or-name)
|
|
"Return whether BUFFER-OR-NAME is a non-file buffer.
|
|
BUFFER-OR-NAME, if omitted, defaults to the current buffer."
|
|
(with-current-buffer (or buffer-or-name (current-buffer))
|
|
(not (buffer-file-name))))
|
|
|
|
(defun user-save-buffer (&optional arg)
|
|
"Save current buffer in visited file if modified.
|
|
This function is precisely the same as `save-buffer', but with
|
|
one modification: it also runs functions in `user-save-hook'.
|
|
This means that if you have some functionality in Emacs to
|
|
automatically save buffers periodically, but have hooks you want
|
|
to automatically run when the buffer saves that are
|
|
computationally expensive or just aren't something you want to
|
|
run all the time, put them in `user-save-hook'.
|
|
|
|
ARG is passed directly to `save-buffer'."
|
|
(interactive '(called-interactively))
|
|
(message "User-Saving the buffer...")
|
|
(user-save-run-hooks)
|
|
(save-buffer arg)
|
|
(message "User-Saving the buffer...Done."))
|
|
|
|
(defun user-save-some-buffers (&optional pred)
|
|
"Save some buffers as though the user saved them.
|
|
This function does not ask the user about each buffer, but PRED
|
|
is used in almost the same way as `save-some-buffers': if it's
|
|
nil or t, it will save all file-visiting modified buffers; if
|
|
it's a zero-argument function, that will be called to determine
|
|
whether the buffer needs to be saved."
|
|
;; This could maybe be much better.
|
|
(interactive "P")
|
|
(unless pred (setq pred save-some-buffers-default-predicate))
|
|
(dolist (buf (buffer-list))
|
|
(with-current-buffer buf
|
|
(when (and (buffer-modified-p)
|
|
(buffer-file-name)
|
|
(or (null pred)
|
|
(if (functionp pred) (funcall pred) pred)))
|
|
(user-save-buffer)))))
|
|
|
|
;;;###autoload
|
|
(define-minor-mode user-save-mode
|
|
"Mode to enable an an extra user-save hook."
|
|
:lighter " US"
|
|
:keymap user-save-mode-map)
|
|
|
|
;;;###autoload
|
|
(defun user-save-mode-disable ()
|
|
"Turn off `user-save-mode' in the current buffer."
|
|
(user-save-mode -1))
|
|
|
|
;;;###autoload
|
|
(defun user-save-mode-in-some-buffers ()
|
|
"Enable `user-save-mode', but only in some buffers.
|
|
The mode will not be enabled in buffers derived from modes in
|
|
`user-save-inhibit-modes', those for which
|
|
`user-save-inhibit-predicates' return t, or in the minibuffer."
|
|
(unless (or (minibufferp)
|
|
(cl-some #'derived-mode-p user-save-inhibit-modes)
|
|
(run-hook-with-args-until-failure 'user-save-inhibit-predicates))
|
|
(user-save-mode +1)))
|
|
|
|
;;;###autoload
|
|
(define-globalized-minor-mode user-save-global-mode user-save-mode user-save-mode-in-some-buffers
|
|
(if user-save-global-mode
|
|
(when user-save-hook-into-kill-emacs
|
|
(add-hook 'kill-emacs-hook #'user-save-some-buffers))
|
|
(remove-hook 'kill-emacs-hook #'user-save-some-buffers)))
|
|
|
|
(provide 'user-save)
|
|
;;; user-save.el ends here
|