[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 87d1ada 137/215: Refactor and improve breakpoint UI
From: |
Rocky Bernstein |
Subject: |
[elpa] master 87d1ada 137/215: Refactor and improve breakpoint UI |
Date: |
Sat, 30 Jul 2016 14:49:00 +0000 (UTC) |
branch: master
commit 87d1ada22563349d86b0b7be90d09814375a1963
Author: Clément Pit--Claudel <address@hidden>
Commit: Clément Pit--Claudel <address@hidden>
Refactor and improve breakpoint UI
Improvements:
* Support non-graphical displays
* Properly handle multiple breakpoints on the same line
* Use fringes instead of margins to display breakpoint icons on
graphical displays (customizable with realgud-bp-use-fringe)
* Let users set or disable breakpoints by clicking on the fringe or
in the margin
* Make breakpoint fringe icons customizable, and default to a hollow
circle for disabled breakpoints
---
realgud/common/bp.el | 314 +++++++++++++++++++++-------------------
realgud/common/cmds.el | 19 +++
realgud/common/fringe-utils.py | 36 +++++
realgud/common/shortkey.el | 2 +
4 files changed, 219 insertions(+), 152 deletions(-)
diff --git a/realgud/common/bp.el b/realgud/common/bp.el
index 7b9365f..4de307a 100644
--- a/realgud/common/bp.el
+++ b/realgud/common/bp.el
@@ -5,176 +5,186 @@
(require 'load-relative)
(require-relative-list '("loc" "bp-image-data") "realgud-")
+(defun realgud-bp-remove-icons (&optional begin-pos end-pos bpnum)
+ "Remove breakpoint icons (overlays) in BEGIN-POS .. END-POS.
+The default value for BEGIN-POS is `point'. The default value
+for END-POS is BEGIN-POS. When BPNUM is non-nil, only remove
+overlays with that breakpoint number.
+
+The way we determine if an overlay is ours is by inspecting the
+overlay for a realgud property."
+ (interactive "r")
+ (setq begin-pos (or begin-pos (point))
+ end-pos (or end-pos begin-pos))
+ (dolist (overlay (overlays-in begin-pos end-pos))
+ (when (overlay-get overlay 'realgud)
+ (when (or (null bpnum) (equal bpnum (overlay-get overlay
'realgud-bp-num)))
+ (delete-overlay overlay)))))
+
(defvar realgud-bp-enabled-icon nil
"Icon for an enabled breakpoint in display margin.")
(defvar realgud-bp-disabled-icon nil
"Icon for a disabled breakpoint in display margin.")
-(defun realgud-bp-remove-icons (&optional opt-begin-pos opt-end-pos)
- "Remove dbgr breakpoint icons (overlays) in the region
-OPT-BEGIN-POS to OPT-END-POS. The default value for OPT-BEGIN-POS
-is `point'. The default value for OPT-END-POS is OPT-BEGIN-POS.
-
-The way we determine if an overlay is ours is by inspecting the
-overlay for a before-string property containing one we normally set.
-"
- (interactive "r")
- (let* ((begin-pos (or opt-begin-pos (point)))
- (end-pos (or opt-end-pos begin-pos))
- )
- (dolist (overlay (overlays-in begin-pos end-pos))
- ;; We determine if this overlay is one we set by seeing if the
- ;; string in its 'before-string property has a 'realgud-bptno property
- (let ((before-string (overlay-get overlay 'before-string)))
- (when (and before-string (get-text-property 0 'realgud-bptno
before-string))
- (delete-overlay overlay)
- )
- )
- )
- )
- )
-
(defun realgud-set-bp-icons()
- (if (display-images-p)
- ;; NOTE: if you don't see the icon, check the that the window margin
- ;; is not nil.
- (progn
- (setq realgud-bp-enabled-icon
- (find-image `((:type xpm :data
- ,realgud-bp-xpm-data
- :ascent 100 :pointer hand)
- (:type svg :data
- ,realgud-bp-enabled-svg-data
- :ascent 100 :pointer hand)
- (:type tiff :data
- ,realgud-bp-enabled-tiff-data
- :ascent 100 :pointer hand)
- (:type pbm :data
- ,realgud-bp-enabled-pbm-data
- :ascent 100 :pointer hand)
- )))
-
- ;; For seeing what realgud-bp-enabled-icon looks like:
- ;; (insert-image realgud-bp-enabled-icon)
-
- (setq realgud-bp-disabled-icon
- (find-image `((:type xpm :data
- ,realgud-bp-xpm-data
- :conversion disabled ;; different than
'enabled'
- :ascent 100 :pointer hand)
- (:type svg :data
- ,realgud-bp-disabled-svg-data
- :ascent 100 :pointer hand)
- (:type tiff :data
- ,realgud-bp-disabled-tiff-data
- :ascent 100 :pointer hand)
- (:type pbm :data
- ,realgud-bp-disabled-pbm-data
- :ascent 100 :pointer hand)
- (:type svg :data
- ,realgud-bp-disabled-svg-data
- :ascent 100 :pointer hand)
- )))
- ;; For seeing what realgud-bp-enabled-icon looks like:
- ;; (insert-image realgud-bp-disabled-icon)
- )
- (message "Display doesn't support breakpoint images in fringe")
- )
- )
-
-
-(defun realgud-bp-put-icon (pos enabled bp-num &optional opt-buf)
- "Add a breakpoint icon in the left margin at POS via a `put-image' overlay.
-The alternate string name for the image is created from the value
-of ENABLED and BP-NUM. In particular, if ENABLED is 't and
-BP-NUM is 5 the overlay string is be 'B5:' If ENABLED is false
-then the overlay string is 'b5:'. Breakpoint text properties are
-also attached to the icon via its display string."
- (let ((enabled-str)
- (buf (or opt-buf (current-buffer)))
- (bp-num-str
- (cond
- ((or (not bp-num) (not (numberp bp-num))) ":")
- ('t (format "%d:" bp-num))))
- (brkpt-icon)
- (bp-str)
- (help-string "mouse-1: enable/disable bkpt")
- )
- (with-current-buffer buf
- (unless realgud-bp-enabled-icon (realgud-set-bp-icons))
- (if enabled
- (progn
- (setq enabled-str "B")
- (setq brkpt-icon realgud-bp-enabled-icon)
- )
- (progn
- (setq enabled-str "b")
- (setq brkpt-icon realgud-bp-disabled-icon)
- ))
- ;; Create alternate display string and attach
- ;; properties it.
- (setq bp-str (concat enabled-str bp-num-str))
- (add-text-properties
- 0 1 `(realgud-bptno ,bp-num enabled ,enabled) bp-str)
- (add-text-properties
- 0 1 (list 'help-echo (format "%s %s" bp-str help-string))
- bp-str)
-
- ;; Display breakpoint icon or display string. If the window is
- ;; nil, the image doesn't get displayed, so make sure it is large
- ;; enough.
+ "Load breakpoint icons, if needed."
+ (when (display-images-p)
+ (unless realgud-bp-enabled-icon
+ (setq realgud-bp-enabled-icon
+ (find-image `((:type xpm :data
+ ,realgud-bp-xpm-data
+ :ascent 100 :pointer hand)
+ (:type svg :data
+ ,realgud-bp-enabled-svg-data
+ :ascent 100 :pointer hand)
+ (:type tiff :data
+ ,realgud-bp-enabled-tiff-data
+ :ascent 100 :pointer hand)
+ (:type pbm :data
+ ,realgud-bp-enabled-pbm-data
+ :ascent 100 :pointer hand)))))
+ (unless realgud-bp-disabled-icon
+ (setq realgud-bp-disabled-icon
+ (find-image `((:type xpm :data
+ ,realgud-bp-xpm-data
+ :conversion disabled ; different than
'enabled'
+ :ascent 100 :pointer hand)
+ (:type svg :data
+ ,realgud-bp-disabled-svg-data
+ :ascent 100 :pointer hand)
+ (:type tiff :data
+ ,realgud-bp-disabled-tiff-data
+ :ascent 100 :pointer hand)
+ (:type pbm :data
+ ,realgud-bp-disabled-pbm-data
+ :ascent 100 :pointer hand)
+ (:type svg :data
+ ,realgud-bp-disabled-svg-data
+ :ascent 100 :pointer hand)))))))
+
+(declare-function define-fringe-bitmap "fringe.c"
+ (bitmap bits &optional height width align))
+
+(when (display-images-p)
+ ;; Taken from gdb-mi
+ (define-fringe-bitmap 'realgud-bp-filled
+ "\x3c\x7e\xff\xff\xff\xff\x7e\x3c")
+ (define-fringe-bitmap 'realgud-bp-hollow
+ "\x3c\x42\x81\x81\x81\x81\x42\x3c"))
+
+(defgroup realgud-bp nil
+ "RealGUD breakpoints UI"
+ :group 'realgud
+ :prefix 'realgud-bp-)
+
+(defface realgud-bp-enabled-face
+ '((t :foreground "red" :weight bold))
+ "Face for enabled breakpoints."
+ :group 'realgud-bp)
+
+(defface realgud-bp-disabled-face
+ '((t :foreground "grey" :weight bold))
+ "Face for disabled breakpoints."
+ :group 'realgud-bp)
+
+(defcustom realgud-bp-fringe-indicator-style '(realgud-bp-filled .
realgud-bp-hollow)
+ "Which fringe icon to use for breakpoints."
+ :type '(choice (const :tag "Disc" (realgud-bp-filled . realgud-bp-hollow))
+ (const :tag "Square" (filled-square . hollow-square))
+ (const :tag "Rectangle" (filled-rectangle .
hollow-rectangle)))
+ :group 'realgud-bp)
+
+(defcustom realgud-bp-use-fringe t
+ "Whether to use the fringe to display breakpoints.
+If nil, use margins instead."
+ :type '(boolean)
+ :group 'realgud-bp)
+
+(defun realgud-bp--fringe-width ()
+ "Compute width of left fringe."
+ (let ((window (get-buffer-window (current-buffer))))
+ (or left-fringe-width
+ (and window (car (window-fringes window)))
+ 0)))
+
+(defun realgud-bp-add-fringe-icon (overlay icon face)
+ "Add a fringe icon to OVERLAY.
+ICON is a fringe icon symbol; the corresponding icon gets
+highlighted with FACE."
+ ;; Ensure that the fringe is wide enough
+ (unless (>= (realgud-bp--fringe-width) 8)
+ (set-fringe-mode `(8 . ,right-fringe-width)))
+ ;; Add the fringe icon
+ (let* ((fringe-spec `(left-fringe ,icon ,face)))
+ (overlay-put overlay 'before-string (propertize "x" 'display
fringe-spec))))
+
+(defun realgud-bp-add-margin-indicator (overlay text image face)
+ "Add a margin breakpoint indicator to OVERLAY.
+TEXT is a string, IMAGE an IMAGE spec or nil; TEXT gets
+highlighted with FACE."
+ ;; Ensure that the margin is large enough (Taken from gdb-mi)
+ (when (< left-margin-width 2)
+ (save-current-buffer
+ (setq left-margin-width 2)
(let ((window (get-buffer-window (current-buffer) 0)))
(if window
- (set-window-margins window 2)
- ;; FIXME: This is all crap, but I don't know how to fix.
- (let ((buffer-save (window-buffer (selected-window))))
- (set-window-buffer (selected-window) (current-buffer))
- (set-window-margins (selected-window) 2)
- (set-window-buffer (selected-window) buffer-save))
- ))
- (realgud-bp-remove-icons pos)
- (if brkpt-icon
- (put-image brkpt-icon pos bp-str 'left-margin))
- )
- )
- )
-
-(defun realgud-bp-del-icon (pos &optional opt-buf)
- "Delete breakpoint icon in the left margin at POS via a `put-image' overlay.
-The alternate string name for the image is created from the value
-of ENABLED and BP-NUM. In particular, if ENABLED is 't and
-BP-NUM is 5 the overlay string is be 'B5:' If ENABLED is false
-then the overlay string is 'b5:'. Breakpoint text properties are
-also attached to the icon via its display string."
- (let ((buf (or opt-buf (current-buffer))))
- (with-current-buffer buf
- (realgud-bp-remove-icons pos)
- )
- )
-)
+ (set-window-margins
+ window left-margin-width right-margin-width)))))
+ ;; Add the margin string
+ (let* ((indicator (or image (propertize text 'face face)))
+ (spec `((margin left-margin) ,indicator)))
+ (overlay-put overlay 'before-string (propertize text 'display spec))))
+
+(defun realgud-bp-put-icon (pos enabled bp-num &optional buf)
+ "Add a breakpoint icon at POS according to breakpoint-display-style.
+Use the fringe if available, and the margin otherwise. Record
+breakpoint status ENABLED and breakpoint number BP-NUM in
+overlay. BUF is the buffer that POS refers to; it detaults to
+the current buffer."
+ (let* ((margin-text) (face) (margin-icon) (fringe-icon))
+ (realgud-set-bp-icons)
+ (if enabled
+ (setq margin-text "B"
+ face 'realgud-bp-enabled-face
+ margin-icon realgud-bp-enabled-icon
+ fringe-icon (car realgud-bp-fringe-indicator-style))
+ (setq margin-text "b"
+ face 'realgud-bp-disabled-face
+ margin-icon realgud-bp-disabled-icon
+ fringe-icon (cdr realgud-bp-fringe-indicator-style)))
+ (let ((help-echo (format "%s%s: mouse-1 to clear" margin-text bp-num)))
+ (setq margin-text (propertize margin-text 'help-echo help-echo)))
+ (with-current-buffer (or buf (current-buffer))
+ (realgud-bp-remove-icons pos pos bp-num)
+ (let ((ov (make-overlay pos pos (current-buffer) t nil)))
+ (if (and realgud-bp-use-fringe (display-images-p))
+ (realgud-bp-add-fringe-icon ov fringe-icon face)
+ (realgud-bp-add-margin-indicator ov margin-text margin-icon face))
+ (overlay-put ov 'realgud t)
+ (overlay-put ov 'realgud-bp-num bp-num)
+ (overlay-put ov 'realgud-bp-enabled enabled)))))
+
+(defun realgud-bp-del-icon (pos &optional buf bpnum)
+ "Delete breakpoint icon at POS.
+BUF is the buffer which pos refers to (default: current buffer).
+If BPNUM is non-nil, only remove overlays maching that breakpoint
+number."
+ (with-current-buffer (or buf (current-buffer))
+ (realgud-bp-remove-icons pos pos bpnum)))
(defun realgud-bp-add-info (loc)
"Record bp information for location LOC."
(if (realgud-loc? loc)
(let* ((marker (realgud-loc-marker loc))
- (bp-num (realgud-loc-num loc))
- )
- (realgud-bp-put-icon marker 't bp-num)
- )
- )
-)
+ (bp-num (realgud-loc-num loc)))
+ (realgud-bp-put-icon marker t bp-num))))
(defun realgud-bp-del-info (loc)
"Remove bp information for location LOC."
(if (realgud-loc? loc)
(let* ((marker (realgud-loc-marker loc))
- (bp-num (realgud-loc-num loc))
- )
- (realgud-bp-del-icon marker)
- )
- )
-)
-
+ (bp-num (realgud-loc-num loc)))
+ (realgud-bp-del-icon marker (current-buffer) bp-num))))
(provide-me "realgud-")
diff --git a/realgud/common/cmds.el b/realgud/common/cmds.el
index c854cd1..03990f5 100644
--- a/realgud/common/cmds.el
+++ b/realgud/common/cmds.el
@@ -173,6 +173,25 @@ be found on the current line, prompt for a breakpoint
number."
(interactive (realgud:bpnum-from-prefix-arg))
(realgud:cmd-run-command bpnum "enable" "enable %p"))
+(defun realgud-cmds--add-remove-bp (pos)
+ "Add or delete breakpoint at POS."
+ (save-excursion
+ (goto-char pos)
+ (let ((existing-bp-num (realgud:bpnum-on-current-line)))
+ (if existing-bp-num
+ (realgud:cmd-delete existing-bp-num)
+ (realgud:cmd-break pos)))))
+
+(defun realgud-cmds--mouse-add-remove-bp (event)
+ "Add or delete breakpoint on line pointed to by EVENT.
+EVENT should be a mouse click on the left fringe or margin."
+ (interactive "e")
+ (let* ((posn (event-end event))
+ (pos (posn-point posn)))
+ (when (numberp pos)
+ (with-current-buffer (window-buffer (posn-window posn))
+ (realgud-cmds--add-remove-bp pos)))))
+
(defun realgud:cmd-eval(arg)
"Evaluate an expression."
(interactive "MEval expesssion: ")
diff --git a/realgud/common/fringe-utils.py b/realgud/common/fringe-utils.py
new file mode 100644
index 0000000..c344e50
--- /dev/null
+++ b/realgud/common/fringe-utils.py
@@ -0,0 +1,36 @@
+def bit2char(byte, offset):
+ return "X" if byte & (1 << offset) else " "
+
+def char2bit(char, offset):
+ return (0 if char == " " else 1) << offset
+
+def decompile_bitmap(bmp_bytes):
+ lines = []
+ for b in bmp_bytes:
+ lines.append("".join(bit2char(b, offset) for offset in range(8)))
+ return lines
+
+def compile_bitmap(bmp_lines):
+ bmp_bytes = []
+ for line in bmp_lines:
+ s = sum(char2bit(c, offset) for (offset, c) in enumerate(line))
+ print(s)
+ bmp_bytes.append(s.to_bytes(1, byteorder="big"))
+ return b"".join(bmp_bytes)
+
+hollow_circle = [" XXXX ",
+ " X X ",
+ "X X",
+ "X X",
+ "X X",
+ "X X",
+ " X X ",
+ " XXXX "]
+
+def print_compiled(bmp):
+ print("".join(r'\x{:02x}'.format(b) for b in bmp))
+
+print("\n".join(decompile_bitmap(b"\x3c\x7e\xff\xff\xff\xff\x7e\x3c")))
+print_compiled(compile_bitmap(decompile_bitmap(b"\x3c\x7e\xff\xff\xff\xff\x7e\x3c")))
+print_compiled(compile_bitmap(hollow_circle))
+
diff --git a/realgud/common/shortkey.el b/realgud/common/shortkey.el
index c236237..6c03a4f 100644
--- a/realgud/common/shortkey.el
+++ b/realgud/common/shortkey.el
@@ -46,6 +46,8 @@
(define-key map "e" 'realgud:cmd-eval-dwim)
(define-key map "U" 'realgud:cmd-until)
(define-key map [mouse-2] 'realgud:tooltip-eval)
+ (define-key map [left-fringe mouse-1] #'realgud-cmds--mouse-add-remove-bp)
+ (define-key map [left-margin mouse-1] #'realgud-cmds--mouse-add-remove-bp)
;; FIXME: these can go to a common routine
(define-key map "<" 'realgud:cmd-newer-frame)
- [elpa] master 01617c5 100/215: ipdb: add regex test, (continued)
- [elpa] master 01617c5 100/215: ipdb: add regex test, Rocky Bernstein, 2016/07/30
- [elpa] master 5395757 091/215: More cleanup from last two commits., Rocky Bernstein, 2016/07/30
- [elpa] master 1e2fe55 098/215: Add rspec loc pattern.., Rocky Bernstein, 2016/07/30
- [elpa] master ea5dcba 094/215: ipdb: add new debugger based on pdb, Rocky Bernstein, 2016/07/30
- [elpa] master d1c1176 146/215: Simplify the implementation of realgud:cmdbuf-associate, Rocky Bernstein, 2016/07/30
- [elpa] master 95f076e 150/215: Improve IPDB completion, Rocky Bernstein, 2016/07/30
- [elpa] master d45788c 152/215: Merge pull request #106 from rocky/75-ipdb-completion, Rocky Bernstein, 2016/07/30
- [elpa] master 032ea4b 143/215: Merge branch 'master' of github.com:rocky/emacs-dbgr, Rocky Bernstein, 2016/07/30
- [elpa] master 549f6f0 135/215: Merge pull request #98 from rocky/83-better-scope-keybindings, Rocky Bernstein, 2016/07/30
- [elpa] master aedea61 138/215: Merge pull request #101 from rocky/bp-icon-in-fringe, Rocky Bernstein, 2016/07/30
- [elpa] master 87d1ada 137/215: Refactor and improve breakpoint UI,
Rocky Bernstein <=
- [elpa] master d6b879d 042/215: Merge branch 'trepanjs', Rocky Bernstein, 2016/07/30
- [elpa] master f3420e9 151/215: Merge pull request #105 from rocky/new-README, Rocky Bernstein, 2016/07/30
- [elpa] master 1c3d8c2 171/215: Add a bit of padding around the logo to make GitHub happy, Rocky Bernstein, 2016/07/30
- [elpa] master 678ec6b 139/215: Use `buffer-live-p' instead of `buffer-name' in `buffer-killed?', Rocky Bernstein, 2016/07/30
- [elpa] master daac75b 175/215: Add a C-u interface to break and clear, Rocky Bernstein, 2016/07/30
- [elpa] master fe679e9 077/215: Don't try source buffer initialization if it doesn't exist., Rocky Bernstein, 2016/07/30
- [elpa] master 3f08b2b 165/215: Update screenshot, Rocky Bernstein, 2016/07/30
- [elpa] master 4ac3ca2 147/215: Fill in missing bits of Cask file, Rocky Bernstein, 2016/07/30
- [elpa] master 785eb68 168/215: Remove unwanted argument in call to cmd-run-command, Rocky Bernstein, 2016/07/30
- [elpa] master bc3fa20 162/215: Add some enable/disble patterns, Rocky Bernstein, 2016/07/30