2 ;;; Copyright (C) 2010-2015 Rocky Bernstein <rocky@gnu.org>
3 (require 'load-relative)
4 (eval-when-compile (require 'cl-lib))
6 '("../key" "helper" "follow" "loc") "realgud-")
9 '("command") "realgud-buffer-")
11 (declare-function realgud-cmdbuf-debugger-name 'realgud-buffer-command)
12 (declare-function realgud-cmdbuf? 'realgud-buffer-command)
13 (declare-function realgud-backtrace-mode (cmdbuf))
14 (declare-function realgud:cmd-backtrace (arg))
15 (declare-function realgud-cmdbuf-pat(key))
16 (declare-function realgud-cmdbuf-info-in-srcbuf?= (arg))
17 (declare-function realgud-get-cmdbuf 'realgud-buffer-helper)
18 (declare-function realgud:file-loc-from-line 'realgud-file)
20 (defstruct realgud-backtrace-info
21 "debugger object/structure specific to a (top-level) program to be debugged."
22 (cmdbuf nil) ;; buffer of the associated debugger process
23 (cur-pos 0) ;; Frame we are at
24 frame-ring ;; ring of marks in buffer of frame numbers. The
25 ;; text at that marker has additional properties about the
29 (declare-function realgud:cmd-frame 'realgud-buffer-command)
30 (declare-function realgud-get-cmdbuf(&optional opt-buffer))
31 (declare-function realgud-command (fmt &optional arg no-record?
32 frame-switch? realgud-prompts?))
34 (make-variable-buffer-local (defvar realgud-backtrace-info))
36 ;: FIXME: not picked up from track. Why?
37 (defvar realgud-track-divert-string nil)
39 (defvar realgud-goto-entry-acc "")
41 (defun realgud-get-buffer-base-name(string)
42 "Leading and ending * in string. For example:
43 *shell<2>* -> shell<2>
46 (if (string-match "^[*]?\\([^*]+\\)[*]?$" string)
47 (let ((string-sans-stars (match-string 1 string)))
48 (if (string-match "\\(.+\\) shell" string-sans-stars)
49 (match-string 1 string-sans-stars)
56 (defun realgud:backtrace-describe (&optional buffer)
58 (unless buffer (setq buffer (current-buffer)))
59 (with-current-buffer buffer
60 (let ((frames (realgud-backtrace-info-frame-ring realgud-backtrace-info))
64 (switch-to-buffer (get-buffer-create "*Describe*"))
65 (while (and (< i (ring-length frames)) (setq frame (ring-ref frames i)))
66 (insert (format "*** %d\n" i))
67 (insert (format "%s\n" frame))
69 (with-current-buffer (marker-buffer frame)
71 (setq loc (get-text-property (point) 'loc))
73 (when loc (realgud:loc-describe loc)))
79 ;; FIXME: create this in a new frame.
80 (defun realgud:backtrace-init ()
82 (let ((buffer (current-buffer))
83 (cmdbuf (realgud-get-cmdbuf))
86 (with-current-buffer-safe cmdbuf
87 (let ((frame-pat (realgud-cmdbuf-pat "debugger-backtrace"))
88 (indicator-re (or (realgud-cmdbuf-pat "selected-frame-indicator")
95 (error "No 'debugger-backtrace' regular expression recorded for debugger %s"
96 (realgud-cmdbuf-debugger-name)))
97 (setq process (get-buffer-process (current-buffer)))
98 (realgud-cmdbuf-info-in-srcbuf?= (not (realgud-cmdbuf? buffer)))
99 (realgud-cmdbuf-info-divert-output?= 't)
100 (setq realgud-track-divert-string nil)
101 (realgud:cmd-backtrace 0)
102 (while (and (eq 'run (process-status process))
103 (null realgud-track-divert-string)
104 (> 1000 (setq sleep-count (1+ sleep-count))))
107 (if (>= sleep-count 1000)
108 (message "Timeout on running debugger command")
110 ;; (message "+++4 %s" realgud-track-divert-string)
111 (let ((bt-buffer (get-buffer-create
112 (format "*Backtrace %s*"
113 (realgud-get-buffer-base-name
115 (divert-string realgud-track-divert-string)
117 (realgud-cmdbuf-info-bt-buf= bt-buffer)
118 (with-current-buffer bt-buffer
119 (setq buffer-read-only nil)
120 (delete-region (point-min) (point-max))
123 (realgud:backtrace-add-text-properties
124 frame-pat cmdbuf indicator-re))
126 (ansi-color-filter-apply (car triple)))
127 (frame-num-pos-list (caddr triple))
129 (setq selected-frame-num (cadr triple))
130 (insert string-with-props)
131 ;; add marks for each position
132 (realgud-backtrace-mode cmdbuf)
134 (make-ring (length frame-num-pos-list)))
135 (dolist (pos frame-num-pos-list)
137 (ring-insert-at-beginning frame-pos-ring (point-marker))
141 ;; realgud-backtrace-mode kills all local variables so
142 ;; we set this after. Alternatively change realgud-backtrace-mode.
143 (set (make-local-variable 'realgud-backtrace-info)
144 (make-realgud-backtrace-info
146 :frame-ring frame-pos-ring
148 (if selected-frame-num
149 (realgud-backtrace-moveto-frame selected-frame-num))
156 (message "Unable to find debugger command buffer for %s" buffer))
160 (defun realgud-backtrace? ( &optional buffer)
161 "Return true if BUFFER is a debugger command buffer."
162 (with-current-buffer-safe
163 (or buffer (current-buffer))
164 (realgud-backtrace-info-set?)))
167 (defalias 'realgud-backtrace-info? 'realgud-backtrace-info-p)
169 (defun realgud-backtrace-info-set? ()
170 "Return true if realgud-backtrace-info is set."
171 (and (boundp 'realgud-backtrace-info)
172 realgud-backtrace-info
173 (realgud-backtrace-info? realgud-backtrace-info)))
176 (defun realgud-backtrace-moveto-frame-selected ()
177 "Set point to the selected frame."
179 (if (realgud-backtrace?)
180 (let* ((cur-pos (realgud-sget 'backtrace-info 'cur-pos))
181 (ring-size (ring-size (realgud-sget 'backtrace-info 'frame-ring)))
183 (if (and cur-pos (> ring-size 0))
184 (realgud-backtrace-moveto-frame cur-pos)
186 (message "No frame information recorded")
192 (defun realgud-backtrace-moveto-frame (num &optional opt-buffer)
194 (if (realgud-backtrace?)
195 (let* ((ring (realgud-sget 'backtrace-info 'frame-ring))
196 (marker (ring-ref ring num)))
197 (setf (realgud-backtrace-info-cur-pos realgud-backtrace-info) num)
202 (message "frame number %s is not an integer" num)
206 (defun realgud-backtrace-moveto-frame-next ()
207 "Set point to the next frame. If we are at the end, wrap to the
208 beginning. Note that we are just moving in the backtrace buffer,
209 not updating the frame stack."
211 (if (realgud-backtrace?)
212 (let* ((cur-pos (realgud-sget 'backtrace-info 'cur-pos))
213 (ring-size (ring-size (realgud-sget 'backtrace-info 'frame-ring)))
215 (if (and cur-pos (> ring-size 0))
216 (realgud-backtrace-moveto-frame (ring-plus1 cur-pos ring-size))
218 (message "No frame information recorded")
224 (defun realgud-backtrace-moveto-frame-prev ()
225 "Set point to the next frame. If we are at the beginning, wrap to the
226 end. Note that we are just moving in the backtrace buffer,
227 not updating the frame stack."
229 (if (realgud-backtrace?)
230 (let* ((cur-pos (realgud-sget 'backtrace-info 'cur-pos))
231 (ring-size (ring-size (realgud-sget 'backtrace-info 'frame-ring)))
233 (if (and cur-pos (> ring-size 0))
234 (realgud-backtrace-moveto-frame (ring-minus1 cur-pos ring-size))
236 (message "No frame information recorded")
242 (defun realgud-goto-frame-n-internal (keys)
243 (if (and (stringp keys)
246 (setq realgud-goto-entry-acc (concat realgud-goto-entry-acc keys))
247 ;; Try to find the longest suffix.
248 (let ((acc realgud-goto-entry-acc))
249 (while (not (string= acc ""))
250 (if (not (realgud-goto-entry-try acc))
251 (setq acc (substring acc 1))
252 (realgud:cmd-frame (string-to-number acc))
255 (message "`realgud-goto-frame-n' must be bound to a number key")))
257 ;; FIXME: replace with ring.
258 (defun realgud-goto-entry-try (str)
259 "See if there is an entry with number STR. If not return nil."
260 (goto-char (point-min))
261 (if (re-search-forward (concat "^[^0-9]*\\(" str "\\)[^0-9]") nil t)
263 (goto-char (match-end 1))
268 ;; The following is split in two to facilitate debugging.
269 (defun realgud-goto-entry-n-internal (keys)
270 (if (and (stringp keys)
273 (setq realgud-goto-entry-acc (concat realgud-goto-entry-acc keys))
274 ;; Try to find the longest suffix.
275 (let ((acc realgud-goto-entry-acc)
277 (while (not (string= acc ""))
278 (if (not (realgud-goto-entry-try acc))
279 (setq acc (substring acc 1))
284 (message "`realgud-goto-entry-n' must be bound to a number key")))
287 (defun realgud-goto-entry-n ()
288 "Go to an entry number.
290 Breakpoints, Display expressions and Stack Frames all have
291 numbers associated with them which are distinct from line
292 numbers. In a secondary buffer, this function is usually bound to
293 a numeric key which will position you at that entry number. To
294 go to an entry above 9, just keep entering the number. For
295 example, if you press 1 and then 9, you should jump to entry
296 1 (if it exists) and then 19 (if that exists). Entering any
297 non-digit will start entry number from the beginning again."
299 (if (not (eq last-command 'realgud-goto-entry-n))
300 (setq realgud-goto-entry-acc ""))
301 (realgud-goto-entry-n-internal (this-command-keys)))
303 (defun realgud-goto-frame ()
304 "Go to the frame number. We get the frame number from the
307 (if (realgud-backtrace?)
308 (let ((frame-num (get-text-property (point) 'frame-num)))
310 (realgud:cmd-frame frame-num)
311 (message "No frame property found at this point")
317 (defun realgud-goto-frame-1 ()
320 (if (realgud-backtrace?)
321 (realgud:cmd-frame 1)
325 (defun realgud-goto-frame-2 ()
328 (if (realgud-backtrace?)
329 (realgud:cmd-frame 2)
333 (defun realgud-goto-frame-3 ()
336 (if (realgud-backtrace?)
337 (realgud:cmd-frame 3)
341 (defun realgud-goto-frame-mouse (event)
343 (let* ((pos (posn-point (event-end event)))
344 (frame-num (get-text-property pos 'frame-num)))
345 (if (realgud-backtrace?)
347 (realgud:cmd-frame frame-num)
348 (message "No frame property found at this point")
354 (defun realgud-goto-frame-n ()
355 "Go to the frame number indicated by the accumulated numeric keys just entered.
357 This function is usually bound to a numeric key in a 'frame'
358 secondary buffer. To go to an entry above 9, just keep entering
359 the number. For example, if you press 1 and then 9, frame 1 is selected
360 \(if it exists) and then frame 19 (if that exists). Entering any
361 non-digit will start entry number from the beginning again."
363 (if (not (eq last-command 'realgud-goto-frame-n))
364 (setq realgud-goto-entry-acc ""))
365 (realgud-goto-frame-n-internal (this-command-keys)))
367 (defun realgud:backtrace-add-text-properties(frame-pat cmdbuf &optional opt-string
369 "Parse OPT-STRING or the current buffer and add frame properties: frame number,
370 filename, line number, whether the frame is selected as text properties."
372 (let* ((string (or opt-string
373 (buffer-substring (point-min) (point-max))
375 (stripped-string (ansi-color-filter-apply string))
376 (frame-regexp (realgud-loc-pat-regexp frame-pat))
377 (frame-group-pat (realgud-loc-pat-num frame-pat))
378 (file-group-pat (realgud-loc-pat-file-group frame-pat))
379 (line-group-pat (realgud-loc-pat-line-group frame-pat))
382 (selected-frame-num nil)
383 (frame-num-pos-list '())
385 (while (string-match frame-regexp stripped-string last-pos)
386 (let ((frame-num-str) (frame-num) (line-num) (filename)
387 ;; FIXME: Remove hack that group 1 is always the frame indicator.
389 (substring stripped-string (match-beginning 1) (match-end 1)))
396 (substring stripped-string
397 (match-beginning frame-group-pat)
398 (match-end frame-group-pat)))
399 (setq frame-num (string-to-number frame-num-str))
400 (setq frame-num-pos (match-beginning frame-group-pat))
401 (add-to-list 'frame-num-pos-list frame-num-pos 't)
402 (add-text-properties (match-beginning frame-group-pat)
403 (match-end frame-group-pat)
404 (list 'mouse-face 'highlight
405 'help-echo "mouse-2: goto this frame"
412 (substring stripped-string (match-beginning 0)
414 (setq frame-num (incf alt-frame-num))
415 (setq frame-num-pos (match-beginning 0))
416 (add-to-list 'frame-num-pos-list frame-num-pos 't)
417 (add-text-properties (match-beginning 0) (match-end 0)
418 (list 'mouse-face 'highlight
419 'help-echo "mouse-2: goto this frame"
425 (setq filename (substring stripped-string
426 (match-beginning file-group-pat)
427 (match-end file-group-pat)))
428 (add-text-properties (match-beginning file-group-pat)
429 (match-end file-group-pat)
430 (list 'mouse-face 'highlight
431 'help-echo "mouse-2: goto this file"
432 'action 'realgud:follow-event
437 (let ((line-num-str (substring stripped-string
438 (match-beginning line-group-pat)
439 (match-end line-group-pat))))
440 (setq line-num (string-to-number (or line-num-str "1")))
443 (when (and (stringp filename) (numberp line-num))
444 (let ((loc (realgud:file-loc-from-line filename line-num cmdbuf)))
445 (put-text-property (match-beginning 0) (match-end 0)
448 (put-text-property (match-beginning 0) (match-end 0)
449 'frame-num frame-num string)
450 (setq last-pos (match-end 0))
452 (if (string-match frame-indicator-re frame-indicator)
453 (setq selected-frame-num frame-num))
456 (list string selected-frame-num frame-num-pos-list)
460 (provide-me "realgud-buffer-")