48595
|
1 ;;; mh-speed.el --- Speedbar interface for MH-E.
|
|
2
|
|
3 ;; Copyright (C) 2002 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Bill Wohler <wohler@newt.com>
|
|
6 ;; Maintainer: Bill Wohler <wohler@newt.com>
|
|
7 ;; Keywords: mail
|
|
8 ;; See: mh-e.el
|
|
9
|
|
10 ;; This file is part of GNU Emacs.
|
|
11
|
|
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
13 ;; it under the terms of the GNU General Public License as published by
|
|
14 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
15 ;; any later version.
|
|
16
|
|
17 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 ;; GNU General Public License for more details.
|
|
21
|
|
22 ;; You should have received a copy of the GNU General Public License
|
|
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
25 ;; Boston, MA 02111-1307, USA.
|
|
26
|
|
27 ;;; Commentary:
|
|
28 ;; Future versions should only use flists.
|
|
29
|
|
30 ;; Speedbar support for MH-E package.
|
|
31
|
|
32 ;;; Change Log:
|
|
33
|
|
34 ;; $Id: mh-speed.el,v 1.26 2002/11/13 19:36:00 wohler Exp $
|
|
35
|
|
36 ;;; Code:
|
|
37
|
|
38 ;; Requires
|
|
39 (require 'cl)
|
|
40 (require 'mh-utils)
|
|
41 (require 'mh-e)
|
|
42 (require 'speedbar)
|
|
43
|
|
44 ;; Autoloads
|
|
45 (autoload 'mh-index-goto-nearest-msg "mh-index")
|
|
46 (autoload 'mh-index-parse-folder "mh-index")
|
|
47 (autoload 'mh-visit-folder "mh-e")
|
|
48
|
|
49 ;; User customizable
|
|
50 (defcustom mh-large-folder 200
|
|
51 "The number of messages that indicates a large folder.
|
|
52 If the number of messages in a folder exceeds this value, confirmation is
|
|
53 required when the folder is visited from the speedbar."
|
|
54 :type 'integer
|
|
55 :group 'mh)
|
|
56
|
|
57 (defcustom mh-speed-flists-interval 60
|
|
58 "Time between calls to flists in seconds.
|
|
59 If 0, flists is not called repeatedly."
|
|
60 :type 'integer
|
|
61 :group 'mh)
|
|
62
|
|
63 (defcustom mh-speed-run-flists-flag t
|
|
64 "Non-nil means flists is used.
|
|
65 If non-nil, flists is executed every `mh-speed-flists-interval' seconds to
|
|
66 update the display of the number of unseen and total messages in each folder.
|
|
67 If resources are limited, this can be set to nil and the speedbar display can
|
|
68 be updated manually with the \\[mh-speed-flists] command."
|
|
69 :type 'boolean
|
|
70 :group 'mh)
|
|
71
|
|
72 (defface mh-speedbar-folder-face
|
|
73 '((((class color) (background light))
|
|
74 (:foreground "blue4"))
|
|
75 (((class color) (background dark))
|
|
76 (:foreground "light blue")))
|
|
77 "Face used for folders in the speedbar buffer."
|
|
78 :group 'mh)
|
|
79
|
|
80 (defface mh-speedbar-selected-folder-face
|
|
81 '((((class color) (background light))
|
|
82 (:foreground "red" :underline t))
|
|
83 (((class color) (background dark))
|
|
84 (:foreground "red" :underline t))
|
|
85 (t (:underline t)))
|
|
86 "Face used for the current folder."
|
|
87 :group 'mh)
|
|
88
|
|
89 (defface mh-speedbar-folder-with-unseen-messages-face
|
|
90 '((t (:inherit mh-speedbar-folder-face :bold t)))
|
|
91 "Face used for folders in the speedbar buffer which have unread messages."
|
|
92 :group 'mh)
|
|
93
|
|
94 (defface mh-speedbar-selected-folder-with-unseen-messages-face
|
|
95 '((t (:inherit mh-speedbar-selected-folder-face :bold t)))
|
|
96 "Face used for the current folder when it has unread messages."
|
|
97 :group 'mh)
|
|
98
|
|
99 ;; Global variables
|
|
100 (defvar mh-speed-refresh-flag nil)
|
|
101 (defvar mh-speed-last-selected-folder nil)
|
|
102 (defvar mh-speed-folder-map (make-hash-table :test #'equal))
|
|
103 (defvar mh-speed-folders-cache (make-hash-table :test #'equal))
|
|
104 (defvar mh-speed-flists-cache (make-hash-table :test #'equal))
|
|
105 (defvar mh-speed-flists-process nil)
|
|
106 (defvar mh-speed-flists-timer nil)
|
|
107 (defvar mh-speed-partial-line "")
|
|
108
|
|
109 ;; Add our stealth update function
|
|
110 (unless (member 'mh-speed-stealth-update
|
|
111 (cdr (assoc "files" speedbar-stealthy-function-list)))
|
|
112 ;; Is changing constant lists in elisp safe?
|
|
113 (setq speedbar-stealthy-function-list
|
|
114 (copy-tree speedbar-stealthy-function-list))
|
|
115 (push 'mh-speed-stealth-update
|
|
116 (cdr (assoc "files" speedbar-stealthy-function-list))))
|
|
117
|
|
118 ;; Functions called by speedbar to initialize display...
|
|
119 (defun mh-folder-speedbar-buttons (buffer)
|
|
120 "Interface function to create MH-E speedbar buffer.
|
|
121 BUFFER is the MH-E buffer for which the speedbar buffer is to be created."
|
|
122 (unless (get-text-property (point-min) 'mh-level)
|
|
123 (erase-buffer)
|
|
124 (clrhash mh-speed-folder-map)
|
|
125 (speedbar-make-tag-line 'bracket ?+ 'mh-speed-toggle nil " " 'ignore nil
|
|
126 'mh-speedbar-folder-face 0)
|
|
127 (forward-line -1)
|
|
128 (setf (gethash nil mh-speed-folder-map)
|
|
129 (set-marker (make-marker) (1+ (line-beginning-position))))
|
|
130 (add-text-properties
|
|
131 (line-beginning-position) (1+ (line-beginning-position))
|
|
132 `(mh-folder nil mh-expanded nil mh-children-p t mh-level 0))
|
|
133 (mh-speed-stealth-update t)
|
|
134 (when mh-speed-run-flists-flag
|
|
135 (mh-speed-flists nil))))
|
|
136
|
|
137 (defalias 'mh-show-speedbar-buttons 'mh-folder-speedbar-buttons)
|
|
138 (defalias 'mh-index-folder-speedbar-buttons 'mh-folder-speedbar-buttons)
|
|
139 (defalias 'mh-index-show-speedbar-buttons 'mh-folder-speedbar-buttons)
|
|
140 (defalias 'mh-letter-speedbar-buttons 'mh-folder-speedbar-buttons)
|
|
141
|
|
142 ;; Keymaps for speedbar...
|
|
143 (defvar mh-folder-speedbar-key-map (speedbar-make-specialized-keymap)
|
|
144 "Specialized speedbar keymap for MH-E buffers.")
|
|
145 (gnus-define-keys mh-folder-speedbar-key-map
|
|
146 "+" mh-speed-expand-folder
|
|
147 "-" mh-speed-contract-folder
|
|
148 "\r" mh-speed-view
|
|
149 "f" mh-speed-flists
|
|
150 "i" mh-speed-invalidate-map)
|
|
151
|
|
152 (defvar mh-show-speedbar-key-map mh-folder-speedbar-key-map)
|
|
153 (defvar mh-index-folder-speedbar-key-map mh-folder-speedbar-key-map)
|
|
154 (defvar mh-index-show-speedbar-key-map mh-folder-speedbar-key-map)
|
|
155 (defvar mh-letter-speedbar-key-map mh-folder-speedbar-key-map)
|
|
156
|
|
157 ;; Menus for speedbar...
|
|
158 (defvar mh-folder-speedbar-menu-items
|
|
159 '(["Visit Folder" mh-speed-view
|
|
160 (save-excursion
|
|
161 (set-buffer speedbar-buffer)
|
|
162 (get-text-property (line-beginning-position) 'mh-folder))]
|
|
163 ["Expand nested folders" mh-speed-expand-folder
|
|
164 (and (get-text-property (line-beginning-position) 'mh-children-p)
|
|
165 (not (get-text-property (line-beginning-position) 'mh-expanded)))]
|
|
166 ["Contract nested folders" mh-speed-contract-folder
|
|
167 (and (get-text-property (line-beginning-position) 'mh-children-p)
|
|
168 (get-text-property (line-beginning-position) 'mh-expanded))]
|
|
169 ["Run Flists" mh-speed-flists t]
|
|
170 ["Invalidate cached folders" mh-speed-invalidate-map t])
|
|
171 "Extra menu items for speedbar.")
|
|
172
|
|
173 (defvar mh-show-speedbar-menu-items mh-folder-speedbar-menu-items)
|
|
174 (defvar mh-index-folder-speedbar-menu-items mh-folder-speedbar-menu-items)
|
|
175 (defvar mh-index-show-speedbar-menu-items mh-folder-speedbar-menu-items)
|
|
176 (defvar mh-letter-speedbar-menu-items mh-folder-speedbar-menu-items)
|
|
177
|
|
178 (defmacro mh-speed-select-attached-frame ()
|
|
179 "Compatibility macro to handle speedbar versions 0.11a and 0.14beta4."
|
|
180 (cond ((fboundp 'dframe-select-attached-frame)
|
|
181 '(dframe-select-attached-frame speedbar-frame))
|
|
182 ((boundp 'speedbar-attached-frame)
|
|
183 '(select-frame speedbar-attached-frame))
|
|
184 (t (error "Installed speedbar version not supported by MH-E"))))
|
|
185
|
|
186 (defun mh-speed-update-current-folder (force)
|
|
187 "Update speedbar highlighting of the current folder.
|
|
188 The function tries to be smart so that work done is minimized. The currently
|
|
189 highlighted folder is cached and no highlighting happens unless it changes.
|
|
190 Also highlighting is suspended while the speedbar frame is selected.
|
|
191 Otherwise you get the disconcerting behavior of folders popping open on their
|
|
192 own when you are trying to navigate around in the speedbar buffer.
|
|
193
|
|
194 The update is always carried out if FORCE is non-nil."
|
|
195 (let* ((lastf (selected-frame))
|
|
196 (newcf (save-excursion
|
|
197 (mh-speed-select-attached-frame)
|
|
198 (prog1 (mh-speed-extract-folder-name (buffer-name))
|
|
199 (select-frame lastf))))
|
|
200 (lastb (current-buffer))
|
|
201 (case-fold-search t))
|
|
202 (when (or force
|
|
203 (and mh-speed-refresh-flag (not (eq lastf speedbar-frame)))
|
|
204 (and (stringp newcf)
|
|
205 (equal (substring newcf 0 1) "+")
|
|
206 (not (equal newcf mh-speed-last-selected-folder))))
|
|
207 (setq mh-speed-refresh-flag nil)
|
|
208 (select-frame speedbar-frame)
|
|
209 (set-buffer speedbar-buffer)
|
|
210
|
|
211 ;; Remove highlight from previous match...
|
|
212 (mh-speed-highlight mh-speed-last-selected-folder
|
|
213 'mh-speedbar-folder-face)
|
|
214
|
|
215 ;; If we found a match highlight it...
|
|
216 (when (mh-speed-goto-folder newcf)
|
|
217 (mh-speed-highlight newcf 'mh-speedbar-selected-folder-face))
|
|
218
|
|
219 (setq mh-speed-last-selected-folder newcf)
|
|
220 (speedbar-position-cursor-on-line)
|
|
221 (set-window-point (frame-first-window speedbar-frame) (point))
|
|
222 (set-buffer lastb)
|
|
223 (select-frame lastf))
|
|
224 (when (eq lastf speedbar-frame)
|
|
225 (setq mh-speed-refresh-flag t))))
|
|
226
|
|
227 (defun mh-speed-normal-face (face)
|
|
228 "Return normal face for given FACE."
|
|
229 (cond ((eq face 'mh-speedbar-folder-with-unseen-messages-face)
|
|
230 'mh-speedbar-folder-face)
|
|
231 ((eq face 'mh-speedbar-selected-folder-with-unseen-messages-face)
|
|
232 'mh-speedbar-selected-folder-face)
|
|
233 (t face)))
|
|
234
|
|
235 (defun mh-speed-bold-face (face)
|
|
236 "Return bold face for given FACE."
|
|
237 (cond ((eq face 'mh-speedbar-folder-face)
|
|
238 'mh-speedbar-folder-with-unseen-messages-face)
|
|
239 ((eq face 'mh-speedbar-selected-folder-face)
|
|
240 'mh-speedbar-selected-folder-with-unseen-messages-face)
|
|
241 (t face)))
|
|
242
|
|
243 (defun mh-speed-highlight (folder face)
|
|
244 "Set FOLDER to FACE."
|
|
245 (save-excursion
|
|
246 (speedbar-with-writable
|
|
247 (goto-char (gethash folder mh-speed-folder-map (point)))
|
|
248 (beginning-of-line)
|
|
249 (if (re-search-forward "([1-9][0-9]*/[0-9]+)" (line-end-position) t)
|
|
250 (setq face (mh-speed-bold-face face))
|
|
251 (setq face (mh-speed-normal-face face)))
|
|
252 (beginning-of-line)
|
|
253 (when (re-search-forward "\\[.\\] " (line-end-position) t)
|
|
254 (put-text-property (point) (line-end-position) 'face face)))))
|
|
255
|
|
256 (defun mh-speed-stealth-update (&optional force)
|
|
257 "Do stealth update.
|
|
258 With non-nil FORCE, the update is always carried out."
|
|
259 (cond ((save-excursion (set-buffer speedbar-buffer)
|
|
260 (get-text-property (point-min) 'mh-level))
|
|
261 ;; Execute this hook and *don't* run anything else
|
|
262 (mh-speed-update-current-folder force)
|
|
263 nil)
|
|
264 ;; Otherwise on to your regular programming
|
|
265 (t t)))
|
|
266
|
|
267 (defun mh-speed-goto-folder (folder)
|
|
268 "Move point to line containing FOLDER.
|
|
269 The function will expand out parent folders of FOLDER if needed."
|
|
270 (let ((prefix folder)
|
|
271 (suffix-list ())
|
|
272 (last-slash t))
|
|
273 (while (and (not (gethash prefix mh-speed-folder-map)) last-slash)
|
|
274 (setq last-slash (search "/" prefix :from-end t))
|
|
275 (when (integerp last-slash)
|
|
276 (push (substring prefix (1+ last-slash)) suffix-list)
|
|
277 (setq prefix (substring prefix 0 last-slash))))
|
|
278 (let ((prefix-position (gethash prefix mh-speed-folder-map)))
|
|
279 (if prefix-position
|
|
280 (goto-char prefix-position)
|
|
281 (goto-char (point-min))
|
|
282 (mh-speed-toggle)
|
|
283 (unless (get-text-property (point) 'mh-expanded)
|
|
284 (mh-speed-toggle))
|
|
285 (goto-char (gethash prefix mh-speed-folder-map))))
|
|
286 (while suffix-list
|
|
287 ;; We always need atleast one toggle. We need two if the directory list
|
|
288 ;; is stale since a folder was added.
|
|
289 (when (equal prefix (get-text-property (line-beginning-position)
|
|
290 'mh-folder))
|
|
291 (mh-speed-toggle)
|
|
292 (unless (get-text-property (point) 'mh-expanded)
|
|
293 (mh-speed-toggle)))
|
|
294 (setq prefix (format "%s/%s" prefix (pop suffix-list)))
|
|
295 (goto-char (gethash prefix mh-speed-folder-map (point))))
|
|
296 (beginning-of-line)
|
|
297 (equal folder (get-text-property (point) 'mh-folder))))
|
|
298
|
|
299 (defun mh-speed-extract-folder-name (buffer)
|
|
300 "Given an MH-E BUFFER find the folder that should be highlighted.
|
|
301 Do the right thing for the different kinds of buffers that MH-E uses."
|
|
302 (save-excursion
|
|
303 (set-buffer buffer)
|
|
304 (cond ((eq major-mode 'mh-folder-mode)
|
|
305 mh-current-folder)
|
|
306 ((eq major-mode 'mh-show-mode)
|
|
307 (set-buffer mh-show-folder-buffer)
|
|
308 mh-current-folder)
|
|
309 ((eq major-mode 'mh-index-folder-mode)
|
|
310 (save-excursion
|
|
311 (mh-index-goto-nearest-msg)
|
|
312 (mh-index-parse-folder)))
|
|
313 ((or (eq major-mode 'mh-index-show-mode)
|
|
314 (eq major-mode 'mh-letter-mode))
|
|
315 (when (string-match mh-user-path buffer-file-name)
|
|
316 (let* ((rel-path (substring buffer-file-name (match-end 0)))
|
|
317 (directory-end (search "/" rel-path :from-end t)))
|
|
318 (when directory-end
|
|
319 (format "+%s" (substring rel-path 0 directory-end)))))))))
|
|
320
|
|
321 (defun mh-speed-add-buttons (folder level)
|
|
322 "Add speedbar button for FOLDER which is at indented by LEVEL amount."
|
|
323 (let ((folder-list (mh-speed-folders folder)))
|
|
324 (mapc
|
|
325 (lambda (f)
|
|
326 (let* ((folder-name (format "%s%s%s" (or folder "+")
|
|
327 (if folder "/" "") (car f)))
|
|
328 (counts (gethash folder-name mh-speed-flists-cache)))
|
|
329 (speedbar-with-writable
|
|
330 (speedbar-make-tag-line
|
|
331 'bracket (if (cdr f) ?+ ? )
|
|
332 'mh-speed-toggle nil
|
|
333 (format "%s%s"
|
|
334 (car f)
|
|
335 (if counts
|
|
336 (format " (%s/%s)" (car counts) (cdr counts))
|
|
337 ""))
|
|
338 'mh-speed-view nil
|
|
339 (if (and counts (> (car counts) 0))
|
|
340 'mh-speedbar-folder-with-unseen-messages-face
|
|
341 'mh-speedbar-folder-face)
|
|
342 level)
|
|
343 (save-excursion
|
|
344 (forward-line -1)
|
|
345 (setf (gethash folder-name mh-speed-folder-map)
|
|
346 (set-marker (make-marker) (1+ (line-beginning-position))))
|
|
347 (add-text-properties
|
|
348 (line-beginning-position) (1+ (line-beginning-position))
|
|
349 `(mh-folder ,folder-name
|
|
350 mh-expanded nil
|
|
351 mh-children-p ,(not (not (cdr f)))
|
|
352 ,@(if counts `(mh-count (,(car counts) . ,(cdr counts))) ())
|
|
353 mh-level ,level))))))
|
|
354 folder-list)))
|
|
355
|
|
356 (defun mh-speed-toggle (&rest args)
|
|
357 "Toggle the display of child folders.
|
|
358 The otional ARGS are ignored and there for compatibilty with speedbar."
|
|
359 (interactive)
|
|
360 (declare (ignore args))
|
|
361 (beginning-of-line)
|
|
362 (let ((parent (get-text-property (point) 'mh-folder))
|
|
363 (kids-p (get-text-property (point) 'mh-children-p))
|
|
364 (expanded (get-text-property (point) 'mh-expanded))
|
|
365 (level (get-text-property (point) 'mh-level))
|
|
366 (point (point))
|
|
367 start-region)
|
|
368 (speedbar-with-writable
|
|
369 (cond ((not kids-p) nil)
|
|
370 (expanded
|
|
371 (forward-line)
|
|
372 (setq start-region (point))
|
|
373 (while (and (get-text-property (point) 'mh-level)
|
|
374 (> (get-text-property (point) 'mh-level) level))
|
|
375 (remhash (get-text-property (point) 'mh-folder)
|
|
376 mh-speed-folder-map)
|
|
377 (forward-line))
|
|
378 (delete-region start-region (point))
|
|
379 (forward-line -1)
|
|
380 (speedbar-change-expand-button-char ?+)
|
|
381 (add-text-properties
|
|
382 (line-beginning-position) (1+ (line-beginning-position))
|
|
383 '(mh-expanded nil)))
|
|
384 (t
|
|
385 (forward-line)
|
|
386 (mh-speed-add-buttons parent (1+ level))
|
|
387 (goto-char point)
|
|
388 (speedbar-change-expand-button-char ?-)
|
|
389 (add-text-properties
|
|
390 (line-beginning-position) (1+ (line-beginning-position))
|
|
391 `(mh-expanded t)))))))
|
|
392
|
|
393 (defalias 'mh-speed-expand-folder 'mh-speed-toggle)
|
|
394 (defalias 'mh-speed-contract-folder 'mh-speed-toggle)
|
|
395
|
|
396 (defun mh-speed-folder-size ()
|
|
397 "Find folder size if folder on current line."
|
|
398 (let ((folder (get-text-property (line-beginning-position) 'mh-folder)))
|
|
399 (or (cdr (get-text-property (line-beginning-position) 'mh-count))
|
|
400 (and (null folder) 0)
|
|
401 (with-temp-buffer
|
|
402 (call-process (expand-file-name "flist" mh-progs) nil t nil
|
|
403 "-norecurse" folder)
|
|
404 (goto-char (point-min))
|
|
405 (unless (re-search-forward "out of " (line-end-position) t)
|
|
406 (error "Call to flist failed on folder %s" folder))
|
|
407 (car (read-from-string
|
|
408 (buffer-substring-no-properties (point)
|
|
409 (line-end-position))))))))
|
|
410
|
|
411 (defun mh-speed-view (&rest args)
|
|
412 "View folder on current line.
|
|
413 Optional ARGS are ignored."
|
|
414 (interactive)
|
|
415 (declare (ignore args))
|
|
416 (let* ((folder (get-text-property (line-beginning-position) 'mh-folder))
|
|
417 (range
|
|
418 (cond ((save-excursion
|
|
419 (beginning-of-line)
|
|
420 (re-search-forward "([1-9][0-9]*/[0-9]+)"
|
|
421 (line-end-position) t))
|
|
422 mh-unseen-seq)
|
|
423 ((> (mh-speed-folder-size) mh-large-folder)
|
|
424 (let* ((size (mh-speed-folder-size))
|
|
425 (prompt
|
|
426 (format "How many messages from %s (default: %s): "
|
|
427 folder size))
|
|
428 (in (read-string prompt nil nil
|
|
429 (number-to-string size)))
|
|
430 (result (car (ignore-errors (read-from-string in)))))
|
|
431 (cond ((null result) (format "last:%s" size))
|
|
432 ((numberp result) (format "last:%s" result))
|
|
433 (t (format "%s" result)))))
|
|
434 (t nil))))
|
|
435 (when (stringp folder)
|
|
436 (speedbar-with-attached-buffer
|
|
437 (mh-visit-folder folder range)
|
|
438 (delete-other-windows)))))
|
|
439
|
|
440 (defun mh-speed-folders (folder)
|
|
441 "Find the subfolders of FOLDER.
|
|
442 The function avoids running folders unnecessarily by caching the results of
|
|
443 the actual folders call."
|
|
444 (let ((match (gethash folder mh-speed-folders-cache 'no-result)))
|
|
445 (cond ((eq match 'no-result)
|
|
446 (setf (gethash folder mh-speed-folders-cache)
|
|
447 (mh-speed-folders-actual folder)))
|
|
448 (t match))))
|
|
449
|
|
450 (defun mh-speed-folders-actual (folder)
|
|
451 "Execute the command folders to return the sub-folders of FOLDER.
|
|
452 Filters out the folder names that start with \".\" so that directories that
|
|
453 aren't usually mail folders are hidden."
|
|
454 (let* ((folder (cond ((and (stringp folder)
|
|
455 (equal (substring folder 0 1) "+"))
|
|
456 folder)
|
|
457 (t nil)))
|
|
458 (arg-list `(,(expand-file-name "folders" mh-progs)
|
|
459 nil (t nil) nil "-noheader" "-norecurse"
|
|
460 ,@(if (stringp folder) (list folder) ())))
|
|
461 (results ()))
|
|
462 (with-temp-buffer
|
|
463 (apply #'call-process arg-list)
|
|
464 (goto-char (point-min))
|
|
465 (while (not (and (eolp) (bolp)))
|
|
466 (let ((folder-end (or (search-forward "+ " (line-end-position) t)
|
|
467 (search-forward " " (line-end-position) t))))
|
|
468 (when (integerp folder-end)
|
|
469 (let ((name (buffer-substring (line-beginning-position)
|
|
470 (match-beginning 0))))
|
|
471 (let ((first-char (substring name 0 1)))
|
|
472 (unless (or (string-equal first-char ".")
|
|
473 (string-equal first-char "#")
|
|
474 (string-equal first-char ","))
|
|
475 (push
|
|
476 (cons name
|
|
477 (search-forward "(others)" (line-end-position) t))
|
|
478 results)))))
|
|
479 (forward-line 1))))
|
|
480 (setq results (nreverse results))
|
|
481 (when (stringp folder)
|
|
482 (setq results (cdr results))
|
|
483 (let ((folder-name-len (length (format "%s/" (substring folder 1)))))
|
|
484 (setq results (mapcar (lambda (f)
|
|
485 (cons (substring (car f) folder-name-len)
|
|
486 (cdr f)))
|
|
487 results))))
|
|
488 results))
|
|
489
|
|
490 (defun mh-speed-flists (force)
|
|
491 "Execute flists -recurse and update message counts.
|
|
492 If FORCE is non-nil the timer is reset."
|
|
493 (interactive (list t))
|
|
494 (when force
|
|
495 (when (timerp mh-speed-flists-timer)
|
|
496 (cancel-timer mh-speed-flists-timer))
|
|
497 (setq mh-speed-flists-timer nil)
|
|
498 (when (and (processp mh-speed-flists-process)
|
|
499 (not (eq (process-status mh-speed-flists-process) 'exit)))
|
|
500 (kill-process mh-speed-flists-process)
|
|
501 (setq mh-speed-flists-process nil)))
|
|
502 (unless mh-speed-flists-timer
|
|
503 (setq mh-speed-flists-timer
|
|
504 (run-at-time
|
|
505 nil mh-speed-flists-interval
|
|
506 (lambda ()
|
|
507 (unless (and (processp mh-speed-flists-process)
|
|
508 (not (eq (process-status mh-speed-flists-process)
|
|
509 'exit)))
|
|
510 (setq mh-speed-flists-process
|
|
511 (start-process (expand-file-name "flists" mh-progs) nil
|
|
512 "flists" "-recurse"))
|
|
513 (set-process-filter mh-speed-flists-process
|
|
514 'mh-speed-parse-flists-output)))))))
|
|
515
|
|
516 ;; Copied from mh-make-folder-list-filter...
|
|
517 (defun mh-speed-parse-flists-output (process output)
|
|
518 "Parse the incremental results from flists.
|
|
519 PROCESS is the flists process and OUTPUT is the results that must be handled
|
|
520 next."
|
|
521 (let ((prevailing-match-data (match-data))
|
|
522 (position 0)
|
|
523 line-end line folder unseen total)
|
|
524 (unwind-protect
|
|
525 (while (setq line-end (string-match "\n" output position))
|
|
526 (setq line (format "%s%s"
|
|
527 mh-speed-partial-line
|
|
528 (substring output position line-end))
|
|
529 mh-speed-partial-line "")
|
|
530 (when (string-match "+? " line)
|
|
531 (setq folder (format "+%s" (subseq line 0 (match-beginning 0))))
|
|
532 (when (string-match " has " line)
|
|
533 (setq unseen (car (read-from-string line (match-end 0))))
|
|
534 (when (string-match "; out of " line)
|
|
535 (setq total (car (read-from-string line (match-end 0))))
|
|
536 (setf (gethash folder mh-speed-flists-cache)
|
|
537 (cons unseen total))
|
|
538 (save-excursion
|
|
539 (when (buffer-live-p (get-buffer speedbar-buffer))
|
|
540 (set-buffer speedbar-buffer)
|
|
541 (speedbar-with-writable
|
|
542 (when (get-text-property (point-min) 'mh-level)
|
|
543 (let ((pos (gethash folder mh-speed-folder-map))
|
|
544 face)
|
|
545 (when pos
|
|
546 (goto-char pos)
|
|
547 (goto-char (line-beginning-position))
|
|
548 (cond
|
|
549 ((null (get-text-property (point) 'mh-count))
|
|
550 (goto-char (line-end-position))
|
|
551 (setq face (get-text-property (1- (point))
|
|
552 'face))
|
|
553 (insert (format " (%s/%s)" unseen total))
|
|
554 (mh-speed-highlight 'unknown face)
|
|
555 (goto-char (line-beginning-position))
|
|
556 (add-text-properties
|
|
557 (point) (1+ (point))
|
|
558 `(mh-count (,unseen . ,total))))
|
|
559 ((not
|
|
560 (equal (get-text-property (point) 'mh-count)
|
|
561 (cons unseen total)))
|
|
562 (goto-char (line-end-position))
|
|
563 (setq face (get-text-property (1- (point))
|
|
564 'face))
|
|
565 (re-search-backward
|
|
566 " " (line-beginning-position) t)
|
|
567 (delete-region (point) (line-end-position))
|
|
568 (insert (format " (%s/%s)" unseen total))
|
|
569 (mh-speed-highlight 'unknown face)
|
|
570 (goto-char (line-beginning-position))
|
|
571 (add-text-properties
|
|
572 (point) (1+ (point))
|
|
573 `(mh-count (,unseen . ,total))))))))))))))
|
|
574 (setq position (1+ line-end)))
|
|
575 (set-match-data prevailing-match-data))
|
|
576 (setq mh-speed-partial-line (subseq output position))))
|
|
577
|
|
578 (defun mh-speed-invalidate-map (folder)
|
|
579 "Remove FOLDER from various optimization caches."
|
|
580 (interactive (list ""))
|
|
581 (save-excursion
|
|
582 (set-buffer speedbar-buffer)
|
|
583 (let* ((speedbar-update-flag nil)
|
|
584 (last-slash (search "/" folder :from-end t))
|
|
585 (parent (if last-slash (substring folder 0 last-slash) nil))
|
|
586 (parent-position (gethash parent mh-speed-folder-map))
|
|
587 (parent-change nil))
|
|
588 (remhash parent mh-speed-folders-cache)
|
|
589 (remhash folder mh-speed-folders-cache)
|
|
590 (when parent-position
|
|
591 (let ((parent-kids (mh-speed-folders parent)))
|
|
592 (cond ((null parent-kids)
|
|
593 (setq parent-change ?+))
|
|
594 ((and (null (cdr parent-kids))
|
|
595 (equal (if last-slash
|
|
596 (substring folder (1+ last-slash))
|
|
597 (substring folder 1))
|
|
598 (caar parent-kids)))
|
|
599 (setq parent-change ? ))))
|
|
600 (goto-char parent-position)
|
|
601 (when (equal (get-text-property (line-beginning-position) 'mh-folder)
|
|
602 parent)
|
|
603 (when (get-text-property (line-beginning-position) 'mh-expanded)
|
|
604 (mh-speed-toggle))
|
|
605 (when parent-change
|
|
606 (speedbar-with-writable
|
|
607 (mh-speedbar-change-expand-button-char parent-change)
|
|
608 (add-text-properties
|
|
609 (line-beginning-position) (1+ (line-beginning-position))
|
|
610 `(mh-children-p ,(equal parent-change ?+)))))
|
|
611 (mh-speed-highlight mh-speed-last-selected-folder
|
|
612 'mh-speedbar-folder-face)
|
|
613 (setq mh-speed-last-selected-folder nil)
|
|
614 (setq mh-speed-refresh-flag t)))
|
|
615 (when (equal folder "")
|
|
616 (clrhash mh-speed-folders-cache)))))
|
|
617
|
|
618 (defun mh-speed-add-folder (folder)
|
|
619 "Add FOLDER since it is being created.
|
|
620 The function invalidates the latest ancestor that is present."
|
|
621 (save-excursion
|
|
622 (set-buffer speedbar-buffer)
|
|
623 (let ((speedbar-update-flag nil)
|
|
624 (last-slash (search "/" folder :from-end t))
|
|
625 (ancestor folder)
|
|
626 (ancestor-pos nil))
|
|
627 (block while-loop
|
|
628 (while last-slash
|
|
629 (setq ancestor (substring ancestor 0 last-slash))
|
|
630 (setq ancestor-pos (gethash ancestor mh-speed-folder-map))
|
|
631 (when ancestor-pos
|
|
632 (return-from while-loop))
|
|
633 (setq last-slash (search "/" ancestor :from-end t))))
|
|
634 (unless ancestor-pos (setq ancestor nil))
|
|
635 (goto-char (or ancestor-pos (gethash nil mh-speed-folder-map)))
|
|
636 (speedbar-with-writable
|
|
637 (mh-speedbar-change-expand-button-char ?+)
|
|
638 (add-text-properties
|
|
639 (line-beginning-position) (1+ (line-beginning-position))
|
|
640 `(mh-children-p t)))
|
|
641 (when (get-text-property (line-beginning-position) 'mh-expanded)
|
|
642 (mh-speed-toggle))
|
|
643 (remhash ancestor mh-speed-folders-cache)
|
|
644 (setq mh-speed-refresh-flag t))))
|
|
645
|
|
646 ;; Make it slightly more general to allow for [ ] buttons to be changed to
|
|
647 ;; [+].
|
|
648 (defun mh-speedbar-change-expand-button-char (char)
|
|
649 "Change the expansion button character to CHAR for the current line."
|
|
650 (save-excursion
|
|
651 (beginning-of-line)
|
|
652 (if (re-search-forward "\\[.\\]" (line-end-position) t)
|
|
653 (speedbar-with-writable
|
|
654 (backward-char 2)
|
|
655 (delete-char 1)
|
|
656 (insert-char char 1 t)
|
|
657 (put-text-property (point) (1- (point)) 'invisible nil)
|
|
658 ;; make sure we fix the image on the text here.
|
|
659 (speedbar-insert-image-button-maybe (- (point) 2) 3)))))
|
|
660
|
|
661 (provide 'mh-speed)
|
|
662
|
|
663 ;;; Local Variables:
|
|
664 ;;; sentence-end-double-space: nil
|
|
665 ;;; End:
|
|
666
|
|
667 ;;; mh-speed.el ends here
|