Mercurial > hg > xemacs-beta
comparison lisp/comint/gdb-highlight.el @ 155:43dd3413c7c7 r20-3b4
Import from CVS: tag r20-3b4
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:39:39 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
154:94141801dd7e | 155:43dd3413c7c7 |
---|---|
1 ;;; gdb-highlight.el --- make gdb buffers be mouse-sensitive. | |
2 | |
3 ;;; Copyright (C) 1997 Jamie Zawinski <jwz@netscape.com> | |
4 | |
5 ;; Author: Jamie Zawinski <jwz@netscape.com> | |
6 ;; Created: 16-Apr-1997 | |
7 ;; Version: 1.2 (17-May-97) | |
8 ;; Keywords: extensions, c, unix, tools, debugging | |
9 | |
10 ;; This file is part of XEmacs. | |
11 | |
12 ;; XEmacs is free software; you can redistribute it and/or modify it | |
13 ;; under the terms of the GNU General Public License as published by | |
14 ;; the Free Software Foundation; either version 2, or (at your option) | |
15 ;; any later version. | |
16 | |
17 ;; XEmacs is distributed in the hope that it will be useful, but | |
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 ;; General Public License for more details. | |
21 | |
22 ;; You should have received a copy of the GNU General Public License | |
23 ;; along with XEmacs; see the file COPYING. If not, write to the Free | |
24 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
25 ;; 02111-1307, USA. | |
26 | |
27 ;;; Synched up with: Not synched. | |
28 | |
29 ;;; Commentary: | |
30 ;; | |
31 ;; This package makes most objects printed in a *gdb* buffer be | |
32 ;; mouse-sensitive: as text shows up in the buffer, it is parsed, | |
33 ;; and objects which are recognized have context-sensitive commands | |
34 ;; attached to them. Generally, the types that are noticed are: | |
35 ;; | |
36 ;; = function and method names; | |
37 ;; = variable and parameter names; | |
38 ;; = structure and object slots; | |
39 ;; = source file names; | |
40 ;; = type names; | |
41 ;; = breakpoint numbers; | |
42 ;; = stack frame numbers. | |
43 ;; | |
44 ;; Any time one of those objects is presented in the *gdb* buffer, | |
45 ;; it will be mousable. Clicking middle mouse button (button2) on | |
46 ;; it will take some default action -- edit the function, select | |
47 ;; the stack frame, disable the breakpoint, etc. Clicking the right | |
48 ;; mouse button (button3) will bring up a menu of commands, including | |
49 ;; commands specific to the object under the mouse, or other objects | |
50 ;; on the same line. | |
51 ;; | |
52 ;; In addition to these context-sensitive commands are more general | |
53 ;; gdb commands which were previously inaccessible via the mouse | |
54 ;; (listing breakpoints, returning values, etc); and the general | |
55 ;; comint/shell-buffer commands which had been present before. | |
56 ;; | |
57 ;; If you notice an object being presented which could (usefully) | |
58 ;; be made mouse sensitive, but which currently is not, please let | |
59 ;; me know. | |
60 | |
61 ;;; Installation: | |
62 ;; | |
63 ;; To install, add this to your .emacs file: | |
64 ;; (add-hook 'gdb-mode-hook '(lambda () (require 'gdb-highlight))) | |
65 | |
66 ;;; TODO: | |
67 ;; | |
68 ;; = It doesn't really work very well unless you've done `set width 0' | |
69 ;; in your .gdbinit. It would be nice if this were fixed. | |
70 ;; (And with `set width 0', `set print pretty on' is the way to go.) | |
71 ;; | |
72 ;; = In some contexts, the toggle-breakpoint command doesn't work, | |
73 ;; because this code doesn't know whether it's enabled. It should | |
74 ;; remember, or figure it out, or something. | |
75 ;; | |
76 ;; = Make it possible to edit the `keep' state of breakpoints. | |
77 ;; | |
78 ;; = Is it useful to make addresses clickable? If an address is | |
79 ;; always acompanied by a variable, then no. | |
80 ;; | |
81 ;; = There has got to be a better way to implement `gdb-guess-file-name'. | |
82 ;; | |
83 ;; = Make some new toolbar icons and put the most common commands on it. | |
84 ;; | |
85 ;; = Maybe make gdb-toolbar-clear work more reliably by consulting a | |
86 ;; breakpoint-number extent? | |
87 ;; | |
88 ;; = I want breakpoint icons in my source files, just like in Energize. | |
89 ;; | |
90 ;; = Add a command to quit-and-restart the debugger, with the same | |
91 ;; breakpoints and program-arguments. (This wouldn't be interesting | |
92 ;; if gdb didn't leak like a sieve...) | |
93 ;; | |
94 ;; = Figure out some way to realize when extents are no longer interesting | |
95 ;; (stack frames and local variables that are no longer on the stack) | |
96 ;; and make them no longer be mousable. This is tricky... Nuke them | |
97 ;; whenever a "run" command is seen? | |
98 ;; | |
99 ;; = Make C-x SPC in a source buffer use gdb-menu-command so that it will | |
100 ;; interrupt-and-continue the debugged program as necessary. | |
101 ;; | |
102 ;; = Do stuff for watchpoints (but I never use them, myself.) | |
103 | |
104 ;;; WISHLIST: | |
105 ;; | |
106 ;; (extracted from my 13-May-1997 message to comp.emacs and | |
107 ;; comp.emacs.xemacs, news:33785828.5A524730@netscape.com) | |
108 ;; | |
109 ;; 6.1. Make gdbsrc-mode not suck. | |
110 ;; | |
111 ;; The idea behind gdbsrc-mode is on the side of the angels: one | |
112 ;; should be able to focus on the source code and not on the | |
113 ;; debugger buffer, absolutely. But the implementation is just | |
114 ;; awful. | |
115 ;; | |
116 ;; First and foremost, it should not change "modes" (in the more | |
117 ;; general sense). Any commands that it defines should be on | |
118 ;; keys which are exclusively used for that purpose, not keys | |
119 ;; which are normally self-inserting. I can't be the only person | |
120 ;; who usually has occasion to actually *edit* the sources which | |
121 ;; the debugger has chosen to display! Switching into and out of | |
122 ;; gdbsrc-mode is prohibitive. | |
123 ;; | |
124 ;; I want to be looking at my sources at all times, yet I don't | |
125 ;; want to have to give up my source-editing gestures. I think | |
126 ;; the right way to accomplish this is to put the gdbsrc commands | |
127 ;; on the toolbar and on popup menus; or to let the user define | |
128 ;; their own keys (I could see devoting my kp_enter key to | |
129 ;; "step", or something common like that.) | |
130 ;; | |
131 ;; Also it's extremely frustrating that one can't turn off gdbsrc | |
132 ;; mode once it has been loaded, without exiting and restarting | |
133 ;; emacs; that alone means that I'd probably never take the time | |
134 ;; to learn how to use it, without first having taken the time to | |
135 ;; repair it... | |
136 ;; | |
137 ;; 6.2. Make it easier access to variable values. | |
138 ;; | |
139 ;; I want to be able to double-click on a variable name to | |
140 ;; highlight it, and then drag it to the debugger window to have | |
141 ;; its value printed. | |
142 ;; | |
143 ;; I want gestures that let me write as well as read: for | |
144 ;; example, to store value A into slot B. | |
145 ;; | |
146 ;; 6.3. Make all breakpoints visible. | |
147 ;; | |
148 ;; Any time there is a running gdb which has breakpoints, the | |
149 ;; buffers holding the lines on which those breakpoints are set | |
150 ;; should have icons in them. These icons should be context- | |
151 ;; sensitive: I should be able to pop up a menu to enable or | |
152 ;; disable them, to delete them, to change their commands or | |
153 ;; conditions. | |
154 ;; | |
155 ;; I should also be able to MOVE them. It's annoying when you | |
156 ;; have a breakpoint with a complex condition or command on it, | |
157 ;; and then you realize that you really want it to be at a | |
158 ;; different location. I want to be able to drag-and-drop the | |
159 ;; icon to its new home. | |
160 ;; | |
161 ;; 6.4. Make a debugger status display window. | |
162 ;; | |
163 ;; o I want a window off to the side that shows persistent | |
164 ;; information -- it should have a pane which is a | |
165 ;; drag-editable, drag-reorderable representation of the | |
166 ;; elements on gdb's "display" list; they should be displayed | |
167 ;; here instead of being just dumped in with the rest of the | |
168 ;; output in the *gdb* buffer. | |
169 ;; | |
170 ;; o I want a pane that displays the current call-stack and | |
171 ;; nothing else. I want a pane that displays the arguments | |
172 ;; and locals of the currently-selected frame and nothing | |
173 ;; else. I want these both to update as I move around on the | |
174 ;; stack. | |
175 ;; | |
176 ;; Since the unfortunate reality is that excavating this | |
177 ;; information from gdb can be slow, it would be a good idea | |
178 ;; for these panes to have a toggle button on them which meant | |
179 ;; "stop updating", so that when I want to move fast, I can, | |
180 ;; but I can easily get the display back when I need it again. | |
181 ;; | |
182 ;; The reason for all of this is that I spend entirely too much | |
183 ;; time scrolling around in the *gdb* buffer; with gdb-highlight, | |
184 ;; I can just click on a line in the backtrace output to go to | |
185 ;; that frame, but I find that I spend a lot of time *looking* | |
186 ;; for that backtrace: since it's mixed in with all the other | |
187 ;; random output, I waste time looking around for things (and | |
188 ;; usually just give up and type "bt" again, then thrash around | |
189 ;; as the buffer scrolls, and I try to find the lower frames that | |
190 ;; I'm interested in, as they have invariably scrolled off the | |
191 ;; window already... | |
192 ;; | |
193 ;; 6.5. Save and restore breakpoints across emacs/debugger sessions. | |
194 ;; | |
195 ;; This would be especially handy given that gdb leaks like a | |
196 ;; sieve, and with a big program, I only get a few dozen | |
197 ;; relink-and-rerun attempts before gdb has blown my swap space. | |
198 ;; | |
199 ;; 6.6. Keep breakpoints in sync with source lines. | |
200 ;; | |
201 ;; When a program is recompiled and then reloaded into gdb, the | |
202 ;; breakpoints often end up in less-than-useful places. For | |
203 ;; example, when I edit text which occurs in a file anywhere | |
204 ;; before a breakpoint, emacs is aware that the line of the bp | |
205 ;; hasn't changed, but just that it is in a different place | |
206 ;; relative to the top of the file. Gdb doesn't know this, so | |
207 ;; your breakpoints end up getting set in the wrong places | |
208 ;; (usually the maximally inconvenient places, like *after* a | |
209 ;; loop instead of *inside* it). But emacs knows, so emacs | |
210 ;; should inform the debugger, and move the breakpoints back to | |
211 ;; the places they were intended to be. | |
212 ;; | |
213 ;; (Possibly the OOBR stuff does some of this, but can't tell, | |
214 ;; because I've never been able to get it to do anything but beep at | |
215 ;; me and mumble about environments. I find it pretty funny that the | |
216 ;; manual keeps explaining to me how intuitive it is, without | |
217 ;; actually giving me a clue how to launch it...) | |
218 | |
219 | |
220 ;;; Code: | |
221 ;; | |
222 ;; This code should be considered an example of how over-use of regular | |
223 ;; expressions leads to code that is an unreadable, unmaintainable mess, | |
224 ;; and why it's unfortunate that so much of emacs's speed depends on | |
225 ;; their use, rather than on the use of more traditional parsers. | |
226 | |
227 (require 'gdb) | |
228 | |
229 (define-key gdb-mode-map 'button3 'gdb-popup-menu) | |
230 (defvar gdb-popup-menu | |
231 '("GDB Commands" | |
232 ["Up Stack" (gdb-menu-command "up" t) t] | |
233 ["Down Stack" (gdb-menu-command "down" t) t] | |
234 ["Next Line" (gdb-menu-command "next" t) t] | |
235 ["Next Line (Step In)" (gdb-menu-command "step" t) t] | |
236 ["Continue" (gdb-menu-command "continue" t) t] | |
237 ["Continue Until Return" (gdb-menu-command "finish" t) t] | |
238 ("Return..." | |
239 ["Return" (gdb-menu-command "return" t) t] | |
240 ["Return 0" (gdb-menu-command "return 0" t) t] | |
241 ["Return 1" (gdb-menu-command "return 1" t) t] | |
242 ["Return -1" (gdb-menu-command "return -1" t) t] | |
243 ["Return $" (gdb-menu-command "return $" t) t] | |
244 ) | |
245 "---" | |
246 ["Backtrace" (gdb-menu-command "backtrace" t) t] | |
247 ["List Breakpoints" (gdb-menu-command "info breakpoints" t) t] | |
248 ["List Local Variables" (gdb-menu-command "info locals" t) t] | |
249 ) | |
250 "Commands for the popup menu in gdb-mode. | |
251 The comint-popup-menu is appended to this, and certain context-sensitive | |
252 commands may be prepended to it, depending on the location of the mouse | |
253 when the `gdb-popup-menu' command is invoked.") | |
254 | |
255 | |
256 ;;; Faces and keymaps used for mousable tokens in the *gdb* buffer. | |
257 | |
258 (defvar gdb-highlight-face 'gdb-highlight-face) ; the base face | |
259 (defvar gdb-breakpoint-number-face 'gdb-breakpoint-number-face) | |
260 ;(defvar gdb-breakpoint-keep-face 'gdb-breakpoint-keep-face) | |
261 (defvar gdb-breakpoint-enabled-face 'gdb-breakpoint-enabled-face) | |
262 (defvar gdb-function-name-face 'gdb-function-name-face) | |
263 (defvar gdb-function-location-face 'gdb-function-location-face) | |
264 (defvar gdb-variable-name-face 'gdb-variable-name-face) | |
265 (defvar gdb-type-name-face 'gdb-type-name-face) | |
266 | |
267 (make-face 'gdb-highlight-face) | |
268 (or (face-differs-from-default-p 'gdb-highlight-face) | |
269 (make-face-italic 'gdb-highlight-face)) | |
270 | |
271 (let ((faces '(gdb-breakpoint-number-face | |
272 gdb-breakpoint-enabled-face | |
273 ;gdb-breakpoint-keep-face | |
274 gdb-function-name-face | |
275 gdb-function-location-face | |
276 gdb-variable-name-face | |
277 gdb-type-name-face))) | |
278 (while faces | |
279 (make-face (car faces)) | |
280 (or (face-differs-from-default-p (car faces)) | |
281 (if (fboundp 'set-face-parent) | |
282 (set-face-parent (car faces) 'gdb-highlight-face) | |
283 (copy-face 'gdb-highlight-face (car faces)))) | |
284 (setq faces (cdr faces)))) | |
285 | |
286 | |
287 (defvar gdb-token-map ; the base map, inherited by all. | |
288 (let ((m (make-sparse-keymap))) | |
289 (set-keymap-name m 'gdb-token-map) | |
290 (define-key m 'button2 'undefined) | |
291 ;;(define-key m 'button3 'gdb-token-popup) | |
292 m)) | |
293 | |
294 (defvar gdb-breakpoint-number-map | |
295 (let ((m (make-sparse-keymap))) | |
296 (set-keymap-name m 'gdb-breakpoint-number-map) | |
297 (set-keymap-parent m gdb-token-map) | |
298 ;; not sure if this is the most useful binding... maybe "delete" is better? | |
299 (define-key m 'button2 'gdb-mouse-disable-breakpoint) | |
300 m)) | |
301 | |
302 (defvar gdb-info-breakpoint-number-map | |
303 (let ((m (make-sparse-keymap))) | |
304 (set-keymap-name m 'gdb-breakpoint-number-map) | |
305 (set-keymap-parent m gdb-token-map) | |
306 ;; not sure if this is the most useful binding... maybe "delete" is better? | |
307 (define-key m 'button2 'gdb-mouse-toggle-breakpoint-enabled) | |
308 m)) | |
309 | |
310 ;(defvar gdb-breakpoint-keep-map | |
311 ; (let ((m (make-sparse-keymap))) | |
312 ; (set-keymap-name m 'gdb-breakpoint-keep-map) | |
313 ; (set-keymap-parent m gdb-token-map) | |
314 ; (define-key m 'button2 'gdb-token-mouse-toggle-keep) | |
315 ; m)) | |
316 | |
317 (defvar gdb-breakpoint-enabled-map | |
318 (let ((m (make-sparse-keymap))) | |
319 (set-keymap-name m 'gdb-breakpoint-enabled-map) | |
320 (set-keymap-parent m gdb-token-map) | |
321 (define-key m 'button2 'gdb-mouse-toggle-breakpoint-enabled) | |
322 m)) | |
323 | |
324 (defvar gdb-function-name-map | |
325 (let ((m (make-sparse-keymap))) | |
326 (set-keymap-name m 'gdb-function-name-map) | |
327 (set-keymap-parent m gdb-token-map) | |
328 (define-key m 'button2 'gdb-mouse-edit-function) | |
329 m)) | |
330 | |
331 (defvar gdb-function-location-map | |
332 (let ((m (make-sparse-keymap))) | |
333 (set-keymap-name m 'gdb-function-location-map) | |
334 (set-keymap-parent m gdb-token-map) | |
335 (define-key m 'button2 'gdb-mouse-edit-function-location) | |
336 m)) | |
337 | |
338 (defvar gdb-frame-number-map | |
339 (let ((m (make-sparse-keymap))) | |
340 (set-keymap-name m 'gdb-frame-number-map) | |
341 (set-keymap-parent m gdb-token-map) | |
342 (define-key m 'button2 'gdb-mouse-goto-frame) | |
343 m)) | |
344 | |
345 (defvar gdb-variable-name-map | |
346 (let ((m (make-sparse-keymap))) | |
347 (set-keymap-name m 'gdb-variable-name-map) | |
348 (set-keymap-parent m gdb-token-map) | |
349 (define-key m 'button2 'gdb-mouse-print-variable) | |
350 m)) | |
351 | |
352 (defvar gdb-type-name-map | |
353 (let ((m (make-sparse-keymap))) | |
354 (set-keymap-name m 'gdb-type-name-map) | |
355 (set-keymap-parent m gdb-token-map) | |
356 (define-key m 'button2 'gdb-mouse-print-type) | |
357 m)) | |
358 | |
359 | |
360 ;;; Token definitions. | |
361 | |
362 ;; These properties enumerate the faces and keymaps that will be put over | |
363 ;; the tokens. | |
364 | |
365 (put 'gdb-frame-number-token 'gdb-token-face gdb-breakpoint-number-face) | |
366 (put 'gdb-frame-number-token 'gdb-token-keymap gdb-frame-number-map) | |
367 | |
368 ;(put 'gdb-breakpoint-keep-token 'gdb-token-face gdb-breakpoint-keep-face) | |
369 ;(put 'gdb-breakpoint-keep-token 'gdb-token-keymap gdb-breakpoint-keep-map) | |
370 | |
371 (put 'gdb-enabled-token 'gdb-token-face gdb-breakpoint-enabled-face) | |
372 (put 'gdb-enabled-token 'gdb-token-keymap gdb-breakpoint-enabled-map) | |
373 | |
374 (put 'gdb-function-name-token 'gdb-token-face gdb-function-name-face) | |
375 (put 'gdb-function-name-token 'gdb-token-keymap gdb-function-name-map) | |
376 | |
377 (put 'gdb-function-location-token 'gdb-token-face gdb-function-location-face) | |
378 (put 'gdb-function-location-token 'gdb-token-keymap gdb-function-location-map) | |
379 | |
380 (put 'gdb-breakpoint-number-token 'gdb-token-face gdb-breakpoint-number-face) | |
381 (put 'gdb-breakpoint-number-token 'gdb-token-keymap gdb-breakpoint-number-map) | |
382 (put 'gdb-info-breakpoint-number-token 'gdb-token-face | |
383 gdb-breakpoint-number-face) | |
384 (put 'gdb-info-breakpoint-number-token 'gdb-token-keymap | |
385 gdb-info-breakpoint-number-map) | |
386 | |
387 (put 'gdb-frame-number-token 'gdb-token-face gdb-breakpoint-number-face) | |
388 (put 'gdb-frame-number-token 'gdb-token-keymap gdb-frame-number-map) | |
389 | |
390 (put 'gdb-variable-name-token 'gdb-token-face gdb-variable-name-face) | |
391 (put 'gdb-variable-name-token 'gdb-token-keymap gdb-variable-name-map) | |
392 | |
393 (put 'gdb-type-name-token 'gdb-token-face gdb-type-name-face) | |
394 (put 'gdb-type-name-token 'gdb-token-keymap gdb-type-name-map) | |
395 | |
396 | |
397 ;;; These regular expressions control what text corresponds to which tokens. | |
398 | |
399 (defconst gdb-highlight-token-patterns | |
400 ;; "May god forgive me for what I have unleashed." -- Evil Dead II. | |
401 (purecopy | |
402 (list | |
403 ;; Breakpoints output: | |
404 ;; | |
405 ;; Breakpoint 5, XCreateWindow () at Window.c:136 | |
406 ;; Breakpoint 6, foobar (x=0x7fff3000 "baz") at blorp.c:5382 | |
407 ;; | |
408 (list (concat "\\(Breakpoint " ; 1 | |
409 "\\([0-9]+\\)" ; .2 | |
410 "\\), " ; 1 | |
411 "\\(0x[0-9a-fA-F]+ in \\)?" ; 3 | |
412 "\\(" ; 4 | |
413 "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; .5 | |
414 "\\|" ; . | |
415 "[a-zA-Z0-9_]+" ; . | |
416 "\\)" ; 4 | |
417 "\\(" ; 6 | |
418 " *\\((.*)\\)" ; .7 | |
419 " at \\(" ; .8 | |
420 "\\([^ \t\n:]+\\):" ; ..9 | |
421 "\\([0-9]+\\)" ; ..10 | |
422 "\\)" ; .8 | |
423 "\\)?" ; 6 | |
424 ) | |
425 '(gdb-breakpoint-number-token ; 1 | |
426 nil ; 2 | |
427 nil ; 3 | |
428 gdb-function-name-token ; 4 (+5) | |
429 gdb-type-name-token ; 5 | |
430 nil ; 6 | |
431 gdb-arglist-token ; 7 | |
432 gdb-function-location-token ; 8 (9+10) | |
433 )) | |
434 | |
435 ;; Output of the "Break" command: | |
436 ;; | |
437 ;; Breakpoint 1 at 0x4881d4 | |
438 ;; Breakpoint 6 at 0xfa50f68: file cuexit.c, line 58. | |
439 ;; | |
440 (list (concat "\\(Breakpoint " ; 1 | |
441 "\\([0-9]+\\)" ; .2 | |
442 "\\) at " ; 1 | |
443 "\\(0x[0-9A-Fa-f]+\\)" ; 3 | |
444 "\\(: file " ; 4 | |
445 "\\(" ; .5 | |
446 "\\([^ \t\n:]+\\)" ; ..6 | |
447 ", line \\([0-9]+\\)" ; ..7 | |
448 "\\)" ; .5 | |
449 "\\)?" ; 4 | |
450 ) | |
451 '(gdb-breakpoint-number-token ; 1 | |
452 nil ; 2 | |
453 nil ;gdb-address-token ; 3 | |
454 nil ; 4 | |
455 gdb-function-location-token ; 5 (6+7) | |
456 )) | |
457 | |
458 ;; Note: breakpoint 5 (disabled) also set at pc 0x40b420. | |
459 ;; Note: breakpoint 5 also set at pc 0x40b420. | |
460 ;; | |
461 (list (concat "Note: " ; | |
462 "\\(breakpoint " ; 1 | |
463 "\\([0-9]+\\)" ; .2 | |
464 "\\)" ; 1 | |
465 ) | |
466 '(gdb-breakpoint-number-token ; 1 | |
467 nil ; 2 | |
468 )) | |
469 | |
470 ;; Stack Frames: | |
471 ;; | |
472 ;; 0xe1b8e0 in _OS_SELECT () at os_IRIX.s:50 | |
473 ;; XCreateWindow () at Window.c:136 | |
474 ;; #0 0x8e0db0 in _OS_SELECT () at os_IRIX.s:50 | |
475 ;; #0 XCreateWindow () at Window.c:136 | |
476 ;; Run till exit from #0 __ll_mul () at llmul.s:51 | |
477 ;; | |
478 (list (concat "\\(Run till exit from \\)?" ; 1 | |
479 "\\(" ; 2 | |
480 "#\\([0-9]+ *\\)" ; .3 | |
481 "\\)?" ; 2 | |
482 "\\(" ; 4 | |
483 "\\(0x[0-9A-Fa-f]+\\)" ; .5 | |
484 " in +\\)?" ; 4 | |
485 "\\(" ; 6 | |
486 "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; .7 | |
487 "\\|" ; 6 | |
488 "[a-zA-Z0-9_]+" ; | |
489 "\\) (" ; 6 | |
490 "\\(" ; 8 | |
491 "\\(.*\\)" ; .9 | |
492 "\\bat \\(" ; .10 | |
493 "\\([^ \t\n:]+\\):" ; ..11 | |
494 "\\([0-9]+\\)" ; ..12 | |
495 "\\)" ; .10 | |
496 "\\)?" ; 8 | |
497 ) | |
498 '(nil ; 1 | |
499 gdb-frame-number-token ; 2 | |
500 nil ; 3 | |
501 nil ; 4 | |
502 nil ;gdb-address-token ; 5 | |
503 gdb-function-name-token ; 6 (+7) | |
504 gdb-type-name-token ; 7 | |
505 nil ; 8 | |
506 gdb-arglist-token ; 9 | |
507 gdb-function-location-token ; 10 (11+12) | |
508 )) | |
509 | |
510 ;; Info Breakpoints output: | |
511 ;; | |
512 ;; 1 breakpoint keep y 0x0fa50f68 in exit at exit.c:58 | |
513 ;; 1 breakpoint keep y 0x000a1b00 <exit+4> | |
514 ;; 1 breakpoint keep y 0x0fa429ac <_write> | |
515 ;; 6 breakpoint keep y 0x00789490 in foo::bar(bad *) at x.cpp:99 | |
516 ;; 7 breakpoint keep y 0x00789490 <foo::bar(bad *)+128> | |
517 ;; | |
518 (list (concat "\\([0-9]+ *\\) " ; 1 | |
519 "\\(breakpoint *\\|watchpoint *\\) " ; 2 | |
520 "\\(keep *\\|del *\\|dis *\\) " ; 3 | |
521 "\\([yn] *\\) " ; 4 | |
522 "\\(0x[0-9A-Fa-f]+\\) *" ; 5 | |
523 "\\(in " ; 6 | |
524 "\\(" ; .7 | |
525 "[a-zA-Z0-9_]+" ; .. | |
526 "\\|" ; .7 | |
527 "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; ..8 | |
528 "\\)" ; .7 | |
529 "\\((.*)\\)?" ; 9 | |
530 " at " ; . | |
531 "\\(" ; .10 | |
532 "\\([^ \t\n:]+\\):" ; ..11 | |
533 "\\([0-9]+\\)" ; ..12 | |
534 "\\)" ; .10 | |
535 "\\|" ; 6 | |
536 "<" ; . | |
537 "\\(" ; .13 | |
538 "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; ..14 | |
539 "\\|" ; .13 | |
540 "[a-zA-Z0-9_]+" ; .. | |
541 "\\)" ; .13 | |
542 "\\((.*)\\)?" ; .15 | |
543 "[^>\n]*>" ; . | |
544 "\\)?" ; 6 | |
545 ) | |
546 '(gdb-info-breakpoint-number-token ; 1 | |
547 nil ; 2 | |
548 nil ;gdb-breakpoint-keep-token ; 3 | |
549 gdb-enabled-token ; 4 | |
550 nil ;gdb-address-token ; 5 | |
551 nil ; 6 | |
552 gdb-function-name-token ; 7 (+8) | |
553 gdb-type-name-token ; 8 | |
554 gdb-arglist-types-token ; 9 | |
555 gdb-function-location-token ; 10 (11+12) | |
556 nil ; 11 | |
557 nil ; 12 | |
558 gdb-function-name-token ; 13 | |
559 gdb-type-name-token ; 14 | |
560 gdb-arglist-types-token ; 15 | |
561 )) | |
562 | |
563 ;; Whatis and Ptype output: | |
564 ;; type = struct _WidgetRec * | |
565 ;; type = struct _WidgetRec { | |
566 ;; type = int () | |
567 ;; type = struct <undefined> *(struct <undefined> *, void *, void (*)()) | |
568 ;; type = struct foo *(struct foo *, unsigned char, int) | |
569 ;; type = unsigned int [4] | |
570 ;; | |
571 (list (concat "type = " | |
572 "\\(" ; 1 | |
573 "\\(signed \\|unsigned \\)?" ; .2 | |
574 "\\(struct \\|class \\|union \\|enum \\)?" ; .3 | |
575 "\\(<?[a-zA-Z_][a-zA-Z0-9_:]*>?\\)" ; .4 | |
576 "\\)" ; 1 | |
577 "[ *]*" ; | |
578 "\\(" ; 5 | |
579 "{?$\\|" ; . | |
580 "\\[[0-9]*\\]$\\|" ; . | |
581 "\\((.*)\\)" ; .6 | |
582 "\\)" ; 5 | |
583 ) | |
584 '(gdb-type-name-token ; 1 (2+3+4) | |
585 nil ; 2 | |
586 nil ; 3 | |
587 nil ; 4 | |
588 nil ; 5 | |
589 gdb-arglist-types-token ; 6 | |
590 )) | |
591 | |
592 ;; Ptype output: | |
593 ;; CorePart core; | |
594 ;; void *constraints; | |
595 ;; short x; | |
596 ;; unsigned short width; | |
597 ;; struct <undefined> *event_table; | |
598 ;; XtTMRec tm; | |
599 ;; void (*class_initialize)(); | |
600 ;; unsigned char (*set_values)(); | |
601 ;; unsigned char st_fstype[16]; | |
602 ;; type = enum {XtGeometryYes, XtGeometryNo, XtGeometryAlmost} | |
603 ;; | |
604 (list (concat " *" | |
605 "\\(" ; 1 | |
606 "\\(signed \\|unsigned \\)?" ; .2 | |
607 "\\(struct \\|class \\|union \\|enum \\)?" ; .3 | |
608 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; .4 | |
609 "\\)" ; 1 | |
610 "[ *]*" | |
611 "\\((\\**\\)?" ; 5 | |
612 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; 6 | |
613 "\\()()\\)?" ; 7 | |
614 "\\( *\\[[0-9]*\\]\\)?" ; 8 | |
615 "; *$" | |
616 ) | |
617 '(gdb-type-name-token ; 1 (2+3+4) | |
618 )) | |
619 | |
620 ;; Ptype output on C++ classes: | |
621 ;; | |
622 ;; virtual foo (int); | |
623 ;; unsigned int foo(void); | |
624 ;; static long unsigned int * foo(bar *, baz *, unsigned int); | |
625 ;; | |
626 ;; not handled: | |
627 ;; foo(bar *, _WidgetRec *, char const *, int); | |
628 ;; foo (foo &); | |
629 ;; foo & operator=(foo const &); | |
630 ;; | |
631 (list (concat " *" | |
632 "\\(static \\)?" ; 1 | |
633 "\\(" ; 2 | |
634 "\\(signed \\|unsigned " ; .3 | |
635 ;; #### not so sure about this: | |
636 "\\|long unsigned \\|short unsigned " ; .3 | |
637 "\\)?" ; .3 | |
638 "\\(struct \\|class \\|union \\|enum \\)?" ; .4 | |
639 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; .5 | |
640 "\\)" ; 1 | |
641 "[ *&]+" ; | |
642 " *\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; 6 | |
643 " *\\((.*)\\)" ; 7 | |
644 "; *$" ; | |
645 ) | |
646 '(nil ; 1 | |
647 gdb-type-name-token ; 2 (3+4+5) | |
648 nil ; 3 | |
649 nil ; 4 | |
650 nil ; 5 | |
651 gdb-function-name-token ; 6 | |
652 gdb-arglist-types-token ; 7 | |
653 )) | |
654 | |
655 ;; Pointers to functions: | |
656 ;; | |
657 ;; $1 = {void ()} 0x4a1334 <fe_pulldown_cb> | |
658 ;; $2 = (void (*)()) 0x4a1334 <fe_pulldown_cb> | |
659 ;; | |
660 (list (concat ".* = " | |
661 "[({]" | |
662 "\\(" ; 1 | |
663 "\\(signed \\|unsigned \\)?" ; .2 | |
664 "\\(struct \\|class \\|union \\|enum \\)?" ; .3 | |
665 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; .4 | |
666 "\\)" ; 1 | |
667 " \\((\\*) ?\\)?" ; 5 | |
668 "\\((.*)\\)" ; 6 | |
669 "[)}] +" ; | |
670 "\\(0x[0-9A-Fa-f]+\\) +" ; 7 | |
671 "<\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)" ; 8 | |
672 "\\+?[0-9]+?>" ; | |
673 ) | |
674 '(gdb-type-name-token ; 1 (2+3+4) | |
675 nil ; 2 | |
676 nil ; 3 | |
677 nil ; 4 | |
678 nil ; 5 | |
679 gdb-arglist-types-token ; 6 | |
680 nil ;gdb-address-token ; 7 | |
681 gdb-function-name-token ; 8 | |
682 )) | |
683 | |
684 ;; Local variables and structures: | |
685 ;; | |
686 ;; shell = (struct _WidgetRec *) 0x10267350 | |
687 ;; delete_response = 270955344 | |
688 ;; allow_resize = 200 'È' | |
689 ;; is_modal = 47 '/' | |
690 ;; class_name = 0xf661d40 "TopLevelShell", | |
691 ;; static foo = 0x10791ec0, | |
692 ;; initialize = 0xf684770 <TopLevelInitialize>, | |
693 ;; av = {{ | |
694 ;; name = "foo", | |
695 ;; value = 270349836 | |
696 ;; }, { | |
697 ;; name = 0x12 <Address 0x12 out of bounds>, | |
698 ;; value = 0 | |
699 ;; }, { | |
700 ;; name = 0x0, | |
701 ;; value = 0 | |
702 ;; }} | |
703 ;; | |
704 (list (concat " *" | |
705 "\\(static \\)?" ; 1 | |
706 "\\([$a-zA-Z_][a-zA-Z0-9_:]*\\) = " ; 2 | |
707 "\\((" ; 3 | |
708 "\\(" ; .4 | |
709 "\\(signed \\|unsigned \\)?" ; ..5 | |
710 "\\(struct \\|class \\|union \\|enum \\)?"; ..6 | |
711 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; ..7 | |
712 "\\)" ; .4 | |
713 "[ *]*)" ; | |
714 "\\)?" ; 3 | |
715 "\\(" ; 8 | |
716 ".*" | |
717 " <\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)" ; .9 | |
718 "\\+?[0-9]+?>" ; . | |
719 "\\)?" ; 8 | |
720 ) | |
721 '(nil ; 1 | |
722 gdb-variable-name-token ; 2 | |
723 nil ; 3 | |
724 gdb-type-name-token ; 4 | |
725 nil ; 5 | |
726 nil ; 6 | |
727 nil ; 7 | |
728 nil ; 8 | |
729 gdb-function-name-token ; 9 | |
730 )) | |
731 | |
732 ;; Purify output: | |
733 ;; UMR: Uninitialized memory read: | |
734 ;; * This is occurring while in: | |
735 ;; SHA1_Update [algsha.c:137] | |
736 ;; * Reading 1 byte from 0xefffdb34 on the stack. | |
737 (list (concat "[ \t]+" | |
738 "\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)[ \t]*" ; 1 | |
739 "\\[\\(" ; 2 | |
740 "\\([^ \t\n:]+\\):" ; .3 | |
741 "\\([0-9]+\\)" ; .4 | |
742 "\\)\\]" ; 2 | |
743 ) | |
744 '(gdb-function-name-token ; 1 | |
745 gdb-function-location-token ; 2 (3+4) | |
746 )) | |
747 | |
748 ;; Purify output: | |
749 ;; * Address 0xefffdb34 is 36 bytes past start of local variable \ | |
750 ;; "data" in function fe_EventForRNG. | |
751 (list (concat ".*\\bAddress " | |
752 "\\(0x[0-9A-Fa-f]+\\) +" ; 1 | |
753 ".*\\bvariable \"" ; | |
754 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)\"" ; 2 | |
755 "\\(" ; 3 | |
756 ".*\\bfunction " ; . | |
757 "\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)" ; .4 | |
758 "\\)?" ; 3 | |
759 ) | |
760 '(nil ;gdb-address-token ; 1 | |
761 gdb-variable-name-token ; 2 | |
762 nil ; 3 | |
763 gdb-function-name-token ; 4 | |
764 )) | |
765 )) | |
766 "Patterns to highlight in gdb buffers. | |
767 Each element of this list takes the form | |
768 ( \"regexp\" ( token-1 token-2 ... )) | |
769 where token-N is the token to place on the text matched | |
770 by sub-pattern N in the match data. | |
771 | |
772 The patterns should not begin with \"^\".") | |
773 | |
774 | |
775 (defun gdb-highlight-line () | |
776 "Highlight any tokens on the line which match gdb-highlight-token-patterns." | |
777 (map-extents #'(lambda (e ignore) (delete-extent e)) | |
778 nil | |
779 (point) (save-excursion (forward-line 1) (point)) | |
780 nil nil 'gdb-token) | |
781 (while (looking-at comint-prompt-regexp) | |
782 (goto-char (match-end 0))) | |
783 (if (eobp) | |
784 nil | |
785 (let ((tokens gdb-highlight-token-patterns) | |
786 (do-magic-variable-hack nil)) | |
787 (while tokens | |
788 (if (not (looking-at (car (car tokens)))) | |
789 (setq tokens (cdr tokens)) | |
790 (let ((i 1) | |
791 (types (nth 1 (car tokens)))) | |
792 (if (eq (car types) 'gdb-variable-name-token) | |
793 (setq do-magic-variable-hack t)) | |
794 (while types | |
795 (cond ((not (and (car types) | |
796 (match-beginning i))) | |
797 nil) | |
798 ((memq (car types) '(gdb-arglist-token | |
799 gdb-arglist-types-token)) | |
800 (gdb-highlight-arglist (car types) | |
801 (match-beginning i) | |
802 (match-end i))) | |
803 ((/= ?$ (char-after (match-beginning i))) | |
804 (gdb-highlight-token (car types) | |
805 (match-beginning i) | |
806 (match-end i)))) | |
807 (setq i (1+ i) | |
808 types (cdr types))) | |
809 | |
810 (if (not do-magic-variable-hack) | |
811 ;; we're done. | |
812 (setq tokens nil) | |
813 ;; else, do a grody hack to cope with multiple variables | |
814 ;; on the same line. | |
815 (save-restriction | |
816 (let ((p (point)) | |
817 (ok nil)) | |
818 (end-of-line) | |
819 (narrow-to-region p (point)) | |
820 (goto-char (match-end 0)) | |
821 (if (= (following-char) ?\{) | |
822 (progn | |
823 (forward-char 1) | |
824 (setq ok t)) | |
825 (setq p (scan-sexps (point) 1 nil t)) | |
826 (setq ok (if (null p) | |
827 nil | |
828 (goto-char p) | |
829 (if (or (= (following-char) ?\,) | |
830 (= (following-char) ?\})) | |
831 t | |
832 (setq p (scan-sexps (point) 1 nil t)) | |
833 (if (null p) | |
834 nil | |
835 (goto-char p) | |
836 t))))) | |
837 (if ok | |
838 ;; skip over the comma and go around again. | |
839 (and (looking-at "}?[ \t]*,[ \t]*") | |
840 (goto-char (match-end 0))) | |
841 ;; saw something unexpected; give up on this line. | |
842 (setq tokens nil))))) | |
843 ))))) | |
844 nil) | |
845 | |
846 (defun gdb-highlight-token (type start end) | |
847 "Helper for gdb-highlight-line -- makes an extent for one matched token." | |
848 (let ((e (make-extent start end))) | |
849 (set-extent-property e 'gdb-token type) | |
850 (set-extent-property e 'highlight 't) | |
851 (set-extent-property e 'help-echo 'gdb-token-help-echo) | |
852 (set-extent-property e 'face (get type 'gdb-token-face)) | |
853 (set-extent-property e 'keymap (get type 'gdb-token-keymap)) | |
854 e)) | |
855 | |
856 (defun gdb-highlight-arglist (type start end) | |
857 "Helper for gdb-highlight-line. | |
858 Makes extents for variables or types in an arglist." | |
859 (save-match-data | |
860 (save-excursion | |
861 (goto-char end) | |
862 (if (eq (preceding-char) ?\)) | |
863 (setq end (1- end))) | |
864 (goto-char start) | |
865 (if (eq (following-char) ?\() | |
866 (forward-char 1)) | |
867 (set-extent-property (make-extent start end) 'gdb-token type) | |
868 | |
869 (cond | |
870 ((eq type 'gdb-arglist-token) | |
871 (let* ((pat1 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)=") | |
872 (pat2 ", \\([a-zA-Z_][a-zA-Z0-9_:]*\\)=") | |
873 (pat pat1)) | |
874 (while(re-search-forward pat end t) | |
875 (gdb-highlight-token 'gdb-variable-name-token | |
876 (match-beginning 1) (match-end 1)) | |
877 (cond ((looking-at | |
878 "0?x?[0-9A-Fa-f]+ <\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)>") | |
879 (goto-char (match-end 0)) | |
880 (gdb-highlight-token 'gdb-function-name-token | |
881 (match-beginning 1) (match-end 1)))) | |
882 (setq pat pat2)))) | |
883 | |
884 ((eq type 'gdb-arglist-types-token) | |
885 (let ((pat (eval-when-compile | |
886 (concat | |
887 "\\(" ; 1 | |
888 "\\(signed \\|unsigned \\)?" ; .2 | |
889 "\\(struct \\|class \\|union \\|enum \\)?" ; .3 | |
890 "\\(<?[a-zA-Z_~][a-zA-Z0-9_:]*>?\\)" ; .4 | |
891 "\\)" ; 1 | |
892 "[ *]*" | |
893 "\\((\\*) *(.*)\\)?" ; 5 | |
894 )))) | |
895 (while (< (point) end) | |
896 (cond ((looking-at pat) | |
897 (goto-char (match-end 0)) | |
898 (gdb-highlight-token 'gdb-type-name-token | |
899 (match-beginning 1) (match-end 1)) | |
900 (if (looking-at " *, *") | |
901 (goto-char (match-end 0)))) | |
902 (t | |
903 ;; error -- try to cope... | |
904 (search-forward "," (1+ end) t)))))) | |
905 (t | |
906 (error "unknown arglist type %s" type))))) | |
907 nil) | |
908 | |
909 (defun gdb-token-help-echo (extent) | |
910 "Used as the 'mouse-help property of gdb-token extents, | |
911 to describe the binding on button2." | |
912 (let* ((map (extent-property extent 'keymap)) | |
913 (key 'button2) | |
914 (fn (and map (lookup-key map key))) | |
915 (doc (and fn (symbolp fn) | |
916 (if (fboundp fn) | |
917 (format "%s: %s" key (documentation fn)) | |
918 (format "Error: %s is undefined" fn))))) | |
919 (if doc | |
920 (save-match-data | |
921 (if (string-match "\n" doc) | |
922 (setq doc (substring doc 0 (match-beginning 0)))))) | |
923 (or doc | |
924 (concat "Error: no doc for " | |
925 (symbol-name (extent-property extent 'gdb-token)))))) | |
926 | |
927 (defun gdb-get-line-token-extents (tokens) | |
928 "Given a list of gdb-tokens, returns this line's extents of those types. | |
929 The returned value is a list of the same length as the `tokens' list, with | |
930 the corresponding extents in the corresponding positions. If an extent | |
931 isn't found, nil is placed in the result-list instead." | |
932 (setq tokens (append tokens nil)) | |
933 (let* ((result (make-list (length tokens) nil))) | |
934 (save-excursion | |
935 (beginning-of-line) | |
936 (map-extents #'(lambda (e ignore) | |
937 (let ((type (extent-property e 'gdb-token)) | |
938 (r1 tokens) | |
939 (r2 result)) | |
940 (while r1 | |
941 (cond ((and (car r1) (eq type (car r1))) | |
942 (setcar r1 nil) | |
943 (setcar r2 e) | |
944 (setq r1 nil))) | |
945 (setq r1 (cdr r1) | |
946 r2 (cdr r2)))) | |
947 nil) | |
948 nil | |
949 (point) | |
950 (progn (forward-line 1) (point)) | |
951 nil nil | |
952 'gdb-token) | |
953 result))) | |
954 | |
955 | |
956 ;;; Remembering directory names. | |
957 ;;; gdb and gdb-mode conspire to hide from us the full file names of things | |
958 ;;; that are presented into the buffer; this is an attempt to circumvent that. | |
959 | |
960 (defvar gdb-highlight-last-directory nil) | |
961 (defvar gdb-highlight-last-directory-table nil) | |
962 | |
963 (defun gdb-highlight-remember-directory () | |
964 ;; When gdb deigns to give us a full pathname, and it's in a different | |
965 ;; directory than last time, cache it away on one of the nearby gdb-token | |
966 ;; extents. (We intern it to avoid hanging on to a lot of strings.) | |
967 (cond ((and (boundp 'gdb-last-frame) | |
968 (car gdb-last-frame)) | |
969 (cond ((not gdb-highlight-last-directory-table) | |
970 (set (make-local-variable 'gdb-highlight-last-directory) nil) | |
971 (set (make-local-variable 'gdb-highlight-last-directory-table) | |
972 (make-vector 211 0)))) | |
973 (let ((dir (file-name-directory (car gdb-last-frame)))) | |
974 (setq dir (intern dir gdb-highlight-last-directory-table)) | |
975 (cond ((not (eq dir gdb-highlight-last-directory)) | |
976 (let ((extent (previous-extent (current-buffer)))) | |
977 (setq gdb-highlight-last-directory dir) | |
978 (while extent | |
979 (cond ((extent-property extent 'gdb-token) | |
980 (set-extent-property extent 'gdb-directory dir) | |
981 (setq extent nil)) | |
982 (t | |
983 (setq extent (previous-extent extent)))))))))))) | |
984 | |
985 (defun gdb-guess-directory () | |
986 "Guess what directory gdb was talking about when it wrote the current line." | |
987 (let ((extent (or (map-extents #'(lambda (e ignore) e) | |
988 (current-buffer) (point) (point-max)) | |
989 (previous-extent (current-buffer)) | |
990 (error "no extents"))) | |
991 (dir nil)) | |
992 (while extent | |
993 (setq dir (extent-property extent 'gdb-directory)) | |
994 (if dir | |
995 (setq extent nil) | |
996 (setq extent (previous-extent extent)))) | |
997 (if dir | |
998 (symbol-name dir) | |
999 default-directory))) | |
1000 | |
1001 (defun gdb-guess-file-name (file) | |
1002 "Given a directoryless file name printed by gdb, find the file. | |
1003 First it tries to expand the file relative to `gdb-guess-directory', | |
1004 and if the resultant file doesn't exist, it tries every other directory | |
1005 gdb has ever told us about, in no particular order." | |
1006 (abbreviate-file-name | |
1007 (if (file-name-absolute-p file) | |
1008 file | |
1009 (let ((file2 (expand-file-name file (gdb-guess-directory)))) | |
1010 (if (file-exists-p file2) | |
1011 file2 | |
1012 ;; Oh boy, gdb didn't tell us what directory it's in. | |
1013 ;; A-hunting we will go. | |
1014 (if (catch 'done | |
1015 (mapatoms #'(lambda (dir) | |
1016 (setq file2 (expand-file-name file | |
1017 (symbol-name dir))) | |
1018 (if (file-exists-p file2) | |
1019 (throw 'done t))) | |
1020 gdb-highlight-last-directory-table) | |
1021 nil) | |
1022 file2 | |
1023 (expand-file-name file))))))) | |
1024 | |
1025 | |
1026 ;;; Commands which are invoked from bindings in the keymaps of the tokens. | |
1027 | |
1028 (defun gdb-mouse-toggle-breakpoint-enabled (event &optional what) | |
1029 "Toggle whether the breakpoint is enabled. | |
1030 Looks for a gdb-breakpoint extent on the line under the mouse, | |
1031 and executes an `enable' or `disable' command as appropriate. | |
1032 Optional arg `what' may be 'enable, 'disable, or 'toggle (default.)" | |
1033 (interactive "@*e") | |
1034 (let (number target enabled-p) | |
1035 (save-excursion | |
1036 (mouse-set-point event) | |
1037 (let* ((extents (gdb-get-line-token-extents | |
1038 '(gdb-breakpoint-number-token | |
1039 gdb-info-breakpoint-number-token | |
1040 gdb-enabled-token))) | |
1041 (be (or (nth 0 extents) (nth 1 extents))) | |
1042 (ee (nth 2 extents))) | |
1043 | |
1044 (or be | |
1045 (error "no breakpoint-number extent on this line")) | |
1046 (setq number | |
1047 (buffer-substring (extent-start-position be) | |
1048 (extent-end-position be))) | |
1049 (if (string-match " [0-9]+\\'" number) | |
1050 (setq number (substring number (1+ (match-beginning 0))))) | |
1051 (setq number (string-to-int number)) | |
1052 (or (> number 0) | |
1053 (error "couldn't find breakpoint number")) | |
1054 (if (null ee) | |
1055 (setq enabled-p 'unknown) | |
1056 (setq target (extent-start-position ee)) | |
1057 (goto-char target) | |
1058 (setq enabled-p | |
1059 (cond ((looking-at "[yY]\\b") t) | |
1060 ((looking-at "[nN]\\b") nil) | |
1061 (t (error "enabled is not y or n?"))))) | |
1062 | |
1063 (cond ((eq what 'enable) | |
1064 (setq enabled-p nil)) | |
1065 ((eq what 'disable) | |
1066 (setq enabled-p t)) | |
1067 ((or (eq what 'toggle) (null what)) | |
1068 (if (eq enabled-p 'unknown) | |
1069 (error | |
1070 "can't toggle breakpoint: don't know current state"))) | |
1071 (t | |
1072 (error "what must be enable, disable, toggle, or nil."))) | |
1073 )) | |
1074 | |
1075 (gdb-menu-command (format "%s %d" | |
1076 (if enabled-p "disable" "enable") | |
1077 number) | |
1078 nil) | |
1079 (message "%s breakpoint %d." | |
1080 (if enabled-p "Disabled" "Enabled") | |
1081 number) | |
1082 (cond (target | |
1083 (save-excursion | |
1084 (goto-char target) | |
1085 (insert (if enabled-p "n" "y")) | |
1086 (delete-char 1) | |
1087 ;; don't let shell-fonts or font-lock second-guess us. | |
1088 (remove-text-properties (1- (point)) (point) '(face)))))) | |
1089 nil) | |
1090 | |
1091 (defun gdb-mouse-enable-breakpoint (event) | |
1092 "Enable the breakpoint. | |
1093 Looks for a gdb-breakpoint extent on the line under the mouse, | |
1094 and executes an `enable' command" | |
1095 (interactive "@*e") | |
1096 (gdb-mouse-toggle-breakpoint-enabled event 'enable)) | |
1097 | |
1098 (defun gdb-mouse-disable-breakpoint (event) | |
1099 "Disable the breakpoint. | |
1100 Looks for a gdb-breakpoint extent on the line under the mouse, | |
1101 and executes a `disable' command" | |
1102 (interactive "@*e") | |
1103 (gdb-mouse-toggle-breakpoint-enabled event 'disable)) | |
1104 | |
1105 | |
1106 ;; compatibility hack... | |
1107 (or (fboundp 'extent-object) (fset 'extent-object 'extent-buffer)) | |
1108 | |
1109 (defun gdb-mouse-edit-function (event) | |
1110 "Edit the definition of this function (as with \\[find-tag]) | |
1111 Looks for a gdb-function-name extent on the line under the mouse, | |
1112 and runs find-tag on the text under that extent." | |
1113 (interactive "@*e") | |
1114 (let (extent) | |
1115 (save-excursion | |
1116 (mouse-set-point event) | |
1117 (setq extent (or (car (gdb-get-line-token-extents | |
1118 '(gdb-function-name-token))) | |
1119 (error "no function-name extent on this line")))) | |
1120 (find-tag | |
1121 (buffer-substring (extent-start-position extent) | |
1122 (extent-end-position extent) | |
1123 (extent-object extent))))) | |
1124 | |
1125 | |
1126 (defun gdb-mouse-edit-function-location (event) | |
1127 "Edit the source file of this function. | |
1128 Looks for a gdb-function-location extent on line of the mouse, | |
1129 and parses the text under it." | |
1130 (interactive "@*e") | |
1131 (let (file line) | |
1132 (save-excursion | |
1133 (mouse-set-point event) | |
1134 (let ((extent (or (car (gdb-get-line-token-extents | |
1135 '(gdb-function-location-token))) | |
1136 (error "no function-location extent on this line")))) | |
1137 (goto-char (extent-start-position extent)) | |
1138 (or (looking-at "\\([^ \t\n:,]+\\):\\([0-9]+\\)") | |
1139 (looking-at "\\([^ \t\n:,]+\\),? line \\([0-9]+\\)") | |
1140 (error "no file position on this line")) | |
1141 (setq file (buffer-substring (match-beginning 1) (match-end 1)) | |
1142 line (buffer-substring (match-beginning 2) (match-end 2))) | |
1143 (setq file (gdb-guess-file-name file) | |
1144 line (string-to-int line)) | |
1145 )) | |
1146 (if (file-exists-p file) | |
1147 (find-file-other-window file) | |
1148 (signal 'file-error (list "File not found" file))) | |
1149 (goto-line line))) | |
1150 | |
1151 | |
1152 (defun gdb-mouse-goto-frame (event) | |
1153 "Select this stack frame. | |
1154 Looks for a gdb-frame-number extent on the line of the mouse, | |
1155 and executes a `frame' command to select that frame." | |
1156 (interactive "@*e") | |
1157 (let (number) | |
1158 (save-excursion | |
1159 (mouse-set-point event) | |
1160 (let ((extent (or (car (gdb-get-line-token-extents | |
1161 '(gdb-frame-number-token))) | |
1162 (error "no frame-number extent on this line")))) | |
1163 (goto-char (extent-start-position extent)) | |
1164 (if (eq (following-char) ?#) | |
1165 (forward-char 1)) | |
1166 (setq number (string-to-int | |
1167 (buffer-substring (point) | |
1168 (extent-end-position extent)))))) | |
1169 (gdb-menu-command (format "frame %d" number) t)) | |
1170 nil) | |
1171 | |
1172 | |
1173 (defun gdb-mouse-get-variable-reference (event) | |
1174 "Returns a string which references the variable under the mouse. | |
1175 This works even if the variable is deep inside nested arrays or structures. | |
1176 If the variable seems to hold a pointer, then a \"*\" will be prepended." | |
1177 (save-excursion | |
1178 (let* ((extent (if (extentp event) | |
1179 event | |
1180 (progn | |
1181 (mouse-set-point event) | |
1182 (extent-at (point) nil 'gdb-token)))) | |
1183 dereference-p | |
1184 name) | |
1185 (or (and extent | |
1186 (eq (extent-property extent 'gdb-token) | |
1187 'gdb-variable-name-token)) | |
1188 (error "not over a variable name")) | |
1189 (setq name (buffer-substring (extent-start-position extent) | |
1190 (extent-end-position extent))) | |
1191 (save-excursion | |
1192 (goto-char (extent-end-position extent)) | |
1193 (if (and (looking-at " *= *\\(([^)]+)\\)? *0x[0-9a-fA-F]+") ; pointer | |
1194 (progn | |
1195 (goto-char (match-end 0)) | |
1196 (not (looking-at " +\"")))) ; but not string | |
1197 (setq dereference-p t)) | |
1198 | |
1199 ;; Now, if this variable is buried in a structure, compose a complete | |
1200 ;; reference-chain to it. | |
1201 (goto-char (extent-start-position extent)) | |
1202 | |
1203 (let ((done nil)) | |
1204 (while (not done) | |
1205 (skip-chars-backward " \t") | |
1206 (if (or (and (/= (preceding-char) ?\n) | |
1207 (/= (preceding-char) ?\,) | |
1208 (/= (preceding-char) ?\{)) | |
1209 (<= (buffer-syntactic-context-depth) 0)) | |
1210 (setq done t) | |
1211 (let ((p (scan-lists (point) -1 1))) | |
1212 (if (null p) | |
1213 (setq done t) | |
1214 (goto-char (setq p (- p 3))) | |
1215 (cond | |
1216 ((looking-at " = {") | |
1217 (skip-chars-backward "a-zA-Z0-9_") | |
1218 (if (= (preceding-char) ?\$) | |
1219 (forward-char -1)) | |
1220 (setq name (concat (buffer-substring (point) p) "." name))) | |
1221 | |
1222 ((looking-at "}, +{") | |
1223 (forward-char 1) | |
1224 (let ((parse-sexp-ignore-comments nil) | |
1225 (count 0)) | |
1226 (while (setq p (scan-sexps (point) -1 nil t)) | |
1227 (goto-char p) | |
1228 (setq count (1+ count))) | |
1229 | |
1230 (setq name (format "[%d].%s" count name)) | |
1231 | |
1232 ;; up out of the list | |
1233 (skip-chars-backward " \t\n") | |
1234 (if (= (preceding-char) ?\{) | |
1235 (forward-char -1)) | |
1236 | |
1237 ;; we might be tightly nested in slot 0... | |
1238 (while (= (preceding-char) ?\{) | |
1239 (forward-char -1) | |
1240 (setq name (concat "[0]" name))) | |
1241 | |
1242 (skip-chars-backward " \t") | |
1243 (if (= (preceding-char) ?=) (forward-char -1)) | |
1244 (skip-chars-backward " \t") | |
1245 (setq p (point)) | |
1246 (skip-chars-backward "a-zA-Z0-9_") | |
1247 (if (= (preceding-char) ?\$) | |
1248 (forward-char -1)) | |
1249 | |
1250 (setq name (concat (buffer-substring (point) p) name)) | |
1251 )) | |
1252 (t | |
1253 (setq done t))))))))) | |
1254 | |
1255 (if dereference-p | |
1256 (setq name (concat "*" name))) | |
1257 name))) | |
1258 | |
1259 (defun gdb-mouse-print-variable (event) | |
1260 "Print the value of this variable. | |
1261 Finds a variable under the mouse, and figures out whether it is inside of | |
1262 a structure, and composes and executes a `print' command. If the variable | |
1263 seems to hold a pointer, prints the object pointed to." | |
1264 (interactive "@*e") | |
1265 (gdb-menu-command (concat "print " | |
1266 (gdb-mouse-get-variable-reference event)) | |
1267 t)) | |
1268 | |
1269 (defun gdb-mouse-print-variable-type (event) | |
1270 "Describe the type of this variable. | |
1271 Finds a variable under the mouse, and figures out whether it is inside of | |
1272 a structure, and composes and executes a `whatis' command. If the variable | |
1273 seems to hold a pointer, describes the type of the object pointed to." | |
1274 (interactive "@*e") | |
1275 (gdb-menu-command (concat "whatis " | |
1276 (gdb-mouse-get-variable-reference event)) | |
1277 t)) | |
1278 | |
1279 (defun gdb-mouse-print-type (event) | |
1280 "Describe this type. | |
1281 Finds a type description under the mouse, and executes a `ptype' command." | |
1282 (interactive "@*e") | |
1283 (let* ((extent (save-excursion | |
1284 (mouse-set-point event) | |
1285 (extent-at (point) nil 'gdb-token))) | |
1286 name) | |
1287 (or (and extent | |
1288 (eq (extent-property extent 'gdb-token) 'gdb-type-name-token)) | |
1289 (error "not over a type name")) | |
1290 (setq name (buffer-substring (extent-start-position extent) | |
1291 (extent-end-position extent))) | |
1292 (gdb-menu-command (format "ptype %s" name) | |
1293 t)) | |
1294 nil) | |
1295 | |
1296 | |
1297 ;;; Popup menus | |
1298 | |
1299 (defun gdb-menu-command (command &optional scroll-to-bottom) | |
1300 "Sends the command to gdb. | |
1301 If gdb is not sitting at a prompt, interrupts it first | |
1302 \(as if with \\[gdb-control-c-subjob]), executes the command, and then lets | |
1303 the debugged program continue. | |
1304 | |
1305 If scroll-to-bottom is true, then point will be moved to after the new | |
1306 output. Otherwise, an effort is made to avoid scrolling the window and | |
1307 to keep point where it was." | |
1308 | |
1309 ;; this is kinda like gdb-call except for the interrupt-first behavior, | |
1310 ;; but also it leaves the commands in the buffer instead of trying to | |
1311 ;; hide them. | |
1312 | |
1313 (let* ((proc (or (get-buffer-process (current-buffer)) | |
1314 (error "no process in %s" (buffer-name (current-buffer))))) | |
1315 (window (selected-window)) | |
1316 wstart | |
1317 (opoint (point)) | |
1318 was-at-bottom | |
1319 running-p) | |
1320 | |
1321 (if (not (eq (current-buffer) (window-buffer window))) | |
1322 (setq window (get-buffer-window (current-buffer)))) | |
1323 (setq wstart (window-start window)) | |
1324 | |
1325 (let ((pmark (process-mark proc))) | |
1326 (setq was-at-bottom (>= (point) pmark)) | |
1327 (goto-char pmark) | |
1328 (delete-region (point) (point-max))) | |
1329 | |
1330 (setq running-p (bolp)) ; maybe not the best way to tell... | |
1331 | |
1332 (cond (running-p | |
1333 (message "Program is running -- interrupting first...") | |
1334 (gdb-control-c-subjob) | |
1335 (while (accept-process-output proc 1) | |
1336 ;; continue accepting output as long as it's arriving | |
1337 ))) | |
1338 | |
1339 (message "%s" command) | |
1340 (goto-char (process-mark proc)) | |
1341 (insert command) | |
1342 (comint-send-input) | |
1343 | |
1344 ;; wait for the command to be accepted | |
1345 (accept-process-output proc) | |
1346 (goto-char (process-mark proc)) | |
1347 | |
1348 ;; continue, if we had interrupted | |
1349 (cond (running-p | |
1350 (insert "continue") | |
1351 (comint-send-input))) | |
1352 | |
1353 (if scroll-to-bottom | |
1354 (goto-char (process-mark proc)) | |
1355 | |
1356 (set-window-start window wstart) | |
1357 (goto-char opoint) | |
1358 (if was-at-bottom | |
1359 (if (pos-visible-in-window-p (process-mark proc) window) | |
1360 (goto-char (process-mark proc)) | |
1361 (goto-char (window-end window)) | |
1362 (forward-line -2)))) | |
1363 ) | |
1364 nil) | |
1365 | |
1366 | |
1367 (defun gdb-make-context-menu (event) | |
1368 "Returns a menu-desc corresponding to the stack-frame line under the mouse. | |
1369 Returns nil if not over a stack-frame." | |
1370 (save-excursion | |
1371 (mouse-set-point event) | |
1372 (let* ((extents (gdb-get-line-token-extents | |
1373 '(gdb-breakpoint-number-token | |
1374 gdb-info-breakpoint-number-token | |
1375 gdb-enabled-token | |
1376 gdb-frame-number-token | |
1377 gdb-function-name-token | |
1378 gdb-function-location-token | |
1379 gdb-arglist-token | |
1380 gdb-arglist-types-token | |
1381 gdb-variable-name-token | |
1382 gdb-type-name-token | |
1383 ))) | |
1384 (bnumber (or (nth 0 extents) | |
1385 (nth 1 extents))) | |
1386 (enabled-p (nth 2 extents)) | |
1387 (fnumber (nth 3 extents)) | |
1388 (name (nth 4 extents)) | |
1389 (loc (nth 5 extents)) | |
1390 (al (nth 6 extents)) | |
1391 (alt (nth 7 extents)) | |
1392 (var (nth 8 extents)) | |
1393 (type (nth 9 extents)) | |
1394 (var-e var)) | |
1395 | |
1396 ;; If this line has an arglist, only document variables and types | |
1397 ;; if the mouse is directly over them. | |
1398 (if (or al alt) | |
1399 (setq var nil | |
1400 type nil)) | |
1401 | |
1402 ;; Always prefer the object under the mouse to one elsewhere on the line. | |
1403 (let* ((e (extent-at (point) nil 'gdb-token)) | |
1404 (p (and e (extent-property e 'gdb-token)))) | |
1405 (cond ((eq p 'gdb-function-name-token) (setq name e)) | |
1406 ((eq p 'gdb-variable-name-token) (setq var e var-e e)) | |
1407 ((eq p 'gdb-type-name-token) (setq type e)) | |
1408 )) | |
1409 | |
1410 ;; Extract the frame number (it may begin with "#".) | |
1411 (cond (fnumber | |
1412 (goto-char (extent-start-position fnumber)) | |
1413 (if (eq (following-char) ?#) | |
1414 (forward-char 1)) | |
1415 (setq fnumber | |
1416 (string-to-int | |
1417 (buffer-substring (point) | |
1418 (extent-end-position fnumber)))))) | |
1419 | |
1420 ;; Extract the breakpoint number (it may begin with "Breakpoint ".) | |
1421 (cond (bnumber | |
1422 (setq bnumber | |
1423 (buffer-substring (extent-start-position bnumber) | |
1424 (extent-end-position bnumber))) | |
1425 (if (string-match " [0-9]+\\'" bnumber) | |
1426 (setq bnumber (substring bnumber (1+ (match-beginning 0))))) | |
1427 (setq bnumber (string-to-int bnumber)) | |
1428 (or (> bnumber 0) | |
1429 (error "couldn't parse breakpoint number")))) | |
1430 | |
1431 (cond ((null enabled-p) | |
1432 (setq enabled-p 'unknown)) | |
1433 ((memq (char-after (extent-start-position enabled-p)) '(?y ?Y)) | |
1434 (setq enabled-p 't)) | |
1435 ((memq (char-after (extent-start-position enabled-p)) '(?n ?N)) | |
1436 (setq enabled-p 'nil)) | |
1437 (t | |
1438 (setq enabled-p 'unknown))) | |
1439 | |
1440 ;; Convert the extents to strings. | |
1441 ;; | |
1442 (if name | |
1443 (setq name (buffer-substring (extent-start-position name) | |
1444 (extent-end-position name)))) | |
1445 (if loc | |
1446 (setq loc (buffer-substring (extent-start-position loc) | |
1447 (extent-end-position loc)))) | |
1448 (if var | |
1449 (setq var (buffer-substring (extent-start-position var) | |
1450 (extent-end-position var)))) | |
1451 (if type | |
1452 (setq type (buffer-substring (extent-start-position type) | |
1453 (extent-end-position type)))) | |
1454 | |
1455 ;; Return a menu description list. | |
1456 ;; | |
1457 (nconc | |
1458 (if (and bnumber (not (eq enabled-p 'nil))) | |
1459 (list (vector (format "Disable Breakpoint %d" | |
1460 bnumber) | |
1461 (list 'gdb-mouse-disable-breakpoint event) | |
1462 t))) | |
1463 (if (and bnumber (not (eq enabled-p 't))) | |
1464 (list (vector (format "Enable Breakpoint %d" | |
1465 bnumber) | |
1466 (list 'gdb-mouse-enable-breakpoint event) | |
1467 t))) | |
1468 (if bnumber | |
1469 (list (vector (format "Delete Breakpoint %d" bnumber) | |
1470 (list 'gdb-menu-command (format "delete %d" bnumber) | |
1471 nil) | |
1472 t))) | |
1473 (if var | |
1474 (list (vector (format "Print Value of `%s'" var) | |
1475 (list 'gdb-mouse-print-variable var-e) | |
1476 t) | |
1477 (vector (format "Print Type of `%s'" var) | |
1478 (list 'gdb-mouse-print-variable-type var-e) | |
1479 t))) | |
1480 (if name | |
1481 (list (vector (format "Edit Definition of `%s'" name) | |
1482 (list 'gdb-mouse-edit-function event) | |
1483 t) | |
1484 (vector (format "Set Breakpoint on `%s'" name) | |
1485 (list 'gdb-menu-command (format "break %s" name) nil) | |
1486 t))) | |
1487 (if loc | |
1488 (list (vector (format "Visit Source Line (%s)" loc) | |
1489 (list 'gdb-mouse-edit-function-location event) | |
1490 t))) | |
1491 (if type | |
1492 (list (vector (format "Describe Type `%s'" type) | |
1493 (list 'gdb-menu-command (format "ptype %s" type) t) | |
1494 t))) | |
1495 (if fnumber | |
1496 (list (vector (format "Select Stack Frame %d" fnumber) | |
1497 (list 'gdb-menu-command (format "frame %d" fnumber) t) | |
1498 t))) | |
1499 )))) | |
1500 | |
1501 | |
1502 (defun gdb-popup-menu (event) | |
1503 "Pop up a context-sensitive menu of gdb-mode commands." | |
1504 (interactive "_@e") | |
1505 (select-window (event-window event)) | |
1506 (let (menu) | |
1507 (save-excursion | |
1508 (setq menu (append (if (boundp 'gdb-popup-menu) | |
1509 (append (cdr gdb-popup-menu) | |
1510 '("---"))) | |
1511 (if (boundp 'comint-popup-menu) | |
1512 (cdr comint-popup-menu)))) | |
1513 (let ((history (if (fboundp 'comint-make-history-menu) | |
1514 (comint-make-history-menu))) | |
1515 (context (gdb-make-context-menu event))) | |
1516 (if history | |
1517 (setq menu | |
1518 (append menu (list "---" (cons "Command History" history))))) | |
1519 (if context | |
1520 (setq menu (append context (cons "---" menu)))) | |
1521 ) | |
1522 (setq menu (cons (if (boundp 'gdb-popup-menu) | |
1523 (car gdb-popup-menu) | |
1524 "GDB Commands") | |
1525 menu))) | |
1526 (popup-menu menu event))) | |
1527 | |
1528 | |
1529 ;;; Patch it in... | |
1530 | |
1531 (or (fboundp 'gdb-highlight-orig-filter) | |
1532 (fset 'gdb-highlight-orig-filter (symbol-function 'gdb-filter))) | |
1533 | |
1534 (defun gdb-highlight-filter (proc string) | |
1535 (let ((p (marker-position (process-mark proc)))) | |
1536 (prog1 | |
1537 (gdb-highlight-orig-filter proc string) | |
1538 | |
1539 (save-match-data | |
1540 ;; | |
1541 ;; If there are no newlines in this string at all, then don't | |
1542 ;; bother processing it -- we will pick up these characters on | |
1543 ;; the next time around, when the line's newline gets inserted. | |
1544 ;; | |
1545 (cond | |
1546 ((string-match "\n" string) | |
1547 (save-excursion | |
1548 (set-buffer (process-buffer proc)) | |
1549 (goto-char p) | |
1550 (let ((p2 (marker-position (process-mark proc))) | |
1551 p3) | |
1552 ;; | |
1553 ;; If gdb has given us a full pathname, remember it. (Do this | |
1554 ;; before emitting any gdb-token extents, so that we attach it | |
1555 ;; to the buffer *before* any of the extents to which it is | |
1556 ;; known to correspond. | |
1557 ;; | |
1558 (gdb-highlight-remember-directory) | |
1559 ;; | |
1560 ;; Now highlight each line that has been written. If we wrote | |
1561 ;; the last half of a line, re-highlight that whole line. (We | |
1562 ;; need to do that so that the regexps will match properly; | |
1563 ;; the "\n" test above also depends on this behavior.) | |
1564 ;; | |
1565 ;; But don't highlight lines longer than 5000 characters -- that | |
1566 ;; probably means something is spewing, and we'll just get stuck | |
1567 ;; hard in the regexp matcher. | |
1568 ;; | |
1569 (beginning-of-line) | |
1570 (while (< (point) p2) | |
1571 (goto-char (prog1 | |
1572 (point) | |
1573 (forward-line 1) | |
1574 (setq p3 (point)))) | |
1575 (if (< (- p3 (point)) 5000) | |
1576 (gdb-highlight-line)) | |
1577 (goto-char p3)))))))))) | |
1578 | |
1579 (fset 'gdb-filter 'gdb-highlight-filter) | |
1580 | |
1581 | |
1582 (provide 'gdb-highlight) | |
1583 | |
1584 ;;; gdb-highlight.el ends here | |
1585 | |
1586 --------------4273DDB4BB90CEC3B645B5AC-- | |
1587 | |
1588 |