]> git.eshelyaron.com Git - emacs.git/commitdiff
* lisp/dired.el (dired--move-to-next-line): Improve to avoid an infinite loop.
authorJuri Linkov <juri@linkov.net>
Thu, 25 Jan 2024 17:38:03 +0000 (19:38 +0200)
committerEshel Yaron <me@eshelyaron.com>
Thu, 25 Jan 2024 18:06:45 +0000 (19:06 +0100)
An infinite loop is possible in a directory without files and subdirectories,
where even lines with . and .. are omitted, so 'dired-between-files' is true
for all Dired lines.  For the case of dired-movement-style=cycle
a guard is triggered when the loop wraps twice while the value 'arg'
is not changing.  And for the case of dired-movement-style=bounded
a guard is triggered when point doesn't move while trying to
go back to the last non-empty line.

(cherry picked from commit b07265f8eed74dda792e13062baae94319484b4b)

lisp/dired.el

index 69fa15dde73a6ea1e74188328c34c7014f6ca534..cef93ab757cd48f1aa3428291da901ec08afefee 100644 (file)
@@ -2817,7 +2817,9 @@ is controlled by `dired-movement-style'."
     (dired--trivial-next-line arg)))
 
 (defun dired--move-to-next-line (arg jumpfun)
-  (let ((old-position (progn
+  (let ((wrapped nil)
+        (old-arg arg)
+        (old-position (progn
                         ;; It's always true that we should move
                         ;; to the filename when possible.
                         (dired-move-to-filename)
@@ -2832,16 +2834,27 @@ is controlled by `dired-movement-style'."
       (when (= old-position (point))
         ;; Now point is at beginning/end of movable area,
         ;; but it still wants to move farther.
-        (if (eq dired-movement-style 'cycle)
-            ;; `cycle': go to the other end.
+        (cond
+         ;; `cycle': go to the other end.
+         ((eq dired-movement-style 'cycle)
+          ;; Argument not changing on the second wrap
+          ;; means infinite loop with no files found.
+          (if (and wrapped (eq old-arg arg))
+              (setq arg 0)
             (goto-char (if (cl-plusp moving-down)
                            (point-min)
-                         (point-max)))
-          ;; `bounded': go back to the last non-empty line.
-          (while (dired-between-files)
-            (funcall jumpfun (- moving-down)))
+                         (point-max))))
+          (setq wrapped t))
+         ;; `bounded': go back to the last non-empty line.
+         ((eq dired-movement-style 'bounded)
+          (while (and (dired-between-files) (not (zerop arg)))
+            (funcall jumpfun (- moving-down))
+            ;; Point not moving means infinite loop.
+            (if (= old-position (point))
+                (setq arg 0)
+              (setq old-position (point))))
           ;; Encountered a boundary, so let's stop movement.
-          (setq arg moving-down)))
+          (setq arg (if (dired-between-files) 0 moving-down)))))
       (unless (dired-between-files)
         ;; Has moved to a non-empty line.  This movement does
         ;; make sense.