Commit | Line | Data |
---|---|---|
6dd12ef2 CY |
1 | ;;; eieio-speedbar.el -- Classes for managing speedbar displays. |
2 | ||
114f9c96 | 3 | ;; Copyright (C) 1999, 2000, 2001, 2002, 2005, 2007, 2008, 2009, 2010 |
cb758101 | 4 | ;; Free Software Foundation, Inc. |
6dd12ef2 | 5 | |
9ffe3f52 | 6 | ;; Author: Eric M. Ludlam <zappo@gnu.org> |
6dd12ef2 CY |
7 | ;; Version: 0.2 |
8 | ;; Keywords: OO, tools | |
bd78fa1d | 9 | ;; Package: eieio |
6dd12ef2 CY |
10 | |
11 | ;; This file is part of GNU Emacs. | |
12 | ||
13 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
14 | ;; it under the terms of the GNU General Public License as published by | |
15 | ;; the Free Software Foundation, either version 3 of the License, or | |
16 | ;; (at your option) any later version. | |
17 | ||
18 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | ;; GNU General Public License for more details. | |
22 | ||
23 | ;; You should have received a copy of the GNU General Public License | |
24 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
25 | ||
26 | ;;; Commentary: | |
27 | ;; | |
28 | ;; This provides some classes that can be used as a parent which | |
29 | ;; will automatically provide SPEEDBAR support for any list of objects | |
30 | ;; of that type. | |
31 | ;; | |
32 | ;; This file requires speedbar version 0.10 or later. | |
33 | ||
34 | ;;; Creating a new speedbar mode based on a pre-existing object hierarchy | |
35 | ;; | |
36 | ;; To create a new speedbar mode based on lists of objects is easier | |
37 | ;; than creating a whole new speedbar mode from scratch. | |
38 | ;; | |
39 | ;; 1) Objects that will have lists of items that can be expanded | |
40 | ;; should also inherit from the classes: | |
41 | ;; * `eieio-speedbar' - specify your own button behavior | |
42 | ;; * `eieio-speedbar-directory-button' - objects that behave like directories | |
43 | ;; * `eieio-speedbar-file-button' - objects that behave like files | |
44 | ;; | |
45 | ;; 2) Objects that have lists of children should implement the method | |
46 | ;; `eieio-speedbar-object-children' which returns a list of more | |
47 | ;; objects, or a list of strings. | |
48 | ;; | |
49 | ;; 3) Objects that return a list of strings should also implement these | |
50 | ;; methods: | |
51 | ;; * `eieio-speedbar-child-make-tag-lines' - make tag lines for a child. | |
52 | ;; * `eieio-speedbar-child-description' - describe non-object children | |
53 | ;; | |
54 | ;; 4) Objects which have expanded information should implement the method | |
55 | ;; `eieio-speedbar-description' to produce more information. | |
56 | ;; | |
57 | ;; 5) Objects that are associated with a directory should implement | |
58 | ;; the method `eieio-speedbar-derive-line-path' which returns a | |
59 | ;; path. | |
60 | ;; | |
61 | ;; 6) Objects that have a specialized behavior when clicked should | |
62 | ;; define the method `eieio-speedbar-handle-click'. | |
63 | ;; | |
64 | ;; To initialize a new eieio based speedbar display, do the following. | |
65 | ;; | |
66 | ;; 1) Create a keymap variable `foo-speedbar-key-map'. | |
67 | ;; This keymap variable should be initialized in a function. | |
68 | ;; If you have no special needs, use `eieio-speedbar-key-map' | |
69 | ;; | |
70 | ;; 2) Create a variable containing an easymenu definition compatible | |
71 | ;; with speedbar. if you have no special needs, use | |
72 | ;; `eieio-speedbar-menu'. | |
73 | ;; | |
74 | ;; 3) Create a function which returns the top-level list of children | |
75 | ;; objects to be displayed in speedbar. | |
76 | ;; | |
77 | ;; 4) Call `eieio-speedbar-create' as specified in it's documentation | |
78 | ;; string. This will automatically handle cases when speedbar is | |
79 | ;; not already loaded, and specifying all overload functions. | |
80 | ;; | |
9ffe3f52 | 81 | ;; 5) Create an initializer function which looks like this: |
6dd12ef2 | 82 | ;; |
9ffe3f52 | 83 | ;; (defun my-speedbar-mode-initialize () |
6dd12ef2 CY |
84 | ;; "documentation" |
85 | ;; (interactive) | |
86 | ;; (speedbar-frame-mode 1) | |
87 | ;; (speedbar-change-initial-expansion-list mymodename) | |
88 | ;; (speedbar-get-focus)) | |
89 | ;; | |
90 | ;; where `mymodename' is the same value as passed to `eieio-speedbar-create' | |
91 | ;; as the MODENAME parameter. | |
92 | ||
93 | ;; @todo - Can we make this ECB friendly? | |
94 | ||
95 | ;;; Code: | |
96 | (require 'eieio) | |
97 | (require 'eieio-custom) | |
98 | (require 'speedbar) | |
99 | ||
100 | ;;; Support a way of adding generic object based modes into speedbar. | |
101 | ;; | |
102 | (defun eieio-speedbar-make-map () | |
103 | "Make the generic object based speedbar keymap." | |
104 | (let ((map (speedbar-make-specialized-keymap))) | |
105 | ||
106 | ;; General viewing things | |
107 | (define-key map "\C-m" 'speedbar-edit-line) | |
108 | (define-key map "+" 'speedbar-expand-line) | |
109 | (define-key map "=" 'speedbar-expand-line) | |
110 | (define-key map "-" 'speedbar-contract-line) | |
111 | ||
112 | ;; Some object based things | |
113 | (define-key map "C" 'eieio-speedbar-customize-line) | |
114 | map)) | |
115 | ||
116 | (defvar eieio-speedbar-key-map (eieio-speedbar-make-map) | |
a8f316ca | 117 | "A generic object based speedbar display keymap.") |
6dd12ef2 CY |
118 | |
119 | (defvar eieio-speedbar-menu | |
120 | '([ "Edit Object/Field" speedbar-edit-line t] | |
121 | [ "Expand Object" speedbar-expand-line | |
122 | (save-excursion (beginning-of-line) | |
123 | (looking-at "[0-9]+: *.\\+. "))] | |
124 | [ "Contract Object" speedbar-contract-line | |
125 | (save-excursion (beginning-of-line) | |
126 | (looking-at "[0-9]+: *.-. "))] | |
127 | "---" | |
128 | [ "Customize Object" eieio-speedbar-customize-line | |
129 | (eieio-object-p (speedbar-line-token)) ] | |
130 | ) | |
131 | "Menu part in easymenu format used in speedbar while browsing objects.") | |
132 | ||
133 | ;; Note to self: Fix this silly thing! | |
134 | (defalias 'eieio-speedbar-customize-line 'speedbar-edit-line) | |
135 | ||
136 | (defun eieio-speedbar-create (map-fn map-var menu-var modename fetcher) | |
137 | "Create a speedbar mode for displaying an object hierarchy. | |
138 | MAP-FN is the keymap generator function used for extra keys. | |
139 | MAP-VAR is the keymap variable used. | |
9ffe3f52 GM |
140 | MENU-VAR is the symbol containing an easymenu compatible menu part to use. |
141 | MODENAME is a string used to identify this browser mode. | |
6dd12ef2 CY |
142 | FETCHER is a generic function used to fetch the base object list used when |
143 | creating the speedbar display." | |
144 | (if (not (featurep 'speedbar)) | |
145 | (add-hook 'speedbar-load-hook | |
146 | (list 'lambda nil | |
147 | (list 'eieio-speedbar-create-engine | |
148 | map-fn map-var menu-var modename fetcher))) | |
149 | (eieio-speedbar-create-engine map-fn map-var menu-var modename fetcher))) | |
150 | ||
151 | (defun eieio-speedbar-create-engine (map-fn map-var menu-var modename fetcher) | |
152 | "Create a speedbar mode for displaying an object hierarchy. | |
153 | Called from `eieio-speedbar-create', or the speedbar load-hook. | |
a8f316ca | 154 | MAP-FN, MAP-VAR, MENU-VAR, MODENAME, and FETCHER are the same as in |
6dd12ef2 CY |
155 | `eieio-speedbar-create'." |
156 | ;; make sure the keymap exists | |
157 | (funcall map-fn) | |
158 | ;; Add to the expansion list. | |
159 | (speedbar-add-expansion-list | |
160 | (list modename | |
161 | menu-var | |
162 | map-var | |
163 | (list 'lambda '(dir depth) | |
164 | (list 'eieio-speedbar-buttons 'dir 'depth | |
165 | (list 'quote fetcher))))) | |
166 | ;; Set the special functions. | |
167 | (speedbar-add-mode-functions-list | |
168 | (list modename | |
169 | '(speedbar-item-info . eieio-speedbar-item-info) | |
170 | '(speedbar-line-directory . eieio-speedbar-line-path)))) | |
171 | ||
172 | (defun eieio-speedbar-buttons (dir-or-object depth fetcher) | |
173 | "Create buttons for the speedbar display. | |
174 | Start in directory DIR-OR-OBJECT. If it is an object, just display that | |
a8f316ca | 175 | object's subelements. |
6dd12ef2 CY |
176 | Argument DEPTH specifies how far down we have already been displayed. |
177 | If it is a directory, use FETCHER to fetch all objects associated with | |
178 | that path." | |
179 | (let ((objlst (cond ((eieio-object-p dir-or-object) | |
180 | (list dir-or-object)) | |
181 | ((stringp dir-or-object) | |
182 | (funcall fetcher dir-or-object)) | |
183 | (t dir-or-object)))) | |
184 | (if (not objlst) | |
185 | (speedbar-make-tag-line nil nil nil nil "Empty display" nil nil nil | |
186 | depth) | |
187 | ;; Dump all objects into speedbar | |
188 | (while objlst | |
189 | (eieio-speedbar-make-tag-line (car objlst) depth) | |
190 | (setq objlst (cdr objlst)))))) | |
191 | ||
192 | \f | |
193 | ;;; DEFAULT SUPERCLASS baseline methods | |
194 | ;; | |
195 | ;; First, define methods onto the superclass so all classes | |
196 | ;; will have some minor support. | |
197 | ||
198 | (defmethod eieio-speedbar-description ((object eieio-default-superclass)) | |
199 | "Return a string describing OBJECT." | |
200 | (object-name-string object)) | |
201 | ||
202 | (defmethod eieio-speedbar-derive-line-path ((object eieio-default-superclass)) | |
203 | "Return the path which OBJECT has something to do with." | |
204 | nil) | |
205 | ||
206 | (defmethod eieio-speedbar-object-buttonname ((object eieio-default-superclass)) | |
207 | "Return a string to use as a speedbar button for OBJECT." | |
208 | (object-name-string object)) | |
209 | ||
210 | (defmethod eieio-speedbar-make-tag-line ((object eieio-default-superclass) | |
211 | depth) | |
212 | "Insert a tag line into speedbar at point for OBJECT. | |
213 | By default, all objects appear as simple TAGS with no need to inherit from | |
214 | the special `eieio-speedbar' classes. Child classes should redefine this | |
215 | method to create more accurate tag lines. | |
216 | Argument DEPTH is the depth at which the tag line is inserted." | |
217 | (speedbar-make-tag-line nil nil nil nil | |
218 | (eieio-speedbar-object-buttonname object) | |
219 | 'eieio-speedbar-object-click | |
220 | object | |
221 | 'speedbar-tag-face | |
222 | depth)) | |
223 | ||
224 | (defmethod eieio-speedbar-handle-click ((object eieio-default-superclass)) | |
225 | "Handle a click action on OBJECT in speedbar. | |
226 | Any object can be represented as a tag in SPEEDBAR without special | |
227 | attributes. These default objects will be pulled up in a custom | |
228 | object edit buffer doing an in-place edit. | |
229 | ||
230 | If your object represents some other item, override this method | |
db9e401b | 231 | and take the appropriate action." |
6dd12ef2 CY |
232 | (require 'eieio-custom) |
233 | (speedbar-with-attached-buffer | |
234 | (eieio-customize-object object)) | |
235 | (speedbar-maybee-jump-to-attached-frame)) | |
236 | ||
237 | \f | |
238 | ;;; Class definitions | |
239 | ;; | |
240 | ;; Now define a special speedbar class with some | |
241 | ;; variables with :allocation class which can be attached into | |
242 | ;; object hierarchies. | |
243 | ;; | |
244 | ;; These more complex types are for objects which wish to display | |
245 | ;; lists of children buttons. | |
246 | ||
247 | (defclass eieio-speedbar nil | |
248 | ((buttontype :initform nil | |
249 | :type symbol | |
250 | :documentation | |
251 | "The type of expansion button used for objects of this class. | |
252 | Possible values are those symbols supported by the `exp-button-type' argument | |
253 | to `speedbar-make-tag-line'." | |
254 | :allocation :class) | |
255 | (buttonface :initform speedbar-tag-face | |
256 | :type (or symbol face) | |
257 | :documentation | |
258 | "The face used on the textual part of the button for this class. | |
259 | See `speedbar-make-tag-line' for details." | |
260 | :allocation :class) | |
261 | (expanded :initform nil | |
262 | :type boolean | |
263 | :documentation | |
264 | "State of an object being expanded in speedbar.") | |
265 | ) | |
266 | "Class which provides basic speedbar support for child classes. | |
a8f316ca | 267 | Add one of the child classes to this class to the parent list of a class." |
6dd12ef2 CY |
268 | :method-invocation-order :depth-first |
269 | :abstract t) | |
270 | ||
271 | (defclass eieio-speedbar-directory-button (eieio-speedbar) | |
272 | ((buttontype :initform angle) | |
273 | (buttonface :initform speedbar-directory-face)) | |
274 | "Class providing support for objects which behave like a directory." | |
275 | :method-invocation-order :depth-first | |
276 | :abstract t) | |
277 | ||
278 | (defclass eieio-speedbar-file-button (eieio-speedbar) | |
279 | ((buttontype :initform bracket) | |
280 | (buttonface :initform speedbar-file-face)) | |
a8f316ca | 281 | "Class providing support for objects which behave like a file." |
6dd12ef2 CY |
282 | :method-invocation-order :depth-first |
283 | :abstract t) | |
284 | ||
285 | \f | |
286 | ;;; Methods to eieio-speedbar-* which do not need to be overriden | |
287 | ;; | |
288 | (defmethod eieio-speedbar-make-tag-line ((object eieio-speedbar) | |
289 | depth) | |
290 | "Insert a tag line into speedbar at point for OBJECT. | |
a8f316ca JB |
291 | All objects a child of symbol `eieio-speedbar' can be created from |
292 | this method. Override this if you need non-traditional tag lines. | |
6dd12ef2 CY |
293 | Argument DEPTH is the depth at which the tag line is inserted." |
294 | (let ((children (eieio-speedbar-object-children object)) | |
295 | (exp (oref object expanded))) | |
296 | (if (not children) | |
297 | (if (eq (oref object buttontype) 'expandtag) | |
298 | (speedbar-make-tag-line 'statictag | |
299 | ? nil nil | |
300 | (eieio-speedbar-object-buttonname object) | |
301 | 'eieio-speedbar-object-click | |
302 | object | |
303 | (oref object buttonface) | |
304 | depth) | |
305 | (speedbar-make-tag-line (oref object buttontype) | |
306 | ? nil nil | |
307 | (eieio-speedbar-object-buttonname object) | |
308 | 'eieio-speedbar-object-click | |
309 | object | |
310 | (oref object buttonface) | |
311 | depth)) | |
312 | (speedbar-make-tag-line (oref object buttontype) | |
313 | (if exp ?- ?+) | |
314 | 'eieio-speedbar-object-expand | |
315 | object | |
316 | (eieio-speedbar-object-buttonname object) | |
317 | 'eieio-speedbar-object-click | |
318 | object | |
319 | (oref object buttonface) | |
320 | depth) | |
321 | (if exp | |
322 | (eieio-speedbar-expand object (1+ depth)))))) | |
323 | ||
324 | (defmethod eieio-speedbar-child-make-tag-lines ((object eieio-speedbar) depth) | |
325 | "Base method for creating tag lines for non-object children." | |
326 | (error "You must implement `eieio-speedbar-child-make-tag-lines' for %s" | |
327 | (object-name object))) | |
328 | ||
329 | (defmethod eieio-speedbar-expand ((object eieio-speedbar) depth) | |
330 | "Expand OBJECT at indentation DEPTH. | |
db9e401b | 331 | Inserts a list of new tag lines representing expanded elements within |
6dd12ef2 CY |
332 | OBJECT." |
333 | (let ((children (eieio-speedbar-object-children object))) | |
334 | (cond ((eieio-object-p (car children)) | |
335 | (mapcar (lambda (car) | |
336 | (eieio-speedbar-make-tag-line car depth)) | |
337 | children)) | |
338 | (children (eieio-speedbar-child-make-tag-lines object depth))))) | |
339 | ||
340 | \f | |
341 | ;;; Speedbar specific function callbacks. | |
342 | ;; | |
343 | (defun eieio-speedbar-object-click (text token indent) | |
344 | "Handle a user click on TEXT representing object TOKEN. | |
345 | The object is at indentation level INDENT." | |
346 | (eieio-speedbar-handle-click token)) | |
347 | ||
348 | (defun eieio-speedbar-object-expand (text token indent) | |
a8f316ca JB |
349 | "Expand object represented by TEXT. |
350 | TOKEN is the object. INDENT is the current indentation level." | |
6dd12ef2 CY |
351 | (cond ((string-match "+" text) ;we have to expand this file |
352 | (speedbar-change-expand-button-char ?-) | |
353 | (oset token expanded t) | |
354 | (speedbar-with-writable | |
355 | (save-excursion | |
356 | (end-of-line) (forward-char 1) | |
357 | (eieio-speedbar-expand token (1+ indent))))) | |
358 | ((string-match "-" text) ;we have to contract this node | |
359 | (speedbar-change-expand-button-char ?+) | |
360 | (oset token expanded nil) | |
361 | (speedbar-delete-subblock indent)) | |
362 | (t (error "Ooops... not sure what to do"))) | |
363 | (speedbar-center-buffer-smartly)) | |
364 | ||
365 | (defmethod eieio-speedbar-child-description ((obj eieio-speedbar)) | |
366 | "Return a description for a child of OBJ which is not an object." | |
367 | (error "You must implement `eieio-speedbar-child-description' for %s" | |
368 | (object-name obj))) | |
369 | ||
370 | (defun eieio-speedbar-item-info () | |
371 | "Display info for the current line when in EDE display mode." | |
372 | ;; Switch across the types of the tokens. | |
373 | (let ((tok (speedbar-line-token))) | |
374 | (cond ((eieio-object-p tok) | |
375 | (message (eieio-speedbar-description tok))) | |
376 | (t | |
377 | (let ((no (eieio-speedbar-find-nearest-object))) | |
378 | (if no | |
379 | (eieio-speedbar-child-description no))))))) | |
380 | ||
381 | (defun eieio-speedbar-find-nearest-object (&optional depth) | |
382 | "Search backwards to the first line associated with an object. | |
383 | Optional argument DEPTH is the current depth of the search." | |
384 | (save-excursion | |
385 | (if (not depth) | |
386 | (progn | |
387 | (beginning-of-line) | |
388 | (when (looking-at "^\\([0-9]+\\):") | |
389 | (setq depth (string-to-number (match-string 1)))))) | |
390 | (when depth | |
391 | (while (and (not (eieio-object-p (speedbar-line-token))) | |
392 | (> depth 0)) | |
393 | (setq depth (1- depth)) | |
394 | (re-search-backward (format "^%d:" depth) nil t)) | |
395 | (speedbar-line-token)))) | |
396 | ||
397 | (defun eieio-speedbar-line-path (&optional depth) | |
398 | "If applicable, return the path to the file the cursor is on. | |
399 | Optional DEPTH is the depth we start at." | |
400 | (save-match-data | |
401 | (if (not depth) | |
402 | (progn | |
403 | (beginning-of-line) | |
404 | (looking-at "^\\([0-9]+\\):") | |
405 | (setq depth (string-to-number (match-string 1))))) | |
406 | ;; This whole function is presently bogus. Make it better later. | |
407 | (let ((tok (eieio-speedbar-find-nearest-object depth))) | |
408 | (if (eieio-object-p tok) | |
409 | (eieio-speedbar-derive-line-path tok) | |
410 | default-directory)))) | |
411 | ||
412 | \f | |
413 | ;;; Methods to the eieio-speedbar-* classes which need to be overriden. | |
414 | ;; | |
415 | (defmethod eieio-speedbar-object-children ((object eieio-speedbar)) | |
a8f316ca | 416 | "Return a list of children to be displayed in speedbar. |
6dd12ef2 CY |
417 | If the return value is a list of OBJECTs, then those objects are |
418 | queried for details. If the return list is made of strings, | |
419 | then this object will be queried for the details needed | |
420 | to create a speedbar button." | |
421 | nil) | |
422 | ||
423 | (provide 'eieio-speedbar) | |
424 | ||
3999968a | 425 | ;; arch-tag: eaac1283-10b0-4419-a929-982b87e83234 |
6dd12ef2 | 426 | ;;; eieio-speedbar.el ends here |