Add duplicate-region-final-position (bug#64185)
* lisp/misc.el (duplicate-region-final-position): New defcustom. (duplicate-dwim): Use it. * lisp/rect.el (rectangle--duplicate-right): Add displacement argument. * test/lisp/misc-tests.el (misc--duplicate-dwim): Extend test.
This commit is contained in:
parent
3ba9f9657f
commit
2195935870
3 changed files with 111 additions and 46 deletions
37
lisp/misc.el
37
lisp/misc.el
|
@ -105,7 +105,18 @@ Also see the `copy-from-above-command' command."
|
|||
(forward-line duplicate-line-final-position)
|
||||
(move-to-column col))))
|
||||
|
||||
(declare-function rectangle--duplicate-right "rect" (n))
|
||||
(defcustom duplicate-region-final-position 0
|
||||
"Where the region ends up after duplicating a region with `duplicate-dwim'.
|
||||
When 0, leave the region in place.
|
||||
When 1, put the region around the first copy.
|
||||
When -1, put the region around the last copy."
|
||||
:type '(choice (const :tag "Leave region in place" 0)
|
||||
(const :tag "Put region around first copy" 1)
|
||||
(const :tag "Put region around last copy" -1))
|
||||
:group 'editing
|
||||
:version "29.1")
|
||||
|
||||
(declare-function rectangle--duplicate-right "rect" (n displacement))
|
||||
|
||||
;; `duplicate-dwim' preserves an active region and changes the buffer
|
||||
;; outside of it: disregard the region when immediately undoing the
|
||||
|
@ -118,24 +129,40 @@ Also see the `copy-from-above-command' command."
|
|||
If the region is inactive, duplicate the current line (like `duplicate-line').
|
||||
Otherwise, duplicate the region, which remains active afterwards.
|
||||
If the region is rectangular, duplicate on its right-hand side.
|
||||
Interactively, N is the prefix numeric argument, and defaults to 1."
|
||||
Interactively, N is the prefix numeric argument, and defaults to 1.
|
||||
The variables `duplicate-line-final-position' and
|
||||
`duplicate-region-final-position' control the position of point
|
||||
and the region after the duplication."
|
||||
(interactive "p")
|
||||
(unless n
|
||||
(setq n 1))
|
||||
(cond
|
||||
((<= n 0) nil)
|
||||
;; Duplicate rectangle.
|
||||
((bound-and-true-p rectangle-mark-mode)
|
||||
(rectangle--duplicate-right n)
|
||||
(rectangle--duplicate-right n
|
||||
(if (< duplicate-region-final-position 0)
|
||||
n
|
||||
duplicate-region-final-position))
|
||||
(setq deactivate-mark nil))
|
||||
|
||||
;; Duplicate (contiguous) region.
|
||||
((use-region-p)
|
||||
(let* ((beg (region-beginning))
|
||||
(end (region-end))
|
||||
(text (buffer-substring beg end)))
|
||||
(text (buffer-substring beg end))
|
||||
(pt (point))
|
||||
(mk (mark)))
|
||||
(save-excursion
|
||||
(goto-char end)
|
||||
(duplicate--insert-copies n text)))
|
||||
(duplicate--insert-copies n text))
|
||||
(let* ((displace (if (< duplicate-region-final-position 0)
|
||||
n
|
||||
duplicate-region-final-position))
|
||||
(d (* displace (- end beg))))
|
||||
(unless (zerop d)
|
||||
(push-mark (+ mk d))
|
||||
(goto-char (+ pt d)))))
|
||||
(setq deactivate-mark nil))
|
||||
|
||||
;; Duplicate line.
|
||||
|
|
25
lisp/rect.el
25
lisp/rect.el
|
@ -930,8 +930,9 @@ Ignores `line-move-visual'."
|
|||
(mapc #'delete-overlay (nthcdr 5 rol))
|
||||
(setcar (cdr rol) nil)))
|
||||
|
||||
(defun rectangle--duplicate-right (n)
|
||||
"Duplicate the rectangular region N times on the right-hand side."
|
||||
(defun rectangle--duplicate-right (n displacement)
|
||||
"Duplicate the rectangular region N times on the right-hand side.
|
||||
Leave the region moved DISPLACEMENT region-wide steps to the right."
|
||||
(let ((cols (rectangle--pos-cols (point) (mark))))
|
||||
(apply-on-rectangle
|
||||
(lambda (startcol endcol)
|
||||
|
@ -940,16 +941,22 @@ Ignores `line-move-visual'."
|
|||
(move-to-column endcol t)
|
||||
(dotimes (_ n)
|
||||
(insert (cadr lines)))))
|
||||
(region-beginning) (region-end))
|
||||
;; Recompute the rectangle state; no crutches should be needed now.
|
||||
(let ((p (point))
|
||||
(m (mark)))
|
||||
(min (point) (mark))
|
||||
(max (point) (mark)))
|
||||
;; Recompute the rectangle state.
|
||||
(let* ((p (point))
|
||||
(m (mark))
|
||||
(point-col (car cols))
|
||||
(mark-col (cdr cols))
|
||||
(d (* displacement (abs (- point-col mark-col)))))
|
||||
(rectangle--reset-crutches)
|
||||
(goto-char m)
|
||||
(move-to-column (cdr cols) t)
|
||||
(set-mark (point))
|
||||
(move-to-column (+ mark-col d) t)
|
||||
(if (= d 0)
|
||||
(set-mark (point))
|
||||
(push-mark (point)))
|
||||
(goto-char p)
|
||||
(move-to-column (car cols) t))))
|
||||
(move-to-column (+ point-col d) t))))
|
||||
|
||||
(provide 'rect)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
(require 'misc)
|
||||
|
||||
(defmacro with-misc-test (original result &rest body)
|
||||
(declare (indent 2))
|
||||
|
@ -113,40 +114,70 @@
|
|||
(require 'rect)
|
||||
|
||||
(ert-deftest misc--duplicate-dwim ()
|
||||
;; Duplicate a line.
|
||||
(with-temp-buffer
|
||||
(insert "abc\ndefg\nh\n")
|
||||
(goto-char 7)
|
||||
(duplicate-dwim 2)
|
||||
(should (equal (buffer-string) "abc\ndefg\ndefg\ndefg\nh\n"))
|
||||
(should (equal (point) 7)))
|
||||
(let ((duplicate-line-final-position 0)
|
||||
(duplicate-region-final-position 0))
|
||||
;; Duplicate a line.
|
||||
(dolist (final-pos '(0 -1 1))
|
||||
(ert-info ((prin1-to-string final-pos) :prefix "final-pos: ")
|
||||
(with-temp-buffer
|
||||
(insert "abc\ndefg\nh\n")
|
||||
(goto-char 7)
|
||||
(let ((duplicate-line-final-position final-pos))
|
||||
(duplicate-dwim 3))
|
||||
(should (equal (buffer-string) "abc\ndefg\ndefg\ndefg\ndefg\nh\n"))
|
||||
(let ((delta (* 5 (if (< final-pos 0) 3 final-pos))))
|
||||
(should (equal (point) (+ 7 delta)))))))
|
||||
|
||||
;; Duplicate a region.
|
||||
(with-temp-buffer
|
||||
(insert "abc\ndef\n")
|
||||
(set-mark 2)
|
||||
(goto-char 7)
|
||||
(transient-mark-mode)
|
||||
(should (use-region-p))
|
||||
(duplicate-dwim)
|
||||
(should (equal (buffer-string) "abc\ndebc\ndef\n"))
|
||||
(should (equal (point) 7))
|
||||
(should (region-active-p))
|
||||
(should (equal (mark) 2)))
|
||||
;; Duplicate a region.
|
||||
(dolist (final-pos '(0 -1 1))
|
||||
(ert-info ((prin1-to-string final-pos) :prefix "final-pos: ")
|
||||
(with-temp-buffer
|
||||
(insert "abCDEFghi")
|
||||
(set-mark 3)
|
||||
(goto-char 7)
|
||||
(transient-mark-mode)
|
||||
(should (use-region-p))
|
||||
(let ((duplicate-region-final-position final-pos))
|
||||
(duplicate-dwim 3))
|
||||
(should (equal (buffer-string) "abCDEFCDEFCDEFCDEFghi"))
|
||||
(should (region-active-p))
|
||||
(let ((delta (* 4 (if (< final-pos 0) 3 final-pos))))
|
||||
(should (equal (point) (+ 7 delta)))
|
||||
(should (equal (mark) (+ 3 delta)))))))
|
||||
|
||||
;; Duplicate a rectangular region (sparse).
|
||||
(with-temp-buffer
|
||||
(insert "x\n>a\n>bcde\n>fg\nyz\n")
|
||||
(goto-char 4)
|
||||
(rectangle-mark-mode)
|
||||
(goto-char 15)
|
||||
(rectangle-forward-char 1)
|
||||
(duplicate-dwim)
|
||||
(should (equal (buffer-string) "x\n>a a \n>bcdbcde\n>fg fg \nyz\n"))
|
||||
(should (equal (point) 24))
|
||||
(should (region-active-p))
|
||||
(should rectangle-mark-mode)
|
||||
(should (equal (mark) 4)))
|
||||
|
||||
;; Idem (dense).
|
||||
(dolist (final-pos '(0 -1 1))
|
||||
(ert-info ((prin1-to-string final-pos) :prefix "final-pos: ")
|
||||
(with-temp-buffer
|
||||
(insert "aBCd\neFGh\niJKl\n")
|
||||
(goto-char 2)
|
||||
(rectangle-mark-mode)
|
||||
(goto-char 14)
|
||||
(let ((duplicate-region-final-position final-pos))
|
||||
(duplicate-dwim 3))
|
||||
(should (equal (buffer-string)
|
||||
"aBCBCBCBCd\neFGFGFGFGh\niJKJKJKJKl\n"))
|
||||
(should (region-active-p))
|
||||
(should rectangle-mark-mode)
|
||||
(let ((hdelta (* 2 (if (< final-pos 0) 3 final-pos)))
|
||||
(vdelta 12))
|
||||
(should (equal (point) (+ 14 vdelta hdelta)))
|
||||
(should (equal (mark) (+ 2 hdelta)))))))))
|
||||
|
||||
;; Duplicate a rectangular region.
|
||||
(with-temp-buffer
|
||||
(insert "x\n>a\n>bcde\n>fg\nyz\n")
|
||||
(goto-char 4)
|
||||
(rectangle-mark-mode)
|
||||
(goto-char 15)
|
||||
(rectangle-forward-char 1)
|
||||
(duplicate-dwim)
|
||||
(should (equal (buffer-string) "x\n>a a \n>bcdbcde\n>fg fg \nyz\n"))
|
||||
(should (equal (point) 24))
|
||||
(should (region-active-p))
|
||||
(should rectangle-mark-mode)
|
||||
(should (equal (mark) 4))))
|
||||
|
||||
(provide 'misc-tests)
|
||||
;;; misc-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue