A JavaScript development environment for Emacs https://indium.readthedocs.io
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.

225 lines
8.4KB

  1. ;;; indium-breakpoint.el --- Add/remove breakpoints -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2017-2018 Nicolas Petton
  3. ;; Author: Nicolas Petton <nicolas@petton.fr>
  4. ;; This program is free software; you can redistribute it and/or modify
  5. ;; it under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation, either version 3 of the License, or
  7. ;; (at your option) any later version.
  8. ;; This program is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU 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 <http://www.gnu.org/licenses/>.
  14. ;;; Commentary:
  15. ;; Add and remove breakpoints to a buffer.
  16. ;;
  17. ;; Breakpoints can be added to buffer even if Indium is not connected. In such
  18. ;; case, Indium will attempt to register them breakpoints in the runtime when a
  19. ;; connection is made.
  20. ;;; Code:
  21. (require 'indium-client)
  22. (require 'indium-faces)
  23. (require 'indium-structs)
  24. (defvar indium-breakpoint--local-breakpoints (make-hash-table :weakness t)
  25. "Table of all local breakpoints and their buffers.")
  26. (defun indium-breakpoint-add (&optional condition)
  27. "Add a breakpoint at point.
  28. When CONDITION is non-nil, the breakpoint will be hit when
  29. CONDITION is true."
  30. (let* ((brk (indium-breakpoint-create :condition (or condition ""))))
  31. (map-put indium-breakpoint--local-breakpoints brk (current-buffer))
  32. (indium-breakpoint--add-overlay brk)
  33. (when (indium-client-process-live-p)
  34. (indium-client-add-breakpoint brk))))
  35. (defun indium-breakpoint-edit-condition ()
  36. "Edit condition of breakpoint at point."
  37. (when-let ((breakpoint (indium-breakpoint-at-point)))
  38. (let* ((old-condition (indium-breakpoint-condition breakpoint))
  39. (new-condition (read-from-minibuffer "Breakpoint condition: "
  40. old-condition nil nil nil old-condition)))
  41. (indium-breakpoint-remove)
  42. (indium-breakpoint-add new-condition))))
  43. (defun indium-breakpoint-remove ()
  44. "Remove all breakpoints from the current line."
  45. (seq-doseq (brk (indium-breakpoint-breakpoints-at-point))
  46. (when (indium-client-process-live-p)
  47. (indium-client-remove-breakpoint brk))
  48. (map-delete indium-breakpoint--local-breakpoints brk)
  49. (indium-breakpoint--remove-overlay)))
  50. (defun indium-breakpoint-remove-breakpoints-from-current-buffer ()
  51. "Remove all breakpoints from the current buffer's file."
  52. (indium-breakpoint--breakpoints-in-buffer-do
  53. (lambda (_ ov)
  54. (save-excursion
  55. (goto-char (overlay-start ov))
  56. (indium-breakpoint-remove)))))
  57. (defun indium-breakpoint-resolve (id line)
  58. "Update the breakpoint with ID for SCRIPT at LINE.
  59. This function should be called upon breakpoint resolution by the
  60. server, or when a breakpoint location gets updated from the
  61. server."
  62. (let* ((brk (indium-breakpoint-breakpoint-with-id id))
  63. (location (indium-breakpoint-location brk)))
  64. (setf (indium-breakpoint-resolved brk) t)
  65. (setf (indium-location-line location) line)
  66. (indium-breakpoint--update-overlay brk location)))
  67. (defun indium-breakpoint-breakpoint-with-id (id)
  68. "Return the breakpoint with ID or nil."
  69. (seq-find (lambda (brk)
  70. (equal id (indium-breakpoint-id brk)))
  71. (map-keys indium-breakpoint--local-breakpoints)))
  72. (defun indium-breakpoint-breakpoints-at-point ()
  73. "Return all breakpoints on the current line.
  74. If there is no breakpoint set on the line, return nil."
  75. (seq-filter (lambda (brk)
  76. (let ((location (indium-breakpoint-location brk)))
  77. (and (equal (indium-location-file location) buffer-file-name)
  78. (equal (indium-location-line location) (line-number-at-pos)))))
  79. (map-keys indium-breakpoint--local-breakpoints)))
  80. (defun indium-breakpoint-at-point ()
  81. "Return the first breakpoint on the current line.
  82. If there is no breakpoint set on the line, return nil."
  83. (car (indium-breakpoint-breakpoints-at-point)))
  84. (defun indium-breakpoint-on-current-line-p ()
  85. "Return non-nil if there is a breakpoint on the current line."
  86. (not (null (indium-breakpoint--overlay-on-current-line))))
  87. (defun indium-breakpoint-remove-overlays-from-current-buffer ()
  88. "Remove all breakpoint markers from the current buffer.
  89. This function does no unset breakpoints."
  90. (remove-overlays (point-min)
  91. (point-max)
  92. 'indium-breakpoint-ov
  93. t))
  94. (defun indium-breakpoint--add-overlay (breakpoint)
  95. "Add an overlay for BREAKPOINT on the current line.
  96. An icon is added to the left fringe."
  97. (let ((ov (indium-breakpoint--ensure-overlay)))
  98. (overlay-put ov
  99. 'before-string
  100. (indium-breakpoint--fringe-icon breakpoint))
  101. (overlay-put ov
  102. 'indium-breakpoint
  103. breakpoint)
  104. (setf (indium-breakpoint-overlay breakpoint) ov)
  105. ov))
  106. (defun indium-breakpoint--remove-overlay ()
  107. "Remove the breakpoint overlay from the current line."
  108. (when-let ((ov (indium-breakpoint--overlay-on-current-line)))
  109. (setf (indium-breakpoint-overlay (overlay-get ov 'indium-breakpoint)) nil)
  110. (remove-overlays (overlay-start ov)
  111. (overlay-end ov)
  112. 'indium-breakpoint-ov
  113. t)))
  114. (defun indium-breakpoint--update-overlay (breakpoint location)
  115. "Set the overlay for BREAKPOINT at LOCATION."
  116. (when-let ((buf (indium-breakpoint-buffer breakpoint)))
  117. (with-current-buffer buf
  118. (save-excursion
  119. (goto-char (overlay-start (indium-breakpoint-overlay breakpoint)))
  120. (indium-breakpoint--remove-overlay))))
  121. (when-let ((file (indium-location-file location))
  122. (line (indium-location-line location)))
  123. (with-current-buffer (find-file-noselect file)
  124. (save-excursion
  125. (goto-char (point-min))
  126. (forward-line (1- line))
  127. (indium-breakpoint--add-overlay breakpoint)))))
  128. (defun indium-breakpoint-buffer (breakpoint)
  129. "Return the buffer in which BREAKPOINT is set, or nil."
  130. (when-let ((ov (indium-breakpoint-overlay breakpoint)))
  131. (overlay-buffer ov)))
  132. (defun indium-breakpoint--register-all-breakpoints ()
  133. "Register all local breakpoints."
  134. (map-apply (lambda (brk _)
  135. (indium-client-add-breakpoint brk))
  136. indium-breakpoint--local-breakpoints))
  137. (defun indium-breakpoint--unregister-all-breakpoints ()
  138. "Remove the registration information from all breakpoints."
  139. (map-apply (lambda (brk _)
  140. (setf (indium-breakpoint-resolved brk) nil)
  141. (indium-breakpoint--update-overlay
  142. brk
  143. (indium-breakpoint-location brk)))
  144. indium-breakpoint--local-breakpoints))
  145. (defun indium-breakpoint--fringe-icon (breakpoint)
  146. "Return the fringe icon used for BREAKPOINT."
  147. (propertize "b" 'display
  148. (list 'left-fringe (if (indium-breakpoint-resolved breakpoint)
  149. 'indium-breakpoint-resolved
  150. 'indium-breakpoint-unresolved)
  151. 'indium-breakpoint-face)))
  152. (defun indium-breakpoint--overlay-on-current-line ()
  153. "Return the breakpoint overlay on the current-line.
  154. If no overlay is present, return nil."
  155. (seq-find (lambda (ov)
  156. (overlay-get ov 'indium-breakpoint-ov))
  157. (overlays-in (point-at-bol) (1+ (point-at-eol)))))
  158. (defun indium-breakpoint--ensure-overlay ()
  159. "Return the breakpoint overlay on the current line.
  160. If there is no overlay, make one."
  161. (or (indium-breakpoint--overlay-on-current-line)
  162. (let ((ov (make-overlay (point-at-bol) (point-at-eol) nil t)))
  163. (overlay-put ov 'indium-breakpoint-ov t)
  164. ov)))
  165. ;; Handle breakpoint resolution
  166. (add-hook 'indium-client-breakpoint-resolved-hook #'indium-breakpoint-resolve)
  167. ;; Update/Restore breakpoints
  168. (add-hook 'indium-client-closed-hook #'indium-breakpoint--unregister-all-breakpoints)
  169. (add-hook 'indium-client-connected-hook #'indium-breakpoint--register-all-breakpoints)
  170. ;; Helpers
  171. (defun indium-breakpoint--breakpoints-in-buffer-do (fn)
  172. "Evaluate FN on all breakpoints in the current buffer.
  173. FN takes two arguments, the breakpoint and its associated
  174. overlay."
  175. (let ((overlays (overlays-in (point-min) (point-max))))
  176. (seq-doseq (ov overlays)
  177. (when-let ((brk (overlay-get ov 'indium-breakpoint)))
  178. (funcall fn brk ov)))))
  179. (when (and (fboundp 'define-fringe-bitmap) (display-images-p))
  180. (define-fringe-bitmap 'indium-breakpoint-resolved
  181. "\x3c\x7e\xff\xff\xff\xff\x7e\x3c")
  182. (define-fringe-bitmap 'indium-breakpoint-unresolved
  183. "\x3c\x42\x81\x81\x81\x81\x42\x3c"))
  184. (provide 'indium-breakpoint)
  185. ;;; indium-breakpoint.el ends here