Mercurial > hg > xemacs-beta
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 |