;;; notes.el --- notes preserved across sessions -*- lexical-binding: t -*- ;; Copyright (C) 2013 Free Software Foundation, Inc. ;; Author: Michal Nazarewicz ;; Maintainer: FSF ;; Keywords: todo pim notes ;; Package: emacs ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; This file defines a `notes' function which creates a buffer whose ;; content is saved on kill-emacs. ;; ;; You may think of it as a *scratch* buffer whose content is preserved. ;; In fact, it was designed as a replacement for *scratch* buffer and can ;; be used that way by doing the following: ;; ;; (setq initial-buffer-choice 'notes ;; notes-buffer-name "*scratch*") ;; ;; Without setting `notes-buffer-name', *scratch* buffer will still be ;; there for notes that do not need to be preserved. ;; ;; To quickly access notes buffer, you might want to bind `notes' or ;; `toggle-notes' function. ;;; Code: (defvar notes--buffer nil "The notes buffer.") (defcustom notes-file (locate-user-emacs-file "notes" ".notes") "File to save notes in. When set via customize `buffer-file-name' variable of the notes buffer \(if it exists) will be changed." :type 'string :set (lambda (symbol value) (set-default symbol value) (when (buffer-live-p notes--buffer) (with-current-buffer notes--buffer (setq buffer-file-name value)))) :group 'initialization) (defcustom notes-recover-from-auto-save-file 'ask "What to do if notes autosave file is newer than the notes file. t means to always recover content from auto-save file, 'ask means to ask user, and nil means never to recover auto-save file (which also disables `auto-save-mode' in the notes buffer. When set via customize, `auto-save-mode' will be enabled or disabled in the notes buffer according to this variable's value." :type '(choice (const :tag "Always recover auto-save" t) (const :tag "Never recover auto-save" nil) (const :tag "Ask whether to recover auto-save" ask)) :set (lambda (symbol value) (set-default symbol value) (when (buffer-live-p notes--buffer) (with-current-buffer notes--buffer (auto-save-mode (if value 1 -1))))) :group 'initialization) (defcustom notes-buffer-name "*notes*" "Name of the notes buffer. Setting it to *scratch* will hijack the *scratch* buffer for the purpose of storing notes." :type 'string :group 'initialization) (defcustom initial-notes-major-mode t "Major mode to set to notes buffer when it's created. If set to t will use the same mode as `initial-major-mode'." :type '(choice (const :tag "Same as `initial-major-mode'" t) (function :tag "Major mode" text-mode)) :group 'initialization) (defcustom bury-notes-on-kill t "Whether to bury notes buffer instead of killing." :type 'boolean :group 'initialization) (defconst notes--initial-message (purecopy "\ ;; This buffer is for notes and for Lisp evaluation. ;; If you want to create a file, visit that file with C-x C-f, ;; then enter the text in that file's own buffer. ;; Contents of this buffer will be saved across restarts. ")) (defvar notes-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-c" 'save-and-bury-buffer) map)) ;;;###autoload (defun notes () "Creates notes buffer and switches to it if called interactively. Name of the created buffer is taken from `notes-buffer-name' variable and if buffer with that name already exist (but was not created by `notes' function), its content will be overwritten. `notes-map' is active in the notes buffer which by default contains only one C-c C-c binding which saves and buries the buffer. Function returns notes buffer. Notes buffer is meant for keeping random notes which you'd like to preserve across Emacs restarts." (interactive) (unless (buffer-live-p notes--buffer) (setq notes--buffer (get-buffer-create notes-buffer-name)) (with-current-buffer notes--buffer (funcall (if (eq initial-notes-major-mode t) initial-major-mode initial-notes-major-mode)) (setq buffer-file-name notes-file buffer-save-without-query t) (auto-save-mode (if notes-recover-from-auto-save-file 1 -1)) ;; We don't want a "Buffer modified" prompt from kill-buffer so ;; we have to use advice rather than a hook. (advice-add 'kill-buffer :around 'notes--kill-buffer-advice) (setq minor-mode-overriding-map-alist (cons (cons 'notes--buffer notes-map) minor-mode-overriding-map-alist)) (notes--insert-content))) (when (called-interactively-p 'all) (switch-to-buffer notes--buffer)) notes--buffer) ;;;###autoload (defun toggle-notes () "Switches to notes buffer unless already there in which case buries it." (interactive) (if (eq (current-buffer) notes--buffer) (bury-buffer) (switch-to-buffer (notes)))) (defun notes--insert-content () (let* ((have-file (file-readable-p buffer-file-name)) (have-auto-save (and buffer-auto-save-file-name (file-readable-p buffer-auto-save-file-name)))) ;; If autosave is older, pretend it does not exist. (and have-file have-auto-save (not (file-newer-than-file-p buffer-auto-save-file-name buffer-file-name)) (setq have-auto-save nil)) ;; If user wants us to always recover, pretend there's no base file. (and have-auto-save (eq t notes-recover-from-auto-save-file) (setq have-file nil)) ;; Ask user what to do. (and have-file have-auto-save (if (y-or-n-p "Recover notes file? ") (setq have-file nil) (setq have-auto-save nil))) (let ((file (cond (have-file buffer-file-name) (have-auto-save buffer-auto-save-file-name)))) (cond (file (insert-file-contents file nil nil nil t) (set-buffer-modified-p nil)) ((zerop (buffer-size)) (insert notes--initial-message) (set-buffer-modified-p t)))))) (defun notes--kill-buffer-advice (func &optional buffer) (if (null notes--buffer) (funcall func buffer) (setq buffer (cond ((null buffer) (current-buffer)) ((stringp buffer) (get-buffer buffer)) (buffer))) (and (buffer-live-p buffer) (not (when (eq buffer notes--buffer) (when (buffer-modified-p buffer) (with-current-buffer buffer (save-buffer))) (when bury-notes-on-kill (bury-buffer (unless (eq buffer (current-buffer)) buffer)) t))) (funcall func buffer) (progn (when (eq buffer notes--buffer) (advice-remove 'kill-buffer 'notes--kill-buffer-advice) (setq notes--buffer nil)) t)))) ;;; notes.el ends here