Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
12 KiB

  1. ;;; Breakpoint buffer
  2. ;; Author: Rocky Bernstein <rocky@gnu.org>
  3. ;; Copyright (C) 2019 Free Software Foundation, Inc
  4. ;; This program is free software: you can redistribute it and/or
  5. ;; modify it under the terms of the GNU General Public License as
  6. ;; published by the Free Software Foundation, either version 3 of the
  7. ;; License, or (at your option) any later version.
  8. ;; This program is distributed in the hope that it will be useful, but
  9. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;; General Public License for more details.
  12. ;; You should have received a copy of the GNU General Public License
  13. ;; along with this program. If not, see
  14. ;; <http://www.gnu.org/licenses/>.
  15. (require 'ansi-color)
  16. (require 'ring)
  17. (require 'seq)
  18. (require 'load-relative)
  19. (eval-when-compile (require 'cl-lib))
  20. (require-relative-list
  21. '("../key" "helper" "../follow" "../loc") "realgud-")
  22. (require-relative-list
  23. '("command") "realgud-buffer-")
  24. (declare-function realgud-breakpoint-mode 'realgud-breakpoint-mod)
  25. (declare-function realgud-get-buffer-base-name 'realgud-buffer-backtrace)
  26. (declare-function realgud-cmdbuf-debugger-name 'realgud-buffer-command)
  27. (declare-function realgud-cmdbuf? 'realgud-buffer-command)
  28. (declare-function realgud-cmdbuf-info-bkpt-buf= 'realgud-buffer-command)
  29. (declare-function realgud-cmdbuf-info-divert-output?= 'realgud-buffer-command)
  30. (declare-function realgud-cmdbuf-info-in-srcbuf?= 'realgud-buffer-command)
  31. (declare-function realgud:cmd-breakpoint 'realgud-cmds)
  32. (declare-function realgud:cmd-info-breakpoints 'realgud-cmds)
  33. (declare-function realgud-cmdbuf-pat 'realgud-buffer-command)
  34. (declare-function realgud-get-cmdbuf 'realgud-buffer-helper)
  35. (declare-function realgud:file-loc-from-line 'realgud-file)
  36. (declare-function realgud-loc-goto 'realgud-loc)
  37. (declare-function buffer-killed? 'realgud-helper)
  38. (declare-function realgud:loc-describe 'realgud-loc)
  39. (cl-defstruct realgud-breakpoint-info
  40. "debugger object/structure specific to a (top-level) program to be debugged."
  41. (cmdbuf nil) ;; buffer of the associated debugger process
  42. (cur-pos 0) ;; beakpoint we are at
  43. breakpoint-ring ;; ring of marks in buffer of breakpoint numbers. The
  44. ;; text at that marker has additional properties about the
  45. ;; breakpoint
  46. )
  47. (declare-function realgud:cmd-frame 'realgud-buffer-command)
  48. (declare-function realgud-get-cmdbuf(&optional opt-buffer))
  49. (declare-function realgud-command 'realgud-send)
  50. (make-variable-buffer-local (defvar realgud-breakpoint-info))
  51. ;: FIXME: not picked up from track. Why?
  52. (defvar realgud-track-divert-string nil)
  53. (defvar realgud-goto-entry-acc "")
  54. (defun realgud:breakpoint-describe (&optional buffer)
  55. (interactive "")
  56. (unless buffer (setq buffer (current-buffer)))
  57. (with-current-buffer buffer
  58. (let ((breakpoints (realgud-breakpoint-info-breakpoint-ring realgud-breakpoint-info))
  59. (brkpt)
  60. (loc)
  61. (i 0))
  62. (switch-to-buffer (get-buffer-create "*Describe Breakpoints*"))
  63. (while (and (< i (ring-length breakpoints)) (setq brkpt (ring-ref breakpoints i)))
  64. (insert (format "*** %d\n" i))
  65. (insert (format "%s\n" brkpt))
  66. (when (markerp brkpt)
  67. (with-current-buffer (marker-buffer brkpt)
  68. (goto-char brkpt)
  69. (setq loc (get-text-property (point) 'loc))
  70. )
  71. (when loc (realgud:loc-describe loc)))
  72. (setq i (1+ i))
  73. )
  74. )
  75. ))
  76. ;; FIXME: create this in a new frame.
  77. (defun realgud:breakpoint-init ()
  78. (interactive)
  79. (let ((buffer (current-buffer))
  80. (cmdbuf (realgud-get-cmdbuf))
  81. (process)
  82. )
  83. (with-current-buffer-safe cmdbuf
  84. (let ((brkpt-pat (realgud-cmdbuf-pat "debugger-breakpoint"))
  85. (brkpt-pos-ring)
  86. (bp-list (realgud-cmdbuf-info-bp-list realgud-cmdbuf-info))
  87. (sleep-count 0)
  88. )
  89. (unless brkpt-pat
  90. (error "No 'debugger-breakpoint' regular expression recorded for debugger %s"
  91. (realgud-cmdbuf-debugger-name)))
  92. (setq process (get-buffer-process (current-buffer)))
  93. (realgud-cmdbuf-info-in-srcbuf?= (not (realgud-cmdbuf? buffer)))
  94. (realgud-cmdbuf-info-divert-output?= t)
  95. (setq realgud-track-divert-string nil)
  96. (realgud:cmd-info-breakpoints)
  97. (while (and (eq 'run (process-status process))
  98. (null realgud-track-divert-string)
  99. (> 1000 (setq sleep-count (1+ sleep-count))))
  100. (sleep-for 0.001)
  101. )
  102. (if (>= sleep-count 1000)
  103. (message "Timeout on running debugger command")
  104. ;; else
  105. ;; (message "+++4 %s" realgud-track-divert-string)
  106. (let ((brkpt-buffer (get-buffer-create
  107. (format "*Breakpoint %s*"
  108. (realgud-get-buffer-base-name
  109. (buffer-name)))))
  110. (divert-string realgud-track-divert-string)
  111. )
  112. (realgud-cmdbuf-info-brkpt-buf= brkpt-buffer)
  113. (with-current-buffer brkpt-buffer
  114. (setq buffer-read-only nil)
  115. (delete-region (point-min) (point-max))
  116. (if divert-string
  117. (let* ((duple
  118. (realgud:breakpoint-add-text-properties
  119. brkpt-pat cmdbuf divert-string bp-list))
  120. (string-with-props
  121. (ansi-color-filter-apply (car duple)))
  122. (brkpt-num-pos-list (cadr duple))
  123. )
  124. (insert string-with-props)
  125. ;; add marks for each position
  126. (realgud-breakpoint-mode cmdbuf)
  127. (setq brkpt-pos-ring
  128. (make-ring (length brkpt-num-pos-list)))
  129. (dolist (pos brkpt-num-pos-list)
  130. (goto-char (1+ pos))
  131. (ring-insert-at-beginning brkpt-pos-ring (point-marker))
  132. )
  133. )
  134. )
  135. ;; realgud-breakpoint-mode kills all local variables so
  136. ;; we set this after. Alternatively change realgud-breakpoint-mode.
  137. (set (make-local-variable 'realgud-breakpoint-info)
  138. (make-realgud-breakpoint-info
  139. :cmdbuf cmdbuf
  140. :breakpoint-ring brkpt-pos-ring
  141. ))
  142. )
  143. )
  144. )
  145. )
  146. )
  147. (unless cmdbuf
  148. (message "Unable to find debugger command buffer for %s" buffer))
  149. )
  150. )
  151. (defun realgud-breakpoint? ( &optional buffer)
  152. "Return true if BUFFER is a debugger command buffer."
  153. (with-current-buffer-safe
  154. (or buffer (current-buffer))
  155. (realgud-breakpoint-info-set?)))
  156. (defalias 'realgud-breakpoint-info? 'realgud-breakpoint-info-p)
  157. (defun realgud-breakpoint-info-set? ()
  158. "Return true if realgud-breakpoint-info is set."
  159. (and (boundp 'realgud-breakpoint-info)
  160. realgud-breakpoint-info
  161. (realgud-breakpoint-info? realgud-breakpoint-info)))
  162. (defun realgud-goto-entry-n ()
  163. "Go to an entry number.
  164. Breakpoints, Display expressions and Stack Frames all have
  165. numbers associated with them which are distinct from line
  166. numbers. In a secondary buffer, this function is usually bound to
  167. a numeric key which will position you at that entry number. To
  168. go to an entry above 9, just keep entering the number. For
  169. example, if you press 1 and then 9, you should jump to entry
  170. 1 (if it exists) and then 19 (if that exists). Entering any
  171. non-digit will start entry number from the beginning again."
  172. (interactive)
  173. (if (not (eq last-command 'realgud-goto-entry-n))
  174. (setq realgud-goto-entry-acc ""))
  175. (realgud-goto-entry-n-internal (this-command-keys)))
  176. (defun realgud-goto-breakpoint ()
  177. "Go to the breakpoint number. We get the breakpoint number from the
  178. 'brkpt-num property"
  179. (interactive)
  180. (if (realgud-breakpoint?)
  181. (let ((loc (get-text-property (point) 'loc)))
  182. (if loc
  183. (realgud-loc-goto loc)
  184. (message "No location property found at this point")
  185. )
  186. )
  187. )
  188. )
  189. (defun realgud-goto-breakpoint-mouse (event)
  190. (interactive "e")
  191. (let* ((pos (posn-point (event-end event)))
  192. (loc (get-text-property pos 'loc)))
  193. (if (realgud-breakpoint?)
  194. (if loc
  195. (realgud-loc-goto loc)
  196. (message "No location property found at this point")
  197. )
  198. )
  199. )
  200. )
  201. (defun realgud-goto-breakpoint-n ()
  202. "Goto breakpoint number indicated by the accumulated numeric keys just entered.
  203. This function is usually bound to a numeric key in a 'frame'
  204. secondary buffer. To go to an entry above 9, just keep entering
  205. the number. For example, if you press 1 and then 9, frame 1 is selected
  206. \(if it exists) and then frame 19 (if that exists). Entering any
  207. non-digit will start entry number from the beginning again."
  208. (interactive)
  209. (if (not (eq last-command 'realgud-goto-breakpoint-n))
  210. (setq realgud-goto-entry-acc ""))
  211. (realgud-goto-breakpoint-n-internal (this-command-keys)))
  212. (defun realgud-goto-breakpoint-n-internal (keys)
  213. (if (and (stringp keys)
  214. (= (length keys) 1))
  215. (progn
  216. (setq realgud-goto-entry-acc (concat realgud-goto-entry-acc keys))
  217. ;; Try to find the longest suffix.
  218. (let ((acc realgud-goto-entry-acc))
  219. (while (not (string= acc ""))
  220. (if (not (realgud-goto-entry-try acc))
  221. (setq acc (substring acc 1))
  222. (realgud:cmd-frame (string-to-number acc))
  223. ;; Break loop.
  224. (setq acc "")))))
  225. (message "`realgud-goto-breakpoint-n' must be bound to a number key")))
  226. (defun realgud:breakpoint-add-text-properties(brkpt-pat cmdbuf string bp-list)
  227. "Parse STRING or the current buffer and add frame properties: breakpoint number,
  228. filename, and line number as text properties."
  229. (let* ((stripped-string (ansi-color-filter-apply string))
  230. (brkpt-regexp (realgud-loc-pat-regexp brkpt-pat))
  231. (brkpt-group-pat (realgud-loc-pat-num brkpt-pat))
  232. (file-group-pat (realgud-loc-pat-file-group brkpt-pat))
  233. (line-group-pat (realgud-loc-pat-line-group brkpt-pat))
  234. (alt-brkpt-num -1)
  235. (last-pos 0)
  236. (selected-brkpt-num nil)
  237. (brkpt-num-pos-list '())
  238. )
  239. (while (string-match brkpt-regexp stripped-string last-pos)
  240. (let ((brkpt-num-str) (brkpt-num) (line-num) (filename)
  241. (loc)
  242. ;; From https://github.com/realgud/realgud/pull/192
  243. ;; Each brkpt of breakpoint is searched via string-match
  244. ;; invocation and a position of the current brkpt is
  245. ;; updated via (setq last-pos (match-end 0)) in the end of
  246. ;; the loop. But somewhere in the body of the loop (I do
  247. ;; not know exactly where), there is another call to
  248. ;; string-match and it messes up all positions.
  249. (whole-match-begin (match-beginning 0))
  250. (whole-match-end (match-end 0))
  251. (brkpt-num-pos)
  252. )
  253. (if brkpt-group-pat
  254. (progn
  255. (setq brkpt-num-str
  256. (substring stripped-string
  257. (match-beginning brkpt-group-pat)
  258. (match-end brkpt-group-pat)))
  259. (setq brkpt-num (string-to-number brkpt-num-str))
  260. (setq loc (seq-find (lambda (elt) (equal brkpt-num (realgud-loc-num elt))) bp-list))
  261. (setq brkpt-num-pos (match-beginning brkpt-group-pat))
  262. (cl-pushnew brkpt-num-pos brkpt-num-pos-list)
  263. (when loc
  264. (add-text-properties (match-beginning brkpt-group-pat)
  265. (match-end brkpt-group-pat)
  266. (list 'mouse-face 'highlight
  267. 'help-echo "mouse-2: goto this brkpt"
  268. 'mark (realgud-loc-marker loc))
  269. string))
  270. )
  271. ; else
  272. (progn
  273. (setq brkpt-num-str
  274. (substring stripped-string (match-beginning 0)
  275. (match-end 0)))
  276. (setq brkpt-num (cl-incf alt-brkpt-num))
  277. (setq brkpt-num-pos (match-beginning 0))
  278. (cl-pushnew brkpt-num-pos brkpt-num-pos-list)
  279. (add-text-properties (match-beginning 0) (match-end 0)
  280. (list 'mouse-face 'highlight
  281. 'help-echo "mouse-2: goto this brkpt"
  282. 'brkpt brkpt-num)
  283. string)
  284. )
  285. )
  286. (when file-group-pat
  287. (setq filename (substring stripped-string
  288. (match-beginning file-group-pat)
  289. (match-end file-group-pat)))
  290. (add-text-properties (match-beginning file-group-pat)
  291. (match-end file-group-pat)
  292. (list 'mouse-face 'highlight
  293. 'help-echo "mouse-2: goto this file"
  294. 'action 'realgud:follow-event
  295. 'file filename)
  296. string)
  297. )
  298. (when line-group-pat
  299. (let ((line-num-str (substring stripped-string
  300. (match-beginning line-group-pat)
  301. (match-end line-group-pat))))
  302. (setq line-num (string-to-number (or line-num-str "1")))
  303. ))
  304. (when (and (stringp filename) (numberp line-num))
  305. (let ((loc (realgud:file-loc-from-line filename line-num cmdbuf)))
  306. (put-text-property whole-match-begin whole-match-end
  307. 'mark loc string)
  308. ))
  309. (put-text-property whole-match-begin whole-match-end
  310. 'brkpt-num brkpt-num string)
  311. (setq last-pos whole-match-end)
  312. ))
  313. (list string (nreverse brkpt-num-pos-list))
  314. )
  315. )
  316. (provide-me "realgud-buffer-")