summaryrefslogtreecommitdiff
path: root/lisp/cedet/ede/speedbar.el
blob: b321cb637bce5faa52c256305bf815d8ba203629 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
;;; ede/speedbar.el --- Speedbar viewing of EDE projects  -*- lexical-binding: t; -*-

;; Copyright (C) 1998-2001, 2003, 2005, 2007-2021 Free Software
;; Foundation, Inc.

;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Keywords: project, make, tags

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Display a project's hierarchy in speedbar.
;;

;;; Code:

(require 'speedbar)
(require 'eieio-speedbar)
(require 'ede)

;;; Speedbar support mode
;;
(defvar ede-speedbar-key-map nil
  "A Generic object based speedbar display keymap.")

(defun ede-speedbar-make-map ()
  "Make the generic object based speedbar keymap."
  (setq ede-speedbar-key-map (speedbar-make-specialized-keymap))

  ;; General viewing things
  (define-key ede-speedbar-key-map "\C-m" #'speedbar-edit-line)
  (define-key ede-speedbar-key-map "+" #'speedbar-expand-line)
  (define-key ede-speedbar-key-map "=" #'speedbar-expand-line)
  (define-key ede-speedbar-key-map "-" #'speedbar-contract-line)
  (define-key ede-speedbar-key-map " " #'speedbar-toggle-line-expansion)

  ;; Some object based things
  (define-key ede-speedbar-key-map "C" #'eieio-speedbar-customize-line)

  ;; Some project based things
  (define-key ede-speedbar-key-map "R" #'ede-speedbar-remove-file-from-target)
  (define-key ede-speedbar-key-map "b" #'ede-speedbar-compile-line)
  (define-key ede-speedbar-key-map "B" #'ede-speedbar-compile-project)
  (define-key ede-speedbar-key-map "D" #'ede-speedbar-make-distribution)
  (define-key ede-speedbar-key-map "E" #'ede-speedbar-edit-projectfile)
  )

(defvar ede-speedbar-menu
  '([ "Compile" ede-speedbar-compile-line t]
    [ "Compile Project" ede-speedbar-compile-project
      (ede-project-child-p (speedbar-line-token)) ]
    "---"
    [ "Edit File/Tag" speedbar-edit-line
      (not (eieio-object-p (speedbar-line-token)))]
    [ "Expand" speedbar-expand-line
      (save-excursion (beginning-of-line)
		      (looking-at "[0-9]+: *.\\+. "))]
    [ "Contract" speedbar-contract-line
      (save-excursion (beginning-of-line)
		      (looking-at "[0-9]+: *.-. "))]
    "---"
    [ "Remove File from Target" ede-speedbar-remove-file-from-target
      (stringp (speedbar-line-token)) ]
    [ "Customize Project/Target" eieio-speedbar-customize-line
      (eieio-object-p (speedbar-line-token)) ]
    [ "Edit Project File" ede-speedbar-edit-projectfile t]
    [ "Make Distribution" ede-speedbar-make-distribution
      (ede-project-child-p (speedbar-line-token)) ]
    )
  "Menu part in easymenu format used in speedbar while browsing objects.")

(eieio-speedbar-create 'ede-speedbar-make-map
		       'ede-speedbar-key-map
		       'ede-speedbar-menu
		       "Project"
		       'ede-speedbar-toplevel-buttons)


(defun ede-speedbar ()
  "EDE development environment project browser for speedbar."
  (interactive)
  (speedbar-frame-mode 1)
  (speedbar-change-initial-expansion-list "Project")
  (speedbar-get-focus)
  )

(defun ede-speedbar-toplevel-buttons (_dir)
  "Return a list of objects to display in speedbar.
Argument DIR is the directory from which to derive the list of objects."
  ede-projects
  )

;;; Some special commands useful in EDE
;;
(defun ede-speedbar-remove-file-from-target ()
  "Remove the file at point from its target."
  (interactive)
  (if (stringp (speedbar-line-token))
      (progn
	(speedbar-edit-line)
	(ede-remove-file))))

(defun ede-speedbar-compile-line ()
  "Compile/Build the project or target on this line."
  (interactive)
  (let ((obj (eieio-speedbar-find-nearest-object)))
    (if (not (eieio-object-p obj))
	nil
      (cond ((obj-of-class-p obj 'ede-project)
	     (project-compile-project obj))
	    ((obj-of-class-p obj 'ede-target)
	     (project-compile-target obj))
	    (t (error "Error in speedbar structure"))))))

(defun ede-speedbar-get-top-project-for-line ()
  "Return a project object for this line."
  (interactive)
  (let ((obj (eieio-speedbar-find-nearest-object)))
    (if (not (eieio-object-p obj))
	(error "Error in speedbar or ede structure")
      (if (obj-of-class-p obj 'ede-target)
	  (setq obj (ede-target-parent obj)))
      (if (obj-of-class-p obj 'ede-project)
	  obj
	(error "Error in speedbar or ede structure")))))

(defun ede-speedbar-compile-project ()
  "Compile/Build the project which owns this line."
  (interactive)
  (project-compile-project (ede-speedbar-get-top-project-for-line)))

(defun ede-speedbar-compile-file-project ()
  "Compile/Build the target which the current file belongs to."
  (interactive)
  (let* ((file (speedbar-line-file))
	 (buf (find-file-noselect file))
	 (bwin (get-buffer-window buf 0)))
    (if bwin
	(progn
	  (select-window bwin)
	  (raise-frame (window-frame bwin)))
      (dframe-select-attached-frame speedbar-frame)
      (set-buffer buf)
      (ede-compile-target))))

(defun ede-speedbar-make-distribution ()
  "Edit the project file based on this line."
  (interactive)
  (project-make-dist (ede-speedbar-get-top-project-for-line)))

(defun ede-speedbar-edit-projectfile ()
  "Edit the project file based on this line."
  (interactive)
  (project-edit-file-target (ede-speedbar-get-top-project-for-line)))

;;; Speedbar Project Methods
;;
(defun ede-find-nearest-file-line ()
  "Go backwards until we find a file."
  (save-excursion
    (beginning-of-line)
    (looking-at "^\\([0-9]+\\):")
    (let ((depth (string-to-number (match-string 1))))
      (while (not (re-search-forward "[]] [^ ]" (point-at-eol) t))
	(re-search-backward (format "^%d:" (1- depth)))
	(setq depth (1- depth)))
      (speedbar-line-token))))

(cl-defmethod eieio-speedbar-derive-line-path ((obj ede-project) &optional _depth)
  "Return the path to OBJ.
Optional DEPTH is the depth we start at."
  (file-name-directory (oref obj file))
  )

(cl-defmethod eieio-speedbar-derive-line-path ((obj ede-target) &optional _depth)
  "Return the path to OBJ.
Optional DEPTH is the depth we start at."
  (let ((proj (ede-target-parent obj)))
    ;; Check the type of line we are currently on.
    ;; If we are on a child, we need a file name too.
    (save-excursion
      (let ((lt (speedbar-line-token)))
	(if (or (eieio-object-p lt) (stringp lt))
	    (eieio-speedbar-derive-line-path proj)
	  ;; a child element is a token.  Do some work to get a filename too.
	  (concat (eieio-speedbar-derive-line-path proj)
		  (ede-find-nearest-file-line)))))))

(cl-defmethod eieio-speedbar-description ((obj ede-project))
  "Provide a speedbar description for OBJ."
  (ede-description obj))

(cl-defmethod eieio-speedbar-description ((obj ede-target))
  "Provide a speedbar description for OBJ."
  (ede-description obj))

(cl-defmethod eieio-speedbar-child-description ((_obj ede-target))
  "Provide a speedbar description for a plain-child of OBJ.
A plain child is a child element which is not an EIEIO object."
  (or (speedbar-item-info-file-helper)
      (speedbar-item-info-tag-helper)))

(cl-defmethod eieio-speedbar-object-buttonname ((object ede-project))
  "Return a string to use as a speedbar button for OBJECT."
  (if (ede-parent-project object)
      (ede-name object)
    (concat (ede-name object) " " (oref object version))))

(cl-defmethod eieio-speedbar-object-buttonname ((object ede-target))
  "Return a string to use as a speedbar button for OBJECT."
  (ede-name object))

(cl-defmethod eieio-speedbar-object-children ((this ede-project))
  "Return the list of speedbar display children for THIS."
  (condition-case nil
      (with-slots (subproj targets) this
	(append subproj targets))
    (error nil)))

(cl-defmethod eieio-speedbar-object-children ((this ede-target))
  "Return the list of speedbar display children for THIS."
  (oref this source))

(cl-defmethod eieio-speedbar-child-make-tag-lines ((this ede-target) depth)
  "Create a speedbar tag line for a child of THIS.
It has depth DEPTH."
  (with-slots (source) this
    (mapcar (lambda (car)
 	      (speedbar-make-tag-line 'bracket ?+
 				      'speedbar-tag-file
				      car
 				      car
 				      'ede-file-find
 				      car
 				      'speedbar-file-face depth))
	    source)))

;;; Generic file management for TARGETS
;;
(defun ede-file-find (_text token indent)
  "Find the file TEXT at path TOKEN.
INDENT is the current indentation level."
  (speedbar-find-file-in-frame
   (expand-file-name token (speedbar-line-directory indent)))
  (dframe-maybee-jump-to-attached-frame))

(defun ede-create-tag-buttons (filename indent)
  "Create the tag buttons associated with FILENAME at INDENT."
  (let* ((lst (speedbar-fetch-dynamic-tags filename)))
    ;; if no list, then remove expando button
    (if (not lst)
	(speedbar-change-expand-button-char ??)
      (speedbar-with-writable
	;; We must do 1- because indent was already incremented.
	(speedbar-insert-generic-list (1- indent)
				      lst
				      'ede-tag-expand
				      'ede-tag-find)))))

(defun ede-tag-expand (text token indent)
  "Expand a tag sublist.  Imenu will return sub-lists of specialized tag types.
Etags does not support this feature.  TEXT will be the button
string.  TOKEN will be the list, and INDENT is the current indentation
level."
  (cond ((string-search "+" text)	;we have to expand this file
	 (speedbar-change-expand-button-char ?-)
	 (speedbar-with-writable
	   (save-excursion
	     (end-of-line) (forward-char 1)
	     (speedbar-insert-generic-list indent token
					   'ede-tag-expand
					   'ede-tag-find))))
	((string-search "-" text)	;we have to contract this node
	 (speedbar-change-expand-button-char ?+)
	 (speedbar-delete-subblock indent))
	(t (error "Ooops...  not sure what to do")))
  (speedbar-center-buffer-smartly))

(defun ede-tag-find (_text token _indent)
  "For the tag TEXT in a file TOKEN, goto that position.
INDENT is the current indentation level."
  (let ((file (ede-find-nearest-file-line)))
    (speedbar-find-file-in-frame file)
    (save-excursion (speedbar-stealthy-updates))
    ;; Reset the timer with a new timeout when clicking a file
    ;; in case the user was navigating directories, we can cancel
    ;; that other timer.
;    (speedbar-set-timer speedbar-update-speed)
    (goto-char token)
    (run-hooks 'speedbar-visiting-tag-hook)
    ;;(recenter)
    (dframe-maybee-jump-to-attached-frame)
    ))

;;; EDE and the speedbar FILE display
;;
;; This will add a couple keybindings and menu items into the
;; FILE display for speedbar.

(defvar ede-speedbar-file-menu-additions
  '("----"
    ["Create EDE Target" ede-new-target (ede-current-project) ]
    ;; ["Add to project" ede-speedbar-file-add-to-project (ede-current-project) ]
    ["Compile project" ede-speedbar-compile-project (ede-current-project) ]
    ;; ["Compile file target" ede-speedbar-compile-file-target (ede-current-project) ]
    ["Make distribution" ede-make-dist (ede-current-project) ]
    )
  "Set of menu items to splice into the speedbar menu.")

(defvar ede-speedbar-file-keymap
  (let ((km (make-sparse-keymap)))
    ;; (define-key km "a" #'ede-speedbar-file-add-to-project)
    (define-key km "t" #'ede-new-target)
    (define-key km "s" #'ede-speedbar)
    (define-key km "C" #'ede-speedbar-compile-project)
    ;; (define-key km "c" #'ede-speedbar-compile-file-target)
    (define-key km "d" #'ede-make-dist)
    km)
  "Keymap spliced into the speedbar keymap.")

;;;###autoload
(defun ede-speedbar-file-setup ()
  "Setup some keybindings in the Speedbar File display."
  (setq speedbar-easymenu-definition-special
	(append speedbar-easymenu-definition-special
		ede-speedbar-file-menu-additions
		))
  (define-key speedbar-file-key-map "." ede-speedbar-file-keymap)
  ;; Finally, if the FILES mode is loaded, force a refresh
  ;; of the menus and such.
  (when (and (string= speedbar-initial-expansion-list-name "files")
	     (buffer-live-p speedbar-buffer)
	     )
    (speedbar-change-initial-expansion-list "files")))

(provide 'ede/speedbar)

;; Local variables:
;; generated-autoload-file: "loaddefs.el"
;; generated-autoload-load-name: "ede/speedbar"
;; End:

;;; ede/speedbar.el ends here