emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 8309dc8: * nhexl-mode.el: Add isearch and highlight to hex


From: Stefan Monnier
Subject: [elpa] master 8309dc8: * nhexl-mode.el: Add isearch and highlight to hex area
Date: Mon, 10 Dec 2018 00:42:25 -0500 (EST)

branch: master
commit 8309dc86c5ca0d11be3620e908bf157422654627
Author: Stefan Monnier <address@hidden>
Commit: Stefan Monnier <address@hidden>

    * nhexl-mode.el: Add isearch and highlight to hex area
    
    (nhexl-isearch-hex-addresses, nhexl-isearch-hex-bytes)
    (nhexl-isearch-hex-highlight): New vars.
    (nhexl--make-line): Copy isearch highlighting from the buffer
    when applicable.
    (nhexl--isearch-match-hex-bytes): New function.
    (nhexl--isearch-match-hex-address): New function, extracted from
    nhexl--isearch-search-fun.  Match the whole corresponding line.
    (nhexl--isearch-search-fun): Use them.
    (nhexl--isearch-highlight-cleanup, nhexl--isearch-highlight-match):
    New functions.
    (lazy-highlight-cleanup, isearch-lazy-highlight-match): Use them as
    advice to propagate isearch highlight to the hex area.
---
 packages/nhexl-mode/nhexl-mode.el | 204 +++++++++++++++++++++++++++-----------
 1 file changed, 147 insertions(+), 57 deletions(-)

diff --git a/packages/nhexl-mode/nhexl-mode.el 
b/packages/nhexl-mode/nhexl-mode.el
index b58b81d..89d9118 100644
--- a/packages/nhexl-mode/nhexl-mode.el
+++ b/packages/nhexl-mode/nhexl-mode.el
@@ -4,7 +4,7 @@
 
 ;; Author: Stefan Monnier <address@hidden>
 ;; Keywords: data
-;; Version: 1.0
+;; Version: 1.1
 ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5"))
 
 ;; This program is free software; you can redistribute it and/or modify
@@ -44,9 +44,10 @@
 ;; - it overrides C-u to use hexadecimal, so you can do C-u a 4 C-f
 ;;   to advance by #xa4 characters.
 
-;; Even though the hex addresses displayed by this mode aren't actually
-;; part of the buffer's text (contrary to hexl-mode, for example), you can
-;; search them with Isearch.
+;; Even though the hex addresses and hex data displayed by this mode aren't
+;; actually part of the buffer's text (contrary to hexl-mode, for example,
+;; they're only added to the display), you can search them with Isearch,
+;; according to nhexl-isearch-hex-addresses and nhexl-isearch-hex-bytes.
 
 ;;;; Known bugs:
 ;;
@@ -89,6 +90,18 @@ Otherwise they are applied unconditionally."
   "If non-nil `nhexl-mode' won't ask before converting the buffer to unibyte."
   :type 'boolean)
 
