remember.el 19.3 KB
Newer Older
Michael Olson's avatar
Michael Olson committed
1 2
;;; remember --- a mode for quickly jotting down things to remember

3 4
;; Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008,
;;   2009, 2010  Free Software Foundation, Inc.
Michael Olson's avatar
Michael Olson committed
5 6 7

;; Author: John Wiegley <johnw@gnu.org>
;; Created: 29 Mar 1999
Michael Olson's avatar
Michael Olson committed
8
;; Version: 2.0
Michael Olson's avatar
Michael Olson committed
9 10 11 12 13
;; Keywords: data memory todo pim
;; URL: http://gna.org/projects/remember-el/

;; This file is part of GNU Emacs.

14
;; GNU Emacs is free software: you can redistribute it and/or modify
Michael Olson's avatar
Michael Olson committed
15
;; it under the terms of the GNU General Public License as published by
16 17
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Michael Olson's avatar
Michael Olson committed
18 19 20 21 22 23 24

;; 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
25
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Michael Olson's avatar
Michael Olson committed
26 27 28

;;; Commentary:

29
;; * The idea
Michael Olson's avatar
Michael Olson committed
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
;;
;; Todo lists, schedules, phone databases... everything we use
;; databases for is really just a way to extend the power of our
;; memory.  To be able to remember what our conscious mind may not
;; currently have access to.
;;
;; There are many different databases out there -- and good ones --
;; which this mode is not trying to replace.  Rather, it's how that
;; data gets there that's the question.  Most of the time, we just
;; want to say "Remember so-and-so's phone number, or that I have to
;; buy dinner for the cats tonight."  That's the FACT.  How it's
;; stored is really the computer's problem.  But at this point in
;; time, it's most definitely also the user's problem, and sometimes
;; so laboriously so that people just let data slip, rather than
;; expend the effort to record it.
;;
;; "Remember" is a mode for remembering data.  It uses whatever
;; back-end is appropriate to record and correlate the data, but it's
;; main intention is to allow you to express as _little_ structure as
;; possible up front.  If you later want to express more powerful
;; relationships between your data, or state assumptions that were at
;; first too implicit to be recognized, you can "study" the data later
;; and rearrange it.  But the initial "just remember this" impulse
;; should be as close to simply throwing the data at Emacs as
;; possible.
;;
56
;; * Implementation
Michael Olson's avatar
Michael Olson committed
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
;;
;; Hyperbole, as a data presentation tool, always struck me as being
;; very powerful, but it seemed to require a lot of "front-end" work
;; before that data was really available.  The problem with BBDB, or
;; keeping up a Bibl-mode file, is that you have to use different
;; functions to record the data, and it always takes time to stop what
;; you're doing, format the data in the manner expected by that
;; particular data interface, and then resume your work.
;;
;; With "remember", you just hit `M-x remember' (you'd probably want
;; to bind this to an easily accessible keystroke, like C-x M-r), slam
;; in your text however you like, and then hit C-c C-c.  It will file
;; the data away for later retrieval, and possibly indexing.
;;
;; Indexing is to data what "studying" is in the real world.  What you
;; do when you study (or lucubrate, for some of us) is to realize
;; certain relationships implicit in the data, so that you can make
;; use of those relationships.  Expressing that a certain quote you
;; remembered was a religious quote, and that you want the ability to
;; pull up all quotes of a religious nature, is what studying does.
;; This is a more labor intensive task than the original remembering
;; of the data, and it's typical in real life to set aside a special
;; period of time for doing this work.
;;
;; "Remember" works in the same way.  When you enter data, either by
;; typing it into a buffer, or using the contents of the selected
;; region, it will store that data -- unindexed, uninterpreted -- in a
;; data pool.  It will also try to remember as much context
;; information as possible (any text properties that were set, where
;; you copied it from, when, how, etc).  Later, you can walk through
;; your accumulated set of data (both organized, and unorganized) and
;; easily begin moving things around, and making annotations that will
;; express the full meaning of that data, as far as you know it.
;;
;; Obviously this latter stage is more user-interface intensive, and
;; it would be nice if "remember" could do it as elegantly as
;; possible, rather than requiring a billion keystrokes to reorganize
;; your hierarchy.  Well, as the future arrives, hopefully experience
;; and user feedback will help to make this as intuitive a tool as
;; possible.
;;
98
;; * Future Goals
Michael Olson's avatar
Michael Olson committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
;;
;; This tool hopes to track (and by doing it with as little new code
;; as possible):
;;
;;  - The raw data that gets entered
;;
;;  - The relationships between that data (either determined
;;    implicitly by parsing the input, or explicitly by the user's
;;    studying the data).
;;
;;  - Revisioning of the data
;;
;;  - Where it came from, and any context information that can be
;;    programmatically determined.
;;
;;  - Allowing particular views of the initially amorphous data pool
;;    (ala the Xanadu concept).
;;
;;  - Storage of the data in a manner most appopriate to that data,
;;    such as keeping address-book type information in BBDB, etc.
;;
120
;; * Using "remember"
Michael Olson's avatar
Michael Olson committed
121 122 123 124 125 126 127 128
;;
;; As a rough beginning, what I do is to keep my .notes file in
;; outline-mode format, with a final entry called "* Raw data".  Then,
;; at intervals, I can move the data that gets appended there into
;; other places.  But certainly this should evolve into an intuitive
;; mechanism for shuffling data off to its appropriate corner of the
;; universe.
;;
129 130
;; To map the primary remember function to the keystroke F8, do the
;; following.
Michael Olson's avatar
Michael Olson committed
131
;;
132
;;   (autoload 'remember "remember" nil t)
Michael Olson's avatar
Michael Olson committed
133
;;
134
;;   (define-key global-map [f8] 'remember)
Michael Olson's avatar
Michael Olson committed
135
;;
136
;; * Feedback
Michael Olson's avatar
Michael Olson committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
;;
;; If Emacs could become a more intelligent data store, where
;; brainstorming would focus on the IDEAS involved -- rather than the
;; structuring and format of those ideas, or having to stop your
;; current flow of work in order to record them -- it would map much
;; more closely to how the mind (well, at least mine) works, and hence
;; would eliminate that very manual-ness which computers from the very
;; beginning have been championed as being able to reduce.
;;
;; Have you ever noticed that having a laptop to write on doesn't
;; _actually_ increase the amount of quality material that you turn
;; out, in the long run?  Perhaps its because the time we save
;; electronically in one way, we're losing electronically in another;
;; the tool should never dominate one's focus.  As the mystic
;; Faridu'd-Din `Attar wrote: "Be occupied as little as possible with
;; things of the outer world but much with things of the inner world;
;; then right action will overcome inaction."
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
;;
;; * Diary integration
;;
;; To use, add the following to your .emacs:
;;
;;   ;; This should be before other entries that may return t
;;   (add-to-list 'remember-handler-functions 'remember-diary-extract-entries)
;;
;; This module recognizes entries of the form
;;
;;   DIARY: ....
;;
;; and puts them in your ~/.diary (or remember-diary-file) together
;; with an annotation.  Dates in the form YYYY.MM.DD are converted to
;; YYYY-MM-DD so that diary can understand them.
;;
;; For example:
;;
;;   DIARY: 2003.08.12 Sacha's birthday
;;
;; is stored as
;;
;;   2003.08.12 Sacha's birthday
Michael Olson's avatar
Michael Olson committed
177 178 179 180 181 182 183

