Subject: dired-movement-style for dirlines

From: Juri Linkov <[email-masked]>
Date: Mon, 20 Nov 2023 20:08:34 +0200

A good option 'dired-movement-style' was implemented in bug#65621.
But it supports only 'n' and 'p', but not counterpart '<' and '>'.
So I took the very nice algorithm that correctly handles the arg
on wrapping in 'dired-next-line', and refactored it to a separate
function 'dired--move-to-next-line' that is used in both:

dired-next-line:
  (dired--move-to-next-line arg #'dired--trivial-next-line)
dired-next-dirline:
  (dired--move-to-next-line arg #'dired--trivial-next-dirline)

diff --git a/lisp/dired.el b/lisp/dired.el
index 583cb2475e2..9d4b3d10ac4 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -2705,44 +2746,41 @@ dired-next-line
 is controlled by `dired-movement-style'."
   (interactive "^p" dired-mode)
   (if dired-movement-style
-      (let ((old-position (progn
-                            ;; It's always true that we should move
-                            ;; to the filename when possible.
-                            (dired-move-to-filename)
-                            (point)))
-            ;; Up/Down indicates the direction.
-            (moving-down (if (cl-plusp arg)
-                             1    ; means Down.
-                           -1)))  ; means Up.
-        ;; Line by line in case we forget to skip empty lines.
-        (while (not (zerop arg))
-          (dired--trivial-next-line moving-down)
-          (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.
-                (goto-char (if (cl-plusp moving-down)
-                               (point-min)
-                             (point-max)))
-              ;; `bounded': go back to the last non-empty line.
-              (while (string-match-p "\\`[[:blank:]]*\\'"
-                                     (buffer-substring-no-properties
-                                      (line-beginning-position)
-                                      (line-end-position)))
-                (dired--trivial-next-line (- moving-down)))
-              ;; Encountered a boundary, so let's stop movement.
-              (setq arg moving-down)))
-          (when (not (string-match-p "\\`[[:blank:]]*\\'"
-                                     (buffer-substring-no-properties
-                                      (line-beginning-position)
-                                      (line-end-position))))
-            ;; Has moved to a non-empty line.  This movement does
-            ;; make sense.
-            (cl-decf arg moving-down))
-          (setq old-position (point))))
+      (dired--move-to-next-line arg #'dired--trivial-next-line)
     (dired--trivial-next-line arg)))
 
+(defun dired--move-to-next-line (arg jumpfun)
+  (let ((old-position (progn
+                        ;; It's always true that we should move
+                        ;; to the filename when possible.
+                        (dired-move-to-filename)
+                        (point)))
+        ;; Up/Down indicates the direction.
+        (moving-down (if (cl-plusp arg)
+                         1              ; means Down.
+                       -1)))            ; means Up.
+    ;; Line by line in case we forget to skip empty lines.
+    (while (not (zerop arg))
+      (funcall jumpfun moving-down)
+      (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.
+            (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)))
+          ;; Encountered a boundary, so let's stop movement.
+          (setq arg moving-down)))
+      (unless (dired-between-files)
+        ;; Has moved to a non-empty line.  This movement does
+        ;; make sense.
+        (cl-decf arg moving-down))
+      (setq old-position (point)))))
+
 (defun dired-previous-line (arg)
   "Move up ARG lines, then position at filename.
 The argument ARG (interactively, prefix argument) says how many lines
@@ -2753,9 +2791,8 @@ dired-previous-line
   (interactive "^p" dired-mode)
   (dired-next-line (- (or arg 1))))
 
-(defun dired-next-dirline (arg &optional opoint)
+(defun dired--trivial-next-dirline (arg &optional opoint)
   "Goto ARGth next directory file line."
-  (interactive "p" dired-mode)
   (or opoint (setq opoint (point)))
   (if (if (> arg 0)
 	  (re-search-forward dired-re-dir nil t arg)
@@ -2763,7 +2800,15 @@ dired-next-dirline
 	(re-search-backward dired-re-dir nil t (- arg)))
       (dired-move-to-filename)		; user may type `i' or `f'
     (goto-char opoint)
-    (error "No more subdirectories")))
+    (unless dired-movement-style
+      (error "No more subdirectories"))))
+
+(defun dired-next-dirline (arg &optional _opoint)
+  "Goto ARGth next directory file line."
+  (interactive "p" dired-mode)
+  (if dired-movement-style
+      (dired--move-to-next-line arg #'dired--trivial-next-dirline)
+    (dired--trivial-next-dirline arg)))
 
 (defun dired-prev-dirline (arg)
   "Goto ARGth previous directory file line."