comparison lisp/utils/speedbar.el @ 24:4103f0995bd7 r19-15b95

Import from CVS: tag r19-15b95
author cvs
date Mon, 13 Aug 2007 08:51:03 +0200
parents
children
comparison
equal deleted inserted replaced
23:0edd3412f124 24:4103f0995bd7
1 ;;; speedbar - quick access to files and tags
2 ;;;
3 ;;; Copyright (C) 1996 Eric M. Ludlam
4 ;;;
5 ;;; Author: Eric M. Ludlam <zappo@gnu.ai.mit.edu>
6 ;;; RCS: $Id: speedbar.el,v 1.1 1997/02/17 06:40:34 steve Exp $
7 ;;; Version: 0.3.1
8 ;;; Keywords: file, tags, tools
9 ;;;
10 ;;; This program is free software; you can redistribute it and/or modify
11 ;;; it under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 2, or (at your option)
13 ;;; any later version.
14 ;;;
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with this program; if not, you can either send email to this
22 ;;; program's author (see below) or write to:
23 ;;;
24 ;;; The Free Software Foundation, Inc.
25 ;;; 675 Mass Ave.
26 ;;; Cambridge, MA 02139, USA.
27 ;;;
28 ;;; Please send bug reports, etc. to zappo@gnu.ai.mit.edu.
29 ;;;
30
31 ;;; Commentary:
32 ;;;
33 ;;; The speedbar provides a frame in which files, and locations in
34 ;;; files are displayed. These items can be clicked on with mouse-2
35 ;;; in order to make the last active frame display that file location.
36 ;;;
37 ;;; If you want to choose it from a menu or something, do this:
38 ;;;
39 ;;; (autoload 'speedbar-frame-mode "speedbar" "Popup a speedbar frame" t)
40 ;;; (define-key-after (lookup-key global-map [menu-bar tools])
41 ;;; [speedbar] '("Speedbar" . speedbar-frame-mode) [calendar])
42 ;;;
43 ;;; To activate speedbar without the menu, type: M-x speedbar-frame-mode RET
44 ;;;
45 ;;; Once a speedbar frame is active, it takes advantage of idle time
46 ;;; to keep it's contents updated. The contents is usually a list of
47 ;;; files in the directory of the currently active buffer. When
48 ;;; applicable, tags in the active file can be expanded.
49 ;;;
50 ;;; Speedbar uses multiple methods for creating tags to jump to.
51 ;;; When the variable `speedbar-use-imenu-package' is set, then
52 ;;; speedbar will first try to use imenu to get tags. If the mode of
53 ;;; the buffer doesn't support imenu, then etags is used. Using Imenu
54 ;;; has the advantage that tags are cached, so opening and closing
55 ;;; tags lists is faster. Speedbar-imenu will also load the file into
56 ;;; a non-selected buffer so clicking the file later will be faster.
57 ;;;
58 ;;; To add new files types into the speedbar, modify
59 ;;; `speedbar-file-regexp' to include the extension of the file type
60 ;;; you wish to include. If speedbar complains that the file type is
61 ;;; not supported, that means there is no built in support from imenu,
62 ;;; and the etags part wasn't set up right.
63 ;;;
64 ;;; To add new file types to imenu, see the documentation in the
65 ;;; file imenu.el that comes with emacs. To add new file types which
66 ;;; etags supports, you need to modify the variable
67 ;;; `speedbar-fetch-etags-parse-list'. This variable is an
68 ;;; association list with each element of the form: (extension-regex
69 ;;; . parse-one-line) The extension-regex would be something like
70 ;;; "\\.c$" for a .c file, and the parse-one-line would be either a
71 ;;; regular expression where match tag 1 is the element you wish
72 ;;; displayed as a tag. If you need to do something more complex,
73 ;;; then you can also write a function which parses one line, and put
74 ;;; its symbol there instead.
75 ;;;
76 ;;; If the updates are going to slow for you, modify the variable
77 ;;; `speedbar-update-speed' to a longer idle time before updates.
78 ;;;
79 ;;; If you navigate directories, you will probably notice that you
80 ;;; will navigate to a directory which is eventually replaced after
81 ;;; you go back to editing a file (unless you pull up a new file.)
82 ;;; The delay time before this happens is in
83 ;;; `speedbar-navigating-speed', and defaults to 20 seconds.
84 ;;;
85 ;;; XEmacs users may want to change the default timeouts for
86 ;;; `speedbar-update-speed' to something longer as XEmacs doesn't have
87 ;;; idle timers, the speedbar timer keeps going off arbitrarilly while
88 ;;; you're typing. It's quite pesky.
89 ;;;
90 ;;; To get speedbar-configure-faces to work, you will need to
91 ;;; download my eieio package from my ftp site.
92 ;;;
93 ;;; EIEIO is NOT required when using speedbar. It is ONLY needed
94 ;;; if you want to use a fancy dialog face editor for speedbar.
95
96 ;;; Speedbar updates can be found at:
97 ;;; ftp://ftp.ultranet.com/pub/zappo/speedbar.*.el
98 ;;;
99
100 ;;; HISTORY:
101 ;;; 0.1 Initial Revision
102 ;;; 0.2 Fixed problem with x-pointer-shape causing future frames not
103 ;;; to be created.
104 ;;; Fixed annoying habit of `speedbar-update-contents' to make
105 ;;; it possible to accidentally kill the speedbar buffer.
106 ;;; Clicking directory names now only changes the contents of
107 ;;; the speedbar, and does not cause a dired mode to appear.
108 ;;; Clicking the <+> next to the directory does cause dired to
109 ;;; be run.
110 ;;; Added XEmacs support, which means timer support moved to a
111 ;;; platform independant call.
112 ;;; Added imenu support. Now modes are supported by imenu
113 ;;; first, and etags only if the imenu call doesn't work.
114 ;;; Imenu is a little faster than etags, and is more emacs
115 ;;; friendly.
116 ;;; Added more user control variables described in the commentary.
117 ;;; Added smart recentering when nodes are opened and closed.
118 ;;; 0.3 x-pointer-shape fixed for emacs 19.35, so I put that check in.
119 ;;; Added invisible codes to the beginning of each line.
120 ;;; Added list aproach to node expansion for easier addition of new
121 ;;; types of things to expand by
122 ;;; Added multi-level path name support
123 ;;; Added multi-level tag name support.
124 ;;; Only mouse-2 is now used for node expansion
125 ;;; Added keys e + - to edit expand, and contract node lines
126 ;;; Added longer legal file regexp for all those modes which support
127 ;;; imenu. (pascal, fortran90, ada, pearl)
128 ;;; Fixed centering algorithm
129 ;;; Tried to choose background independent colors. Made more robust.
130 ;;; Rearranged code into a more logical order
131 ;;; 0.3.1 Fixed doc & broken keybindings
132 ;;; Added mode hooks.
133 ;;; Improved color selection to be background mode smart
134 ;;; `nil' passed to `speedbar-frame-mode' now toggles the frame as
135 ;;; advertised in the doc string
136 ;;;
137 ;;; TODO:
138 ;;; 1) Rember contents of directories when leaving them so it's faster
139 ;;; when returning.
140 ;;; 2) List of directories to never visit. (User might be browsing
141 ;;; there temporarilly such as info files, documentation and the
142 ;;; like)
143 ;;; 3) Implement SHIFT-mouse2 to rescan buffers with imenu.
144 ;;; 4) Better XEmacs support of menus and button-bar
145 ;;; 5) More functions to create buttons and options
146 ;;; 6) filtering algoritms to reduce the number of tags/files
147 ;;; displayed.
148 ;;; 7) Build `speedbar-file-regexp' on the fly.
149 ;;; 8) More intelligent current file highlighting.
150
151 (defvar speedbar-xemacsp (string-match "XEmacs" emacs-version))
152
153 (defvar speedbar-initial-expansion-list
154 '(speedbar-directory-buttons speedbar-default-directory-list)
155 "*List of functions to call to fill in the speedbar buffer whenever
156 a top level update is issued. These functions will allways get the
157 default directory to use passed in as the first parameter, and a 0 as
158 the second parameter. They must assume that the cursor is at the
159 postion where they start inserting buttons.")
160
161 (defvar speedbar-show-unknown-files nil
162 "*Non-nil shows files with a ? in the expansion tag for files we can't
163 expand. `nil' means don't show the file in the list.")
164
165 ;; Xemacs timers aren't based on idleness. Therefore tune it down a little
166 ;; or suffer mightilly!
167 (defvar speedbar-update-speed (if speedbar-xemacsp 5 1)
168 "*Time in seconds of idle time needed before speedbar will update
169 it's buffer to match what you've been doing in your other frame.")
170 (defvar speedbar-navigating-speed 10
171 "*Idle time to wait before re-running the timer proc to pick up any new
172 activity if the user has started navigating directories in the speedbar.")
173
174 (defvar speedbar-width 20
175 "*Initial size of the speedbar window")
176
177 (defvar speedbar-scrollbar-width 10
178 "*Initial sizeo of the speedbar scrollbar. The thinner, the more
179 display room you will have.")
180
181 (defvar speedbar-raise-lower t
182 "*Non-nil means speedbar will auto raise and lower itself. When this
183 is set, you can have only a tiny strip visible under your main emacs,
184 and it will raise and lower itself when you put the pointer in it.")
185
186 (defvar speedbar-use-imenu-package (not speedbar-xemacsp)
187 "*Optionally use the imenu package instead of etags for parsing. This
188 is experimental for performace testing.")
189
190 (defvar speedbar-before-delete-hook nil
191 "*Hooks called before deletiing the speedbar frame.")
192
193 (defvar speedbar-mode-hook nil
194 "*Hooks called after creating a speedbar buffer")
195
196 (defvar speedbar-timer-hook nil
197 "*Hooks called after running the speedbar timer function")
198
199 (defvar speedbar-file-unshown-regexp
200 (let ((nstr "") (noext completion-ignored-extensions))
201 (while noext
202 (setq nstr (concat nstr (regexp-quote (car noext)) "$"
203 (if (cdr noext) "\\|" ""))
204 noext (cdr noext)))
205 (concat nstr "\\|#[^#]+#$\\|\\.\\.?$"))
206 "*Regular expression matching files we don't want to display in a
207 speedbar buffer")
208
209 (defvar speedbar-file-regexp
210 (if speedbar-use-imenu-package
211 "\\(\\.\\([CchH]\\|c\\(++\\|pp\\)\\|f90\\|ada\\|pl?\\|el\\|t\\(ex\\(i\\(nfo\\)?\\)?\\|cl\\)\\|emacs\\)$\\)\\|[Mm]akefile\\(\\.in\\)?"
212 "\\.\\([CchH]\\|c\\(++\\|pp\\)\\|p\\|el\\|tex\\(i\\(nfo\\)?\\)?\\|emacs\\)$")
213 "*Regular expresson matching files we know how to expand.")
214
215 (defvar speedbar-syntax-table nil
216 "Syntax-table used on the speedbar")
217
218 (if speedbar-syntax-table
219 nil
220 (setq speedbar-syntax-table (make-syntax-table))
221 ;; turn off paren matching around here.
222 (modify-syntax-entry ?\' " " speedbar-syntax-table)
223 (modify-syntax-entry ?\" " " speedbar-syntax-table)
224 (modify-syntax-entry ?( " " speedbar-syntax-table)
225 (modify-syntax-entry ?) " " speedbar-syntax-table)
226 (modify-syntax-entry ?[ " " speedbar-syntax-table)
227 (modify-syntax-entry ?] " " speedbar-syntax-table))
228
229
230 (defvar speedbar-key-map nil
231 "Keymap used in speedbar buffer.")
232 (defvar speedbar-menu-map nil
233 "Keymap used in speedbar menu buffer.")
234
235 (if speedbar-key-map
236 nil
237 (setq speedbar-key-map (make-keymap))
238 (suppress-keymap speedbar-key-map t)
239
240 (define-key speedbar-key-map "e" 'speedbar-edit-line)
241 (define-key speedbar-key-map "+" 'speedbar-expand-line)
242 (define-key speedbar-key-map "-" 'speedbar-contract-line)
243
244 (if (string-match "XEmacs" emacs-version)
245 (progn
246 ;; bind mouse bindings so we can manipulate the items on each line
247 (define-key speedbar-key-map 'button2 'speedbar-click)
248
249 ;; Xemacs users. You probably want your own toolbar for
250 ;; the speedbar frame or mode or whatever. Make some buttons
251 ;; and mail me how to do it!
252 ;; Also, how do you disable all those menu items? Email me that too
253 ;; as it would be most helpful.
254 )
255 ;; bind mouse bindings so we can manipulate the items on each line
256 (define-key speedbar-key-map [mouse-2] 'speedbar-click)
257 (define-key speedbar-key-map [down-mouse-2] 'speedbar-quick-mouse)
258
259 ;; this was meant to do a rescan or something
260 ;;(define-key speedbar-key-map [shift-mouse-2] 'speedbar-hard-click)
261
262 ;; disable all menus - we don't have a lot of space to play with
263 ;; in such a skinny frame.
264 (define-key speedbar-key-map [menu-bar buffer] 'undefined)
265 (define-key speedbar-key-map [menu-bar files] 'undefined)
266 (define-key speedbar-key-map [menu-bar tools] 'undefined)
267 (define-key speedbar-key-map [menu-bar edit] 'undefined)
268 (define-key speedbar-key-map [menu-bar search] 'undefined)
269 (define-key speedbar-key-map [menu-bar help-menu] 'undefined)
270
271 ;; This lets the user scroll as if we had a scrollbar... well maybe not
272 (define-key speedbar-key-map [mode-line mouse-2] 'speedbar-mouse-hscroll)
273
274 ;; Create a menu for speedbar
275 (setq speedbar-menu-map (make-sparse-keymap))
276 (define-key speedbar-key-map [menu-bar speedbar]
277 (cons "Speedbar" speedbar-menu-map))
278 (define-key speedbar-menu-map [close]
279 (cons "Close" 'speedbar-close-frame))
280 (define-key speedbar-menu-map [clonfigure]
281 (cons "Configure Faces" 'speedbar-configure-faces))
282 (define-key speedbar-menu-map [configopt]
283 (cons "Configure Options" 'speedbar-configure-options))
284 (define-key speedbar-menu-map [Update]
285 (cons "Update" 'speedbar-update-contents))
286 ))
287
288 (put 'speedbar-configure-faces 'menu-enable '(featurep 'dialog))
289 (put 'speedbar-configure-options 'menu-enable '(featurep 'dialog))
290
291 (defvar speedbar-buffer nil
292 "The buffer displaying the speedbar.")
293 (defvar speedbar-frame nil
294 "The frame displaying speedbar.")
295 (defvar speedbar-timer nil
296 "The speedbar timer used for updating the buffer.")
297 (defvar speedbar-attached-frame nil
298 "The frame which started speedbar mode. This is the frame from
299 which all data displayed in the speedbar is gathered, and in which files
300 and such are displayed.")
301
302 (defvar speedbar-last-selected-file nil
303 "The last file which was selected in speedbar buffer")
304
305 (defvar speedbar-shown-directories nil
306 "Used to maintain list of directories simultaneously open in the current
307 speedbar.")
308
309
310 ;;;
311 ;;; Mode definitions/ user commands
312 ;;;
313 ;;;###autoload
314 (defun speedbar-frame-mode (&optional arg)
315 "Enable or disable use of a speedbar. Positive number means turn
316 on, negative turns speedbar off, and nil means toggle. Once the
317 speedbar frame is activated, a buffer in `speedbar-mode' will be
318 displayed. Currently, only one speedbar is supported at a time."
319 (interactive "P")
320 (if (not window-system)
321 (error "Speedbar is not useful outside of a windowing environement"))
322 ;; toggle frame on and off.
323 (if (not arg) (if speedbar-frame (setq arg -1) (setq arg 1)))
324 ;; turn the frame off on neg number
325 (if (and (numberp arg) (< arg 0))
326 (progn
327 (run-hooks 'speedbar-before-delete-hook)
328 (if (and speedbar-frame (frame-live-p speedbar-frame))
329 (delete-frame speedbar-frame))
330 (speedbar-set-timer nil)
331 (setq speedbar-frame nil)
332 (if (bufferp speedbar-buffer)
333 (kill-buffer speedbar-buffer)))
334 ;; Set this as our currently attached frame
335 (setq speedbar-attached-frame (selected-frame))
336 ;; Get the buffer to play with
337 (speedbar-mode)
338 ;; Get the frame to work in
339 (if (and speedbar-frame (frame-live-p speedbar-frame))
340 (raise-frame speedbar-frame)
341 (let ((params (list
342 ;; Xemacs fails to delete speedbar
343 ;; if minibuffer is off.
344 ;; JTL <<<< Seems to be OK for 19.15.
345 ;; removed tool- & menubar.
346 ;; <<<< JTL
347 (cons 'minibuffer nil)
348 (cons 'width speedbar-width)
349 (cons 'height (frame-height))
350 (cons 'scroll-bar-width speedbar-scrollbar-width)
351 (cons 'auto-raise speedbar-raise-lower)
352 (cons 'auto-lower speedbar-raise-lower)
353 '(modeline . nil)
354 '(border-width . 0)
355 '(unsplittable . t)
356 '(default-toolbar-visible-p . nil)
357 '(menubar-visible-p . nil))))
358 (setq speedbar-frame
359 (if (< emacs-minor-version 35)
360 (make-frame params)
361 (let ((x-pointer-shape x-pointer-top-left-arrow)
362 (x-sensitive-text-pointer-shape x-pointer-hand2))
363 (make-frame params)))))
364 ;; reset the selection variable
365 (setq speedbar-last-selected-file nil)
366 ;; Put the buffer into the frame
367 (save-window-excursion
368 (select-frame speedbar-frame)
369 (switch-to-buffer speedbar-buffer)
370 (setq default-minibuffer-frame speedbar-attached-frame))
371 (speedbar-set-timer speedbar-update-speed)
372 )))
373
374 (defun speedbar-close-frame ()
375 "Turn off speedbar mode"
376 (interactive)
377 (speedbar-frame-mode -1))
378
379 (defun speedbar-mode ()
380 "Create and return a SPEEDBAR buffer. The speedbar buffer allows
381 the user to manage a list of directories and paths at different
382 depths. The first line represents the default path of the speedbar
383 frame. Each directory segment is a button which jumps speedbar's
384 default directory to that path. Buttons are activated by clicking
385 mouse-2.
386
387 Each line starting with <+> represents a directory. Click on the <+>
388 to insert the directory listing into the current tree. Click on the
389 <-> to retract that list. Click on the directory name to go to that
390 directory as the default.
391
392 Each line starting with [+] is a file. If the variable
393 `speedbar-show-unknown-files' is t, the lines starting with [?] are
394 files which don't have imenu support, but are not expressly ignored.
395 Files are completely ignored if they match `speedbar-file-unshown-regexp'
396 which is generated from `completion-ignored-extensions'.
397
398 Click on the [+] to display a list of tags from that file. Click on
399 the [-] to retract the list. Click on the file name to edit the file
400 in the attached frame.
401
402 If you open tags, you might find a node starting with {+}, which is a
403 category of tags. Click the {+} to expand the category. Jumpable
404 tags start with >. Click the name of the tag to go to that position
405 in the selected file.
406
407 Keybindings: \\<speedbar-key-map>
408 \\[speedbar-click] Activate the button under the mouse.
409 \\[speedbar-edit-line] Edit the file/directory on this line. Same as clicking
410 on the name on the selected line.)
411 \\[speedbar-expand-line] Expand the current line. Same as clicking on the + on a line.
412 \\[speedbar-contract-line] Contract the current line. Same as clicking on the - on a line."
413 (setq speedbar-buffer (set-buffer (get-buffer-create "SPEEDBAR")))
414 (kill-all-local-variables)
415 (setq major-mode 'speedbar-mode)
416 (setq mode-name "SB")
417 (use-local-map speedbar-key-map)
418 (set-syntax-table speedbar-syntax-table)
419 (setq mode-line-format
420 '("<< SPEEDBAR " (line-number-mode " %3l ") " >>"))
421 (setq font-lock-keywords nil) ;; no font-locking please
422 (setq truncate-lines t)
423 (if (not speedbar-xemacsp) (setq auto-show-mode nil)) ;no auto-show for FSF
424 (run-hooks 'speedbar-mode-hook)
425 (speedbar-update-contents)
426 )
427
428 (defun speedbar-mouse-hscroll (e)
429 "Read a mouse event from the mode line, and horizontally scroll if the
430 mouse is being clicked on the far left, or far right of the modeline."
431 (interactive "e")
432 (let* ((xp (car (nth 2 (car (cdr e)))))
433 (cpw (/ (frame-pixel-width)
434 (frame-width)))
435 (oc (1+ (/ xp cpw)))
436 )
437 (cond ((< oc 3)
438 (scroll-left 2))
439 ((> oc (- (window-width) 3))
440 (scroll-right 2))
441 (t (message "Click on the edge of the modeline to scroll left/right")))
442 ;;(message "X: Pixel %d Char Pixels %d On char %d" xp cpw oc)
443 ))
444
445
446 ;;;
447 ;;; Utility functions
448 ;;;
449 (defun speedbar-set-timer (timeout)
450 "Unset an old timer (if there is one) and activate a new timer with the
451 given timeout value."
452 (cond
453 ;; Xemacs
454 (speedbar-xemacsp
455 (if speedbar-timer
456 (progn (delete-itimer speedbar-timer)
457 (setq speedbar-timer nil)))
458 (if timeout
459 (setq speedbar-timer (start-itimer "speedbar"
460 'speedbar-timer-fn
461 timeout
462 nil))))
463 ;; GNU emacs
464 (t
465 (if speedbar-timer
466 (progn (cancel-timer speedbar-timer)
467 (setq speedbar-timer nil)))
468 (if timeout
469 (setq speedbar-timer
470 (run-with-idle-timer timeout nil 'speedbar-timer-fn))))
471 ))
472
473 (defmacro speedbar-with-writable (&rest forms)
474 "Allow the buffer to be writable and evaluate forms. Turn read-only back
475 on when done."
476 (list 'let '((speedbar-with-writable-buff (current-buffer)))
477 '(toggle-read-only -1)
478 (cons 'progn forms)
479 '(save-excursion (set-buffer speedbar-with-writable-buff)
480 (toggle-read-only 1))))
481 (put 'speedbar-with-writable 'lisp-indent-function 0)
482
483 (defun speedbar-make-button (start end face mouse function &optional token)
484 "Create a button from START to END, with FACE as the display face
485 and MOUSE and the mouse face. When this button is clicked on FUNCTION
486 will be run with the token parameter of TOKEN (any lisp object)"
487 (put-text-property start end 'face face)
488 (put-text-property start end 'mouse-face mouse)
489 (put-text-property start end 'invisible nil)
490 (if function (put-text-property start end 'speedbar-function function))
491 (if token (put-text-property start end 'speedbar-token token))
492 )
493
494 (defun speedbar-file-lists (directory)
495 "Create file lists for DIRECTORY. The car is the list of
496 directories, the cdr is list of files not matching ignored headers."
497 (let ((default-directory directory)
498 (dir (directory-files directory nil))
499 (dirs nil)
500 (files nil))
501 (while dir
502 (if (not (string-match speedbar-file-unshown-regexp (car dir)))
503 (if (file-directory-p (car dir))
504 (setq dirs (cons (car dir) dirs))
505 (setq files (cons (car dir) files))))
506 (setq dir (cdr dir)))
507 (cons (nreverse dirs) (list (nreverse files))))
508 )
509
510 (defun speedbar-directory-buttons (directory index)
511 "Inserts a single button group at point for DIRECTORY. Each directory
512 path part is a different button. If part of the path matches the user
513 directory ~, then it is replaced with a ~"
514 (let* ((tilde (expand-file-name "~"))
515 (dd (expand-file-name directory))
516 (junk (string-match (regexp-quote tilde) dd))
517 (displayme (if junk
518 (concat "~" (substring dd (match-end 0)))
519 dd))
520 (p (point)))
521 (if (string-match "^~/?$" displayme) (setq displayme (concat tilde "/")))
522 (insert displayme)
523 (save-excursion
524 (goto-char p)
525 (while (re-search-forward "\\([^/]+\\)/" nil t)
526 (speedbar-make-button (match-beginning 1) (match-end 1)
527 'speedbar-directory-face
528 'speedbar-highlight-face
529 'speedbar-directory-buttons-follow
530 (if (= (match-beginning 1) p)
531 (expand-file-name "~/") ;the tilde
532 (buffer-substring-no-properties
533 p (match-end 0))))))
534 (if (string-match "^/[^/]+/$" displayme)
535 (progn
536 (insert " ")
537 (let ((p (point)))
538 (insert "<root>")
539 (speedbar-make-button p (point)
540 'speedbar-directory-face
541 'speedbar-highlight-face
542 'speedbar-directory-buttons-follow
543 "/"))))
544 (insert-char ?\n 1 nil)))
545
546 (defun speedbar-make-tag-line (exp-button-type
547 exp-button-char exp-button-function
548 exp-button-data
549 tag-button tag-button-function tag-button-data
550 tag-button-face depth)
551 "Creates a tag line with BUTTON-TYPE for the small button that
552 expands or contracts a node (if applicable), and BUTTON-CHAR the
553 character in it (+, -, ?, etc). BUTTON-FUNCTION is the function to
554 call if it's clicked on. Button types are 'bracket, 'angle, 'curly, or nil.
555
556 Next, TAG-BUTTON is the text of the tag. TAG-FUNCTION is the function
557 to call if clicked on, and TAG-DATA is the data to attach to the text
558 field (such a tag positioning, etc). TAG-FACE is a face used for this
559 type of tag.
560
561 Lastly, DEPTH shows the depth of expansion.
562
563 This function assumes that the cursor is in the speecbar window at the
564 position to insert a new item, and that the new item will end with a CR"
565 (let ((start (point))
566 (end (progn
567 (insert (int-to-string depth) ":")
568 (point))))
569 (put-text-property start end 'invisible t)
570 )
571 (insert-char ? depth nil)
572 (put-text-property (- (point) depth) (point) 'invisible nil)
573 (let* ((exp-button (cond ((eq exp-button-type 'bracket) "[%c]")
574 ((eq exp-button-type 'angle) "<%c>")
575 ((eq exp-button-type 'curly) "{%c}")
576 (t ">")))
577 (buttxt (format exp-button exp-button-char))
578 (start (point))
579 (end (progn (insert buttxt) (point)))
580 (bf (if exp-button-type 'speedbar-button-face nil))
581 (mf (if exp-button-function 'speedbar-highlight-face nil))
582 )
583 (speedbar-make-button start end bf mf exp-button-function exp-button-data)
584 )
585 (insert-char ? 1 nil)
586 (put-text-property (1- (point)) (point) 'invisible nil)
587 (let ((start (point))
588 (end (progn (insert tag-button) (point))))
589 (insert-char ?\n 1 nil)
590 (put-text-property (1- (point)) (point) 'invisible nil)
591 (speedbar-make-button start end tag-button-face
592 (if tag-button-function 'speedbar-highlight-face nil)
593 tag-button-function tag-button-data))
594 )
595
596 (defun speedbar-change-expand-button-char (char)
597 "Change the expanson button character to CHAR for the current line."
598 (save-excursion
599 (beginning-of-line)
600 (if (re-search-forward ":\\s-*.\\([-+?]\\)" (save-excursion (end-of-line)
601 (point)) t)
602 (speedbar-with-writable
603 (goto-char (match-beginning 1))
604 (delete-char 1)
605 (insert-char char 1 t)))))
606
607
608 ;;;
609 ;;; Build button lists
610 ;;;
611 (defun speedbar-insert-files-at-point (files level)
612 "Insert list of FILES starting at point, and indenting all files to LEVEL
613 depth. Tag exapndable items with a +, otherwise a ?. Don't highlight ? as
614 we don't know how to manage them. The input parameter FILES is a cons
615 cell of the form ( 'dir-list . 'file-list )"
616 ;; Start inserting all the directories
617 (let ((dirs (car files)))
618 (while dirs
619 (speedbar-make-tag-line 'angle ?+ 'speedbar-dired (car dirs)
620 (car dirs) 'speedbar-dir-follow nil
621 'speedbar-directory-face level)
622 (setq dirs (cdr dirs))))
623 (let ((lst (car (cdr files))))
624 (while lst
625 (let* ((known (string-match speedbar-file-regexp (car lst)))
626 (expchar (if known ?+ ??))
627 (fn (if known 'speedbar-tag-file nil)))
628 (if (or speedbar-show-unknown-files (/= expchar ??))
629 (speedbar-make-tag-line 'bracket expchar fn (car lst)
630 (car lst) 'speedbar-find-file nil
631 'speedbar-file-face level)))
632 (setq lst (cdr lst)))))
633
634 (defun speedbar-default-directory-list (directory index)
635 "Inserts files for DIRECTORY with level INDEX at point"
636 (speedbar-insert-files-at-point
637 (speedbar-file-lists directory) index)
638 )
639
640 (defun speedbar-insert-generic-list (level lst expand-fun find-fun)
641 "At LEVEL, inserts a generic multi-level alist LIST. Associations with
642 lists get {+} tags (to expand into more nodes) and those with positions
643 just get a > as the indicator. {+} buttons will have the function
644 EXPAND-FUN and the token is the CDR list. The token name will have the
645 function FIND-FUN and not token."
646 ;; Remove imenu rescan button
647 (if (string= (car (car lst)) "*Rescan*")
648 (setq lst (cdr lst)))
649 ;; insert the parts
650 (while lst
651 (cond ((null (car-safe lst)) nil) ;this would be a separator
652 ((numberp (cdr-safe (car-safe lst)))
653 (speedbar-make-tag-line nil nil nil nil ;no expand button data
654 (car (car lst)) ;button name
655 find-fun ;function
656 (cdr (car lst)) ;token is position
657 'speedbar-tag-face
658 (1+ level)))
659 ((listp (cdr-safe (car-safe lst)))
660 (speedbar-make-tag-line 'curly ?+ expand-fun (cdr (car lst))
661 (car (car lst)) ;button name
662 nil nil 'speedbar-tag-face
663 (1+ level)))
664 (t (message "Ooops!")))
665 (setq lst (cdr lst))))
666
667 ;;;
668 ;;; Timed functions
669 ;;;
670 (defun speedbar-update-contents ()
671 "Update the contents of the speedbar buffer."
672 (interactive)
673 (setq speedbar-last-selected-file nil)
674 (setq speedbar-shown-directories (list (expand-file-name default-directory)))
675 (let ((cbd default-directory)
676 (funclst speedbar-initial-expansion-list))
677 (save-excursion
678 (set-buffer speedbar-buffer)
679 (speedbar-with-writable
680 (setq default-directory cbd)
681 (delete-region (point-min) (point-max))
682 (while funclst
683 (funcall (car funclst) cbd 0)
684 (setq funclst (cdr funclst)))))))
685
686 (defun speedbar-timer-fn ()
687 "Run whenever emacs is idle to update the speedbar item"
688 (if (not (and speedbar-frame
689 (frame-live-p speedbar-frame)
690 speedbar-attached-frame
691 (frame-live-p speedbar-attached-frame)))
692 (speedbar-set-timer nil)
693 (unwind-protect
694 (if (frame-visible-p speedbar-frame)
695 (let ((af (selected-frame)))
696 (save-window-excursion
697 (select-frame speedbar-attached-frame)
698 ;; make sure we at least choose a window to
699 ;; get a good directory from
700 (if (string-match "\\*Minibuf-[0-9]+\\*" (buffer-name))
701 (other-window 1))
702 ;; Update all the contents if directories change!
703 (if (or (member (expand-file-name default-directory)
704 speedbar-shown-directories)
705 (eq af speedbar-frame)
706 (not (buffer-file-name))
707 )
708 nil
709 (message "Updating speedbar to: %s..." default-directory)
710 (speedbar-update-contents)
711 (message "Updating speedbar to: %s...done" default-directory)))))
712 ;; Reset the timer
713 (speedbar-set-timer speedbar-update-speed)
714 ;; Ok, un-underline old file, underline current file
715 (speedbar-update-current-file)))
716 (run-hooks 'speedbar-timer-hook)
717 )
718
719 (defun speedbar-update-current-file ()
720 "Find out what the current file is, and update our visuals to indicate
721 what it is. This is specific to file names."
722 (let* ((lastf (selected-frame))
723 (newcf (save-excursion
724 (select-frame speedbar-attached-frame)
725 (let ((rf (if (buffer-file-name)
726 (file-name-nondirectory (buffer-file-name))
727 nil)))
728 (select-frame lastf)
729 rf)))
730 (lastb (current-buffer)))
731 (if (and newcf (not (string= newcf speedbar-last-selected-file)))
732 (progn
733 (select-frame speedbar-frame)
734 (set-buffer speedbar-buffer)
735 (speedbar-with-writable
736 (goto-char (point-min))
737 (if (and
738 speedbar-last-selected-file
739 (re-search-forward
740 (concat " \\(" (regexp-quote speedbar-last-selected-file) "\\)\n")
741 nil t))
742 (put-text-property (match-beginning 1)
743 (match-end 1)
744 'face
745 'speedbar-file-face))
746 (goto-char (point-min))
747 (if (re-search-forward
748 (concat " \\(" (regexp-quote newcf) "\\)\n") nil t)
749 (put-text-property (match-beginning 1)
750 (match-end 1)
751 'face
752 'speedbar-selected-face))
753 (setq speedbar-last-selected-file newcf))
754 (forward-line -1)
755 (speedbar-position-cursor-on-line)
756 (set-buffer lastb)
757 (select-frame lastf)))))
758
759 ;;;
760 ;;; Clicking Activity
761 ;;;
762 (defun speedbar-quick-mouse (e)
763 "Since mouse events are strange, this will keep the mouse nicely
764 positioned."
765 (interactive "e")
766 (mouse-set-point e)
767 (beginning-of-line)
768 (forward-char 3)
769 )
770
771 (defun speedbar-position-cursor-on-line ()
772 "Position the cursor on a line."
773 (beginning-of-line)
774 (re-search-forward "[]>}]" (save-excursion (end-of-line) (point)) t))
775
776 (defun speedbar-line-path (depth)
777 "Retrieve the pathname associated with the current line. This may
778 require traversing backwards and combinding the default directory with
779 these items."
780 (save-excursion
781 (let ((path nil))
782 (setq depth (1- depth))
783 (while (/= depth -1)
784 (if (not (re-search-backward (format "^%d:" depth) nil t))
785 (error "Error building path of tag")
786 (cond ((looking-at "[0-9]+:\\s-*<->\\s-+\\([^\n]+\\)$")
787 (setq path (concat (buffer-substring-no-properties
788 (match-beginning 1) (match-end 1))
789 "/"
790 path)))
791 ((looking-at "[0-9]+:\\s-*[-]\\s-+\\([^\n]+\\)$")
792 ;; This is the start of our path.
793 (setq path (buffer-substring-no-properties
794 (match-beginning 1) (match-end 1))))))
795 (setq depth (1- depth)))
796 (concat default-directory path))))
797
798 (defun speedbar-edit-line ()
799 "Edit whatever tag or file is on the current speedbar line."
800 (interactive)
801 (beginning-of-line)
802 (re-search-forward "[]>}] [a-zA-Z0-9]" (save-excursion (end-of-line) (point)))
803 (speedbar-do-function-pointer))
804
805 (defun speedbar-expand-line ()
806 "Expand the line under the cursor."
807 (interactive)
808 (beginning-of-line)
809 (re-search-forward ":\\s-*.\\+. " (save-excursion (end-of-line) (point)))
810 (forward-char -2)
811 (speedbar-do-function-pointer))
812
813 (defun speedbar-contract-line ()
814 "Expand the line under the cursor."
815 (interactive)
816 (beginning-of-line)
817 (re-search-forward ":\\s-*.-. " (save-excursion (end-of-line) (point)))
818 (forward-char -2)
819 (speedbar-do-function-pointer))
820
821 (defun speedbar-click (e)
822 "When the user clicks mouse 1 on our speedbar, we must decide what
823 we want to do! The entire speedbar has functions attached to
824 buttons. All we have to do is extract from the buffer the information
825 we need. See `speedbar-mode' for the type of behaviour we want to achieve"
826 (interactive "e")
827 (mouse-set-point e)
828 (speedbar-do-function-pointer))
829
830 (defun speedbar-do-function-pointer ()
831 "Look under the cursor and examine the text properties. From this extract
832 the file/tag name, token, indentation level and call a function if apropriate"
833 (let* ((fn (get-text-property (point) 'speedbar-function))
834 (tok (get-text-property (point) 'speedbar-token))
835 ;; The 1-,+ is safe because scaning starts AFTER the point
836 ;; specified. This lets the search include the character the
837 ;; cursor is on.
838 (tp (previous-single-property-change
839 (if (get-text-property (1+ (point)) 'speedbar-function)
840 (1+ (point)) (point)) 'speedbar-function))
841 (np (next-single-property-change
842 (if (and (> (point) 1) (get-text-property (1- (point)) 'speedbar-function))
843 (1- (point)) (point)) 'speedbar-function))
844 (txt (buffer-substring-no-properties (or tp (point-min))
845 (or np (point-max))))
846 (dent (save-excursion (beginning-of-line)
847 (string-to-number
848 (if (looking-at "[0-9]+")
849 (buffer-substring-no-properties
850 (match-beginning 0) (match-end 0))
851 "0")))))
852 ;;(message "%S:%S:%S:%s" fn tok txt dent)
853 (and fn (funcall fn txt tok dent)))
854 (speedbar-position-cursor-on-line))
855
856 (defun speedbar-find-file (text token indent)
857 "Speedbar click handler for filenames. Clicking the filename loads
858 that file into the attached buffer."
859 (let ((cdd (speedbar-line-path indent)))
860 (select-frame speedbar-attached-frame)
861 (find-file (concat cdd text))
862 (speedbar-update-current-file)
863 ;; Reset the timer with a new timeout when cliking a file
864 ;; in case the user was navigating directories, we can cancel
865 ;; that other timer.
866 (speedbar-set-timer speedbar-update-speed)))
867
868 (defun speedbar-dir-follow (text token indent)
869 "Speedbar click handler for directory names. Clicking a directory will
870 cause the speedbar to list files in the selected subdirectory."
871 (setq default-directory
872 (concat (expand-file-name (concat (speedbar-line-path indent) text))
873 "/"))
874 ;; Because we leave speedbar as the current buffer,
875 ;; update contents will change directory without
876 ;; having to touch the attached frame.
877 (speedbar-update-contents)
878 (speedbar-set-timer speedbar-navigating-speed)
879 (setq speedbar-last-selected-file nil)
880 (speedbar-update-current-file))
881
882
883 (defun speedbar-dired (text token indent)
884 "Speedbar click handler for filenames. Clicking the filename loads
885 that file into the attached buffer."
886 (cond ((string-match "+" text) ;we have to expand this file
887 (setq speedbar-shown-directories
888 (cons (expand-file-name
889 (concat (speedbar-line-path indent) token "/"))
890 speedbar-shown-directories))
891 (speedbar-change-expand-button-char ?-)
892 (save-excursion
893 (end-of-line) (forward-char 1)
894 (speedbar-with-writable
895 (speedbar-default-directory-list
896 (concat (speedbar-line-path indent) token "/")
897 (1+ indent)))))
898 ((string-match "-" text) ;we have to contract this node
899 (let ((oldl speedbar-shown-directories)
900 (newl nil)
901 (td (expand-file-name
902 (concat (speedbar-line-path indent) token))))
903 (while oldl
904 (if (not (string-match (concat "^" (regexp-quote td)) (car oldl)))
905 (setq newl (cons (car oldl) newl)))
906 (setq oldl (cdr oldl)))
907 (setq speedbar-shown-directories newl))
908 (speedbar-change-expand-button-char ?+)
909 (save-excursion
910 (end-of-line) (forward-char 1)
911 (speedbar-with-writable
912 (if (save-excursion (re-search-forward (format "^%d:" indent) nil t))
913 (delete-region (point) (match-beginning 0))
914 (delete-region (point) (point-max)))))
915 )
916 (t (error "Ooops... not sure what to do.")))
917 (speedbar-center-buffer-smartly)
918 (setq speedbar-last-selected-file nil)
919 (save-excursion (speedbar-update-current-file)))
920
921 (defun speedbar-directory-buttons-follow (text token ident)
922 "Speedbar click handler for default directory buttons."
923 (setq default-directory token)
924 ;; Because we leave speedbar as the current buffer,
925 ;; update contents will change directory without
926 ;; having to touch the attached frame.
927 (speedbar-update-contents)
928 (speedbar-set-timer speedbar-navigating-speed))
929
930 (defun speedbar-tag-file (text token indent)
931 "The cursor is on a selected line. Expand the tags in the specified
932 file. The parameter TXT and TOK are required, where TXT is the button
933 clicked, and TOK is the file to expand."
934 (cond ((string-match "+" text) ;we have to expand this file
935 (let* ((fn (expand-file-name (concat (speedbar-line-path indent)
936 token)))
937 (lst (if speedbar-use-imenu-package
938 (let ((tim (speedbar-fetch-dynamic-imenu fn)))
939 (if (eq tim t)
940 (speedbar-fetch-dynamic-etags fn)
941 tim))
942 (speedbar-fetch-dynamic-etags fn))))
943 ;; if no list, then remove expando button
944 (if (not lst)
945 (speedbar-change-expand-button-char ??)
946 (speedbar-change-expand-button-char ?-)
947 (speedbar-with-writable
948 (save-excursion
949 (end-of-line) (forward-char 1)
950 (speedbar-insert-generic-list indent
951 lst 'speedbar-tag-expand
952 'speedbar-tag-find))))))
953 ((string-match "-" text) ;we have to contract this node
954 (speedbar-change-expand-button-char ?+)
955 (speedbar-with-writable
956 (save-excursion
957 (end-of-line) (forward-char 1)
958 (if (save-excursion (re-search-forward (format "^%d:" indent) nil t))
959 (delete-region (point) (match-beginning 0))
960 (delete-region (point) (point-max))))))
961 (t (error "Ooops... not sure what to do.")))
962 (speedbar-center-buffer-smartly))
963
964 (defun speedbar-tag-find (text token indent)
965 "For the tag in a file, goto that position"
966 (let ((file (speedbar-line-path indent)))
967 (select-frame speedbar-attached-frame)
968 (find-file file)
969 (save-excursion (speedbar-update-current-file))
970 ;; Reset the timer with a new timeout when cliking a file
971 ;; in case the user was navigating directories, we can cancel
972 ;; that other timer.
973 (speedbar-set-timer speedbar-update-speed)
974 (goto-char token)))
975
976 (defun speedbar-tag-expand (text token indent)
977 "For the tag in a file which is really a list of tags of a certain type,
978 expand or contract that list."
979 (cond ((string-match "+" text) ;we have to expand this file
980 (speedbar-change-expand-button-char ?-)
981 (speedbar-with-writable
982 (save-excursion
983 (end-of-line) (forward-char 1)
984 (speedbar-insert-generic-list indent
985 token 'speedbar-tag-expand
986 'speedbar-tag-find))))
987 ((string-match "-" text) ;we have to contract this node
988 (speedbar-change-expand-button-char ?+)
989 (speedbar-with-writable
990 (save-excursion
991 (end-of-line) (forward-char 1)
992 (if (save-excursion (re-search-forward (format "^%d:" indent) nil t))
993 (delete-region (point) (match-beginning 0))))))
994 (t (error "Ooops... not sure what to do.")))
995 (speedbar-center-buffer-smartly))
996
997 ;;;
998 ;;; Centering Utility
999 ;;;
1000 (defun speedbar-center-buffer-smartly ()
1001 "Look at the buffer, and center it so that which the user is most
1002 interested in (as far as we can tell) is all visible. This assumes
1003 that the cursor is on a file, or tag of a file which the user is
1004 interested in."
1005 (if (<= (count-lines (point-min) (point-max))
1006 (window-height (selected-window)))
1007 ;; whole buffer fits
1008 (let ((cp (point)))
1009 (goto-char (point-min))
1010 (recenter 0)
1011 (goto-char cp))
1012 ;; too big
1013 (let (depth start end exp p)
1014 (save-excursion
1015 (beginning-of-line)
1016 (setq depth (if (looking-at "[0-9]+")
1017 (string-to-int (buffer-substring-no-properties
1018 (match-beginning 0) (match-end 0)))
1019 0))
1020 (setq exp (format "^%d:\\s-*[[{<]\\([?+-]\\)[]>}]" depth)))
1021 (save-excursion
1022 (end-of-line)
1023 (if (re-search-backward exp nil t)
1024 (setq start (point))
1025 (error "Center error"))
1026 (save-excursion ;Not sure about this part.
1027 (end-of-line)
1028 (setq p (point))
1029 (while (and (not (re-search-forward exp nil t))
1030 (>= depth 0))
1031 (setq depth (1- depth))
1032 (setq exp (format "^%d:\\s-*[[{<]\\([?+-]\\)[]>}]" depth)))
1033 (if (/= (point) p)
1034 (setq end (point))
1035 (setq end (point-max)))))
1036 ;; Now work out the details of centering
1037 (let ((nl (count-lines start end))
1038 (cp (point)))
1039 (if (> nl (window-height (selected-window)))
1040 ;; We can't fit it all, so just center on cursor
1041 (progn (goto-char start)
1042 (recenter 1))
1043 ;; we can fit everything on the screen, but...
1044 (if (and (pos-visible-in-window-p start (selected-window))
1045 (pos-visible-in-window-p end (selected-window)))
1046 ;; we are all set!
1047 nil
1048 ;; we need to do something...
1049 (goto-char start)
1050 (let ((newcent (/ (- (window-height (selected-window)) nl) 2))
1051 (lte (count-lines start (point-max))))
1052 (if (and (< (+ newcent lte) (window-height (selected-window)))
1053 (> (- (window-height (selected-window)) lte 1)
1054 newcent))
1055 (setq newcent (- (window-height (selected-window))
1056 lte 1)))
1057 (recenter newcent))))
1058 (goto-char cp)))))
1059
1060
1061 ;;;
1062 ;;; Tag Management -- Imenu
1063 ;;;
1064 (defun speedbar-fetch-dynamic-imenu (file)
1065 "Use the imenu package to load in file, and extract all the items
1066 tags we wish to display in the speedbar package."
1067 ;; (eval-when-compile (require 'imenu))
1068 (save-excursion
1069 (set-buffer (find-file-noselect file))
1070 (condition-case nil
1071 (imenu--make-index-alist t)
1072 (error t))))
1073
1074
1075 ;;;
1076 ;;; Tag Management -- etags (Not useful for FSF emacs)
1077 ;;;
1078 (defvar speedbar-fetch-etags-parse-list
1079 '(("\\.\\([cChH]\\|c++\\|cpp\\|cc\\)$" . speedbar-parse-c-or-c++tag)
1080 ("\\.el\\|\\.emacs" .
1081 "defun\\s-+\\(\\(\\w\\|[-_]\\)+\\)\\s-*\C-?")
1082 ("\\.tex$" . speedbar-parse-tex-string)
1083 ("\\.p" .
1084 "\\(\\(FUNCTION\\|function\\|PROCEDURE\\|procedure\\)\\s-+\\([a-zA-Z0-9_.:]+\\)\\)\\s-*(?^?")
1085
1086 )
1087 "*Alist matching extension vs an expression which will extract the
1088 symbol name we wish to display as match 1. To add a new file type, you
1089 would want to add a new association to the list, where the car
1090 is the file match, and the cdr is the way to extract an element from
1091 the tags output. If the output is complex, use a function symbol
1092 instead of regexp. The function should expect to be at the beginning
1093 of a line in the etags buffer.
1094
1095 This variable is ignored if `speedbar-use-imenu-package' is `t'")
1096
1097 (defvar speedbar-fetch-etags-command "etags"
1098 "*Command used to create an etags file.
1099
1100 This variable is ignored if `speedbar-use-imenu-package' is `t'")
1101
1102 (defvar speedbar-fetch-etags-arguments '("-D" "-I" "-o" "-")
1103 "*List of arguments to use with `speedbar-fetch-etags-command' to create
1104 an etags output buffer.
1105
1106 This variable is ignored if `speedbar-use-imenu-package' is `t'")
1107
1108 (defun speedbar-fetch-dynamic-etags (file)
1109 "For the complete file definition FILE, run etags as a subprocess,
1110 fetch it's output, and create a list of symbols extracted, and their
1111 position in FILE."
1112 (let ((newlist nil))
1113 (unwind-protect
1114 (save-excursion
1115 (if (get-buffer "*etags tmp*")
1116 (kill-buffer "*etags tmp*")) ;kill to clean it up
1117 (set-buffer (get-buffer-create "*etags tmp*"))
1118 (apply 'call-process speedbar-fetch-etags-command nil
1119 (current-buffer) nil
1120 (append speedbar-fetch-etags-arguments (list file)))
1121 (goto-char (point-min))
1122 (let ((expr
1123 (let ((exprlst speedbar-fetch-etags-parse-list)
1124 (ans nil))
1125 (while (and (not ans) exprlst)
1126 (if (string-match (car (car exprlst)) file)
1127 (setq ans (car exprlst)))
1128 (setq exprlst (cdr exprlst)))
1129 (cdr ans))))
1130 (if expr
1131 (let (tnl)
1132 (while (not (save-excursion (end-of-line) (eobp)))
1133 (save-excursion
1134 (setq tnl (speedbar-extract-one-symbol expr)))
1135 (if tnl (setq newlist (cons tnl newlist)))
1136 (forward-line 1)))
1137 (message "Sorry, no support for a file of that extension"))))
1138 )
1139 (reverse newlist)))
1140
1141 (defun speedbar-extract-one-symbol (expr)
1142 "At point in current buffer, return nil, or one alist of the form
1143 of a dotted pair: ( symbol . position ) from etags output. Parse the
1144 output using the regular expression EXPR"
1145 (let* ((sym (if (stringp expr)
1146 (if (save-excursion
1147 (re-search-forward expr (save-excursion
1148 (end-of-line)
1149 (point)) t))
1150 (buffer-substring-no-properties (match-beginning 1)
1151 (match-end 1)))
1152 (funcall expr)))
1153 (pos (let ((j (re-search-forward "[\C-?\C-a]\\([0-9]+\\),\\([0-9]+\\)"
1154 (save-excursion
1155 (end-of-line)
1156 (point))
1157 t)))
1158 (if (and j sym)
1159 (1+ (string-to-int (buffer-substring-no-properties
1160 (match-beginning 2)
1161 (match-end 2))))
1162 0))))
1163 (if (/= pos 0)
1164 (cons sym pos)
1165 nil)))
1166
1167 (defun speedbar-parse-c-or-c++tag ()
1168 "Parse a c or c++ tag, which tends to be a little complex."
1169 (save-excursion
1170 (let ((bound (save-excursion (end-of-line) (point))))
1171 (cond ((re-search-forward "\C-?\\([^\C-a]+\\)\C-a" bound t)
1172 (buffer-substring-no-properties (match-beginning 1)
1173 (match-end 1)))
1174 ((re-search-forward "\\<\\([^ \t]+\\)\\s-+new(" bound t)
1175 (buffer-substring-no-properties (match-beginning 1)
1176 (match-end 1)))
1177 ((re-search-forward "\\<\\([^ \t(]+\\)\\s-*(\C-?" bound t)
1178 (buffer-substring-no-properties (match-beginning 1)
1179 (match-end 1)))
1180 (t nil))
1181 )))
1182
1183 (defun speedbar-parse-tex-string ()
1184 "Parse a tex string. Only find data which is relevant"
1185 (save-excursion
1186 (let ((bound (save-excursion (end-of-line) (point))))
1187 (cond ((re-search-forward "\\(section\\|chapter\\|cite\\)\\s-*{[^\C-?}]*}?" bound t)
1188 (buffer-substring-no-properties (match-beginning 0)
1189 (match-end 0)))
1190 (t nil)))))
1191
1192
1193 ;;;
1194 ;;; configuration scripts (optional)
1195 ;;;
1196 (defun speedbar-configure-options ()
1197 "Configure variable options for the speedbar program using dlg-config"
1198 (interactive)
1199 (require 'dlg-config)
1200 (save-excursion
1201 (select-frame speedbar-attached-frame)
1202 (dlg-init)
1203 (let ((oframe (create-widget "Speedbar Options" widget-frame
1204 widget-toplevel-shell
1205 :x 2 :y -3
1206 :frame-label "Speedbar Options"))
1207 )
1208 (create-widget "show-unknown" widget-toggle-button oframe
1209 :x 1 :y 1 :label-value "Show files that are not supported by imenu"
1210 :state (data-object-symbol "speedbar-show-unknown-files"
1211 :value speedbar-show-unknown-files
1212 :symbol 'speedbar-show-unknown-files))
1213
1214 (create-widget "raiselower" widget-toggle-button oframe
1215 :x 1 :y -1 :label-value "Use frame auto raise/lower property"
1216 :state (data-object-symbol "speedbar-raise-lower"
1217 :value speedbar-raise-lower
1218 :symbol 'speedbar-raise-lower))
1219
1220 (create-widget "update-speed" widget-label oframe
1221 :x 1 :y -2 :label-value "Update Delay :")
1222 (create-widget "update-speed-txt" widget-text-field oframe
1223 :width 5 :height 1 :x -2 :y t
1224 :value (data-object-symbol-string-to-int
1225 "update-speed"
1226 :symbol 'speedbar-update-speed
1227 :value (int-to-string speedbar-update-speed)))
1228 (create-widget "update-speed-unit" widget-label oframe
1229 :x -3 :y t :label-value "Seconds")
1230
1231 (create-widget "navigating-speed" widget-label oframe
1232 :x 1 :y -1 :label-value "Navigating Delay:")
1233 (create-widget "navigating-speed-txt" widget-text-field oframe
1234 :width 5 :height 1 :x -2 :y t
1235 :value (data-object-symbol-string-to-int
1236 "navigating-speed"
1237 :symbol 'speedbar-navigating-speed
1238 :value (int-to-string speedbar-navigating-speed)))
1239 (create-widget "navigating-speed-unit" widget-label oframe
1240 :x -3 :y t :label-value "Seconds")
1241
1242 (create-widget "width" widget-label oframe
1243 :x 1 :y -2 :label-value "Display Width :")
1244 (create-widget "width-txt" widget-text-field oframe
1245 :width 5 :height 1 :x -2 :y t
1246 :value (data-object-symbol-string-to-int
1247 "width"
1248 :symbol 'speedbar-width
1249 :value (int-to-string speedbar-width)))
1250 (create-widget "width-unit" widget-label oframe
1251 :x -3 :y t :label-value "Characters")
1252
1253 (create-widget "scrollbar-width" widget-label oframe
1254 :x 1 :y -1 :label-value "Scrollbar Width :")
1255 (create-widget "scrollbar-width-txt" widget-text-field oframe
1256 :width 5 :height 1 :x -2 :y t
1257 :value (data-object-symbol-string-to-int
1258 "width"
1259 :symbol 'speedbar-width
1260 :value (int-to-string speedbar-scrollbar-width)))
1261 (create-widget "scrollbar-width-unit" widget-label oframe
1262 :x -3 :y t :label-value "Pixels")
1263
1264
1265 )
1266 (dlg-end)
1267 (dialog-refresh)
1268 ))
1269
1270 (defun speedbar-configure-faces ()
1271 "Configure faces for the speedbar program using dlg-config."
1272 (interactive)
1273 (require 'dlg-config)
1274 (save-excursion
1275 (select-frame speedbar-attached-frame)
1276 (dlg-faces '(speedbar-button-face
1277 speedbar-file-face
1278 speedbar-directory-face
1279 speedbar-tag-face
1280 speedbar-highlight-face
1281 speedbar-selected-face))))
1282
1283 ;;;
1284 ;;; Color loading section This is message *Blech!*
1285 ;;;
1286 (defun speedbar-load-color (sym l-fg l-bg d-fg d-bg &optional bold italic underline)
1287 "Create a color for SYM with a L-FG and L-BG color, or D-FG and
1288 D-BG. Optionally make BOLD, ITALIC, or UNDERLINED if applicable. If
1289 the background attribute of the current frame is determined to be
1290 light (white, for example) then L-FG and L-BG is used. If not, then
1291 D-FG and D-BG is used. This will allocate the colors in the best
1292 possible mannor. This will allow me to store multiple defaults and
1293 dynamically determine which colors to use."
1294 (let* ((params (frame-parameters))
1295 (disp-res (if (fboundp 'x-get-resource)
1296 (if speedbar-xemacsp
1297 (x-get-resource ".displayType" "DisplayType" 'string)
1298 (x-get-resource ".displayType" "DisplayType"))
1299 nil))
1300 (display-type
1301 (cond (disp-res (intern (downcase disp-res)))
1302 ((and (fboundp 'x-display-color-p) (x-display-color-p)) 'color)
1303 (t 'mono)))
1304 (bg-res (if (fboundp 'x-get-resource)
1305 (if speedbar-xemacsp
1306 (x-get-resource ".backgroundMode" "BackgroundMode" 'string)
1307 (x-get-resource ".backgroundMode" "BackgroundMode"))
1308 nil))
1309 (bgmode
1310 (cond (bg-res (intern (downcase bg-res)))
1311 ((and params
1312 (fboundp 'x-color-values)
1313 (< (apply '+ (x-color-values
1314 (cdr (assq 'background-color params))))
1315 (/ (apply '+ (x-color-values "white")) 3)))
1316 'dark)
1317 (t 'light))) ;our default
1318 (set-p (function (lambda (face-name resource)
1319 (if speedbar-xemacsp
1320 (x-get-resource
1321 (concat face-name ".attribute" resource)
1322 (concat "Face.Attribute" resource)
1323 'string)
1324 (x-get-resource
1325 (concat face-name ".attribute" resource)
1326 (concat "Face.Attribute" resource)))
1327 )))
1328 (nbg (cond ((eq bgmode 'dark) d-bg)
1329 (t l-bg)))
1330 (nfg (cond ((eq bgmode 'dark) d-fg)
1331 (t l-fg))))
1332
1333 (if (not (eq display-type 'color))
1334 ;; we need a face of some sort, so just make due with default
1335 (progn
1336 (copy-face 'default sym)
1337 (if bold (condition-case nil
1338 (make-face-bold sym)
1339 (error (message "Cannot make face %s bold!"
1340 (symbol-name sym)))))
1341 (if italic (condition-case nil
1342 (make-face-italic sym)
1343 (error (message "Cannot make face %s italic!"
1344 (symbol-name sym)))))
1345 (set-face-underline-p sym underline)
1346 )
1347 ;; make a colorized version of a face. Be sure to check Xdefaults
1348 ;; for possible overrides first!
1349 (let ((newface (make-face sym)))
1350 ;; For each attribute, check if it might already be set by Xdefaults
1351 (if (and nfg (not (funcall set-p (symbol-name sym) "Foreground")))
1352 (set-face-foreground sym nfg))
1353 (if (and nbg (not (funcall set-p (symbol-name sym) "Background")))
1354 (set-face-background sym nbg))
1355
1356 (if bold (condition-case nil
1357 (make-face-bold sym)
1358 (error (message "Cannot make face %s bold!"
1359 (symbol-name sym)))))
1360 (if italic (condition-case nil
1361 (make-face-italic sym)
1362 (error (message "Cannot make face %s italic!"
1363 (symbol-name sym)))))
1364 (set-face-underline-p sym underline)
1365 ))))
1366
1367 ;; JTL <<<<
1368 (if nil ;;(x-display-color-p) ;; just a quick hack so it will run.
1369 ;; we can use customize for this.
1370 ;; <<<< JTL
1371 (progn
1372 (speedbar-load-color 'speedbar-button-face "green4" "default" "green3" "default")
1373 (speedbar-load-color 'speedbar-file-face "cyan4" nil "cyan" nil nil nil nil)
1374 (speedbar-load-color 'speedbar-directory-face "blue4" nil "light blue" nil nil nil nil)
1375 (speedbar-load-color 'speedbar-tag-face "brown" nil "yellow" nil nil nil nil)
1376 (speedbar-load-color 'speedbar-selected-face "red" nil "red" nil nil nil t)
1377 (speedbar-load-color 'speedbar-highlight-face nil "green" nil "sea green" nil nil nil)
1378 ) ; color
1379 (make-face 'speedbar-button-face)
1380 ;;(make-face 'speedbar-file-face)
1381 (copy-face 'bold 'speedbar-file-face)
1382 (make-face 'speedbar-directory-face)
1383 (make-face 'speedbar-tag-face)
1384 ;;(make-face 'speedbar-selected-face)
1385 (copy-face 'underline 'speedbar-selected-face)
1386 ;;(make-face 'speedbar-highlight-face)
1387 (copy-face 'highlight 'speedbar-highlight-face)
1388
1389 ) ;; monochrome
1390
1391 ;;; end of lisp
1392 (provide 'speedbar)
1393
1394 ;;; speedbar.el ends here