Mercurial > hg > xemacs-beta
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/comint/gdb-highlight.el Mon Aug 13 09:39:39 2007 +0200 @@ -0,0 +1,1588 @@ +;;; gdb-highlight.el --- make gdb buffers be mouse-sensitive. + +;;; Copyright (C) 1997 Jamie Zawinski <jwz@netscape.com> + +;; Author: Jamie Zawinski <jwz@netscape.com> +;; Created: 16-Apr-1997 +;; Version: 1.2 (17-May-97) +;; Keywords: extensions, c, unix, tools, debugging + +;; This file is part of XEmacs. + +;; XEmacs 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 2, or (at your option) +;; any later version. + +;; XEmacs 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 XEmacs; see the file COPYING. If not, write to the Free +;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +;; 02111-1307, USA. + +;;; Synched up with: Not synched. + +;;; Commentary: +;; +;; This package makes most objects printed in a *gdb* buffer be +;; mouse-sensitive: as text shows up in the buffer, it is parsed, +;; and objects which are recognized have context-sensitive commands +;; attached to them. Generally, the types that are noticed are: +;; +;; = function and method names; +;; = variable and parameter names; +;; = structure and object slots; +;; = source file names; +;; = type names; +;; = breakpoint numbers; +;; = stack frame numbers. +;; +;; Any time one of those objects is presented in the *gdb* buffer, +;; it will be mousable. Clicking middle mouse button (button2) on +;; it will take some default action -- edit the function, select +;; the stack frame, disable the breakpoint, etc. Clicking the right +;; mouse button (button3) will bring up a menu of commands, including +;; commands specific to the object under the mouse, or other objects +;; on the same line. +;; +;; In addition to these context-sensitive commands are more general +;; gdb commands which were previously inaccessible via the mouse +;; (listing breakpoints, returning values, etc); and the general +;; comint/shell-buffer commands which had been present before. +;; +;; If you notice an object being presented which could (usefully) +;; be made mouse sensitive, but which currently is not, please let +;; me know. + +;;; Installation: +;; +;; To install, add this to your .emacs file: +;; (add-hook 'gdb-mode-hook '(lambda () (require 'gdb-highlight))) + +;;; TODO: +;; +;; = It doesn't really work very well unless you've done `set width 0' +;; in your .gdbinit. It would be nice if this were fixed. +;; (And with `set width 0', `set print pretty on' is the way to go.) +;; +;; = In some contexts, the toggle-breakpoint command doesn't work, +;; because this code doesn't know whether it's enabled. It should +;; remember, or figure it out, or something. +;; +;; = Make it possible to edit the `keep' state of breakpoints. +;; +;; = Is it useful to make addresses clickable? If an address is +;; always acompanied by a variable, then no. +;; +;; = There has got to be a better way to implement `gdb-guess-file-name'. +;; +;; = Make some new toolbar icons and put the most common commands on it. +;; +;; = Maybe make gdb-toolbar-clear work more reliably by consulting a +;; breakpoint-number extent? +;; +;; = I want breakpoint icons in my source files, just like in Energize. +;; +;; = Add a command to quit-and-restart the debugger, with the same +;; breakpoints and program-arguments. (This wouldn't be interesting +;; if gdb didn't leak like a sieve...) +;; +;; = Figure out some way to realize when extents are no longer interesting +;; (stack frames and local variables that are no longer on the stack) +;; and make them no longer be mousable. This is tricky... Nuke them +;; whenever a "run" command is seen? +;; +;; = Make C-x SPC in a source buffer use gdb-menu-command so that it will +;; interrupt-and-continue the debugged program as necessary. +;; +;; = Do stuff for watchpoints (but I never use them, myself.) + +;;; WISHLIST: +;; +;; (extracted from my 13-May-1997 message to comp.emacs and +;; comp.emacs.xemacs, news:33785828.5A524730@netscape.com) +;; +;; 6.1. Make gdbsrc-mode not suck. +;; +;; The idea behind gdbsrc-mode is on the side of the angels: one +;; should be able to focus on the source code and not on the +;; debugger buffer, absolutely. But the implementation is just +;; awful. +;; +;; First and foremost, it should not change "modes" (in the more +;; general sense). Any commands that it defines should be on +;; keys which are exclusively used for that purpose, not keys +;; which are normally self-inserting. I can't be the only person +;; who usually has occasion to actually *edit* the sources which +;; the debugger has chosen to display! Switching into and out of +;; gdbsrc-mode is prohibitive. +;; +;; I want to be looking at my sources at all times, yet I don't +;; want to have to give up my source-editing gestures. I think +;; the right way to accomplish this is to put the gdbsrc commands +;; on the toolbar and on popup menus; or to let the user define +;; their own keys (I could see devoting my kp_enter key to +;; "step", or something common like that.) +;; +;; Also it's extremely frustrating that one can't turn off gdbsrc +;; mode once it has been loaded, without exiting and restarting +;; emacs; that alone means that I'd probably never take the time +;; to learn how to use it, without first having taken the time to +;; repair it... +;; +;; 6.2. Make it easier access to variable values. +;; +;; I want to be able to double-click on a variable name to +;; highlight it, and then drag it to the debugger window to have +;; its value printed. +;; +;; I want gestures that let me write as well as read: for +;; example, to store value A into slot B. +;; +;; 6.3. Make all breakpoints visible. +;; +;; Any time there is a running gdb which has breakpoints, the +;; buffers holding the lines on which those breakpoints are set +;; should have icons in them. These icons should be context- +;; sensitive: I should be able to pop up a menu to enable or +;; disable them, to delete them, to change their commands or +;; conditions. +;; +;; I should also be able to MOVE them. It's annoying when you +;; have a breakpoint with a complex condition or command on it, +;; and then you realize that you really want it to be at a +;; different location. I want to be able to drag-and-drop the +;; icon to its new home. +;; +;; 6.4. Make a debugger status display window. +;; +;; o I want a window off to the side that shows persistent +;; information -- it should have a pane which is a +;; drag-editable, drag-reorderable representation of the +;; elements on gdb's "display" list; they should be displayed +;; here instead of being just dumped in with the rest of the +;; output in the *gdb* buffer. +;; +;; o I want a pane that displays the current call-stack and +;; nothing else. I want a pane that displays the arguments +;; and locals of the currently-selected frame and nothing +;; else. I want these both to update as I move around on the +;; stack. +;; +;; Since the unfortunate reality is that excavating this +;; information from gdb can be slow, it would be a good idea +;; for these panes to have a toggle button on them which meant +;; "stop updating", so that when I want to move fast, I can, +;; but I can easily get the display back when I need it again. +;; +;; The reason for all of this is that I spend entirely too much +;; time scrolling around in the *gdb* buffer; with gdb-highlight, +;; I can just click on a line in the backtrace output to go to +;; that frame, but I find that I spend a lot of time *looking* +;; for that backtrace: since it's mixed in with all the other +;; random output, I waste time looking around for things (and +;; usually just give up and type "bt" again, then thrash around +;; as the buffer scrolls, and I try to find the lower frames that +;; I'm interested in, as they have invariably scrolled off the +;; window already... +;; +;; 6.5. Save and restore breakpoints across emacs/debugger sessions. +;; +;; This would be especially handy given that gdb leaks like a +;; sieve, and with a big program, I only get a few dozen +;; relink-and-rerun attempts before gdb has blown my swap space. +;; +;; 6.6. Keep breakpoints in sync with source lines. +;; +;; When a program is recompiled and then reloaded into gdb, the +;; breakpoints often end up in less-than-useful places. For +;; example, when I edit text which occurs in a file anywhere +;; before a breakpoint, emacs is aware that the line of the bp +;; hasn't changed, but just that it is in a different place +;; relative to the top of the file. Gdb doesn't know this, so +;; your breakpoints end up getting set in the wrong places +;; (usually the maximally inconvenient places, like *after* a +;; loop instead of *inside* it). But emacs knows, so emacs +;; should inform the debugger, and move the breakpoints back to +;; the places they were intended to be. +;; +;; (Possibly the OOBR stuff does some of this, but can't tell, +;; because I've never been able to get it to do anything but beep at +;; me and mumble about environments. I find it pretty funny that the +;; manual keeps explaining to me how intuitive it is, without +;; actually giving me a clue how to launch it...) + + +;;; Code: +;; +;; This code should be considered an example of how over-use of regular +;; expressions leads to code that is an unreadable, unmaintainable mess, +;; and why it's unfortunate that so much of emacs's speed depends on +;; their use, rather than on the use of more traditional parsers. + +(require 'gdb) + +(define-key gdb-mode-map 'button3 'gdb-popup-menu) +(defvar gdb-popup-menu + '("GDB Commands" + ["Up Stack" (gdb-menu-command "up" t) t] + ["Down Stack" (gdb-menu-command "down" t) t] + ["Next Line" (gdb-menu-command "next" t) t] + ["Next Line (Step In)" (gdb-menu-command "step" t) t] + ["Continue" (gdb-menu-command "continue" t) t] + ["Continue Until Return" (gdb-menu-command "finish" t) t] + ("Return..." + ["Return" (gdb-menu-command "return" t) t] + ["Return 0" (gdb-menu-command "return 0" t) t] + ["Return 1" (gdb-menu-command "return 1" t) t] + ["Return -1" (gdb-menu-command "return -1" t) t] + ["Return $" (gdb-menu-command "return $" t) t] + ) + "---" + ["Backtrace" (gdb-menu-command "backtrace" t) t] + ["List Breakpoints" (gdb-menu-command "info breakpoints" t) t] + ["List Local Variables" (gdb-menu-command "info locals" t) t] + ) + "Commands for the popup menu in gdb-mode. +The comint-popup-menu is appended to this, and certain context-sensitive +commands may be prepended to it, depending on the location of the mouse +when the `gdb-popup-menu' command is invoked.") + + +;;; Faces and keymaps used for mousable tokens in the *gdb* buffer. + +(defvar gdb-highlight-face 'gdb-highlight-face) ; the base face +(defvar gdb-breakpoint-number-face 'gdb-breakpoint-number-face) +;(defvar gdb-breakpoint-keep-face 'gdb-breakpoint-keep-face) +(defvar gdb-breakpoint-enabled-face 'gdb-breakpoint-enabled-face) +(defvar gdb-function-name-face 'gdb-function-name-face) +(defvar gdb-function-location-face 'gdb-function-location-face) +(defvar gdb-variable-name-face 'gdb-variable-name-face) +(defvar gdb-type-name-face 'gdb-type-name-face) + +(make-face 'gdb-highlight-face) +(or (face-differs-from-default-p 'gdb-highlight-face) + (make-face-italic 'gdb-highlight-face)) + +(let ((faces '(gdb-breakpoint-number-face + gdb-breakpoint-enabled-face + ;gdb-breakpoint-keep-face + gdb-function-name-face + gdb-function-location-face + gdb-variable-name-face + gdb-type-name-face))) + (while faces + (make-face (car faces)) + (or (face-differs-from-default-p (car faces)) + (if (fboundp 'set-face-parent) + (set-face-parent (car faces) 'gdb-highlight-face) + (copy-face 'gdb-highlight-face (car faces)))) + (setq faces (cdr faces)))) + + +(defvar gdb-token-map ; the base map, inherited by all. + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-token-map) + (define-key m 'button2 'undefined) + ;;(define-key m 'button3 'gdb-token-popup) + m)) + +(defvar gdb-breakpoint-number-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-breakpoint-number-map) + (set-keymap-parent m gdb-token-map) + ;; not sure if this is the most useful binding... maybe "delete" is better? + (define-key m 'button2 'gdb-mouse-disable-breakpoint) + m)) + +(defvar gdb-info-breakpoint-number-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-breakpoint-number-map) + (set-keymap-parent m gdb-token-map) + ;; not sure if this is the most useful binding... maybe "delete" is better? + (define-key m 'button2 'gdb-mouse-toggle-breakpoint-enabled) + m)) + +;(defvar gdb-breakpoint-keep-map +; (let ((m (make-sparse-keymap))) +; (set-keymap-name m 'gdb-breakpoint-keep-map) +; (set-keymap-parent m gdb-token-map) +; (define-key m 'button2 'gdb-token-mouse-toggle-keep) +; m)) + +(defvar gdb-breakpoint-enabled-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-breakpoint-enabled-map) + (set-keymap-parent m gdb-token-map) + (define-key m 'button2 'gdb-mouse-toggle-breakpoint-enabled) + m)) + +(defvar gdb-function-name-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-function-name-map) + (set-keymap-parent m gdb-token-map) + (define-key m 'button2 'gdb-mouse-edit-function) + m)) + +(defvar gdb-function-location-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-function-location-map) + (set-keymap-parent m gdb-token-map) + (define-key m 'button2 'gdb-mouse-edit-function-location) + m)) + +(defvar gdb-frame-number-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-frame-number-map) + (set-keymap-parent m gdb-token-map) + (define-key m 'button2 'gdb-mouse-goto-frame) + m)) + +(defvar gdb-variable-name-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-variable-name-map) + (set-keymap-parent m gdb-token-map) + (define-key m 'button2 'gdb-mouse-print-variable) + m)) + +(defvar gdb-type-name-map + (let ((m (make-sparse-keymap))) + (set-keymap-name m 'gdb-type-name-map) + (set-keymap-parent m gdb-token-map) + (define-key m 'button2 'gdb-mouse-print-type) + m)) + + +;;; Token definitions. + +;; These properties enumerate the faces and keymaps that will be put over +;; the tokens. + +(put 'gdb-frame-number-token 'gdb-token-face gdb-breakpoint-number-face) +(put 'gdb-frame-number-token 'gdb-token-keymap gdb-frame-number-map) + +;(put 'gdb-breakpoint-keep-token 'gdb-token-face gdb-breakpoint-keep-face) +;(put 'gdb-breakpoint-keep-token 'gdb-token-keymap gdb-breakpoint-keep-map) + +(put 'gdb-enabled-token 'gdb-token-face gdb-breakpoint-enabled-face) +(put 'gdb-enabled-token 'gdb-token-keymap gdb-breakpoint-enabled-map) + +(put 'gdb-function-name-token 'gdb-token-face gdb-function-name-face) +(put 'gdb-function-name-token 'gdb-token-keymap gdb-function-name-map) + +(put 'gdb-function-location-token 'gdb-token-face gdb-function-location-face) +(put 'gdb-function-location-token 'gdb-token-keymap gdb-function-location-map) + +(put 'gdb-breakpoint-number-token 'gdb-token-face gdb-breakpoint-number-face) +(put 'gdb-breakpoint-number-token 'gdb-token-keymap gdb-breakpoint-number-map) +(put 'gdb-info-breakpoint-number-token 'gdb-token-face + gdb-breakpoint-number-face) +(put 'gdb-info-breakpoint-number-token 'gdb-token-keymap + gdb-info-breakpoint-number-map) + +(put 'gdb-frame-number-token 'gdb-token-face gdb-breakpoint-number-face) +(put 'gdb-frame-number-token 'gdb-token-keymap gdb-frame-number-map) + +(put 'gdb-variable-name-token 'gdb-token-face gdb-variable-name-face) +(put 'gdb-variable-name-token 'gdb-token-keymap gdb-variable-name-map) + +(put 'gdb-type-name-token 'gdb-token-face gdb-type-name-face) +(put 'gdb-type-name-token 'gdb-token-keymap gdb-type-name-map) + + +;;; These regular expressions control what text corresponds to which tokens. + +(defconst gdb-highlight-token-patterns + ;; "May god forgive me for what I have unleashed." -- Evil Dead II. + (purecopy + (list + ;; Breakpoints output: + ;; + ;; Breakpoint 5, XCreateWindow () at Window.c:136 + ;; Breakpoint 6, foobar (x=0x7fff3000 "baz") at blorp.c:5382 + ;; + (list (concat "\\(Breakpoint " ; 1 + "\\([0-9]+\\)" ; .2 + "\\), " ; 1 + "\\(0x[0-9a-fA-F]+ in \\)?" ; 3 + "\\(" ; 4 + "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; .5 + "\\|" ; . + "[a-zA-Z0-9_]+" ; . + "\\)" ; 4 + "\\(" ; 6 + " *\\((.*)\\)" ; .7 + " at \\(" ; .8 + "\\([^ \t\n:]+\\):" ; ..9 + "\\([0-9]+\\)" ; ..10 + "\\)" ; .8 + "\\)?" ; 6 + ) + '(gdb-breakpoint-number-token ; 1 + nil ; 2 + nil ; 3 + gdb-function-name-token ; 4 (+5) + gdb-type-name-token ; 5 + nil ; 6 + gdb-arglist-token ; 7 + gdb-function-location-token ; 8 (9+10) + )) + + ;; Output of the "Break" command: + ;; + ;; Breakpoint 1 at 0x4881d4 + ;; Breakpoint 6 at 0xfa50f68: file cuexit.c, line 58. + ;; + (list (concat "\\(Breakpoint " ; 1 + "\\([0-9]+\\)" ; .2 + "\\) at " ; 1 + "\\(0x[0-9A-Fa-f]+\\)" ; 3 + "\\(: file " ; 4 + "\\(" ; .5 + "\\([^ \t\n:]+\\)" ; ..6 + ", line \\([0-9]+\\)" ; ..7 + "\\)" ; .5 + "\\)?" ; 4 + ) + '(gdb-breakpoint-number-token ; 1 + nil ; 2 + nil ;gdb-address-token ; 3 + nil ; 4 + gdb-function-location-token ; 5 (6+7) + )) + + ;; Note: breakpoint 5 (disabled) also set at pc 0x40b420. + ;; Note: breakpoint 5 also set at pc 0x40b420. + ;; + (list (concat "Note: " ; + "\\(breakpoint " ; 1 + "\\([0-9]+\\)" ; .2 + "\\)" ; 1 + ) + '(gdb-breakpoint-number-token ; 1 + nil ; 2 + )) + + ;; Stack Frames: + ;; + ;; 0xe1b8e0 in _OS_SELECT () at os_IRIX.s:50 + ;; XCreateWindow () at Window.c:136 + ;; #0 0x8e0db0 in _OS_SELECT () at os_IRIX.s:50 + ;; #0 XCreateWindow () at Window.c:136 + ;; Run till exit from #0 __ll_mul () at llmul.s:51 + ;; + (list (concat "\\(Run till exit from \\)?" ; 1 + "\\(" ; 2 + "#\\([0-9]+ *\\)" ; .3 + "\\)?" ; 2 + "\\(" ; 4 + "\\(0x[0-9A-Fa-f]+\\)" ; .5 + " in +\\)?" ; 4 + "\\(" ; 6 + "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; .7 + "\\|" ; 6 + "[a-zA-Z0-9_]+" ; + "\\) (" ; 6 + "\\(" ; 8 + "\\(.*\\)" ; .9 + "\\bat \\(" ; .10 + "\\([^ \t\n:]+\\):" ; ..11 + "\\([0-9]+\\)" ; ..12 + "\\)" ; .10 + "\\)?" ; 8 + ) + '(nil ; 1 + gdb-frame-number-token ; 2 + nil ; 3 + nil ; 4 + nil ;gdb-address-token ; 5 + gdb-function-name-token ; 6 (+7) + gdb-type-name-token ; 7 + nil ; 8 + gdb-arglist-token ; 9 + gdb-function-location-token ; 10 (11+12) + )) + + ;; Info Breakpoints output: + ;; + ;; 1 breakpoint keep y 0x0fa50f68 in exit at exit.c:58 + ;; 1 breakpoint keep y 0x000a1b00 <exit+4> + ;; 1 breakpoint keep y 0x0fa429ac <_write> + ;; 6 breakpoint keep y 0x00789490 in foo::bar(bad *) at x.cpp:99 + ;; 7 breakpoint keep y 0x00789490 <foo::bar(bad *)+128> + ;; + (list (concat "\\([0-9]+ *\\) " ; 1 + "\\(breakpoint *\\|watchpoint *\\) " ; 2 + "\\(keep *\\|del *\\|dis *\\) " ; 3 + "\\([yn] *\\) " ; 4 + "\\(0x[0-9A-Fa-f]+\\) *" ; 5 + "\\(in " ; 6 + "\\(" ; .7 + "[a-zA-Z0-9_]+" ; .. + "\\|" ; .7 + "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; ..8 + "\\)" ; .7 + "\\((.*)\\)?" ; 9 + " at " ; . + "\\(" ; .10 + "\\([^ \t\n:]+\\):" ; ..11 + "\\([0-9]+\\)" ; ..12 + "\\)" ; .10 + "\\|" ; 6 + "<" ; . + "\\(" ; .13 + "\\([a-zA-Z0-9_]+\\):[a-zA-Z0-9_:~]+" ; ..14 + "\\|" ; .13 + "[a-zA-Z0-9_]+" ; .. + "\\)" ; .13 + "\\((.*)\\)?" ; .15 + "[^>\n]*>" ; . + "\\)?" ; 6 + ) + '(gdb-info-breakpoint-number-token ; 1 + nil ; 2 + nil ;gdb-breakpoint-keep-token ; 3 + gdb-enabled-token ; 4 + nil ;gdb-address-token ; 5 + nil ; 6 + gdb-function-name-token ; 7 (+8) + gdb-type-name-token ; 8 + gdb-arglist-types-token ; 9 + gdb-function-location-token ; 10 (11+12) + nil ; 11 + nil ; 12 + gdb-function-name-token ; 13 + gdb-type-name-token ; 14 + gdb-arglist-types-token ; 15 + )) + + ;; Whatis and Ptype output: + ;; type = struct _WidgetRec * + ;; type = struct _WidgetRec { + ;; type = int () + ;; type = struct <undefined> *(struct <undefined> *, void *, void (*)()) + ;; type = struct foo *(struct foo *, unsigned char, int) + ;; type = unsigned int [4] + ;; + (list (concat "type = " + "\\(" ; 1 + "\\(signed \\|unsigned \\)?" ; .2 + "\\(struct \\|class \\|union \\|enum \\)?" ; .3 + "\\(<?[a-zA-Z_][a-zA-Z0-9_:]*>?\\)" ; .4 + "\\)" ; 1 + "[ *]*" ; + "\\(" ; 5 + "{?$\\|" ; . + "\\[[0-9]*\\]$\\|" ; . + "\\((.*)\\)" ; .6 + "\\)" ; 5 + ) + '(gdb-type-name-token ; 1 (2+3+4) + nil ; 2 + nil ; 3 + nil ; 4 + nil ; 5 + gdb-arglist-types-token ; 6 + )) + + ;; Ptype output: + ;; CorePart core; + ;; void *constraints; + ;; short x; + ;; unsigned short width; + ;; struct <undefined> *event_table; + ;; XtTMRec tm; + ;; void (*class_initialize)(); + ;; unsigned char (*set_values)(); + ;; unsigned char st_fstype[16]; + ;; type = enum {XtGeometryYes, XtGeometryNo, XtGeometryAlmost} + ;; + (list (concat " *" + "\\(" ; 1 + "\\(signed \\|unsigned \\)?" ; .2 + "\\(struct \\|class \\|union \\|enum \\)?" ; .3 + "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; .4 + "\\)" ; 1 + "[ *]*" + "\\((\\**\\)?" ; 5 + "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; 6 + "\\()()\\)?" ; 7 + "\\( *\\[[0-9]*\\]\\)?" ; 8 + "; *$" + ) + '(gdb-type-name-token ; 1 (2+3+4) + )) + + ;; Ptype output on C++ classes: + ;; + ;; virtual foo (int); + ;; unsigned int foo(void); + ;; static long unsigned int * foo(bar *, baz *, unsigned int); + ;; + ;; not handled: + ;; foo(bar *, _WidgetRec *, char const *, int); + ;; foo (foo &); + ;; foo & operator=(foo const &); + ;; + (list (concat " *" + "\\(static \\)?" ; 1 + "\\(" ; 2 + "\\(signed \\|unsigned " ; .3 + ;; #### not so sure about this: + "\\|long unsigned \\|short unsigned " ; .3 + "\\)?" ; .3 + "\\(struct \\|class \\|union \\|enum \\)?" ; .4 + "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; .5 + "\\)" ; 1 + "[ *&]+" ; + " *\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; 6 + " *\\((.*)\\)" ; 7 + "; *$" ; + ) + '(nil ; 1 + gdb-type-name-token ; 2 (3+4+5) + nil ; 3 + nil ; 4 + nil ; 5 + gdb-function-name-token ; 6 + gdb-arglist-types-token ; 7 + )) + + ;; Pointers to functions: + ;; + ;; $1 = {void ()} 0x4a1334 <fe_pulldown_cb> + ;; $2 = (void (*)()) 0x4a1334 <fe_pulldown_cb> + ;; + (list (concat ".* = " + "[({]" + "\\(" ; 1 + "\\(signed \\|unsigned \\)?" ; .2 + "\\(struct \\|class \\|union \\|enum \\)?" ; .3 + "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; .4 + "\\)" ; 1 + " \\((\\*) ?\\)?" ; 5 + "\\((.*)\\)" ; 6 + "[)}] +" ; + "\\(0x[0-9A-Fa-f]+\\) +" ; 7 + "<\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)" ; 8 + "\\+?[0-9]+?>" ; + ) + '(gdb-type-name-token ; 1 (2+3+4) + nil ; 2 + nil ; 3 + nil ; 4 + nil ; 5 + gdb-arglist-types-token ; 6 + nil ;gdb-address-token ; 7 + gdb-function-name-token ; 8 + )) + + ;; Local variables and structures: + ;; + ;; shell = (struct _WidgetRec *) 0x10267350 + ;; delete_response = 270955344 + ;; allow_resize = 200 'È' + ;; is_modal = 47 '/' + ;; class_name = 0xf661d40 "TopLevelShell", + ;; static foo = 0x10791ec0, + ;; initialize = 0xf684770 <TopLevelInitialize>, + ;; av = {{ + ;; name = "foo", + ;; value = 270349836 + ;; }, { + ;; name = 0x12 <Address 0x12 out of bounds>, + ;; value = 0 + ;; }, { + ;; name = 0x0, + ;; value = 0 + ;; }} + ;; + (list (concat " *" + "\\(static \\)?" ; 1 + "\\([$a-zA-Z_][a-zA-Z0-9_:]*\\) = " ; 2 + "\\((" ; 3 + "\\(" ; .4 + "\\(signed \\|unsigned \\)?" ; ..5 + "\\(struct \\|class \\|union \\|enum \\)?"; ..6 + "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)" ; ..7 + "\\)" ; .4 + "[ *]*)" ; + "\\)?" ; 3 + "\\(" ; 8 + ".*" + " <\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)" ; .9 + "\\+?[0-9]+?>" ; . + "\\)?" ; 8 + ) + '(nil ; 1 + gdb-variable-name-token ; 2 + nil ; 3 + gdb-type-name-token ; 4 + nil ; 5 + nil ; 6 + nil ; 7 + nil ; 8 + gdb-function-name-token ; 9 + )) + + ;; Purify output: + ;; UMR: Uninitialized memory read: + ;; * This is occurring while in: + ;; SHA1_Update [algsha.c:137] + ;; * Reading 1 byte from 0xefffdb34 on the stack. + (list (concat "[ \t]+" + "\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)[ \t]*" ; 1 + "\\[\\(" ; 2 + "\\([^ \t\n:]+\\):" ; .3 + "\\([0-9]+\\)" ; .4 + "\\)\\]" ; 2 + ) + '(gdb-function-name-token ; 1 + gdb-function-location-token ; 2 (3+4) + )) + + ;; Purify output: + ;; * Address 0xefffdb34 is 36 bytes past start of local variable \ + ;; "data" in function fe_EventForRNG. + (list (concat ".*\\bAddress " + "\\(0x[0-9A-Fa-f]+\\) +" ; 1 + ".*\\bvariable \"" ; + "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)\"" ; 2 + "\\(" ; 3 + ".*\\bfunction " ; . + "\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)" ; .4 + "\\)?" ; 3 + ) + '(nil ;gdb-address-token ; 1 + gdb-variable-name-token ; 2 + nil ; 3 + gdb-function-name-token ; 4 + )) + )) + "Patterns to highlight in gdb buffers. +Each element of this list takes the form + ( \"regexp\" ( token-1 token-2 ... )) +where token-N is the token to place on the text matched + by sub-pattern N in the match data. + +The patterns should not begin with \"^\".") + + +(defun gdb-highlight-line () + "Highlight any tokens on the line which match gdb-highlight-token-patterns." + (map-extents #'(lambda (e ignore) (delete-extent e)) + nil + (point) (save-excursion (forward-line 1) (point)) + nil nil 'gdb-token) + (while (looking-at comint-prompt-regexp) + (goto-char (match-end 0))) + (if (eobp) + nil + (let ((tokens gdb-highlight-token-patterns) + (do-magic-variable-hack nil)) + (while tokens + (if (not (looking-at (car (car tokens)))) + (setq tokens (cdr tokens)) + (let ((i 1) + (types (nth 1 (car tokens)))) + (if (eq (car types) 'gdb-variable-name-token) + (setq do-magic-variable-hack t)) + (while types + (cond ((not (and (car types) + (match-beginning i))) + nil) + ((memq (car types) '(gdb-arglist-token + gdb-arglist-types-token)) + (gdb-highlight-arglist (car types) + (match-beginning i) + (match-end i))) + ((/= ?$ (char-after (match-beginning i))) + (gdb-highlight-token (car types) + (match-beginning i) + (match-end i)))) + (setq i (1+ i) + types (cdr types))) + + (if (not do-magic-variable-hack) + ;; we're done. + (setq tokens nil) + ;; else, do a grody hack to cope with multiple variables + ;; on the same line. + (save-restriction + (let ((p (point)) + (ok nil)) + (end-of-line) + (narrow-to-region p (point)) + (goto-char (match-end 0)) + (if (= (following-char) ?\{) + (progn + (forward-char 1) + (setq ok t)) + (setq p (scan-sexps (point) 1 nil t)) + (setq ok (if (null p) + nil + (goto-char p) + (if (or (= (following-char) ?\,) + (= (following-char) ?\})) + t + (setq p (scan-sexps (point) 1 nil t)) + (if (null p) + nil + (goto-char p) + t))))) + (if ok + ;; skip over the comma and go around again. + (and (looking-at "}?[ \t]*,[ \t]*") + (goto-char (match-end 0))) + ;; saw something unexpected; give up on this line. + (setq tokens nil))))) + ))))) + nil) + +(defun gdb-highlight-token (type start end) + "Helper for gdb-highlight-line -- makes an extent for one matched token." + (let ((e (make-extent start end))) + (set-extent-property e 'gdb-token type) + (set-extent-property e 'highlight 't) + (set-extent-property e 'help-echo 'gdb-token-help-echo) + (set-extent-property e 'face (get type 'gdb-token-face)) + (set-extent-property e 'keymap (get type 'gdb-token-keymap)) + e)) + +(defun gdb-highlight-arglist (type start end) + "Helper for gdb-highlight-line. +Makes extents for variables or types in an arglist." + (save-match-data + (save-excursion + (goto-char end) + (if (eq (preceding-char) ?\)) + (setq end (1- end))) + (goto-char start) + (if (eq (following-char) ?\() + (forward-char 1)) + (set-extent-property (make-extent start end) 'gdb-token type) + + (cond + ((eq type 'gdb-arglist-token) + (let* ((pat1 "\\([a-zA-Z_][a-zA-Z0-9_:]*\\)=") + (pat2 ", \\([a-zA-Z_][a-zA-Z0-9_:]*\\)=") + (pat pat1)) + (while(re-search-forward pat end t) + (gdb-highlight-token 'gdb-variable-name-token + (match-beginning 1) (match-end 1)) + (cond ((looking-at + "0?x?[0-9A-Fa-f]+ <\\([a-zA-Z_~][a-zA-Z0-9_:]*\\)>") + (goto-char (match-end 0)) + (gdb-highlight-token 'gdb-function-name-token + (match-beginning 1) (match-end 1)))) + (setq pat pat2)))) + + ((eq type 'gdb-arglist-types-token) + (let ((pat (eval-when-compile + (concat + "\\(" ; 1 + "\\(signed \\|unsigned \\)?" ; .2 + "\\(struct \\|class \\|union \\|enum \\)?" ; .3 + "\\(<?[a-zA-Z_~][a-zA-Z0-9_:]*>?\\)" ; .4 + "\\)" ; 1 + "[ *]*" + "\\((\\*) *(.*)\\)?" ; 5 + )))) + (while (< (point) end) + (cond ((looking-at pat) + (goto-char (match-end 0)) + (gdb-highlight-token 'gdb-type-name-token + (match-beginning 1) (match-end 1)) + (if (looking-at " *, *") + (goto-char (match-end 0)))) + (t + ;; error -- try to cope... + (search-forward "," (1+ end) t)))))) + (t + (error "unknown arglist type %s" type))))) + nil) + +(defun gdb-token-help-echo (extent) + "Used as the 'mouse-help property of gdb-token extents, +to describe the binding on button2." + (let* ((map (extent-property extent 'keymap)) + (key 'button2) + (fn (and map (lookup-key map key))) + (doc (and fn (symbolp fn) + (if (fboundp fn) + (format "%s: %s" key (documentation fn)) + (format "Error: %s is undefined" fn))))) + (if doc + (save-match-data + (if (string-match "\n" doc) + (setq doc (substring doc 0 (match-beginning 0)))))) + (or doc + (concat "Error: no doc for " + (symbol-name (extent-property extent 'gdb-token)))))) + +(defun gdb-get-line-token-extents (tokens) + "Given a list of gdb-tokens, returns this line's extents of those types. +The returned value is a list of the same length as the `tokens' list, with +the corresponding extents in the corresponding positions. If an extent +isn't found, nil is placed in the result-list instead." + (setq tokens (append tokens nil)) + (let* ((result (make-list (length tokens) nil))) + (save-excursion + (beginning-of-line) + (map-extents #'(lambda (e ignore) + (let ((type (extent-property e 'gdb-token)) + (r1 tokens) + (r2 result)) + (while r1 + (cond ((and (car r1) (eq type (car r1))) + (setcar r1 nil) + (setcar r2 e) + (setq r1 nil))) + (setq r1 (cdr r1) + r2 (cdr r2)))) + nil) + nil + (point) + (progn (forward-line 1) (point)) + nil nil + 'gdb-token) + result))) + + +;;; Remembering directory names. +;;; gdb and gdb-mode conspire to hide from us the full file names of things +;;; that are presented into the buffer; this is an attempt to circumvent that. + +(defvar gdb-highlight-last-directory nil) +(defvar gdb-highlight-last-directory-table nil) + +(defun gdb-highlight-remember-directory () + ;; When gdb deigns to give us a full pathname, and it's in a different + ;; directory than last time, cache it away on one of the nearby gdb-token + ;; extents. (We intern it to avoid hanging on to a lot of strings.) + (cond ((and (boundp 'gdb-last-frame) + (car gdb-last-frame)) + (cond ((not gdb-highlight-last-directory-table) + (set (make-local-variable 'gdb-highlight-last-directory) nil) + (set (make-local-variable 'gdb-highlight-last-directory-table) + (make-vector 211 0)))) + (let ((dir (file-name-directory (car gdb-last-frame)))) + (setq dir (intern dir gdb-highlight-last-directory-table)) + (cond ((not (eq dir gdb-highlight-last-directory)) + (let ((extent (previous-extent (current-buffer)))) + (setq gdb-highlight-last-directory dir) + (while extent + (cond ((extent-property extent 'gdb-token) + (set-extent-property extent 'gdb-directory dir) + (setq extent nil)) + (t + (setq extent (previous-extent extent)))))))))))) + +(defun gdb-guess-directory () + "Guess what directory gdb was talking about when it wrote the current line." + (let ((extent (or (map-extents #'(lambda (e ignore) e) + (current-buffer) (point) (point-max)) + (previous-extent (current-buffer)) + (error "no extents"))) + (dir nil)) + (while extent + (setq dir (extent-property extent 'gdb-directory)) + (if dir + (setq extent nil) + (setq extent (previous-extent extent)))) + (if dir + (symbol-name dir) + default-directory))) + +(defun gdb-guess-file-name (file) + "Given a directoryless file name printed by gdb, find the file. +First it tries to expand the file relative to `gdb-guess-directory', +and if the resultant file doesn't exist, it tries every other directory +gdb has ever told us about, in no particular order." + (abbreviate-file-name + (if (file-name-absolute-p file) + file + (let ((file2 (expand-file-name file (gdb-guess-directory)))) + (if (file-exists-p file2) + file2 + ;; Oh boy, gdb didn't tell us what directory it's in. + ;; A-hunting we will go. + (if (catch 'done + (mapatoms #'(lambda (dir) + (setq file2 (expand-file-name file + (symbol-name dir))) + (if (file-exists-p file2) + (throw 'done t))) + gdb-highlight-last-directory-table) + nil) + file2 + (expand-file-name file))))))) + + +;;; Commands which are invoked from bindings in the keymaps of the tokens. + +(defun gdb-mouse-toggle-breakpoint-enabled (event &optional what) + "Toggle whether the breakpoint is enabled. +Looks for a gdb-breakpoint extent on the line under the mouse, +and executes an `enable' or `disable' command as appropriate. +Optional arg `what' may be 'enable, 'disable, or 'toggle (default.)" + (interactive "@*e") + (let (number target enabled-p) + (save-excursion + (mouse-set-point event) + (let* ((extents (gdb-get-line-token-extents + '(gdb-breakpoint-number-token + gdb-info-breakpoint-number-token + gdb-enabled-token))) + (be (or (nth 0 extents) (nth 1 extents))) + (ee (nth 2 extents))) + + (or be + (error "no breakpoint-number extent on this line")) + (setq number + (buffer-substring (extent-start-position be) + (extent-end-position be))) + (if (string-match " [0-9]+\\'" number) + (setq number (substring number (1+ (match-beginning 0))))) + (setq number (string-to-int number)) + (or (> number 0) + (error "couldn't find breakpoint number")) + (if (null ee) + (setq enabled-p 'unknown) + (setq target (extent-start-position ee)) + (goto-char target) + (setq enabled-p + (cond ((looking-at "[yY]\\b") t) + ((looking-at "[nN]\\b") nil) + (t (error "enabled is not y or n?"))))) + + (cond ((eq what 'enable) + (setq enabled-p nil)) + ((eq what 'disable) + (setq enabled-p t)) + ((or (eq what 'toggle) (null what)) + (if (eq enabled-p 'unknown) + (error + "can't toggle breakpoint: don't know current state"))) + (t + (error "what must be enable, disable, toggle, or nil."))) + )) + + (gdb-menu-command (format "%s %d" + (if enabled-p "disable" "enable") + number) + nil) + (message "%s breakpoint %d." + (if enabled-p "Disabled" "Enabled") + number) + (cond (target + (save-excursion + (goto-char target) + (insert (if enabled-p "n" "y")) + (delete-char 1) + ;; don't let shell-fonts or font-lock second-guess us. + (remove-text-properties (1- (point)) (point) '(face)))))) + nil) + +(defun gdb-mouse-enable-breakpoint (event) + "Enable the breakpoint. +Looks for a gdb-breakpoint extent on the line under the mouse, +and executes an `enable' command" + (interactive "@*e") + (gdb-mouse-toggle-breakpoint-enabled event 'enable)) + +(defun gdb-mouse-disable-breakpoint (event) + "Disable the breakpoint. +Looks for a gdb-breakpoint extent on the line under the mouse, +and executes a `disable' command" + (interactive "@*e") + (gdb-mouse-toggle-breakpoint-enabled event 'disable)) + + +;; compatibility hack... +(or (fboundp 'extent-object) (fset 'extent-object 'extent-buffer)) + +(defun gdb-mouse-edit-function (event) + "Edit the definition of this function (as with \\[find-tag]) +Looks for a gdb-function-name extent on the line under the mouse, +and runs find-tag on the text under that extent." + (interactive "@*e") + (let (extent) + (save-excursion + (mouse-set-point event) + (setq extent (or (car (gdb-get-line-token-extents + '(gdb-function-name-token))) + (error "no function-name extent on this line")))) + (find-tag + (buffer-substring (extent-start-position extent) + (extent-end-position extent) + (extent-object extent))))) + + +(defun gdb-mouse-edit-function-location (event) + "Edit the source file of this function. +Looks for a gdb-function-location extent on line of the mouse, +and parses the text under it." + (interactive "@*e") + (let (file line) + (save-excursion + (mouse-set-point event) + (let ((extent (or (car (gdb-get-line-token-extents + '(gdb-function-location-token))) + (error "no function-location extent on this line")))) + (goto-char (extent-start-position extent)) + (or (looking-at "\\([^ \t\n:,]+\\):\\([0-9]+\\)") + (looking-at "\\([^ \t\n:,]+\\),? line \\([0-9]+\\)") + (error "no file position on this line")) + (setq file (buffer-substring (match-beginning 1) (match-end 1)) + line (buffer-substring (match-beginning 2) (match-end 2))) + (setq file (gdb-guess-file-name file) + line (string-to-int line)) + )) + (if (file-exists-p file) + (find-file-other-window file) + (signal 'file-error (list "File not found" file))) + (goto-line line))) + + +(defun gdb-mouse-goto-frame (event) + "Select this stack frame. +Looks for a gdb-frame-number extent on the line of the mouse, +and executes a `frame' command to select that frame." + (interactive "@*e") + (let (number) + (save-excursion + (mouse-set-point event) + (let ((extent (or (car (gdb-get-line-token-extents + '(gdb-frame-number-token))) + (error "no frame-number extent on this line")))) + (goto-char (extent-start-position extent)) + (if (eq (following-char) ?#) + (forward-char 1)) + (setq number (string-to-int + (buffer-substring (point) + (extent-end-position extent)))))) + (gdb-menu-command (format "frame %d" number) t)) + nil) + + +(defun gdb-mouse-get-variable-reference (event) + "Returns a string which references the variable under the mouse. +This works even if the variable is deep inside nested arrays or structures. +If the variable seems to hold a pointer, then a \"*\" will be prepended." + (save-excursion + (let* ((extent (if (extentp event) + event + (progn + (mouse-set-point event) + (extent-at (point) nil 'gdb-token)))) + dereference-p + name) + (or (and extent + (eq (extent-property extent 'gdb-token) + 'gdb-variable-name-token)) + (error "not over a variable name")) + (setq name (buffer-substring (extent-start-position extent) + (extent-end-position extent))) + (save-excursion + (goto-char (extent-end-position extent)) + (if (and (looking-at " *= *\\(([^)]+)\\)? *0x[0-9a-fA-F]+") ; pointer + (progn + (goto-char (match-end 0)) + (not (looking-at " +\"")))) ; but not string + (setq dereference-p t)) + + ;; Now, if this variable is buried in a structure, compose a complete + ;; reference-chain to it. + (goto-char (extent-start-position extent)) + + (let ((done nil)) + (while (not done) + (skip-chars-backward " \t") + (if (or (and (/= (preceding-char) ?\n) + (/= (preceding-char) ?\,) + (/= (preceding-char) ?\{)) + (<= (buffer-syntactic-context-depth) 0)) + (setq done t) + (let ((p (scan-lists (point) -1 1))) + (if (null p) + (setq done t) + (goto-char (setq p (- p 3))) + (cond + ((looking-at " = {") + (skip-chars-backward "a-zA-Z0-9_") + (if (= (preceding-char) ?\$) + (forward-char -1)) + (setq name (concat (buffer-substring (point) p) "." name))) + + ((looking-at "}, +{") + (forward-char 1) + (let ((parse-sexp-ignore-comments nil) + (count 0)) + (while (setq p (scan-sexps (point) -1 nil t)) + (goto-char p) + (setq count (1+ count))) + + (setq name (format "[%d].%s" count name)) + + ;; up out of the list + (skip-chars-backward " \t\n") + (if (= (preceding-char) ?\{) + (forward-char -1)) + + ;; we might be tightly nested in slot 0... + (while (= (preceding-char) ?\{) + (forward-char -1) + (setq name (concat "[0]" name))) + + (skip-chars-backward " \t") + (if (= (preceding-char) ?=) (forward-char -1)) + (skip-chars-backward " \t") + (setq p (point)) + (skip-chars-backward "a-zA-Z0-9_") + (if (= (preceding-char) ?\$) + (forward-char -1)) + + (setq name (concat (buffer-substring (point) p) name)) + )) + (t + (setq done t))))))))) + + (if dereference-p + (setq name (concat "*" name))) + name))) + +(defun gdb-mouse-print-variable (event) + "Print the value of this variable. +Finds a variable under the mouse, and figures out whether it is inside of +a structure, and composes and executes a `print' command. If the variable +seems to hold a pointer, prints the object pointed to." + (interactive "@*e") + (gdb-menu-command (concat "print " + (gdb-mouse-get-variable-reference event)) + t)) + +(defun gdb-mouse-print-variable-type (event) + "Describe the type of this variable. +Finds a variable under the mouse, and figures out whether it is inside of +a structure, and composes and executes a `whatis' command. If the variable +seems to hold a pointer, describes the type of the object pointed to." + (interactive "@*e") + (gdb-menu-command (concat "whatis " + (gdb-mouse-get-variable-reference event)) + t)) + +(defun gdb-mouse-print-type (event) + "Describe this type. +Finds a type description under the mouse, and executes a `ptype' command." + (interactive "@*e") + (let* ((extent (save-excursion + (mouse-set-point event) + (extent-at (point) nil 'gdb-token))) + name) + (or (and extent + (eq (extent-property extent 'gdb-token) 'gdb-type-name-token)) + (error "not over a type name")) + (setq name (buffer-substring (extent-start-position extent) + (extent-end-position extent))) + (gdb-menu-command (format "ptype %s" name) + t)) + nil) + + +;;; Popup menus + +(defun gdb-menu-command (command &optional scroll-to-bottom) + "Sends the command to gdb. +If gdb is not sitting at a prompt, interrupts it first +\(as if with \\[gdb-control-c-subjob]), executes the command, and then lets +the debugged program continue. + +If scroll-to-bottom is true, then point will be moved to after the new +output. Otherwise, an effort is made to avoid scrolling the window and +to keep point where it was." + + ;; this is kinda like gdb-call except for the interrupt-first behavior, + ;; but also it leaves the commands in the buffer instead of trying to + ;; hide them. + + (let* ((proc (or (get-buffer-process (current-buffer)) + (error "no process in %s" (buffer-name (current-buffer))))) + (window (selected-window)) + wstart + (opoint (point)) + was-at-bottom + running-p) + + (if (not (eq (current-buffer) (window-buffer window))) + (setq window (get-buffer-window (current-buffer)))) + (setq wstart (window-start window)) + + (let ((pmark (process-mark proc))) + (setq was-at-bottom (>= (point) pmark)) + (goto-char pmark) + (delete-region (point) (point-max))) + + (setq running-p (bolp)) ; maybe not the best way to tell... + + (cond (running-p + (message "Program is running -- interrupting first...") + (gdb-control-c-subjob) + (while (accept-process-output proc 1) + ;; continue accepting output as long as it's arriving + ))) + + (message "%s" command) + (goto-char (process-mark proc)) + (insert command) + (comint-send-input) + + ;; wait for the command to be accepted + (accept-process-output proc) + (goto-char (process-mark proc)) + + ;; continue, if we had interrupted + (cond (running-p + (insert "continue") + (comint-send-input))) + + (if scroll-to-bottom + (goto-char (process-mark proc)) + + (set-window-start window wstart) + (goto-char opoint) + (if was-at-bottom + (if (pos-visible-in-window-p (process-mark proc) window) + (goto-char (process-mark proc)) + (goto-char (window-end window)) + (forward-line -2)))) + ) + nil) + + +(defun gdb-make-context-menu (event) + "Returns a menu-desc corresponding to the stack-frame line under the mouse. +Returns nil if not over a stack-frame." + (save-excursion + (mouse-set-point event) + (let* ((extents (gdb-get-line-token-extents + '(gdb-breakpoint-number-token + gdb-info-breakpoint-number-token + gdb-enabled-token + gdb-frame-number-token + gdb-function-name-token + gdb-function-location-token + gdb-arglist-token + gdb-arglist-types-token + gdb-variable-name-token + gdb-type-name-token + ))) + (bnumber (or (nth 0 extents) + (nth 1 extents))) + (enabled-p (nth 2 extents)) + (fnumber (nth 3 extents)) + (name (nth 4 extents)) + (loc (nth 5 extents)) + (al (nth 6 extents)) + (alt (nth 7 extents)) + (var (nth 8 extents)) + (type (nth 9 extents)) + (var-e var)) + + ;; If this line has an arglist, only document variables and types + ;; if the mouse is directly over them. + (if (or al alt) + (setq var nil + type nil)) + + ;; Always prefer the object under the mouse to one elsewhere on the line. + (let* ((e (extent-at (point) nil 'gdb-token)) + (p (and e (extent-property e 'gdb-token)))) + (cond ((eq p 'gdb-function-name-token) (setq name e)) + ((eq p 'gdb-variable-name-token) (setq var e var-e e)) + ((eq p 'gdb-type-name-token) (setq type e)) + )) + + ;; Extract the frame number (it may begin with "#".) + (cond (fnumber + (goto-char (extent-start-position fnumber)) + (if (eq (following-char) ?#) + (forward-char 1)) + (setq fnumber + (string-to-int + (buffer-substring (point) + (extent-end-position fnumber)))))) + + ;; Extract the breakpoint number (it may begin with "Breakpoint ".) + (cond (bnumber + (setq bnumber + (buffer-substring (extent-start-position bnumber) + (extent-end-position bnumber))) + (if (string-match " [0-9]+\\'" bnumber) + (setq bnumber (substring bnumber (1+ (match-beginning 0))))) + (setq bnumber (string-to-int bnumber)) + (or (> bnumber 0) + (error "couldn't parse breakpoint number")))) + + (cond ((null enabled-p) + (setq enabled-p 'unknown)) + ((memq (char-after (extent-start-position enabled-p)) '(?y ?Y)) + (setq enabled-p 't)) + ((memq (char-after (extent-start-position enabled-p)) '(?n ?N)) + (setq enabled-p 'nil)) + (t + (setq enabled-p 'unknown))) + + ;; Convert the extents to strings. + ;; + (if name + (setq name (buffer-substring (extent-start-position name) + (extent-end-position name)))) + (if loc + (setq loc (buffer-substring (extent-start-position loc) + (extent-end-position loc)))) + (if var + (setq var (buffer-substring (extent-start-position var) + (extent-end-position var)))) + (if type + (setq type (buffer-substring (extent-start-position type) + (extent-end-position type)))) + + ;; Return a menu description list. + ;; + (nconc + (if (and bnumber (not (eq enabled-p 'nil))) + (list (vector (format "Disable Breakpoint %d" + bnumber) + (list 'gdb-mouse-disable-breakpoint event) + t))) + (if (and bnumber (not (eq enabled-p 't))) + (list (vector (format "Enable Breakpoint %d" + bnumber) + (list 'gdb-mouse-enable-breakpoint event) + t))) + (if bnumber + (list (vector (format "Delete Breakpoint %d" bnumber) + (list 'gdb-menu-command (format "delete %d" bnumber) + nil) + t))) + (if var + (list (vector (format "Print Value of `%s'" var) + (list 'gdb-mouse-print-variable var-e) + t) + (vector (format "Print Type of `%s'" var) + (list 'gdb-mouse-print-variable-type var-e) + t))) + (if name + (list (vector (format "Edit Definition of `%s'" name) + (list 'gdb-mouse-edit-function event) + t) + (vector (format "Set Breakpoint on `%s'" name) + (list 'gdb-menu-command (format "break %s" name) nil) + t))) + (if loc + (list (vector (format "Visit Source Line (%s)" loc) + (list 'gdb-mouse-edit-function-location event) + t))) + (if type + (list (vector (format "Describe Type `%s'" type) + (list 'gdb-menu-command (format "ptype %s" type) t) + t))) + (if fnumber + (list (vector (format "Select Stack Frame %d" fnumber) + (list 'gdb-menu-command (format "frame %d" fnumber) t) + t))) + )))) + + +(defun gdb-popup-menu (event) + "Pop up a context-sensitive menu of gdb-mode commands." + (interactive "_@e") + (select-window (event-window event)) + (let (menu) + (save-excursion + (setq menu (append (if (boundp 'gdb-popup-menu) + (append (cdr gdb-popup-menu) + '("---"))) + (if (boundp 'comint-popup-menu) + (cdr comint-popup-menu)))) + (let ((history (if (fboundp 'comint-make-history-menu) + (comint-make-history-menu))) + (context (gdb-make-context-menu event))) + (if history + (setq menu + (append menu (list "---" (cons "Command History" history))))) + (if context + (setq menu (append context (cons "---" menu)))) + ) + (setq menu (cons (if (boundp 'gdb-popup-menu) + (car gdb-popup-menu) + "GDB Commands") + menu))) + (popup-menu menu event))) + + +;;; Patch it in... + +(or (fboundp 'gdb-highlight-orig-filter) + (fset 'gdb-highlight-orig-filter (symbol-function 'gdb-filter))) + +(defun gdb-highlight-filter (proc string) + (let ((p (marker-position (process-mark proc)))) + (prog1 + (gdb-highlight-orig-filter proc string) + + (save-match-data + ;; + ;; If there are no newlines in this string at all, then don't + ;; bother processing it -- we will pick up these characters on + ;; the next time around, when the line's newline gets inserted. + ;; + (cond + ((string-match "\n" string) + (save-excursion + (set-buffer (process-buffer proc)) + (goto-char p) + (let ((p2 (marker-position (process-mark proc))) + p3) + ;; + ;; If gdb has given us a full pathname, remember it. (Do this + ;; before emitting any gdb-token extents, so that we attach it + ;; to the buffer *before* any of the extents to which it is + ;; known to correspond. + ;; + (gdb-highlight-remember-directory) + ;; + ;; Now highlight each line that has been written. If we wrote + ;; the last half of a line, re-highlight that whole line. (We + ;; need to do that so that the regexps will match properly; + ;; the "\n" test above also depends on this behavior.) + ;; + ;; But don't highlight lines longer than 5000 characters -- that + ;; probably means something is spewing, and we'll just get stuck + ;; hard in the regexp matcher. + ;; + (beginning-of-line) + (while (< (point) p2) + (goto-char (prog1 + (point) + (forward-line 1) + (setq p3 (point)))) + (if (< (- p3 (point)) 5000) + (gdb-highlight-line)) + (goto-char p3)))))))))) + +(fset 'gdb-filter 'gdb-highlight-filter) + + +(provide 'gdb-highlight) + +;;; gdb-highlight.el ends here + +--------------4273DDB4BB90CEC3B645B5AC-- + +