Commit | Line | Data |
---|---|---|
6dd12ef2 CY |
1 | ;;; eieio-speedbar.el -- Classes for managing speedbar displays. |
2 | ||
ab422c4d PE |
3 | ;; Copyright (C) 1999-2002, 2005, 2007-2013 Free Software Foundation, |
4 | ;; Inc. | |
6dd12ef2 | 5 | |
9ffe3f52 | 6 | ;; Author: Eric M. Ludlam <zappo@gnu.org> |
6dd12ef2 | 7 | ;; Keywords: OO, tools |
bd78fa1d | 8 | ;; Package: eieio |
6dd12ef2 CY |
9 | |
10 | ;; This file is part of GNU Emacs. | |
11 | ||
12 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
14 | ;; the Free Software Foundation, either version 3 of the License, or | |
15 | ;; (at your option) any later version. | |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
24 | ||
25 | ;;; Commentary: | |
26 | ;; | |
27 | ;; This provides some classes that can be used as a parent which | |
28 | ;; will automatically provide SPEEDBAR support for any list of objects | |
29 | ;; of that type. | |
30 | ;; | |
31 | ;; This file requires speedbar version 0.10 or later. | |
32 | ||
33 | ;;; Creating a new speedbar mode based on a pre-existing object hierarchy | |
34 | ;; | |
35 | ;; To create a new speedbar mode based on lists of objects is easier | |
36 | ;; than creating a whole new speedbar mode from scratch. | |
37 | ;; | |
38 | ;; 1) Objects that will have lists of items that can be expanded | |
39 | ;; should also inherit from the classes: | |
40 | ;; * `eieio-speedbar' - specify your own button behavior | |
41 | ;; * `eieio-speedbar-directory-button' - objects that behave like directories | |
42 | ;; * `eieio-speedbar-file-button' - objects that behave like files | |
43 | ;; | |
44 | ;; 2) Objects that have lists of children should implement the method | |
45 | ;; `eieio-speedbar-object-children' which returns a list of more | |
46 | ;; objects, or a list of strings. | |
47 | ;; | |
48 | ;; 3) Objects that return a list of strings should also implement these | |
49 | ;; methods: | |
50 | ;; * `eieio-speedbar-child-make-tag-lines' - make tag lines for a child. | |
51 | ;; * `eieio-speedbar-child-description' - describe non-object children | |
52 | ;; | |
53 | ;; 4) Objects which have expanded information should implement the method | |
54 | ;; `eieio-speedbar-description' to produce more information. | |
55 | ;; | |
56 | ;; 5) Objects that are associated with a directory should implement | |
57 | ;; the method `eieio-speedbar-derive-line-path' which returns a | |
58 | ;; path. | |
59 | ;; | |
60 | ;; 6) Objects that have a specialized behavior when clicked should | |
61 | ;; define the method `eieio-speedbar-handle-click'. | |
62 | ;; | |
63 | ;; To initialize a new eieio based speedbar display, do the following. | |
64 | ;; | |
65 | ;; 1) Create a keymap variable `foo-speedbar-key-map'. | |
66 | ;; This keymap variable should be initialized in a function. | |
67 | ;; If you have no special needs, use `eieio-speedbar-key-map' | |
68 | ;; | |
69 | ;; 2) Create a variable containing an easymenu definition compatible | |
70 | ;; with speedbar. if you have no special needs, use | |
71 | ;; `eieio-speedbar-menu'. | |
72 | ;; | |
73 | ;; 3) Create a function which returns the top-level list of children | |
74 | ;; objects to be displayed in speedbar. | |
75 | ;; | |
76 | ;; 4) Call `eieio-speedbar-create' as specified in it's documentation | |
77 | ;; string. This will automatically handle cases when speedbar is | |
78 | ;; not already loaded, and specifying all overload functions. | |
79 | ;; | |
9ffe3f52 | 80 | ;; 5) Create an initializer function which looks like this: |
6dd12ef2 | 81 | ;; |
9ffe3f52 | 82 | ;; (defun my-speedbar-mode-initialize () |
6dd12ef2 CY |
83 | ;; "documentation" |
84 | ;; (interactive) | |
85 | ;; (speedbar-frame-mode 1) | |
86 | ;; (speedbar-change-initial-expansion-list mymodename) | |
87 | ;; (speedbar-get-focus)) | |
88 | ;; | |
89 | ;; where `mymodename' is the same value as passed to `eieio-speedbar-create' | |
90 | ;; as the MODENAME parameter. | |
91 | ||
92 | ;; @todo - Can we make this ECB friendly? | |
93 | ||
94 | ;;; Code: | |
95 | (require 'eieio) | |
96 | (require 'eieio-custom) | |
97 | (require 'speedbar) | |
98 | ||
99 | ;;; Support a way of adding generic object based modes into speedbar. | |
100 | ;; | |
101 | (defun eieio-speedbar-make-map () | |
102 | "Make the generic object based speedbar keymap." | |
103 | (let ((map (speedbar-make-specialized-keymap))) | |
104 | ||
105 | ;; General viewing things | |
106 | (define-key map "\C-m" 'speedbar-edit-line) | |
107 | (define-key map "+" 'speedbar-expand-line) | |
108 | (define-key map "=" 'speedbar-expand-line) | |
109 | (define-key map "-" 'speedbar-contract-line) | |
110 | ||
111 | ;; Some object based things | |
112 | (define-key map "C" 'eieio-speedbar-customize-line) | |
113 | map)) | |
114 | ||
115 | (defvar eieio-speedbar-key-map (eieio-speedbar-make-map) | |
a8f316ca | 116 | "A generic object based speedbar display keymap.") |
6dd12ef2 CY |
117 | |
118 | (defvar eieio-speedbar-menu | |
119 | '([ "Edit Object/Field" speedbar-edit-line t] | |
120 | [ "Expand Object" speedbar-expand-line | |
121 | (save-excursion (beginning-of-line) | |
122 | (looking-at "[0-9]+: *.\\+. "))] | |
123 | [ "Contract Object" speedbar-contract-line | |
124 | (save-excursion (beginning-of-line) | |
125 | (looking-at "[0-9]+: *.-. "))] | |
126 | "---" | |
127 | [ "Customize Object" eieio-speedbar-customize-line | |
128 | (eieio-object-p (speedbar-line-token)) ] | |
129 | ) | |
130 | "Menu part in easymenu format used in speedbar while browsing objects.") | |
131 | ||
132 | ;; Note to self: Fix this silly thing! | |
133 | (defalias 'eieio-speedbar-customize-line 'speedbar-edit-line) | |
134 | ||
135 | (defun eieio-speedbar-create (map-fn map-var menu-var modename fetcher) | |
136 | "Create a speedbar mode for displaying an object hierarchy. | |
137 | MAP-FN is the keymap generator function used for extra keys. | |
138 | MAP-VAR is the keymap variable used. | |
9ffe3f52 GM |
139 | MENU-VAR is the symbol containing an easymenu compatible menu part to use. |
140 | MODENAME is a string used to identify this browser mode. | |
6dd12ef2 CY |
141 | FETCHER is a generic function used to fetch the base object list used when |
142 | creating the speedbar display." | |
143 | (if (not (featurep 'speedbar)) | |
144 | (add-hook 'speedbar-load-hook | |
145 | (list 'lambda nil | |
146 | (list 'eieio-speedbar-create-engine | |
147 | map-fn map-var menu-var modename fetcher))) | |
148 | (eieio-speedbar-create-engine map-fn map-var menu-var modename fetcher))) | |
149 | ||
150 | (defun eieio-speedbar-create-engine (map-fn map-var menu-var modename fetcher) | |
151 | "Create a speedbar mode for displaying an object hierarchy. | |
152 | Called from `eieio-speedbar-create', or the speedbar load-hook. | |
a8f316ca | 153 | MAP-FN, MAP-VAR, MENU-VAR, MODENAME, and FETCHER are the same as in |
6dd12ef2 CY |
154 | `eieio-speedbar-create'." |
155 | ;; make sure the keymap exists | |
156 | (funcall map-fn) | |
157 | ;; Add to the expansion list. | |
158 | (speedbar-add-expansion-list | |
159 | (list modename | |
160 | menu-var | |
161 | map-var | |
162 | (list 'lambda '(dir depth) | |
163 | (list 'eieio-speedbar-buttons 'dir 'depth | |
164 | (list 'quote fetcher))))) | |
165 | ;; Set the special functions. | |
166 | (speedbar-add-mode-functions-list | |
167 | (list modename | |
168 | '(speedbar-item-info . eieio-speedbar-item-info) | |
169 | '(speedbar-line-directory . eieio-speedbar-line-path)))) | |
170 | ||
171 | (defun eieio-speedbar-buttons (dir-or-object depth fetcher) | |
172 | "Create buttons for the speedbar display. | |
173 | Start in directory DIR-OR-OBJECT. If it is an object, just display that | |
a8f316ca | 174 | object's subelements. |
6dd12ef2 CY |
175 | Argument DEPTH specifies how far down we have already been displayed. |
176 | If it is a directory, use FETCHER to fetch all objects associated with | |
177 | that path." | |
178 | (let ((objlst (cond ((eieio-object-p dir-or-object) | |
179 | (list dir-or-object)) | |
180 | ((stringp dir-or-object) | |
181 | (funcall fetcher dir-or-object)) | |
182 | (t dir-or-object)))) | |
183 | (if (not objlst) | |
184 | (speedbar-make-tag-line nil nil nil nil "Empty display" nil nil nil | |
185 | depth) | |
186 | ;; Dump all objects into speedbar | |
187 | (while objlst | |
188 | (eieio-speedbar-make-tag-line (car objlst) depth) | |
189 | (setq objlst (cdr objlst)))))) | |
190 | ||
191 | \f | |
192 | ;;; DEFAULT SUPERCLASS baseline methods | |
193 | ;; | |
62a81506 CY |
194 | ;; First, define methods with no class defined. These will work as if |
195 | ;; on the default superclass. Specifying no class will allow these to be used | |
196 | ;; when no other methods are found, allowing multiple inheritance to work | |
197 | ;; reliably with eieio-speedbar. | |
6dd12ef2 | 198 | |
62a81506 | 199 | (defmethod eieio-speedbar-description (object) |
6dd12ef2 | 200 | "Return a string describing OBJECT." |
8ca4f1e0 | 201 | (eieio-object-name-string object)) |
6dd12ef2 | 202 | |
62a81506 | 203 | (defmethod eieio-speedbar-derive-line-path (object) |
6dd12ef2 CY |
204 | "Return the path which OBJECT has something to do with." |
205 | nil) | |
206 | ||
62a81506 | 207 | (defmethod eieio-speedbar-object-buttonname (object) |
6dd12ef2 | 208 | "Return a string to use as a speedbar button for OBJECT." |
8ca4f1e0 | 209 | (eieio-object-name-string object)) |
6dd12ef2 | 210 | |
62a81506 | 211 | (defmethod eieio-speedbar-make-tag-line (object depth) |
6dd12ef2 CY |
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 | ||
62a81506 | 224 | (defmethod eieio-speedbar-handle-click (object) |
6dd12ef2 CY |
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 | |
0b381c7e | 286 | ;;; Methods to eieio-speedbar-* which do not need to be overridden |
6dd12ef2 CY |
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" | |
8ca4f1e0 | 327 | (eieio-object-name object))) |
6dd12ef2 CY |
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" | |
8ca4f1e0 | 368 | (eieio-object-name obj))) |
6dd12ef2 CY |
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 | |
0b381c7e | 413 | ;;; Methods to the eieio-speedbar-* classes which need to be overridden. |
6dd12ef2 CY |
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 | ||
425 | ;;; eieio-speedbar.el ends here |