;;; History:

;;; Code:

(provide 'remember)

Michael Olson's avatar
Michael Olson committed
184
(defconst remember-version "2.0"
Michael Olson's avatar
Michael Olson committed
185 186 187 188 189 190 191 192 193 194 195
  "This version of remember.")

(defgroup remember nil
  "A mode to remember information."
  :group 'data)

;;; User Variables:

(defcustom remember-mode-hook nil
  "Functions run upon entering `remember-mode'."
  :type 'hook
196
  :options '(flyspell-mode turn-on-auto-fill org-remember-apply-template)
Michael Olson's avatar
Michael Olson committed
197 198 199 200 201 202 203 204 205 206 207 208 209
  :group 'remember)

(defcustom remember-in-new-frame nil
  "Non-nil means use a separate frame for capturing remember data."
  :type 'boolean
  :group 'remember)

(defcustom remember-register ?R
  "The register in which the window configuration is stored."
  :type 'character
  :group 'remember)

(defcustom remember-filter-functions nil
210
  "Functions run to filter remember data.
Michael Olson's avatar
Michael Olson committed
211 212 213 214 215
All functions are run in the remember buffer."
  :type 'hook
  :group 'remember)

(defcustom remember-handler-functions '(remember-append-to-file)
216
  "Functions run to process remember data.
Michael Olson's avatar
Michael Olson committed
217 218 219 220 221
Each function is called with the current buffer narrowed to what the
user wants remembered.
If any function returns non-nil, the data is assumed to have been
recorded somewhere by that function. "
  :type 'hook
222 223 224 225
  :options '(remember-store-in-mailbox
             remember-append-to-file
             remember-diary-extract-entries
             org-remember-handler)
Michael Olson's avatar
Michael Olson committed
226 227 228
  :group 'remember)

(defcustom remember-all-handler-functions nil
229
  "If non-nil every function in `remember-handler-functions' is called."
Michael Olson's avatar
Michael Olson committed
230 231 232 233 234 235 236 237 238
  :type 'boolean
  :group 'remember)

;;; Internal Variables:

(defvar remember-buffer "*Remember*"
  "The name of the remember data entry buffer.")

(defcustom remember-save-after-remembering t
239
  "Non-nil means automatically save after remembering."
Michael Olson's avatar
Michael Olson committed
240 241 242 243 244
  :type 'boolean
  :group 'remember)

;;; User Functions:

245 246
(defcustom remember-annotation-functions '(buffer-file-name)
  "Hook that returns an annotation to be inserted into the remember buffer."
Michael Olson's avatar
Michael Olson committed
247
  :type 'hook
248
  :options '(org-remember-annotation buffer-file-name)
Michael Olson's avatar
Michael Olson committed
249 250 251 252 253 254
  :group 'remember)

(defvar remember-annotation nil
  "Current annotation.")
(defvar remember-initial-contents nil
  "Initial contents to place into *Remember* buffer.")
255 256 257 258 259

(defcustom remember-before-remember-hook nil
  "Functions run before switching to the *Remember* buffer."
  :type 'hook
  :group 'remember)
Michael Olson's avatar
Michael Olson committed
260 261

(defcustom remember-run-all-annotation-functions-flag nil
262
  "Non-nil means use all annotations returned by `remember-annotation-functions'."
Michael Olson's avatar
Michael Olson committed
263 264 265 266 267 268
  :type 'boolean
  :group 'remember)

;;;###autoload
(defun remember (&optional initial)
  "Remember an arbitrary piece of data.
269 270 271
INITIAL is the text to initially place in the *Remember* buffer,
or nil to bring up a blank *Remember* buffer.

272
With a prefix or a visible region, use the region as INITIAL."
Michael Olson's avatar
Michael Olson committed
273
  (interactive
274 275 276 277
   (list (when (or current-prefix-arg
                   (and mark-active
                        transient-mark-mode))
           (buffer-substring (region-beginning) (region-end)))))
Michael Olson's avatar
Michael Olson committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
  (funcall (if remember-in-new-frame
               #'frame-configuration-to-register
             #'window-configuration-to-register) remember-register)
  (let* ((annotation
          (if remember-run-all-annotation-functions-flag
              (mapconcat 'identity
                         (delq nil
                               (mapcar 'funcall remember-annotation-functions))
                         "\n")
            (run-hook-with-args-until-success
             'remember-annotation-functions)))
         (buf (get-buffer-create remember-buffer)))
    (run-hooks 'remember-before-remember-hook)
    (funcall (if remember-in-new-frame
                 #'switch-to-buffer-other-frame
               #'switch-to-buffer-other-window) buf)
    (if remember-in-new-frame
        (set-window-dedicated-p
         (get-buffer-window (current-buffer) (selected-frame)) t))
    (remember-mode)
    (when (= (point-max) (point-min))
      (when initial (insert initial))
      (setq remember-annotation annotation)
      (when remember-initial-contents (insert remember-initial-contents))
      (when (and (stringp annotation)
                 (not (equal annotation "")))
        (insert "\n\n" annotation))
      (setq remember-initial-contents nil)
      (goto-char (point-min)))
    (message "Use C-c C-c to remember the data.")))

;;;###autoload
(defun remember-other-frame (&optional initial)
  "Call `remember' in another frame."
  (interactive
   (list (when current-prefix-arg
           (buffer-substring (point) (mark)))))
  (let ((remember-in-new-frame t))
    (remember initial)))

(defsubst remember-mail-date (&optional rfc822-p)
  "Return a simple date.  Nothing fancy."
  (if rfc822-p
      (format-time-string "%a, %e %b %Y %T %z" (current-time))
322
    (format-time-string "%a %b %e %T %Y" (current-time))))
Michael Olson's avatar
Michael Olson committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336

(defun remember-buffer-desc ()
  "Using the first line of the current buffer, create a short description."
  (buffer-substring (point-min)
                    (save-excursion
                      (goto-char (point-min))
                      (end-of-line)
                      (if (> (- (point) (point-min)) 60)
                          (goto-char (+ (point-min) 60)))
                      (point))))

;; Remembering to UNIX mailboxes

(defcustom remember-mailbox "~/Mail/remember"
337
  "The file in which to store remember data as mail."
Michael Olson's avatar
Michael Olson committed
338 339 340 341
  :type 'file
  :group 'remember)

(defcustom remember-default-priority "medium"
342
  "The default priority for remembered mail messages."
Michael Olson's avatar
Michael Olson committed
343 344 345 346 347 348
  :type 'string
  :group 'remember)

(defun remember-store-in-mailbox ()
  "Store remember data as if it were incoming mail.
In which case `remember-mailbox' should be the name of the mailbox.
349
Each piece of pseudo-mail created will have an `X-Todo-Priority'
Michael Olson's avatar
Michael Olson committed
350 351
field, for the purpose of appropriate splitting."
  (let ((who (read-string "Who is this item related to? "))
352
        (moment (format "%.0f" (float-time)))
Michael Olson's avatar
Michael Olson committed
353 354 355
        (desc (remember-buffer-desc))
        (text (buffer-string)))
    (with-temp-buffer
356
      (insert (format "From %s  %s
Michael Olson's avatar
Michael Olson committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
Date: %s
From: %s
Message-Id: <remember-%s@%s>
X-Todo-Priority: %s
To: %s <%s>
Subject: %s\n\n"
                      (user-login-name)
                      (remember-mail-date)
                      (remember-mail-date t)
                      who
                      moment (system-name)
                      remember-default-priority
                      (user-full-name) user-mail-address
                      desc))
      (let ((here (point)))
        (insert text)
        (unless (bolp)
          (insert "\n"))
        (insert "\n")
        (goto-char here)
        (while (re-search-forward "^\\(From[: ]\\)" nil t)
          (replace-match ">\\1")))
      (append-to-file (point-min) (point-max) remember-mailbox)
      t)))

;; Remembering to plain files

384
(defcustom remember-data-file (convert-standard-filename "~/.notes")
385
  "The file in which to store unprocessed data."
Michael Olson's avatar
Michael Olson committed
386 387 388 389
  :type 'file
  :group 'remember)

(defcustom remember-leader-text "** "
390
  "The text used to begin each remember item."
Michael Olson's avatar
Michael Olson committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
  :type 'string
  :group 'remember)

(defun remember-append-to-file ()
  "Remember, with description DESC, the given TEXT."
  (let ((text (buffer-string))
        (desc (remember-buffer-desc)))
    (with-temp-buffer
      (insert "\n" remember-leader-text (current-time-string)
              " (" desc ")\n\n" text)
      (if (not (bolp))
          (insert "\n"))
      (if (find-buffer-visiting remember-data-file)
          (let ((remember-text (buffer-string)))
            (set-buffer (get-file-buffer remember-data-file))
            (save-excursion
              (goto-char (point-max))
              (insert remember-text)
              (when remember-save-after-remembering (save-buffer))))
        (append-to-file (point-min) (point-max) remember-data-file)))))

(defun remember-region (&optional beg end)
  "Remember the data from BEG to END.
414
It is called from within the *Remember* buffer to save the text
415
that was entered.
416 417

If BEG and END are nil, the entire buffer will be remembered.
Michael Olson's avatar
Michael Olson committed
418 419

If you want to remember a region, supply a universal prefix to
420
`remember' instead.  For example: \\[universal-argument] \\[remember] RET."
Michael Olson's avatar
Michael Olson committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
  ;; Sacha: I have no idea where remember.el gets this context information, but
  ;; you can just use remember-annotation-functions.
  (interactive)
  (let ((b (or beg (min (point) (or (mark) (point-min)))))
        (e (or end (max (point) (or (mark) (point-max))))))
    (save-restriction
      (narrow-to-region b e)
      (if remember-all-handler-functions
          (run-hooks 'remember-handler-functions)
        (run-hook-with-args-until-success 'remember-handler-functions))
      (remember-destroy))))

;;;###autoload
(defun remember-clipboard ()
  "Remember the contents of the current clipboard.
Most useful for remembering things from Netscape or other X Windows
application."
  (interactive)
  (remember (current-kill 0)))

441
(defun remember-finalize ()
Michael Olson's avatar
Michael Olson committed
442 443 444 445
  "Remember the contents of the current buffer."
  (interactive)
  (remember-region (point-min) (point-max)))

446
;; Org needs this
447
(define-obsolete-function-alias 'remember-buffer 'remember-finalize "23.1")
448

Michael Olson's avatar
Michael Olson committed
449 450 451 452 453 454 455
(defun remember-destroy ()
  "Destroy the current *Remember* buffer."
  (interactive)
  (when (equal remember-buffer (buffer-name))
    (kill-buffer (current-buffer))
    (jump-to-register remember-register)))

456 457 458
;;; Diary integration

(defcustom remember-diary-file nil
459
  "File for extracted diary entries.
460 461 462 463 464 465 466 467 468 469
If this is nil, then `diary-file' will be used instead."
  :type 'file
  :group 'remember)

(defun remember-diary-convert-entry (entry)
  "Translate MSG to an entry readable by diary."
  (save-match-data
    (when remember-annotation
        (setq entry (concat entry " " remember-annotation)))
    (if (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)" entry)
470 471
        (progn
          ;; For calendar-date-style.  This costs us nothing because
472
          ;; the call to diary-make-entry below loads diary-lib
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
          ;; which requires calendar.
          (require 'calendar)
          (replace-match
           (let ((style (if (boundp 'calendar-date-style)
                            calendar-date-style
                          ;; Don't complain about obsoleteness.
                          (if (with-no-warnings european-calendar-style)
                              'european
                            'american))))
             (cond ((eq style 'european)
                    (concat (match-string 3 entry) "/"
                            (match-string 2 entry) "/"
                            (match-string 1 entry)))
                   ((eq style 'iso)
                    (concat (match-string 1 entry) "-"
                            (match-string 2 entry) "-"
                            (match-string 3 entry)))
                   (t (concat (match-string 2 entry) "/"
                              (match-string 3 entry) "/"
                              (match-string 1 entry)))))
           t t entry))
494 495
      entry)))

496
(autoload 'diary-make-entry "diary-lib")
497 498 499 500 501 502 503 504 505 506

;;;###autoload
(defun remember-diary-extract-entries ()
  "Extract diary entries from the region."
  (save-excursion
    (goto-char (point-min))
    (let (list)
      (while (re-search-forward "^DIARY:\\s-*\\(.+\\)" nil t)
        (add-to-list 'list (remember-diary-convert-entry (match-string 1))))
      (when list
507
        (diary-make-entry (mapconcat 'identity list "\n")
508
                          nil remember-diary-file))
509 510
      nil))) ;; Continue processing

Michael Olson's avatar
Michael Olson committed
511 512
;;; Internal Functions:

513 514 515 516
(defvar remember-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\C-x\C-s" 'remember-finalize)
    (define-key map "\C-c\C-c" 'remember-finalize)
517 518
    (define-key map "\C-c\C-k" 'remember-destroy)
    map)
Michael Olson's avatar
Michael Olson committed
519 520
  "Keymap used in Remember mode.")

521
(define-derived-mode remember-mode indented-text-mode "Remember"
Michael Olson's avatar
Michael Olson committed
522
  "Major mode for output from \\[remember].
523
This buffer is used to collect data that you want to remember.
524 525
\\<remember-mode-map>
Just hit \\[remember-finalize] when you're done entering, and it will file
526 527 528
the data away for latter retrieval, and possible indexing.

\\{remember-mode-map}"
529
  (set-keymap-parent remember-mode-map nil))
Michael Olson's avatar
Michael Olson committed
530 531

;;; remember.el ends here