+(defcustom nhexl-isearch-hex-addresses t
+  "If non-nil, hex search terms will look for matching addresses."
+  :type 'boolean)
+
+(defcustom nhexl-isearch-hex-bytes t
+  "If non-nil, hex search terms will look for matching bytes."
+  :type 'boolean)
+
+(defcustom nhexl-isearch-hex-highlight t
+  "If non-nil, nhexl will highlight Isearch matches in the hex areas as well."
+  :type 'boolean)
+
 (defvar nhexl--display-table
   (let ((dt (make-display-table)))
     (unless nhexl-display-unprintables
@@ -611,10 +624,16 @@ Return the corresponding nibble, if applicable."
              (eval-when-compile (propertize " " 'display '(space :align-to 
12)))
              (mapconcat (lambda (c)
                           (setq i (1+ i))
-                          ;; FIXME: In multibyte buffers,
-                          ;; do something clever about
-                          ;; non-ascii chars.
-                          (let ((s (format "%02x" c)))
+                          ;; FIXME: In multibyte buffers, do something clever
+                          ;; about non-ascii chars.
+                          (let ((s (format "%02x" c))
+                                face)
+                            (when (and isearch-mode
+                                       (memq (setq face (get-char-property
+                                                         (+ i from) 'face))
+                                             '(lazy-highlight isearch)))
+                              (put-text-property 0 (length s) 'face
+                                                 `(,face default) s))
                             (when (and point (eq point (+ from i)))
                               (if nhexl-nibble-edit-mode
                                   (let ((nib (min (nhexl--nibble point)
@@ -626,6 +645,9 @@ Return the corresponding nibble, if applicable."
                                                    'face '(highlight default)
                                                    s)))
                             (if (zerop (mod i 2))
+                                ;; FIXME: If this char and the next are both
+                                ;; covered by isearch highlight, we should
+                                ;; also highlight the space.
                                 s (concat s " "))))
                         bufstr
                         "")
@@ -775,61 +797,129 @@ Return the corresponding nibble, if applicable."
               (truncate (- oldpoint zero) lw))
           (nhexl--refresh-cursor oldpoint)))))
 
+(defun nhexl--isearch-match-hex-bytes (string bound noerror)
+  ;; "57a" can be taken as "57a." or ".57a", but we currently
+  ;; only handle "57a."
+  ;; TODO: Maybe we could support hex regexps as well?
+  (let ((i 0)
+        (chars ()))
+    (while (< (1+ i) (length string))
+      (push (string-to-number (substring string i (+ i 2)) 16)
+            chars)
+      (setq i (+ i 2)))
+    (let* ((base (regexp-quote (apply #'string (nreverse chars))))
+           (newstr
+            (if (>= i (length string))
+                base
+              (cl-assert (= (1+ i) (length string)))
+              (let ((nibble (string-to-number (substring string i) 16)))
+                ;; FIXME: if one of the two bounds is a special char
+                ;; like `]` or `^' we can get into trouble!
+                (format "%s[%c-%c]" base
+                        (* 16 nibble)
+                        (+ 15 (* 16 nibble)))))))
+      (let ((case-fold-search nil))
+        (funcall (if isearch-forward
+                     #'re-search-forward
+                   #'re-search-backward)
+                 newstr bound noerror)))))
+
 (defun nhexl--isearch-search-fun (orig-fun)
   (let ((def-fun (funcall orig-fun)))
     (lambda (string bound noerror)
+      (unless bound
+        (setq bound (if isearch-forward (point-max) (point-min))))
       (let ((startpos (point))
             (def (funcall def-fun string bound noerror)))
-        (setq bound
-              ;; Don't search further than what `def-fun' found.
-              (if def (match-beginning 0)
-                (if isearch-forward (point-max) (point-min))))
-        (cond
-         ((string-match-p "\\`[[:xdigit:]]+:?\\'" string)
+        ;; Don't search further than what `def-fun' found.
+        (if def (setq bound (match-beginning 0)))
+        (when (and nhexl-isearch-hex-bytes
+                   (> (length string) 1)
+                   (string-match-p "\\`[[:xdigit:]]+\\'" string))
+          ;; Could be a search pattern specified in hex.
+          (goto-char startpos)
+          (let ((newdef (nhexl--isearch-match-hex-bytes string bound noerror)))
+            (when newdef
+              (setq def newdef)
+              (setq bound (match-beginning 0)))))
+        (when (and nhexl-isearch-hex-addresses
+                   (> (length string) 1)
+                   (string-match-p "\\`[[:xdigit:]]+:?\\'" string))
           ;; Could be a hexadecimal address.
-          ;; FIXME: The code below works well to find the address, but the
-          ;; resulting isearch-highlighting is wrong (the char at that position
-          ;; is highlighted, instead of the actual address matched in the
-          ;; before-string).
-          (let* ((addr (string-to-number string 16))
-                 ;; If `string' says "7a:", then it's "anchored", meaning that
-                 ;; we'll only look for nearest address of the form "XXX7a"
-                 ;; whereas if `string' says just "7a", then we look for 
nearest
-                 ;; address of the form "XXX7a", or "XXX7aX", or "XXX7aXX", ...
-                 (anchored (eq ?: (aref string (1- (length string)))))
-                 (mod (lsh 1 (* 4 (- (length string) (if anchored 1 0)))))
-                 (base (save-restriction (widen) (point-min)))
-                 (bestnext nil)
-                 (maxaddr (- (max startpos bound) base)))
-            (while (< addr maxaddr)
-              (let ((next (+ addr base (* (/ (- startpos base) mod) mod))))
-                (if isearch-forward
-                    (progn
-                      (when (<= next startpos)
-                        (setq next (+ next mod)))
-                      (cl-assert (> next startpos))
-                      (and (< next bound)
-                           (or (null bestnext) (< next bestnext))
-                           (setq bestnext next)))
-                  (when (>= next startpos)
-                    (setq next (- next mod)))
-                  (cl-assert (< next startpos))
-                  (and (> next bound)
-                       (or (null bestnext) (> next bestnext))
-                       (setq bestnext next))))
-              (let ((nextmod (* mod 16)))
-                (if (or anchored
-                        ;; Overflow!  let's get out of the loop right away.
-                        (< nextmod mod))
-                    (setq maxaddr -1)
-                  (setq addr (* addr 16))
-                  (setq mod nextmod))))
-            (cond
-             ((null bestnext) def)
-             (isearch-forward
-              (goto-char bestnext) (re-search-forward "."))
-             (t (goto-char (1+ bestnext)) (re-search-backward ".")))))
-         (t def))))))
+          (goto-char startpos)
+          (let ((newdef (nhexl--isearch-match-hex-address string bound 
noerror)))
+            (when newdef
+              (setq def newdef)
+              (setq bound (match-beginning 0)))))
+        (when def
+          (goto-char def)
+          def)))))
+
+(defun nhexl--isearch-match-hex-address (string bound _noerror)
+  ;; FIXME: The code below works well to find the address, but the
+  ;; resulting isearch-highlighting is wrong (the char(s) at that position
+  ;; is highlighted, instead of the actual address matched in the
+  ;; before-string).
+  (let* ((addr (string-to-number string 16))
+         ;; If `string' says "7a:", then it's "anchored", meaning that
+         ;; we'll only look for nearest address of the form "XXX7a"
+         ;; whereas if `string' says just "7a", then we look for nearest
+         ;; address of the form "XXX7a", or "XXX7aX", or "XXX7aXX", ...
+         (anchored (eq ?: (aref string (1- (length string)))))
+         (mod (lsh 1 (* 4 (- (length string) (if anchored 1 0)))))
+         (base (save-restriction (widen) (point-min)))
+         (bestnext nil)
+         (maxaddr (- (max (point) bound) base)))
+    (while (< addr maxaddr)
+      (let ((next (+ addr base (* (/ (- (point) base) mod) mod))))
+        (if isearch-forward
+            (progn
+              (when (<= next (point))
+                (setq next (+ next mod)))
+              (cl-assert (> next (point)))
+              (and (< next bound)
+                   (or (null bestnext) (< next bestnext))
+                   (setq bestnext next)))
+          (when (>= next (point))
+            (setq next (- next mod)))
+          (cl-assert (< next (point)))
+          (and (> next bound)
+               (or (null bestnext) (> next bestnext))
+               (setq bestnext next))))
+      (let ((nextmod (* mod 16)))
+        (if (or anchored
+                ;; Overflow!  let's get out of the loop right away.
+                (< nextmod mod))
+            (setq maxaddr -1)
+          (setq addr (* addr 16))
+          (setq mod nextmod))))
+    (when bestnext
+      (let* ((lw (nhexl--line-width))
+             (me (+ (* lw (/ (- bestnext (point-min)) lw))
+                    (point-min) lw)))
+        (set-match-data (list bestnext me))
+        (if isearch-forward
+            ;; Go to just before the last char on the line,
+            ;; otherwise, the cursor ends up on the
+            ;; next line!
+            (1- me)
+          bestnext)))))
+
+(advice-add 'lazy-highlight-cleanup :before
+            #'nhexl--isearch-highlight-cleanup)
+(defun nhexl--isearch-highlight-cleanup (&rest _)
+  (when (and nhexl-mode nhexl-isearch-hex-highlight)
+    (dolist (ol isearch-lazy-highlight-overlays)
+      (when (and (overlayp ol) (eq (overlay-buffer ol) (current-buffer)))
+        (put-text-property (overlay-start ol) (overlay-end ol)
+                           'fontified nil)))))
+
+(advice-add 'isearch-lazy-highlight-match :after
+            #'nhexl--isearch-highlight-match)
+(defun nhexl--isearch-highlight-match (&optional mb me)
+  (when (and nhexl-mode nhexl-isearch-hex-highlight
+             (integerp mb) (integerp me))
+    (put-text-property mb me 'fontified nil)))
 
 (defun nhexl--line-width-watcher (_sym _newval op where)
   (when (eq op 'set)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]