Commit | Line | Data |
---|---|---|
15f3eb73 MO |
1 | ;;; remember --- a mode for quickly jotting down things to remember |
2 | ||
3 | ;; Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, | |
4 | ;; 2007 Free Software Foundation, Inc. | |
5 | ||
6 | ;; Author: John Wiegley <johnw@gnu.org> | |
7 | ;; Created: 29 Mar 1999 | |
8 | ;; Version: 1.9 | |
9 | ;; Keywords: data memory todo pim | |
10 | ;; URL: http://gna.org/projects/remember-el/ | |
11 | ||
12 | ;; This file is part of GNU Emacs. | |
13 | ||
14 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
15 | ;; it under the terms of the GNU General Public License as published by | |
16 | ;; the Free Software Foundation; either version 3, or (at your option) | |
17 | ;; any later version. | |
18 | ||
19 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | ;; GNU General Public License for more details. | |
23 | ||
24 | ;; You should have received a copy of the GNU General Public License | |
25 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
26 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
27 | ;; Boston, MA 02110-1301, USA. | |
28 | ||
29 | ;;; Commentary: | |
30 | ||
77210f1e | 31 | ;; * The idea |
15f3eb73 MO |
32 | ;; |
33 | ;; Todo lists, schedules, phone databases... everything we use | |
34 | ;; databases for is really just a way to extend the power of our | |
35 | ;; memory. To be able to remember what our conscious mind may not | |
36 | ;; currently have access to. | |
37 | ;; | |
38 | ;; There are many different databases out there -- and good ones -- | |
39 | ;; which this mode is not trying to replace. Rather, it's how that | |
40 | ;; data gets there that's the question. Most of the time, we just | |
41 | ;; want to say "Remember so-and-so's phone number, or that I have to | |
42 | ;; buy dinner for the cats tonight." That's the FACT. How it's | |
43 | ;; stored is really the computer's problem. But at this point in | |
44 | ;; time, it's most definitely also the user's problem, and sometimes | |
45 | ;; so laboriously so that people just let data slip, rather than | |
46 | ;; expend the effort to record it. | |
47 | ;; | |
48 | ;; "Remember" is a mode for remembering data. It uses whatever | |
49 | ;; back-end is appropriate to record and correlate the data, but it's | |
50 | ;; main intention is to allow you to express as _little_ structure as | |
51 | ;; possible up front. If you later want to express more powerful | |
52 | ;; relationships between your data, or state assumptions that were at | |
53 | ;; first too implicit to be recognized, you can "study" the data later | |
54 | ;; and rearrange it. But the initial "just remember this" impulse | |
55 | ;; should be as close to simply throwing the data at Emacs as | |
56 | ;; possible. | |
57 | ;; | |
77210f1e | 58 | ;; * Implementation |
15f3eb73 MO |
59 | ;; |
60 | ;; Hyperbole, as a data presentation tool, always struck me as being | |
61 | ;; very powerful, but it seemed to require a lot of "front-end" work | |
62 | ;; before that data was really available. The problem with BBDB, or | |
63 | ;; keeping up a Bibl-mode file, is that you have to use different | |
64 | ;; functions to record the data, and it always takes time to stop what | |
65 | ;; you're doing, format the data in the manner expected by that | |
66 | ;; particular data interface, and then resume your work. | |
67 | ;; | |
68 | ;; With "remember", you just hit `M-x remember' (you'd probably want | |
69 | ;; to bind this to an easily accessible keystroke, like C-x M-r), slam | |
70 | ;; in your text however you like, and then hit C-c C-c. It will file | |
71 | ;; the data away for later retrieval, and possibly indexing. | |
72 | ;; | |
73 | ;; Indexing is to data what "studying" is in the real world. What you | |
74 | ;; do when you study (or lucubrate, for some of us) is to realize | |
75 | ;; certain relationships implicit in the data, so that you can make | |
76 | ;; use of those relationships. Expressing that a certain quote you | |
77 | ;; remembered was a religious quote, and that you want the ability to | |
78 | ;; pull up all quotes of a religious nature, is what studying does. | |
79 | ;; This is a more labor intensive task than the original remembering | |
80 | ;; of the data, and it's typical in real life to set aside a special | |
81 | ;; period of time for doing this work. | |
82 | ;; | |
83 | ;; "Remember" works in the same way. When you enter data, either by | |
84 | ;; typing it into a buffer, or using the contents of the selected | |
85 | ;; region, it will store that data -- unindexed, uninterpreted -- in a | |
86 | ;; data pool. It will also try to remember as much context | |
87 | ;; information as possible (any text properties that were set, where | |
88 | ;; you copied it from, when, how, etc). Later, you can walk through | |
89 | ;; your accumulated set of data (both organized, and unorganized) and | |
90 | ;; easily begin moving things around, and making annotations that will | |
91 | ;; express the full meaning of that data, as far as you know it. | |
92 | ;; | |
93 | ;; Obviously this latter stage is more user-interface intensive, and | |
94 | ;; it would be nice if "remember" could do it as elegantly as | |
95 | ;; possible, rather than requiring a billion keystrokes to reorganize | |
96 | ;; your hierarchy. Well, as the future arrives, hopefully experience | |
97 | ;; and user feedback will help to make this as intuitive a tool as | |
98 | ;; possible. | |
99 | ;; | |
77210f1e | 100 | ;; * Future Goals |
15f3eb73 MO |
101 | ;; |
102 | ;; This tool hopes to track (and by doing it with as little new code | |
103 | ;; as possible): | |
104 | ;; | |
105 | ;; - The raw data that gets entered | |
106 | ;; | |
107 | ;; - The relationships between that data (either determined | |
108 | ;; implicitly by parsing the input, or explicitly by the user's | |
109 | ;; studying the data). | |
110 | ;; | |
111 | ;; - Revisioning of the data | |
112 | ;; | |
113 | ;; - Where it came from, and any context information that can be | |
114 | ;; programmatically determined. | |
115 | ;; | |
116 | ;; - Allowing particular views of the initially amorphous data pool | |
117 | ;; (ala the Xanadu concept). | |
118 | ;; | |
119 | ;; - Storage of the data in a manner most appopriate to that data, | |
120 | ;; such as keeping address-book type information in BBDB, etc. | |
121 | ;; | |
77210f1e | 122 | ;; * Using "remember" |
15f3eb73 MO |
123 | ;; |
124 | ;; As a rough beginning, what I do is to keep my .notes file in | |
125 | ;; outline-mode format, with a final entry called "* Raw data". Then, | |
126 | ;; at intervals, I can move the data that gets appended there into | |
127 | ;; other places. But certainly this should evolve into an intuitive | |
128 | ;; mechanism for shuffling data off to its appropriate corner of the | |
129 | ;; universe. | |
130 | ;; | |
77210f1e MO |
131 | ;; To map the primary remember function to the keystroke F8, do the |
132 | ;; following. | |
15f3eb73 | 133 | ;; |
77210f1e | 134 | ;; (autoload 'remember "remember" nil t) |
15f3eb73 | 135 | ;; |
77210f1e | 136 | ;; (define-key global-map [f8] 'remember) |
15f3eb73 | 137 | ;; |
77210f1e | 138 | ;; * Feedback |
15f3eb73 MO |
139 | ;; |
140 | ;; If Emacs could become a more intelligent data store, where | |
141 | ;; brainstorming would focus on the IDEAS involved -- rather than the | |
142 | ;; structuring and format of those ideas, or having to stop your | |
143 | ;; current flow of work in order to record them -- it would map much | |
144 | ;; more closely to how the mind (well, at least mine) works, and hence | |
145 | ;; would eliminate that very manual-ness which computers from the very | |
146 | ;; beginning have been championed as being able to reduce. | |
147 | ;; | |
148 | ;; Have you ever noticed that having a laptop to write on doesn't | |
149 | ;; _actually_ increase the amount of quality material that you turn | |
150 | ;; out, in the long run? Perhaps its because the time we save | |
151 | ;; electronically in one way, we're losing electronically in another; | |
152 | ;; the tool should never dominate one's focus. As the mystic | |
153 | ;; Faridu'd-Din `Attar wrote: "Be occupied as little as possible with | |
154 | ;; things of the outer world but much with things of the inner world; | |
155 | ;; then right action will overcome inaction." | |
156 | ||
157 | ;;; History: | |
158 | ||
159 | ;;; Code: | |
160 | ||
161 | (provide 'remember) | |
162 | ||
163 | (defconst remember-version "1.9" | |
164 | "This version of remember.") | |
165 | ||
166 | (defgroup remember nil | |
167 | "A mode to remember information." | |
168 | :group 'data) | |
169 | ||
170 | ;;; User Variables: | |
171 | ||
172 | (defcustom remember-mode-hook nil | |
173 | "Functions run upon entering `remember-mode'." | |
174 | :type 'hook | |
e0628060 | 175 | :options '(flyspell-mode turn-on-auto-fill org-remember-apply-template) |
15f3eb73 MO |
176 | :group 'remember) |
177 | ||
178 | (defcustom remember-in-new-frame nil | |
179 | "Non-nil means use a separate frame for capturing remember data." | |
180 | :type 'boolean | |
181 | :group 'remember) | |
182 | ||
183 | (defcustom remember-register ?R | |
184 | "The register in which the window configuration is stored." | |
185 | :type 'character | |
186 | :group 'remember) | |
187 | ||
188 | (defcustom remember-filter-functions nil | |
189 | "*Functions run to filter remember data. | |
190 | All functions are run in the remember buffer." | |
191 | :type 'hook | |
192 | :group 'remember) | |
193 | ||
194 | (defcustom remember-handler-functions '(remember-append-to-file) | |
195 | "*Functions run to process remember data. | |
196 | Each function is called with the current buffer narrowed to what the | |
197 | user wants remembered. | |
198 | If any function returns non-nil, the data is assumed to have been | |
199 | recorded somewhere by that function. " | |
200 | :type 'hook | |
e0628060 MO |
201 | :options '(remember-store-in-mailbox |
202 | remember-append-to-file | |
203 | remember-diary-extract-entries | |
204 | org-remember-handler) | |
15f3eb73 MO |
205 | :group 'remember) |
206 | ||
207 | (defcustom remember-all-handler-functions nil | |
208 | "If non-nil every function in `remember-handler-functions' is | |
209 | called." | |
210 | :type 'boolean | |
211 | :group 'remember) | |
212 | ||
213 | ;;; Internal Variables: | |
214 | ||
215 | (defvar remember-buffer "*Remember*" | |
216 | "The name of the remember data entry buffer.") | |
217 | ||
218 | (defcustom remember-save-after-remembering t | |
219 | "*Non-nil means automatically save after remembering." | |
220 | :type 'boolean | |
221 | :group 'remember) | |
222 | ||
223 | ;;; User Functions: | |
224 | ||
77210f1e MO |
225 | (defcustom remember-annotation-functions '(buffer-file-name) |
226 | "Hook that returns an annotation to be inserted into the remember buffer." | |
15f3eb73 | 227 | :type 'hook |
e0628060 | 228 | :options '(org-remember-annotation buffer-file-name) |
15f3eb73 MO |
229 | :group 'remember) |
230 | ||
231 | (defvar remember-annotation nil | |
232 | "Current annotation.") | |
233 | (defvar remember-initial-contents nil | |
234 | "Initial contents to place into *Remember* buffer.") | |
174a72ea MO |
235 | |
236 | (defcustom remember-before-remember-hook nil | |
237 | "Functions run before switching to the *Remember* buffer." | |
238 | :type 'hook | |
239 | :group 'remember) | |
15f3eb73 MO |
240 | |
241 | (defcustom remember-run-all-annotation-functions-flag nil | |
242 | "Non-nil means use all annotations returned by | |
243 | `remember-annotation-functions'." | |
244 | :type 'boolean | |
245 | :group 'remember) | |
246 | ||
247 | ;;;###autoload | |
248 | (defun remember (&optional initial) | |
249 | "Remember an arbitrary piece of data. | |
174a72ea MO |
250 | INITIAL is the text to initially place in the *Remember* buffer, |
251 | or nil to bring up a blank *Remember* buffer. | |
252 | ||
253 | With a prefix, use the region as INITIAL." | |
15f3eb73 MO |
254 | (interactive |
255 | (list (when current-prefix-arg | |
256 | (buffer-substring (point) (mark))))) | |
257 | (funcall (if remember-in-new-frame | |
258 | #'frame-configuration-to-register | |
259 | #'window-configuration-to-register) remember-register) | |
260 | (let* ((annotation | |
261 | (if remember-run-all-annotation-functions-flag | |
262 | (mapconcat 'identity | |
263 | (delq nil | |
264 | (mapcar 'funcall remember-annotation-functions)) | |
265 | "\n") | |
266 | (run-hook-with-args-until-success | |
267 | 'remember-annotation-functions))) | |
268 | (buf (get-buffer-create remember-buffer))) | |
269 | (run-hooks 'remember-before-remember-hook) | |
270 | (funcall (if remember-in-new-frame | |
271 | #'switch-to-buffer-other-frame | |
272 | #'switch-to-buffer-other-window) buf) | |
273 | (if remember-in-new-frame | |
274 | (set-window-dedicated-p | |
275 | (get-buffer-window (current-buffer) (selected-frame)) t)) | |
276 | (remember-mode) | |
277 | (when (= (point-max) (point-min)) | |
278 | (when initial (insert initial)) | |
279 | (setq remember-annotation annotation) | |
280 | (when remember-initial-contents (insert remember-initial-contents)) | |
281 | (when (and (stringp annotation) | |
282 | (not (equal annotation ""))) | |
283 | (insert "\n\n" annotation)) | |
284 | (setq remember-initial-contents nil) | |
285 | (goto-char (point-min))) | |
286 | (message "Use C-c C-c to remember the data."))) | |
287 | ||
288 | ;;;###autoload | |
289 | (defun remember-other-frame (&optional initial) | |
290 | "Call `remember' in another frame." | |
291 | (interactive | |
292 | (list (when current-prefix-arg | |
293 | (buffer-substring (point) (mark))))) | |
294 | (let ((remember-in-new-frame t)) | |
295 | (remember initial))) | |
296 | ||
297 | (defsubst remember-time-to-seconds (time) | |
298 | "Convert TIME to a floating point number." | |
299 | (+ (* (car time) 65536.0) | |
300 | (cadr time) | |
301 | (/ (or (car (cdr (cdr time))) 0) 1000000.0))) | |
302 | ||
303 | (defsubst remember-mail-date (&optional rfc822-p) | |
304 | "Return a simple date. Nothing fancy." | |
305 | (if rfc822-p | |
306 | (format-time-string "%a, %e %b %Y %T %z" (current-time)) | |
307 | (format-time-string "%c" (current-time)))) | |
308 | ||
309 | (defun remember-buffer-desc () | |
310 | "Using the first line of the current buffer, create a short description." | |
311 | (buffer-substring (point-min) | |
312 | (save-excursion | |
313 | (goto-char (point-min)) | |
314 | (end-of-line) | |
315 | (if (> (- (point) (point-min)) 60) | |
316 | (goto-char (+ (point-min) 60))) | |
317 | (point)))) | |
318 | ||
319 | ;; Remembering to UNIX mailboxes | |
320 | ||
321 | (defcustom remember-mailbox "~/Mail/remember" | |
322 | "*The file in which to store remember data as mail." | |
323 | :type 'file | |
324 | :group 'remember) | |
325 | ||
326 | (defcustom remember-default-priority "medium" | |
327 | "*The default priority for remembered mail messages." | |
328 | :type 'string | |
329 | :group 'remember) | |
330 | ||
331 | (defun remember-store-in-mailbox () | |
332 | "Store remember data as if it were incoming mail. | |
333 | In which case `remember-mailbox' should be the name of the mailbox. | |
334 | Each piece of psuedo-mail created will have an `X-Todo-Priority' | |
335 | field, for the purpose of appropriate splitting." | |
336 | (let ((who (read-string "Who is this item related to? ")) | |
337 | (moment | |
338 | (format "%.0f" (remember-time-to-seconds (current-time)))) | |
339 | (desc (remember-buffer-desc)) | |
340 | (text (buffer-string))) | |
341 | (with-temp-buffer | |
342 | (insert (format " | |
343 | From %s %s | |
344 | Date: %s | |
345 | From: %s | |
346 | Message-Id: <remember-%s@%s> | |
347 | X-Todo-Priority: %s | |
348 | To: %s <%s> | |
349 | Subject: %s\n\n" | |
350 | (user-login-name) | |
351 | (remember-mail-date) | |
352 | (remember-mail-date t) | |
353 | who | |
354 | moment (system-name) | |
355 | remember-default-priority | |
356 | (user-full-name) user-mail-address | |
357 | desc)) | |
358 | (let ((here (point))) | |
359 | (insert text) | |
360 | (unless (bolp) | |
361 | (insert "\n")) | |
362 | (insert "\n") | |
363 | (goto-char here) | |
364 | (while (re-search-forward "^\\(From[: ]\\)" nil t) | |
365 | (replace-match ">\\1"))) | |
366 | (append-to-file (point-min) (point-max) remember-mailbox) | |
367 | t))) | |
368 | ||
15f3eb73 MO |
369 | ;; Remembering to plain files |
370 | ||
371 | (defcustom remember-data-file "~/.notes" | |
372 | "*The file in which to store unprocessed data." | |
373 | :type 'file | |
374 | :group 'remember) | |
375 | ||
376 | (defcustom remember-leader-text "** " | |
377 | "*The text used to begin each remember item." | |
378 | :type 'string | |
379 | :group 'remember) | |
380 | ||
381 | (defun remember-append-to-file () | |
382 | "Remember, with description DESC, the given TEXT." | |
383 | (let ((text (buffer-string)) | |
384 | (desc (remember-buffer-desc))) | |
385 | (with-temp-buffer | |
386 | (insert "\n" remember-leader-text (current-time-string) | |
387 | " (" desc ")\n\n" text) | |
388 | (if (not (bolp)) | |
389 | (insert "\n")) | |
390 | (if (find-buffer-visiting remember-data-file) | |
391 | (let ((remember-text (buffer-string))) | |
392 | (set-buffer (get-file-buffer remember-data-file)) | |
393 | (save-excursion | |
394 | (goto-char (point-max)) | |
395 | (insert remember-text) | |
396 | (when remember-save-after-remembering (save-buffer)))) | |
397 | (append-to-file (point-min) (point-max) remember-data-file))))) | |
398 | ||
15f3eb73 MO |
399 | (defun remember-region (&optional beg end) |
400 | "Remember the data from BEG to END. | |
174a72ea MO |
401 | It is called from within the *Remember* buffer to save the text |
402 | that was entered, | |
403 | ||
404 | If BEG and END are nil, the entire buffer will be remembered. | |
15f3eb73 | 405 | |
15f3eb73 | 406 | If you want to remember a region, supply a universal prefix to |
174a72ea | 407 | `remember' instead. For example: C-u M-x remember RET." |
15f3eb73 MO |
408 | ;; Sacha: I have no idea where remember.el gets this context information, but |
409 | ;; you can just use remember-annotation-functions. | |
410 | (interactive) | |
411 | (let ((b (or beg (min (point) (or (mark) (point-min))))) | |
412 | (e (or end (max (point) (or (mark) (point-max)))))) | |
413 | (save-restriction | |
414 | (narrow-to-region b e) | |
415 | (if remember-all-handler-functions | |
416 | (run-hooks 'remember-handler-functions) | |
417 | (run-hook-with-args-until-success 'remember-handler-functions)) | |
418 | (remember-destroy)))) | |
419 | ||
420 | ;;;###autoload | |
421 | (defun remember-clipboard () | |
422 | "Remember the contents of the current clipboard. | |
423 | Most useful for remembering things from Netscape or other X Windows | |
424 | application." | |
425 | (interactive) | |
426 | (remember (current-kill 0))) | |
427 | ||
174a72ea | 428 | (defun remember-finalize () |
15f3eb73 MO |
429 | "Remember the contents of the current buffer." |
430 | (interactive) | |
431 | (remember-region (point-min) (point-max))) | |
432 | ||
246a4316 MO |
433 | ;; Org needs this |
434 | (defalias 'remember-buffer 'remember-finalize) | |
435 | ||
15f3eb73 MO |
436 | (defun remember-destroy () |
437 | "Destroy the current *Remember* buffer." | |
438 | (interactive) | |
439 | (when (equal remember-buffer (buffer-name)) | |
440 | (kill-buffer (current-buffer)) | |
441 | (jump-to-register remember-register))) | |
442 | ||
443 | ;;; Internal Functions: | |
444 | ||
80f0c18f MO |
445 | (defvar remember-mode-map |
446 | (let ((map (make-sparse-keymap))) | |
447 | (define-key map "\C-x\C-s" 'remember-finalize) | |
448 | (define-key map "\C-c\C-c" 'remember-finalize) | |
869dc290 MO |
449 | (define-key map "\C-c\C-k" 'remember-destroy) |
450 | ||
451 | map) | |
15f3eb73 | 452 | "Keymap used in Remember mode.") |
15f3eb73 MO |
453 | |
454 | (defun remember-mode () | |
455 | "Major mode for output from \\[remember]. | |
869dc290 MO |
456 | This buffer is used to collect data that you want to remember. |
457 | ||
458 | Just hit `C-c C-c' when you're done entering, and it will file | |
459 | the data away for latter retrieval, and possible indexing. | |
460 | ||
461 | \\{remember-mode-map}" | |
15f3eb73 MO |
462 | (interactive) |
463 | (kill-all-local-variables) | |
464 | (indented-text-mode) | |
465 | (use-local-map remember-mode-map) | |
466 | (setq major-mode 'remember-mode | |
467 | mode-name "Remember") | |
468 | (run-hooks 'remember-mode-hook)) | |
469 | ||
904fac67 | 470 | ;; arch-tag: 59312a05-06c7-4da1-b6f7-5ea41c9d5577 |
15f3eb73 | 471 | ;;; remember.el ends here |