diff options
| author | shynur | 2023-09-02 22:39:00 +0800 |
|---|---|---|
| committer | Eli Zaretskii | 2023-09-10 10:37:43 +0300 |
| commit | d04c538e7c331a643c61c4af5070288ce220ebfb (patch) | |
| tree | ec902722733c1dd4114950b096c4017f5ac1f6f9 | |
| parent | 4101464b4765b67c1b8b5ec099ffccf37385ef9c (diff) | |
| download | emacs-d04c538e7c331a643c61c4af5070288ce220ebfb.tar.gz emacs-d04c538e7c331a643c61c4af5070288ce220ebfb.zip | |
`dired-next-line' movement style (bug#65621)
Point will skips empty lines and optionally goto the other
end when encountering a boundary.
* lisp/dired.el (dired-movement-style): Control whether to
skip empty lines and whether to cycle through non-empty
lines.
* lisp/dired.el (dired-next-line): Add a new movement style
controlled by `dired-movement-style'.
* etc/NEWS (dired-movement-style):
| -rw-r--r-- | etc/NEWS | 9 | ||||
| -rw-r--r-- | lisp/dired.el | 76 |
2 files changed, 78 insertions, 7 deletions
| @@ -290,6 +290,15 @@ This allows changing which type of whitespace changes are ignored when | |||
| 290 | regenerating hunks with 'diff-ignore-whitespace-hunk'. Defaults to | 290 | regenerating hunks with 'diff-ignore-whitespace-hunk'. Defaults to |
| 291 | the previously hard-coded "-b". | 291 | the previously hard-coded "-b". |
| 292 | 292 | ||
| 293 | ** Dired | ||
| 294 | |||
| 295 | --- | ||
| 296 | *** New user option 'dired-movement-style'. | ||
| 297 | When non-nil, make 'dired-next-line' and 'dired-previous-line' skip | ||
| 298 | empty lines. It also controls how to move point when encountering a | ||
| 299 | boundary (e.g., if every line is visible, invoking 'dired-next-line' | ||
| 300 | at the last line will move to the first line). | ||
| 301 | |||
| 293 | ** Ediff | 302 | ** Ediff |
| 294 | 303 | ||
| 295 | --- | 304 | --- |
diff --git a/lisp/dired.el b/lisp/dired.el index e96b85a173a..88f856350b0 100644 --- a/lisp/dired.el +++ b/lisp/dired.el | |||
| @@ -495,6 +495,21 @@ to nil: a pipe using `zcat' or `gunzip -c' will be used." | |||
| 495 | (string :tag "Switches")) | 495 | (string :tag "Switches")) |
| 496 | :version "29.1") | 496 | :version "29.1") |
| 497 | 497 | ||
| 498 | (defcustom dired-movement-style nil | ||
| 499 | "Non-nil means point skips empty lines when moving. | ||
| 500 | This affects only `dired-next-line' and `dired-previous-line'. | ||
| 501 | |||
| 502 | Possible non-nil values: | ||
| 503 | * `cycle': the next/previous line of the last/first visible line is | ||
| 504 | the first/last visible line. | ||
| 505 | * `bounded': cannot move up/down if the current line is the | ||
| 506 | first/last visible line." | ||
| 507 | :type '(choice (const :tag "Move to any line" nil) | ||
| 508 | (const :tag "Loop through non-empty lines" cycle) | ||
| 509 | (const :tag "Only to non-empty line" bounded)) | ||
| 510 | :group 'dired | ||
| 511 | :version "30.1") | ||
| 512 | |||
| 498 | (defcustom dired-hide-details-preserved-columns nil | 513 | (defcustom dired-hide-details-preserved-columns nil |
| 499 | "List of columns which are not hidden in `dired-hide-details-mode'." | 514 | "List of columns which are not hidden in `dired-hide-details-mode'." |
| 500 | :type '(repeat integer) | 515 | :type '(repeat integer) |
| @@ -2666,22 +2681,69 @@ Otherwise, toggle `read-only-mode'." | |||
| 2666 | (wdired-change-to-wdired-mode) | 2681 | (wdired-change-to-wdired-mode) |
| 2667 | (read-only-mode 'toggle))) | 2682 | (read-only-mode 'toggle))) |
| 2668 | 2683 | ||
| 2669 | (defun dired-next-line (arg) | 2684 | (defun dired--trivial-next-line (arg) |
| 2670 | "Move down lines then position at filename. | 2685 | "Move down ARG lines then position at filename." |
| 2671 | Optional prefix ARG says how many lines to move; default is one line." | ||
| 2672 | (interactive "^p") | ||
| 2673 | (let ((line-move-visual) | 2686 | (let ((line-move-visual) |
| 2674 | (goal-column)) | 2687 | (goal-column)) |
| 2675 | (line-move arg t)) | 2688 | (line-move arg t)) |
| 2676 | ;; We never want to move point into an invisible line. | 2689 | ;; We never want to move point into an invisible line. |
| 2677 | (while (and (invisible-p (point)) | 2690 | (while (and (invisible-p (point)) |
| 2678 | (not (if (and arg (< arg 0)) (bobp) (eobp)))) | 2691 | (not (if (and arg (< arg 0)) (bobp) (eobp)))) |
| 2679 | (forward-char (if (and arg (< arg 0)) -1 1))) | 2692 | (forward-char (if (and arg (< arg 0)) -1 1))) |
| 2680 | (dired-move-to-filename)) | 2693 | (dired-move-to-filename)) |
| 2681 | 2694 | ||
| 2695 | (defun dired-next-line (arg) | ||
| 2696 | "Move down lines then position at filename. | ||
| 2697 | Optional prefix ARG says how many lines to move; default is one line. | ||
| 2698 | |||
| 2699 | Whether to skip empty lines and how to move when encountering a | ||
| 2700 | boundary are controlled by `dired-movement-style'." | ||
| 2701 | (interactive "^p") | ||
| 2702 | (if dired-movement-style | ||
| 2703 | (let ((old-position (progn | ||
| 2704 | ;; It's always true that we should move | ||
| 2705 | ;; to the filename when possible. | ||
| 2706 | (dired-move-to-filename) | ||
| 2707 | (point))) | ||
| 2708 | ;; Up/Down indicates the direction. | ||
| 2709 | (moving-down (if (cl-plusp arg) | ||
| 2710 | 1 ; means Down. | ||
| 2711 | -1))) ; means Up. | ||
| 2712 | ;; Line by line in case we forget to skip empty lines. | ||
| 2713 | (while (not (zerop arg)) | ||
| 2714 | (dired--trivial-next-line moving-down) | ||
| 2715 | (when (= old-position (point)) | ||
| 2716 | ;; Now point is at beginning/end of movable area, | ||
| 2717 | ;; but it still wants to move farther. | ||
| 2718 | (if (eq dired-movement-style 'cycle) | ||
| 2719 | ;; `cycle': go to the other end. | ||
| 2720 | (goto-char (if (cl-plusp moving-down) | ||
| 2721 | (point-min) | ||
| 2722 | (point-max))) | ||
| 2723 | ;; `bounded': go back to the last non-empty line. | ||
| 2724 | (while (string-match-p "\\`[[:blank:]]*\\'" | ||
| 2725 | (buffer-substring-no-properties | ||
| 2726 | (line-beginning-position) | ||
| 2727 | (line-end-position))) | ||
| 2728 | (dired--trivial-next-line (- moving-down))) | ||
| 2729 | ;; Encountered a boundary, so let's stop movement. | ||
| 2730 | (setq arg moving-down))) | ||
| 2731 | (when (not (string-match-p "\\`[[:blank:]]*\\'" | ||
| 2732 | (buffer-substring-no-properties | ||
| 2733 | (line-beginning-position) | ||
| 2734 | (line-end-position)))) | ||
| 2735 | ;; Has moved to a non-empty line. This movement does | ||
| 2736 | ;; make sense. | ||
| 2737 | (cl-decf arg moving-down)) | ||
| 2738 | (setq old-position (point)))) | ||
| 2739 | (dired--trivial-next-line arg))) | ||
| 2740 | |||
| 2682 | (defun dired-previous-line (arg) | 2741 | (defun dired-previous-line (arg) |
| 2683 | "Move up lines then position at filename. | 2742 | "Move up lines then position at filename. |
| 2684 | Optional prefix ARG says how many lines to move; default is one line." | 2743 | Optional prefix ARG says how many lines to move; default is one line. |
| 2744 | |||
| 2745 | Whether to skip empty lines and how to move when encountering a | ||
| 2746 | boundary are controlled by `dired-movement-style'." | ||
| 2685 | (interactive "^p") | 2747 | (interactive "^p") |
| 2686 | (dired-next-line (- (or arg 1)))) | 2748 | (dired-next-line (- (or arg 1)))) |
| 2687 | 2749 | ||