]> code.delx.au - gnu-emacs-elpa/blob - packages/notes-mode/notes-mode.el
First cut at cleaning up notes-mode.
[gnu-emacs-elpa] / packages / notes-mode / notes-mode.el
1 ;;; notes-mode.el --- Indexing system for on-line note-taking
2
3 ;;; Copyright (C) 1994-2007,2012 Free Software Foundation, Inc.
4
5 ;; Author: <johnh@isi.edu>.
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22
23 ;;; Commentary:
24 ;;
25
26 ;;; Code:
27
28 (require 'notes-variables)
29 (require 'notes-aux)
30
31 (defvar notes-mode-hooks nil
32 "Hooks to run when entering notes-mode.")
33 (defvar notes-load-mode-hooks nil
34 "Hooks to run when entering notes-mode is loaded.")
35
36 (defconst notes-beginning-of-defun-regexp "^\\* .*\n\\-"
37 "Regexp matching the beginning of notes section.")
38
39 (defvar notes-default-tab-binding (global-key-binding "\t")
40 "Saved tab binding for notes-complete-subject.")
41 (defvar notes-default-return-binding (global-key-binding "\r")
42 "Saved return binding for notes-electric-return.")
43
44
45 (defun notes-beginning-of-defun ()
46 "Go to the beginning of a notes ``section''."
47 (interactive)
48 (let
49 ((old-point (point)))
50 (beginning-of-line)
51 ;; handle starting on a title
52 (if (and (looking-at notes-beginning-of-defun-regexp)
53 (/= (point) old-point))
54 nil
55 (goto-char old-point)
56 (if (looking-at "^-") ;; handle starting on the underline under a title
57 (forward-char 1))
58 (re-search-backward notes-beginning-of-defun-regexp nil 'to-limit))))
59
60 (defun notes-end-of-defun ()
61 "Go to the end of a notes ``section''."
62 (interactive)
63 (let ((regexp notes-beginning-of-defun-regexp))
64 (if (looking-at regexp)
65 (goto-char (match-end 0)))
66 ;; Find next section and leave cursor at section beginning
67 (if (re-search-forward regexp nil 'to-limit)
68 (re-search-backward regexp 0 t)
69 ;;(goto-char restore-point)
70 )))
71
72 (defun notes-follow-link (which)
73 "Go to the WHICH link for this topic.
74 WHICH is either \"next\" or \"prev\".
75 If there are no links for the current note,
76 we go to the last note based upon the index file."
77 (let
78 (beginning-of-note
79 end-of-note
80 (start-buffer (current-buffer))
81 ;; We have to handle links in the same buffer,
82 ;; so the following code figure out where we go
83 ;; and returns it out of the save-excursion.
84 ;; If we end up in another buffer, we let the save-excursion
85 ;; leave the original buffer unchanged. If we end up in
86 ;; the same buffer, we need to go wherever we end up.
87 ;; Can anyone suggest a better way?
88 (end-buffer-and-point
89 (save-excursion
90 (notes-end-of-defun)
91 (setq end-of-note (point))
92 (notes-beginning-of-defun)
93 (setq beginning-of-note (point))
94 (if (and (= beginning-of-note 1) (not (looking-at notes-beginning-of-defun-regexp)))
95 (progn
96 ;; When "above" the first note, search to end of first
97 ;; real note (otherwise end-of-note is just the start
98 ;; of the first real note and there are no links).
99 (notes-end-of-defun)
100 (notes-end-of-defun)
101 (setq end-of-note (point))
102 (goto-char beginning-of-note)))
103 (if (re-search-forward (concat "^"
104 (if (eq which 'next) "next" "prev")
105 ":[ ]+<") end-of-note t)
106 (progn ; link exists, just take it
107 (beginning-of-line)
108 (notes-w3-follow-link (point))
109 (cons (current-buffer) (point)))
110 ;; No link; go through the index file.
111 (if (notes-goto-index-entry which)
112 (let ((index-buffer (current-buffer)))
113 (notes-index-follow-link (point))
114 (bury-buffer index-buffer))
115 (error "No known notes in that direction.")
116 (bury-buffer (current-buffer)))
117 (cons (current-buffer) (point))))))
118 ;; Check for going to the same buffer (and the save-excursion
119 ;; undoing our work).
120 (if (eq start-buffer (car end-buffer-and-point))
121 (goto-char (cdr end-buffer-and-point)))))
122
123
124 (defun notes-follow-next-link ()
125 "Go to the next link for this topic."
126 (interactive)
127 (notes-follow-link 'next))
128
129 (defun notes-follow-prev-link ()
130 "Go to the previous link for this topic."
131 (interactive)
132 (notes-follow-link 'prev))
133
134 (defvar notes-complete-subject-abbrevs-alist
135 '(("SP2010" "USC/Classes/CS551/SP2010")
136 ("FA2011" "USC/Classes/CS551/FA2011"))
137 "Alist of simple substitution of subjects.
138 If subject completion is requested, then subject that matches
139 the left-side of an alist value is replaced by the right-side value.")
140
141 (defun notes-complete-subject-abbrevs (key)
142 "Handle abbreviations on notes SUBJECTS.
143 Currently this is just a hack."
144 (let ((value (assoc key notes-complete-subject-abbrevs-alist)))
145 (if value
146 (car (cdr value))
147 key)))
148
149
150 (defun notes-complete-subject ()
151 "Complete the notes subject under point."
152 (interactive)
153 (let
154 ((subject (save-excursion
155 (beginning-of-line)
156 (notes-extract-subject t)))
157 old-completion-ignore-case
158 full-subject)
159 (if (not (and notes-mode-complete-subjects subject))
160 (call-interactively notes-default-tab-binding)
161 ;; Complete the title.
162 (if (null notes-subject-table)
163 (save-excursion ;; FIXME: Why??
164 (find-file-noselect (expand-file-name "index" notes-dir))))
165 ;; Do completion.
166 ;; Run completer if it's loaded,
167 ;; otherwise do our own thing.
168 (setq completion-ignore-case t)
169 (cond
170 ((fboundp 'completer-complete-goto)
171 (completer-complete-goto "^ \t\n\"" " " notes-subject-table nil))
172 ;; NEEDSWORK: should try other completers, too.
173 (t ;; Do our own completion.
174 (setq full-subject (try-completion subject notes-subject-table)
175 subject (completing-read "Subject: "
176 notes-subject-table nil nil
177 (if (stringp full-subject)
178 full-subject
179 subject)))
180 (delete-region (line-beginning-position) (line-end-position))
181 (insert "* " (notes-complete-subject-abbrevs subject))))
182 (setq completion-ignore-case old-completion-ignore-case))))
183
184 (defun notes-fix-prevnext-this-entry ()
185 "Fix up the prev link for the current entry, if necessary.
186 Currently this code only handles brand new entries."
187 ;; Contributed from Takashi Nishimoto <g96p0935@mse.waseda.ac.jp>.
188 ;; Thanks!
189 (interactive)
190 (let ((subject (notes-extract-subject nil t))
191 (this-url (notes-current-url))
192 last-url)
193 (with-current-buffer (find-file-noselect
194 (expand-file-name "index" notes-dir))
195 (goto-char (point-min))
196 (if (re-search-forward
197 (concat "^" (regexp-quote subject) ":.* \\([0-9]+\\)$")
198 (point-max) t)
199 (save-window-excursion
200 (cond ((and (notes-w3-url
201 (notes-file-to-url (match-string 1) subject))
202 (re-search-forward "^next: " nil t)
203 (looking-at "<none>"))
204 (let
205 (pre-modified (buffer-modified-p))
206 (delete-char 6)
207 (insert this-url)
208 (setq last-url (notes-current-url))
209 (if (and (null pre-modified)
210 (>= notes-electric-prevnext 2))
211 (save-buffer))))))))
212 (if last-url
213 (progn
214 (notes-beginning-of-defun)
215 (forward-line 2)
216 (if (not (looking-at "prev: "))
217 (insert "prev: " last-url "\n" "next: <none>\n\n")
218 (forward-line 3))))))
219
220 (defun notes-electric-return (arg)
221 "* Return, underlining if we're on a subject."
222 (interactive "*P")
223 (if (let ((cur-point (point)))
224 (save-excursion
225 (beginning-of-line)
226 (and (not (eq cur-point (point))) ;; normal return if at b-o-ln
227 (notes-extract-subject t))))
228 (progn (notes-underline-line)
229 (if notes-electric-prevnext
230 (notes-fix-prevnext-this-entry)))
231 (call-interactively notes-default-return-binding)))
232
233 (defun notes-current-url ()
234 "Return the notes-URL of the current entry around the current point."
235 (let ((subject (notes-extract-subject nil t))
236 (date (file-name-nondirectory buffer-file-name)))
237 (concat "<file:///"
238 (abbreviate-file-name buffer-file-name)
239 (if subject (concat "#* " subject) "")
240 ">")))
241
242 (defun notes-current-url-as-kill ()
243 "* Put the notes-URL of the current entry into the kill ring."
244 (interactive)
245 (kill-new (notes-current-url)))
246
247 (defun notes-goto-index-entry (&optional direction)
248 "* Jump to the index entry corresponding to our current note entry.
249 If we're not in an entry, we leave you in the index file.
250 If the current date doesn't exist, error in DIRECTION.
251 Returns nil if on errors (no index; no date in DIRECTION),
252 otherwise the point of the hit."
253 (interactive)
254 (let ((start-buffer (current-buffer))
255 (subject (notes-extract-subject)) ; get subject if on it
256 (date (if (null (buffer-file-name)) nil
257 (file-name-nondirectory (buffer-file-name)))))
258 ;; Try and get the subject, either forward...
259 (if (not subject)
260 (save-excursion
261 (notes-beginning-of-defun)
262 (setq subject (notes-extract-subject))))
263 ;; ...or backwards.
264 (if (not subject)
265 (save-excursion
266 (notes-end-of-defun)
267 (setq subject (notes-extract-subject))))
268 ;; Form and jump to the url for the index-entry.
269 (if (and (notes-w3-url (concat notes-url-prefix
270 "index"
271 (if subject (concat "#" subject) ""))
272 nil t)
273 ;; Go to the current date, if any.
274 (notes-index-goto-date date direction))
275 t
276 nil)))
277
278 (defun notes-extract-subject (&optional relaxed search)
279 "Extract the subject under the point in the current buffer.
280 If RELAXED, then accept non-underlined subjects.
281 If SEARCH we'll search back in the buffer for the nearest
282 subject title.
283
284 Returns nil if we're not on as subject."
285 (save-match-data
286 (cond
287 ;; directly on a note
288 ((or (looking-at notes-beginning-of-defun-regexp)
289 (and relaxed
290 (looking-at "^\\* ")))
291 (save-excursion
292 (let
293 ((start (+ (point) 2))
294 (end (progn (end-of-line) (point))))
295 (buffer-substring start end))))
296 (search
297 (save-excursion
298 (notes-beginning-of-defun)
299 (notes-extract-subject relaxed nil)))
300 (t
301 nil))))
302
303
304 ;;;###autoload
305 (defun notes-underline-line ()
306 "* Create a row of dashes as long as this line, or adjust the current underline."
307 (interactive)
308 ;; check to see if it's already underlined
309 (if (save-excursion
310 (forward-line 1)
311 (looking-at "^[ \t]*--*$"))
312 (notes-old-underline-line)
313 (progn
314 (notes-new-underline-line)
315 (insert "\n\n"))))
316
317 (defun notes-new-underline-line ()
318 "Underline a line with a row of dashes. Move point after the dashes.
319 \\[notes-new-underline-line] reproduces leading spaces."
320 (interactive)
321 (let*
322 ((bol (progn (beginning-of-line)
323 (point)))
324 (bospaces (progn (skip-chars-forward " \t")
325 (point)))
326 (nospaces (- bospaces bol))
327 (eol (progn (end-of-line)
328 (untabify bol (point))
329 (point))))
330 (insert "\n" (buffer-substring bol bospaces))
331 (insert-char ?- (- eol bospaces))))
332
333 (defun notes-old-underline-line ()
334 "Replace the following line with a row of dashes. Leave the point unchanged."
335 (save-excursion
336 (save-excursion
337 (forward-line 1)
338 (delete-region (line-beginning-position) (1+ (line-end-position))))
339 (notes-new-underline-line)))
340
341 (defun notes-mode-initialize-note-from-cache ()
342 "Build a new note from the cache. Return valid cache contents or nil."
343 (save-excursion
344 (let*
345 ((new-buffer (current-buffer))
346 (cache-file (expand-file-name "mknew.cache" notes-dir))
347 (buf (find-file cache-file))
348 magic-line
349 prev-file
350 this-file
351 cache-contents
352 m
353 (result
354 (if (and buf
355 (>= (count-lines (point-min) (point-max)) 3))
356 (progn
357 ;; If you know a more elegant way to extact the first
358 ;; three lines of a file, please let me know.
359 (goto-char (point-min))
360 (setq m (point))
361 (forward-line 1)
362 (setq magic-line (buffer-substring m (- (point) 1)))
363 (setq m (point))
364 (forward-line 1)
365 (setq prev-file (buffer-substring m (- (point) 1)))
366 (setq m (point))
367 (forward-line 1)
368 (setq this-file (buffer-substring m (- (point) 1)))
369 (setq cache-contents (buffer-substring (point) (point-max)))
370 (bury-buffer buf)
371 ;; is cache valid?
372 (if
373 (and
374 (string-equal magic-line "mknew.cache 830494922")
375 (file-newer-than-file-p cache-file prev-file)
376 (string-equal (file-name-nondirectory this-file)
377 (file-name-nondirectory (buffer-file-name
378 new-buffer))))
379 cache-contents
380 nil))
381 nil)))
382 ;; Kill the buffer to avoid "buf changed, reload?" warnings.
383 (if buf
384 (kill-buffer buf))
385 result)))
386
387 (defun notes-mode-initialize-note ()
388 "Fill in an empty new note.
389 Create any directories as necessary.
390 Use the mknew cache if possible."
391 (interactive)
392 (let
393 ((dir (directory-file-name (file-name-directory (buffer-file-name)))))
394 (if (file-exists-p dir)
395 t
396 (make-directory dir t)
397 (message "New intermediate directory created.")))
398 (if notes-mode-initialization-program
399 (let
400 ((cache-contents (notes-mode-initialize-note-from-cache)))
401 (if cache-contents
402 (insert cache-contents)
403 (shell-command-on-region
404 (point-min)
405 (point-max)
406 (concat (expand-file-name notes-mode-initialization-program
407 notes-bin-dir)
408 " '"
409 ;; FIXME: Use shell-quote-argument.
410 (buffer-file-name) "'") 't)))))
411
412 \f
413 ;;;
414 ;;; encryption
415 ;; requires "PEM - PGP Enhanced Messaging for GNU Emacs"
416 ;; from Roy Frederick Busdiecker, III (Rick)
417 ;; or mailcrypt 3.4.x or >=3.5.x
418 ;;
419
420 (defvar notes-encryption-library
421 'mailcrypt
422 ; (cond
423 ; ((fboundp 'mc-encrypt-region) 'mailcrypt)
424 ; ((fboundp 'npgp:encrypt-region) 'npgp)
425 ; (t nil))
426 "PGP library to use.")
427
428 (defvar notes-encryption-sub-library
429 'gpg
430 "Variant of mailcrypt to use (`pgp', `pgp50', or `gpg').")
431
432 (defvar notes-encryption-npgp-userid nil
433 "PGP key for the current user.")
434
435 (defvar notes-encryption-npgp-key-id nil
436 "Keyid of PGP key for the current user.
437 Useful if your \\[user-full-name] doesn't match a unique key.
438 Should have a leading 0x.")
439
440 (defun notes-encryption-npgp-userid ()
441 "Return notes-encryption-userid, initializing it if necessary."
442 (require 'pam)
443 (if (and notes-encryption-userid
444 npgp:*pass-phrases*)
445 notes-encryption-userid
446 (setq notes-encryption-userid
447 (list
448 (if notes-encryption-key-id
449 (npgp:get-key-by-key-id notes-encryption-key-id)
450 (pam:read-name-key (user-full-name)))))))
451
452 (defun notes-encryption-mailcrypt-keyid ()
453 "Do the right thing."
454 (require 'mailcrypt)
455 (cond
456 ((eq notes-encryption-sub-library 'pgp)
457 (cdr (mc-pgp-lookup-key mc-pgp-user-id)))
458 ((eq notes-encryption-sub-library 'pgp50)
459 (cdr (mc-pgp50-lookup-key mc-pgp50-user-id)))
460 ((eq notes-encryption-sub-library 'gpg)
461 (cdr (mc-gpg-lookup-key mc-gpg-user-id)))
462 (t (error "notes-encryption-decrypt-region: no pgp sub-library."))))
463
464 (defun notes-encryption-load-mailcrypt ()
465 (require 'mailcrypt)
466 ;; ick ick ick this code needs to be cleaned up
467 (cond
468 ((null (eq notes-encryption-library 'mailcrypt))
469 t)
470 ((eq notes-encryption-sub-library 'pgp)
471 (load-library "mc-pgp"))
472 ((eq notes-encryption-sub-library 'pgp50)
473 (load-library "mc-pgp5"))
474 ((eq notes-encryption-sub-library 'gpg)
475 (load-library "mc-gpg"))
476 (t (error "notes-encryption-load-mailcrypt: no pgp sub-library."))))
477
478 (defun notes-encryption-decrypt-region (start end)
479 (cond
480 ((eq notes-encryption-library 'npgp)
481 (require 'pam)
482 (require 'npgp)
483 (npgp:decrypt-region start end))
484 ((eq notes-encryption-library 'mailcrypt)
485 (notes-encryption-load-mailcrypt)
486 (cond
487 ((eq notes-encryption-sub-library 'pgp)
488 (mc-pgp-decrypt-region start end))
489 ((eq notes-encryption-sub-library 'pgp50)
490 (mc-pgp50-decrypt-region start end))
491 ((eq notes-encryption-sub-library 'gpg)
492 (mc-gpg-decrypt-region start end))
493 (t (error "notes-encryption-decrypt-region: no pgp sub-library."))))
494 (t (error "notes-encryption-decrypt-region: no pgp library."))))
495
496 (defun notes-encryption-encrypt-region (start end)
497 (cond
498 ((eq notes-encryption-library 'npgp)
499 (npgp:encrypt-region (notes-encryption-npgp-userid) start end))
500 ((eq notes-encryption-library 'mailcrypt)
501 (notes-encryption-load-mailcrypt)
502 (let ((old-sign mc-pgp-always-sign)
503 old-comment recipients)
504 (setq mc-pgp-always-sign 'never
505 recipients (list (notes-encryption-mailcrypt-keyid)))
506 (cond
507 ((eq notes-encryption-sub-library 'pgp)
508 (setq old-comment mc-pgp-comment
509 mc-pgp-comment "")
510 (mc-pgp-encrypt-region recipients start end
511 (notes-encryption-mailcrypt-keyid) nil)
512 (setq mc-pgp-comment old-comment))
513 ((eq notes-encryption-sub-library 'pgp50)
514 (setq old-comment mc-pgp50-comment
515 mc-pgp50-comment "")
516 (mc-pgp50-encrypt-region recipients start end
517 (notes-encryption-mailcrypt-keyid) nil)
518 (setq mc-pgp50-comment old-comment))
519 ((eq notes-encryption-sub-library 'gpg)
520 (setq old-comment mc-gpg-comment
521 mc-gpg-comment "")
522 (mc-gpg-encrypt-region recipients start end
523 (notes-encryption-mailcrypt-keyid) nil)
524 (setq mc-gpg-comment old-comment))
525 (t (error "notes-encryption-decrypt-region: no gpg sub-library.")))
526 (setq mc-pgp-always-sign old-sign)))
527 (t (error "notes-encryption-decrypt-region: no pgp library."))))
528
529 (defun notes-encrypt-note (prefix)
530 "Encrypt the current note for the current user. With PREFIX, start from point."
531 (interactive "P")
532 (save-excursion
533 (let (start end)
534 ;; Unless a prefix arg, start at beginning-of-note.
535 (if prefix
536 nil
537 (if (not (looking-at notes-beginning-of-defun-regexp))
538 (notes-beginning-of-defun))
539 ;; skip over the header
540 (while (and (or (looking-at notes-beginning-of-defun-regexp)
541 (looking-at "^-+$")
542 (looking-at "^\\(prev\\|next\\): ")
543 (looking-at "^[ \t]*$"))
544 (< (point) (point-max)))
545 (forward-line 1)))
546 (setq start (point))
547 ;; sanity check
548 (if (re-search-forward "^-----BEGIN PGP MESSAGE"
549 (progn
550 (save-excursion
551 (notes-end-of-defun)
552 (point))) t)
553 (error "Note is already encrypted."))
554 ;; find the end
555 (notes-end-of-defun)
556 (while (or (looking-at notes-beginning-of-defun-regexp)
557 (looking-at "^[ \t]*$"))
558 (forward-line -1))
559 (forward-line 1)
560 (setq end (point))
561 (notes-encryption-encrypt-region start end))))
562
563 (defun notes-decrypt-note ()
564 "Decrypt the current note for the current user."
565 (interactive)
566 (save-excursion
567 (if (not (looking-at notes-beginning-of-defun-regexp))
568 (notes-beginning-of-defun))
569 (if (null (re-search-forward "^-----BEGIN PGP"
570 (progn
571 (save-excursion
572 (notes-end-of-defun)
573 (point))) t))
574 (error "Note is not encrypted."))
575 (beginning-of-line)
576 (let ((start (point)))
577 (if (null (re-search-forward "^-----END PGP"
578 (progn
579 (save-excursion
580 (notes-end-of-defun)
581 (point))) t))
582 (error "Could not find end of encrypted note."))
583 (forward-line)
584 (beginning-of-line)
585 (notes-encryption-decrypt-region start (point)))))
586
587 \f
588 ;;;
589 ;;; notes or notes-index?
590 ;;;
591 (defun notes-summarize-subject (regexp-subject &optional subject)
592 "* Collect all of a subject."
593 (interactive "P")
594 (require 'notes-index-mode)
595 (if (null subject)
596 (cond
597 ((eq major-mode 'notes-mode)
598 (setq subject (notes-extract-subject nil t)))
599 ((eq major-mode 'notes-index-mode)
600 (setq subject (notes-index-extract-subject)))))
601 (if (null subject)
602 (error "notes-summarize-subject: no subject specified or inferable."))
603 (let
604 ((buf (get-buffer-create (concat "*notes on " subject "*"))))
605 (pop-to-buffer buf)
606 (erase-buffer)
607 (apply 'call-process (expand-file-name "catsubject" notes-bin-dir)
608 nil buf t
609 (if regexp-subject
610 (list "-m" subject)
611 (list subject)))
612 (notes-mode)))
613
614 \f
615 ;;;
616 ;;; notes-rename-subject
617 ;;;
618 (defun notes-rename-subject ()
619 "* Rename the current subject.
620 Assumes working next/prev linkage between the entries."
621 (interactive)
622 (let ((subject (notes-extract-subject)))
623 (condition-case nil
624 (progn
625 (end-of-line)
626 (beginning-of-defun)
627 (if (not (looking-at "* "))
628 (error "confused"))
629 (forward-char 2)
630 (error "not yet done")
631 )
632 (error nil))))
633
634 \f
635 ;;;
636 ;;; notes-mode
637 ;;;
638
639 (defvar notes-mode-map
640 (let ((map (make-sparse-keymap)))
641 ;; Random key-bindings.
642 (define-key map "\M-\C-a" 'notes-beginning-of-defun)
643 (define-key map "\M-\C-e" 'notes-end-of-defun)
644 (define-key map "\C-c\C-d" 'notes-decrypt-note)
645 (define-key map "\C-c\C-e" 'notes-encrypt-note)
646 (define-key map "\C-c\r" 'notes-w3-follow-link)
647 (define-key map "\C-c\C-p" 'notes-follow-prev-link)
648 (define-key map "\C-c\C-n" 'notes-follow-next-link)
649 (define-key map "\C-c\C-i" 'notes-goto-index-entry)
650 (define-key map "\C-c\C-k" 'notes-current-url-as-kill)
651 (define-key map "\C-c\C-s" 'notes-summarize-subject)
652 (define-key map "\C-c-" 'notes-underline-line)
653 ;; FIXME: Use completion-at-point-functions instead.
654 (define-key map "\t" 'notes-complete-subject)
655 ;; FIXME: Use post-self-insert-hook instead.
656 (define-key map "\r" 'notes-electric-return)
657 (define-key map "\n" 'notes-electric-return) ; a more common synonym
658 (notes-platform-bind-mouse map 'S-mouse-2 'notes-w3-follow-link-mouse)
659 map))
660
661 ;;;###autoload
662 (define-derived-mode notes-mode indented-text-mode "Notes"
663 "Enable notes-mode for a buffer.
664
665 Inside a notes buffer one can click on URLs and follow them to
666 other notes files.
667
668 See the file notes-variables.el for all customization options.
669 To change options, (require 'notes-variables) in your .emacs
670 and then change things.
671
672 Subjects in notes mode are lines beginning with an asterisk
673 and underlined with dashes. Subjects can be completed
674 with \\[notes-complete-subject] and are automatically underlined.
675
676 You may wish to add this code to your .emacs file:
677 (add-to-list 'auto-mode-alist
678 (cons \"/9[0-9][0-9][0-9][0-9][0-9].?\\\\'\" 'notes-mode))
679 (define-key global-map [?\\C-c ?n] 'notes-index-todays-link)
680 to automatically enter notes mode.
681
682 I have two suggestions for how to organize your notes files.
683 First, I collect my notes into a separate file per day. (If you have
684 fewer notes, you may find once-per-week or month more suitable.)
685 Second, at the beginning of each file I have a subject \"* Today\".
686 Since every file has this subject, I can use its prev and next links
687 to easily move around the collection of files.
688
689 The key-bindings of this mode are:
690 \\{notes-mode-map}"
691 (notes-platform-init)
692
693 ;; Now set up the mode.
694 (auto-fill-mode 1)
695
696 ;; Imenu stuff.
697 (set (make-local-variable 'imenu-prev-index-position-function)
698 'notes-beginning-of-defun)
699 (set (make-local-variable 'imenu-extract-index-name-function)
700 'notes-extract-subject)
701
702 (set (make-local-variable 'font-lock-defaults)
703 `(notes-font-lock-keywords
704 t nil nil beginning-of-line))
705
706 ;; Finally, try to fill in an empty note.
707 (if (zerop (buffer-size))
708 (notes-mode-initialize-note))
709
710 ;; Enable outline-minor-mode (forcebly, in case someone already
711 ;; has it in their text-mode hook). Bug found by
712 ;; Nils Ackermann <nils@nieback.de>.
713 (if notes-use-outline-mode
714 (outline-minor-mode 1)))
715
716
717
718 \f
719 ;;;
720
721 (run-hooks 'notes-mode-load-hooks)
722 (provide 'notes-mode)
723 ;;; notes-mode.el ends here