]> git.eshelyaron.com Git - emacs.git/commitdiff
Make 'n'/'p' in image mode buffers respect dired sorting
authorLars Ingebrigtsen <larsi@gnus.org>
Thu, 6 Aug 2020 09:51:22 +0000 (11:51 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Thu, 6 Aug 2020 09:51:22 +0000 (11:51 +0200)
The commands now also now work on archive and tar mode parent buffers.

* doc/emacs/files.texi (Image Mode): Document it.

* lisp/arc-mode.el (archive-goto-file): New function (bug#38647).
(archive-next-file-displayer): Ditto.

* lisp/image-mode.el (image-next-file): Reimplement to work on
displayed dired buffers and the like.  This means that `n' and `p'
now works on the displayed ordering in the dired buffer, so if
you've reversed the sorting, `n' picks the right "next" file.
(image-mode--directory-buffers): New function.
(image-mode--next-file): Ditto.

* lisp/tar-mode.el (tar-goto-file): New function.
(tar-next-file-displayer): Ditto.

doc/emacs/files.texi
etc/NEWS
lisp/arc-mode.el
lisp/image-mode.el
lisp/tar-mode.el

index 5998326ffefb64313a8fc28bd1a2e119b5560d50..2fa1ecc003dea77462575e1a236caf282750be91 100644 (file)
@@ -2149,7 +2149,12 @@ To reset all transformations to the initial state, use
 @findex image-previous-file
 You can press @kbd{n} (@code{image-next-file}) and @kbd{p}
 (@code{image-previous-file}) to visit the next image file and the
-previous image file in the same directory, respectively.
+previous image file in the same directory, respectively.  These
+commands will consult the ``parent'' dired buffer to determine what
+the next/previous image file is.  These commands also work when
+opening a file from archive files (like zip or tar files), and will
+then instead consult the archive mode buffer.  If neither an archive
+nor a dired ``parent'' buffer can be found, a dired buffer is opened.
 
 @findex image-mode-mark-file
 @findex image-mode-unmark-file
index 8c6e3e781394c3b5810bb60df3d48fb0f21fe273..cbb1842e139f7fd8fcc1bf6cade09b73d0c824c3 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -538,6 +538,15 @@ took more than two seconds to display.  The new algorithm maintains a
 decaying average of delays, and if this number gets too high, the
 animation is stopped.
 
++++
+*** The 'n' and 'p' commands (next/previous image) now respects dired order.
+These commands would previously display the next/previous image in
+alphabetical order, but will now find the "parent" dired buffer and
+select the next/previous image file according to how the files are
+sorted there.  The commands have also been extended to work when the
+"parent" buffer is an archive mode (i.e., zip file or the like) or tar
+mode buffer.
+
 ** EWW
 
 +++
index 6781c292d82558bc6b1848bc0b7e1b4c8dd199c7..901f09302efdeca3446f8262351915130ea8ee0e 100644 (file)
@@ -989,6 +989,53 @@ using `make-temp-file', and the generated name is returned."
       (kill-local-variable 'buffer-file-coding-system)
       (after-insert-file-set-coding (- (point-max) (point-min))))))
 
+(defun archive-goto-file (file)
+  "Go to FILE in the current buffer.
+FILE should be a relative file name.  If FILE can't be found,
+return nil.  Otherwise point is returned."
+  (let ((start (point))
+        found)
+    (goto-char (point-min))
+    (while (and (not found)
+                (not (eobp)))
+      (forward-line 1)
+      (when-let ((descr (archive-get-descr t)))
+        (when (equal (archive--file-desc-ext-file-name descr) file)
+          (setq found t))))
+    (if (not found)
+        (progn
+          (goto-char start)
+          nil)
+      (point))))
+
+(defun archive-next-file-displayer (file regexp n)
+  "Return a closure to display the next file after FILE that matches REGEXP."
+  (let ((short (replace-regexp-in-string "\\`.*:" "" file))
+        next)
+    (archive-goto-file short)
+    (while (and (not next)
+                ;; Stop if we reach the end/start of the buffer.
+                (if (> n 0)
+                    (not (eobp))
+                  (not (save-excursion
+                         (beginning-of-line)
+                         (bobp)))))
+      (archive-next-line n)
+      (when-let ((descr (archive-get-descr t)))
+        (let ((candidate (archive--file-desc-ext-file-name descr))
+              (buffer (current-buffer)))
+          (when (and candidate
+                     (string-match-p regexp candidate))
+            (setq next (lambda ()
+                         (kill-buffer (current-buffer))
+                         (switch-to-buffer buffer)
+                         (archive-extract)))))))
+    (unless next
+      ;; If we didn't find a next/prev file, then restore
+      ;; point.
+      (archive-goto-file short))
+    next))
+
 (defun archive-extract (&optional other-window-p event)
   "In archive mode, extract this entry of the archive into its own buffer."
   (interactive (list nil last-input-event))
index c417be43da57c353c0b6bbf3e5b3e6954bc92699..948e62e10d010ce0e06d2b0f21bc08a1ea4d51b5 100644 (file)
@@ -40,6 +40,7 @@
 
 (require 'image)
 (require 'exif)
+(require 'dired)
 (eval-when-compile (require 'cl-lib))
 
 ;;; Image mode window-info management.
@@ -1085,28 +1086,87 @@ replacing the current Image mode buffer."
     (error "The buffer is not in Image mode"))
   (unless buffer-file-name
     (error "The current image is not associated with a file"))
-  (let* ((file (file-name-nondirectory buffer-file-name))
-        (images (image-mode--images-in-directory file))
-        (idx 0))
-    (catch 'image-visit-next-file
-      (dolist (f images)
-       (if (string= f file)
-           (throw 'image-visit-next-file (1+ idx)))
-       (setq idx (1+ idx))))
-    (setq idx (mod (+ idx (or n 1)) (length images)))
-    (let ((image (nth idx images))
-          (dir (file-name-directory buffer-file-name)))
-      (find-alternate-file image)
-      ;; If we have dired buffer(s) open to where this image is, then
-      ;; place point on it.
+  (let ((next (image-mode--next-file buffer-file-name n)))
+    (unless next
+      (user-error "No %s file in this directory"
+                  (if (> n 0)
+                      "next"
+                    "prev")))
+    (if (stringp next)
+        (find-alternate-file next)
+      (funcall next))))
+
+(defun image-mode--directory-buffers (file)
+  "Return a alist of type/buffer for all \"parent\" buffers to image FILE.
+This is normally a list of dired buffers, but can also be archive and
+tar mode buffers."
+  (let ((buffers nil)
+        (dir (file-name-directory file)))
+    (cond
+     ((and (boundp 'tar-superior-buffer)
+          tar-superior-buffer)
+      (when (buffer-live-p tar-superior-buffer)
+        (push (cons 'tar tar-superior-buffer) buffers)))
+     ((and (boundp 'archive-superior-buffer)
+          archive-superior-buffer)
+      (when (buffer-live-p archive-superior-buffer)
+        (push (cons 'archive archive-superior-buffer) buffers)))
+     (t
+      ;; Find a dired buffer.
       (dolist (buffer (buffer-list))
-       (with-current-buffer buffer
-         (when (and (derived-mode-p 'dired-mode)
+        (with-current-buffer buffer
+          (when (and (derived-mode-p 'dired-mode)
                     (equal (file-truename dir)
                            (file-truename default-directory)))
-            (save-window-excursion
-              (switch-to-buffer (current-buffer) t t)
-              (dired-goto-file (expand-file-name image dir)))))))))
+            (push (cons 'dired (current-buffer)) buffers))))
+      ;; If we can't find any buffers to navigate in, we open a dired
+      ;; buffer.
+      (unless buffers
+        (push (cons 'dired (find-file-noselect dir)) buffers)
+        (message "Opened a dired buffer on %s" dir))))
+    buffers))
+
+(declare-function archive-next-file-displayer "arc-mode")
+(declare-function tar-next-file-displayer "tar-mode")
+
+(defun image-mode--next-file (file n)
+  "Go to the next image file in the parent buffer of FILE.
+This is typically a dired buffer, but may also be a tar/archive buffer.
+Return the next image file from that buffer.
+If N is negative, go to the previous file."
+  (let ((regexp (image-file-name-regexp))
+        (buffers (image-mode--directory-buffers file))
+        next)
+    (dolist (buffer buffers)
+      ;; We do this traversal for all the dired buffers open on this
+      ;; directory.  There probably is just one, but we want to move
+      ;; point in all of them.
+      (save-window-excursion
+        (switch-to-buffer (cdr buffer) t t)
+        (cl-case (car buffer)
+          ('dired
+           (dired-goto-file file)
+           (let (found)
+             (while (and (not found)
+                         ;; Stop if we reach the end/start of the buffer.
+                         (if (> n 0)
+                             (not (eobp))
+                           (not (bobp))))
+               (dired-next-line n)
+               (let ((candidate (dired-get-filename nil t)))
+                 (when (and candidate
+                            (string-match-p regexp candidate))
+                   (setq found candidate))))
+             (if found
+                 (setq next found)
+               ;; If we didn't find a next/prev file, then restore
+               ;; point.
+               (dired-goto-file file))))
+          ('archive
+           (setq next (archive-next-file-displayer file regexp n)))
+          ('tar
+           (setq next (tar-next-file-displayer file regexp n))))))
+    next))
 
 (defun image-previous-file (&optional n)
   "Visit the preceding image in the same directory as the current file.
index 73978ffc4a7db9ca840a5a0a7d33483a40a50a1b..5cf09f9055e56d0ec14fcbfe3d66dc373cb2e088 100644 (file)
@@ -922,6 +922,56 @@ actually appear on disk when you save the tar-file's buffer."
           (setq buffer-undo-list nil))))
     buffer))
 
+(defun tar-goto-file (file)
+  "Go to FILE in the current buffer.
+FILE should be a relative file name.  If FILE can't be found,
+return nil.  Otherwise point is returned."
+  (let ((start (point))
+        found)
+    (goto-char (point-min))
+    (while (and (not found)
+                (not (eobp)))
+      (forward-line 1)
+      (when-let ((descriptor (ignore-errors (tar-get-descriptor))))
+        (when (equal (tar-header-name descriptor) file)
+          (setq found t))))
+    (if (not found)
+        (progn
+          (goto-char start)
+          nil)
+      (point))))
+
+(defun tar-next-file-displayer (file regexp n)
+  "Return a closure to display the next file after FILE that matches REGEXP."
+  (let ((short (replace-regexp-in-string "\\`.*!" "" file))
+        next)
+    ;; The tar buffer chops off leading "./", so do the same
+    ;; here.
+    (setq short (replace-regexp-in-string "\\`\\./" "" file))
+    (tar-goto-file short)
+    (while (and (not next)
+                ;; Stop if we reach the end/start of the buffer.
+                (if (> n 0)
+                    (not (eobp))
+                  (not (save-excursion
+                         (beginning-of-line)
+                         (bobp)))))
+      (tar-next-line n)
+      (when-let ((descriptor (ignore-errors (tar-get-descriptor))))
+        (let ((candidate (tar-header-name descriptor))
+              (buffer (current-buffer)))
+          (when (and candidate
+                     (string-match-p regexp candidate))
+            (setq next (lambda ()
+                         (kill-buffer (current-buffer))
+                         (switch-to-buffer buffer)
+                         (tar-extract)))))))
+    (unless next
+      ;; If we didn't find a next/prev file, then restore
+      ;; point.
+      (tar-goto-file short))
+    next))
+
 (defun tar-extract (&optional other-window-p)
   "In Tar mode, extract this entry of the tar file into its own buffer."
   (interactive)