Remove copyright from author with assignment and merge years into FSF years.
[bpt/emacs.git] / lisp / textmodes / remember.el
CommitLineData
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."
3728bf03
MO
156;;
157;; * Diary integration
158;;
159;; To use, add the following to your .emacs:
160;;
161;; ;; This should be before other entries that may return t
162;; (add-to-list 'remember-handler-functions 'remember-diary-extract-entries)
163;;
164;; This module recognizes entries of the form
165;;
166;; DIARY: ....
167;;
168;; and puts them in your ~/.diary (or remember-diary-file) together
169;; with an annotation. Dates in the form YYYY.MM.DD are converted to
170;; YYYY-MM-DD so that diary can understand them.
171;;
172;; For example:
173;;
174;; DIARY: 2003.08.12 Sacha's birthday
175;;
176;; is stored as
177;;
178;; 2003.08.12 Sacha's birthday
15f3eb73
MO
179
180;;; History:
181
182;;; Code:
183
184(provide 'remember)
185
186(defconst remember-version "1.9"
187 "This version of remember.")
188
189(defgroup remember nil
190 "A mode to remember information."
191 :group 'data)
192
193;;; User Variables:
194
195(defcustom remember-mode-hook nil
196 "Functions run upon entering `remember-mode'."
197 :type 'hook
e0628060 198 :options '(flyspell-mode turn-on-auto-fill org-remember-apply-template)
15f3eb73
MO
199 :group 'remember)
200
201(defcustom remember-in-new-frame nil
202 "Non-nil means use a separate frame for capturing remember data."
203 :type 'boolean
204 :group 'remember)
205
206(defcustom remember-register ?R
207 "The register in which the window configuration is stored."
208 :type 'character
209 :group 'remember)
210
211(defcustom remember-filter-functions nil
212 "*Functions run to filter remember data.
213All functions are run in the remember buffer."
214 :type 'hook
215 :group 'remember)
216
217(defcustom remember-handler-functions '(remember-append-to-file)
218 "*Functions run to process remember data.
219Each function is called with the current buffer narrowed to what the
220user wants remembered.
221If any function returns non-nil, the data is assumed to have been
222recorded somewhere by that function. "
223 :type 'hook
e0628060
MO
224 :options '(remember-store-in-mailbox
225 remember-append-to-file
226 remember-diary-extract-entries
227 org-remember-handler)
15f3eb73
MO
228 :group 'remember)
229
230(defcustom remember-all-handler-functions nil
231 "If non-nil every function in `remember-handler-functions' is
232called."
233 :type 'boolean
234 :group 'remember)
235
236;;; Internal Variables:
237
238(defvar remember-buffer "*Remember*"
239 "The name of the remember data entry buffer.")
240
241(defcustom remember-save-after-remembering t
242 "*Non-nil means automatically save after remembering."
243 :type 'boolean
244 :group 'remember)
245
246;;; User Functions:
247
77210f1e
MO
248(defcustom remember-annotation-functions '(buffer-file-name)
249 "Hook that returns an annotation to be inserted into the remember buffer."
15f3eb73 250 :type 'hook
e0628060 251 :options '(org-remember-annotation buffer-file-name)
15f3eb73
MO
252 :group 'remember)
253
254(defvar remember-annotation nil
255 "Current annotation.")
256(defvar remember-initial-contents nil
257 "Initial contents to place into *Remember* buffer.")
174a72ea
MO
258
259(defcustom remember-before-remember-hook nil
260 "Functions run before switching to the *Remember* buffer."
261 :type 'hook
262 :group 'remember)
15f3eb73
MO
263
264(defcustom remember-run-all-annotation-functions-flag nil
265 "Non-nil means use all annotations returned by
266`remember-annotation-functions'."
267 :type 'boolean
268 :group 'remember)
269
270;;;###autoload
271(defun remember (&optional initial)
272 "Remember an arbitrary piece of data.
174a72ea
MO
273INITIAL is the text to initially place in the *Remember* buffer,
274or nil to bring up a blank *Remember* buffer.
275
043989e3 276With a prefix or a visible region, use the region as INITIAL."
15f3eb73 277 (interactive
043989e3
MO
278 (list (when (or current-prefix-arg
279 (and mark-active
280 transient-mark-mode))
281 (buffer-substring (region-beginning) (region-end)))))
15f3eb73
MO
282 (funcall (if remember-in-new-frame
283 #'frame-configuration-to-register
284 #'window-configuration-to-register) remember-register)
285 (let* ((annotation
286 (if remember-run-all-annotation-functions-flag
287 (mapconcat 'identity
288 (delq nil
289 (mapcar 'funcall remember-annotation-functions))
290 "\n")
291 (run-hook-with-args-until-success
292 'remember-annotation-functions)))
293 (buf (get-buffer-create remember-buffer)))
294 (run-hooks 'remember-before-remember-hook)
295 (funcall (if remember-in-new-frame
296 #'switch-to-buffer-other-frame
297 #'switch-to-buffer-other-window) buf)
298 (if remember-in-new-frame
299 (set-window-dedicated-p
300 (get-buffer-window (current-buffer) (selected-frame)) t))
301 (remember-mode)
302 (when (= (point-max) (point-min))
303 (when initial (insert initial))
304 (setq remember-annotation annotation)
305 (when remember-initial-contents (insert remember-initial-contents))
306 (when (and (stringp annotation)
307 (not (equal annotation "")))
308 (insert "\n\n" annotation))
309 (setq remember-initial-contents nil)
310 (goto-char (point-min)))
311 (message "Use C-c C-c to remember the data.")))
312
313;;;###autoload
314(defun remember-other-frame (&optional initial)
315 "Call `remember' in another frame."
316 (interactive
317 (list (when current-prefix-arg
318 (buffer-substring (point) (mark)))))
319 (let ((remember-in-new-frame t))
320 (remember initial)))
321
322(defsubst remember-time-to-seconds (time)
323 "Convert TIME to a floating point number."
324 (+ (* (car time) 65536.0)
325 (cadr time)
326 (/ (or (car (cdr (cdr time))) 0) 1000000.0)))
327
328(defsubst remember-mail-date (&optional rfc822-p)
329 "Return a simple date. Nothing fancy."
330 (if rfc822-p
331 (format-time-string "%a, %e %b %Y %T %z" (current-time))
332 (format-time-string "%c" (current-time))))
333
334(defun remember-buffer-desc ()
335 "Using the first line of the current buffer, create a short description."
336 (buffer-substring (point-min)
337 (save-excursion
338 (goto-char (point-min))
339 (end-of-line)
340 (if (> (- (point) (point-min)) 60)
341 (goto-char (+ (point-min) 60)))
342 (point))))
343
344;; Remembering to UNIX mailboxes
345
346(defcustom remember-mailbox "~/Mail/remember"
347 "*The file in which to store remember data as mail."
348 :type 'file
349 :group 'remember)
350
351(defcustom remember-default-priority "medium"
352 "*The default priority for remembered mail messages."
353 :type 'string
354 :group 'remember)
355
356(defun remember-store-in-mailbox ()
357 "Store remember data as if it were incoming mail.
358In which case `remember-mailbox' should be the name of the mailbox.
359Each piece of psuedo-mail created will have an `X-Todo-Priority'
360field, for the purpose of appropriate splitting."
361 (let ((who (read-string "Who is this item related to? "))
362 (moment
363 (format "%.0f" (remember-time-to-seconds (current-time))))
364 (desc (remember-buffer-desc))
365 (text (buffer-string)))
366 (with-temp-buffer
367 (insert (format "
368From %s %s
369Date: %s
370From: %s
371Message-Id: <remember-%s@%s>
372X-Todo-Priority: %s
373To: %s <%s>
374Subject: %s\n\n"
375 (user-login-name)
376 (remember-mail-date)
377 (remember-mail-date t)
378 who
379 moment (system-name)
380 remember-default-priority
381 (user-full-name) user-mail-address
382 desc))
383 (let ((here (point)))
384 (insert text)
385 (unless (bolp)
386 (insert "\n"))
387 (insert "\n")
388 (goto-char here)
389 (while (re-search-forward "^\\(From[: ]\\)" nil t)
390 (replace-match ">\\1")))
391 (append-to-file (point-min) (point-max) remember-mailbox)
392 t)))
393
15f3eb73
MO
394;; Remembering to plain files
395
396(defcustom remember-data-file "~/.notes"
397 "*The file in which to store unprocessed data."
398 :type 'file
399 :group 'remember)
400
401(defcustom remember-leader-text "** "
402 "*The text used to begin each remember item."
403 :type 'string
404 :group 'remember)
405
406(defun remember-append-to-file ()
407 "Remember, with description DESC, the given TEXT."
408 (let ((text (buffer-string))
409 (desc (remember-buffer-desc)))
410 (with-temp-buffer
411 (insert "\n" remember-leader-text (current-time-string)
412 " (" desc ")\n\n" text)
413 (if (not (bolp))
414 (insert "\n"))
415 (if (find-buffer-visiting remember-data-file)
416 (let ((remember-text (buffer-string)))
417 (set-buffer (get-file-buffer remember-data-file))
418 (save-excursion
419 (goto-char (point-max))
420 (insert remember-text)
421 (when remember-save-after-remembering (save-buffer))))
422 (append-to-file (point-min) (point-max) remember-data-file)))))
423
15f3eb73
MO
424(defun remember-region (&optional beg end)
425 "Remember the data from BEG to END.
174a72ea 426It is called from within the *Remember* buffer to save the text
043989e3 427that was entered.
174a72ea
MO
428
429If BEG and END are nil, the entire buffer will be remembered.
15f3eb73 430
15f3eb73 431If you want to remember a region, supply a universal prefix to
174a72ea 432`remember' instead. For example: C-u M-x remember RET."
15f3eb73
MO
433 ;; Sacha: I have no idea where remember.el gets this context information, but
434 ;; you can just use remember-annotation-functions.
435 (interactive)
436 (let ((b (or beg (min (point) (or (mark) (point-min)))))
437 (e (or end (max (point) (or (mark) (point-max))))))
438 (save-restriction
439 (narrow-to-region b e)
440 (if remember-all-handler-functions
441 (run-hooks 'remember-handler-functions)
442 (run-hook-with-args-until-success 'remember-handler-functions))
443 (remember-destroy))))
444
445;;;###autoload
446(defun remember-clipboard ()
447 "Remember the contents of the current clipboard.
448Most useful for remembering things from Netscape or other X Windows
449application."
450 (interactive)
451 (remember (current-kill 0)))
452
174a72ea 453(defun remember-finalize ()
15f3eb73
MO
454 "Remember the contents of the current buffer."
455 (interactive)
456 (remember-region (point-min) (point-max)))
457
246a4316 458;; Org needs this
6159985a 459(define-obsolete-function-alias 'remember-buffer 'remember-finalize)
246a4316 460
15f3eb73
MO
461(defun remember-destroy ()
462 "Destroy the current *Remember* buffer."
463 (interactive)
464 (when (equal remember-buffer (buffer-name))
465 (kill-buffer (current-buffer))
466 (jump-to-register remember-register)))
467
3728bf03
MO
468;;; Diary integration
469
470(defcustom remember-diary-file nil
471 "*File for extracted diary entries.
472If this is nil, then `diary-file' will be used instead."
473 :type 'file
474 :group 'remember)
475
476(defun remember-diary-convert-entry (entry)
477 "Translate MSG to an entry readable by diary."
478 (save-match-data
479 (when remember-annotation
480 (setq entry (concat entry " " remember-annotation)))
481 (if (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)" entry)
482 (replace-match
483 (if european-calendar-style
484 (concat (match-string 3 entry) "/"
485 (match-string 2 entry) "/"
486 (match-string 1 entry))
487 (concat (match-string 2 entry) "/"
488 (match-string 3 entry) "/"
489 (match-string 1 entry)))
490 t t entry)
491 entry)))
492
493(autoload 'make-diary-entry "diary-lib")
494
495;;;###autoload
496(defun remember-diary-extract-entries ()
497 "Extract diary entries from the region."
498 (save-excursion
499 (goto-char (point-min))
500 (let (list)
501 (while (re-search-forward "^DIARY:\\s-*\\(.+\\)" nil t)
502 (add-to-list 'list (remember-diary-convert-entry (match-string 1))))
503 (when list
504 (make-diary-entry (mapconcat 'identity list "\n")
505 nil (or remember-diary-file diary-file)))
506 nil))) ;; Continue processing
507
15f3eb73
MO
508;;; Internal Functions:
509
80f0c18f
MO
510(defvar remember-mode-map
511 (let ((map (make-sparse-keymap)))
512 (define-key map "\C-x\C-s" 'remember-finalize)
513 (define-key map "\C-c\C-c" 'remember-finalize)
869dc290
MO
514 (define-key map "\C-c\C-k" 'remember-destroy)
515
516 map)
15f3eb73 517 "Keymap used in Remember mode.")
15f3eb73
MO
518
519(defun remember-mode ()
520 "Major mode for output from \\[remember].
869dc290
MO
521This buffer is used to collect data that you want to remember.
522
523Just hit `C-c C-c' when you're done entering, and it will file
524the data away for latter retrieval, and possible indexing.
525
526\\{remember-mode-map}"
15f3eb73
MO
527 (interactive)
528 (kill-all-local-variables)
529 (indented-text-mode)
530 (use-local-map remember-mode-map)
531 (setq major-mode 'remember-mode
532 mode-name "Remember")
533 (run-hooks 'remember-mode-hook))
534
904fac67 535;; arch-tag: 59312a05-06c7-4da1-b6f7-5ea41c9d5577
15f3eb73 536;;; remember.el ends here