3 ;;; notes-index-mode.el
4 ;;; $Id: notes-index-mode.el,v 1.32 2009/02/04 18:18:45 johnh Exp $
6 ;;; Copyright (C) 1994-1998 by John Heidemann
7 ;;; Comments to <johnh@isi.edu>.
9 ;;; This file is under the Gnu Public License.
12 (require 'notes-variables)
16 (defvar notes-index-mode-map nil)
17 (if notes-index-mode-map
19 (setq notes-index-mode-map (make-sparse-keymap))
20 ;; There were bindings to make mouse-1 do pointer following,
21 ;; but I removed it because all the rest of emacs uses mouse-2.
22 ;; If you want them, add them with notes-index-mode-hook.
23 (notes-platform-bind-mouse notes-index-mode-map 'mouse-2 'notes-index-mouse-follow-link)
24 (notes-platform-bind-mouse notes-index-mode-map 'S-mouse-2 'notes-index-mouse-follow-link-other-window)
25 (define-key notes-index-mode-map "\r" 'notes-index-follow-link)
26 (define-key notes-index-mode-map "o" 'notes-index-link)
30 (defvar notes-index-lazy-message-old-time 0)
32 (defun notes-index-lazy-percent-message (text fraction whole)
33 "Put up a message occasionally.
34 Displays TEXT (a format string), with FRACTION of WHOLE
35 shown as a percentage. (Read the code to see what this
36 cryptic statement means.)"
37 (if (= notes-index-lazy-message-old-time
38 (setq notes-index-lazy-message-old-time (+ 1 (nth 1 (current-time)))))
40 (message text (/ (* fraction 100) whole))))
42 (defun notes-index-parse-buffer ()
43 "Parse a notes-index buffer, fontifying and building subject completion.
45 If fontification is enabled, subjects will be emboldened
46 and dates will be mouse-highlighted.
48 In any event a subject completion table will be built.
50 This routine works by calling either \[notes-index-parse-buffer-uncached]
51 or \[notes-index-parse-buffer-cached] (if possible)."
54 ((old-buffer-read-only buffer-read-only))
55 (setq buffer-read-only nil)
56 (if (and (file-exists-p (concat notes-dir "/index_cache.el"))
57 (file-newer-than-file-p (concat notes-dir "/index_cache.el")
58 (concat notes-dir "/index")))
60 (load (concat notes-dir "/index_cache"))
61 (notes-index-parse-buffer-cached))
63 (message "notes-index-parse-buffer: cache is not present or is not up-to-date")
64 (notes-index-parse-buffer-uncached))
65 ;; clean some things up
67 (setq buffer-read-only old-buffer-read-only)
68 (set-buffer-modified-p nil)))
70 (defun notes-index-parse-buffer-uncached ()
71 "Parse a notes-index buffer, fontifying and building subject completion.
73 If fontification is enabled, subjects will be emboldened
74 and dates will be mouse-highlighted.
76 In any event a subject completion table will be built.
78 Tenses passive will be."
81 (let ((start (point-min))
84 (if notes-use-font-lock
85 (set-text-properties (point-min) (point-max) nil))
86 ;; There used to be problem that we used a fixed obarray length,
87 ;; creating a lot of hash collisions. Now we dynamically compute it
88 ;; by rounding up the number of lines to the next power of 8.
89 (if (and notes-mode-complete-subjects (not notes-subject-table))
90 (setq notes-subject-table (make-vector
103 (while (< start (point-max))
104 ;; find the end-of-line
110 (while (not (eq (following-char) ?\ ))
111 (skip-chars-forward "^:" end)
114 (if notes-subject-table
115 (intern (buffer-substring start (point)) notes-subject-table))
116 (notes-index-lazy-percent-message "Notes-index'ing (%d%%)..." start (point-max))
118 (if notes-use-font-lock
120 ;; highlight the title
121 (put-text-property start (point) 'face notes-bold-face)
122 (if notes-index-fontify-dates
124 ;; now highlight each date
125 (skip-chars-forward "^0-9" end)
126 (while (looking-at "[0-9]")
128 (skip-chars-forward "0-9")
129 (put-text-property start (point) 'mouse-face 'highlight)
130 (skip-chars-forward "^0-9" end))))))
131 ;; set up for next line
133 (setq start (point))))))
136 (defun notes-index-date-search (start end iter-proc done-proc done-arg)
137 "Iterate over a notes-index entry bounded by START to END.
138 Iteration is done by (ITER-PROC END), which leaves match 0
139 set to what we're looking for.
140 A match terminates iteration if (DONE-PROC match DONE-ARG) is non-nil.
141 Returns the buffer position of a successful hit, or nil."
144 (while (and (not stop)
145 (funcall iter-proc end))
146 (if (funcall done-proc
147 (buffer-substring (match-beginning 0) (match-end 0))
149 (setq stop (goto-char (match-beginning 0)))))
153 (defun notes-index-goto-date (date &optional direction)
154 "Goto the DATE in the current line of the index file, modified by DIRECTION.
155 If DIRECTION is 'this, go there.
156 If DIRECTION is 'next or 'prev, go to the corresponding entry.
157 If the entry doesn't exist, then go to the nearest entry according
158 to DIRECTION (and the next one if DIRECTION is 'this)."
160 ((eq direction 'prev)
161 (notes-index-date-search
162 (get-end-of-line) (get-beginning-of-line)
163 (function (lambda (end) (re-search-backward notes-file-regexp end t)))
164 (function (lambda (trial target) (string-lessp trial target)))
166 ((eq direction 'next)
167 (notes-index-date-search
168 (get-beginning-of-line) (get-end-of-line)
169 (function (lambda (end) (re-search-forward notes-file-regexp end t)))
170 (function (lambda (trial target) (string-lessp target trial)))
173 (notes-index-date-search
174 (get-beginning-of-line) (get-end-of-line)
175 (function (lambda (end) (re-search-forward notes-file-regexp end t)))
176 (function (lambda (trial target) (string-equal trial target)))
179 (defun notes-index-link (link &optional tag where)
180 "* Follow a notes-index LINK.
181 Optionally takes a subject TAG and
182 WHERE ('otherwindow or nil) to open the new file."
183 (interactive "sNotes-index link: ")
184 (notes-w3-url (notes-file-to-url link tag) where t))
187 (defun notes-index-todays-link ()
188 "* Open the notes file for today."
190 (notes-index-link (format-time-string notes-file-form (current-time))))
192 (defun notes-index-follow-link (pt &optional where)
193 "Follow a link at PT in notes-index-mode.
194 The link is taken from the location PT,
195 and the new information is shown WHERE (either 'otherwindow or not)."
198 (let (start date tag)
199 ;; determine the date
200 (skip-chars-backward "0-9")
202 (if (not (re-search-forward notes-file-regexp (+ (point) 6) t))
203 (error "Not on notes-index-mode link."))
204 (setq date (buffer-substring (match-beginning 0) (match-end 0)))
207 (if (not (re-search-forward "^\\([^:]*\\):" start t))
208 (error "Not on notes-index-mode link line."))
209 (setq tag (buffer-substring (match-beginning 1) (match-end 1)))
210 ;; make and process the url
211 (notes-index-link date tag where))))
213 (defun notes-index-mouse-follow-link (e)
214 "Handle a mouse click in notes-index-mode."
217 (notes-index-follow-link (point) nil))
219 (defun notes-index-mouse-follow-link-other-window (e)
220 "Handle a mouse click in notes-index-mode (other-window)."
223 (notes-index-follow-link (point) 'otherwindow))
225 (defun notes-index-extract-subject ()
226 "Extract the notes-index subject for the current line."
229 (if (re-search-forward "^\\(.*\\): " (get-end-of-line) t)
230 (buffer-substring (match-beginning 1) (match-end 1))
234 (defun notes-index-mode ()
235 "Notes-index-mode with mouse support.
237 You may wish to change notes-bold-face and notes-use-font-lock.
239 There should be no need to add notes-index-mode to auto-mode-alist
240 since the index generation functions add code to the index file
241 which invokes notes-index-mode.
244 \\{notes-index-mode-map}"
247 (notes-platform-init)
249 (setq buffer-read-only nil)
250 (notes-index-parse-buffer)
251 (setq major-mode 'notes-index-mode
252 mode-name "Notes-index")
253 (use-local-map notes-index-mode-map)
254 (define-key notes-index-mode-map "\C-c\C-s" 'notes-summarize-subject)
256 (if notes-use-font-lock
257 (notes-platform-font-lock notes-index-font-lock-keywords))
259 (run-mode-hooks 'notes-index-mode-hooks)
260 ;; No editing is allowed.
261 (setq buffer-read-only t)
264 (put 'notes-index-mode 'mode-class 'special)
265 (provide 'notes-index-mode)