1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
| | ;;; notes.el --- notes preserved across sessions -*- lexical-binding: t -*-
;; Copyright (C) 2013 Free Software Foundation, Inc.
;; Author: Michal Nazarewicz <mina86@mina86.com>
;; 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 <http://www.gnu.org/licenses/>.
;;; 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
|