diff options
-rw-r--r-- | etc/NEWS | 7 | ||||
-rw-r--r-- | lisp/misc.el | 37 | ||||
-rw-r--r-- | lisp/rect.el | 21 | ||||
-rw-r--r-- | test/lisp/misc-tests.el | 38 |
4 files changed, 101 insertions, 2 deletions
@@ -401,8 +401,11 @@ between these modes while the user is inputting a command by hitting ** Interactively, 'kill-buffer' will now offer to save the buffer if unsaved. --- -** New command 'duplicate-line'. -This command duplicates the current line the specified number of times. +** New commands 'duplicate-line' and 'duplicate-dwim'. +'duplicate-line' duplicates the current line the specified number of times. +'duplicate-dwim' duplicates the region if it is active. If not, it +works like 'duplicate-line'. An active rectangular region is +duplicated on its right-hand side. --- ** Files with the ".eld" extension are now visited in 'lisp-data-mode'. diff --git a/lisp/misc.el b/lisp/misc.el index 28c5d6e07f5..a53571f4639 100644 --- a/lisp/misc.el +++ b/lisp/misc.el @@ -79,6 +79,43 @@ Also see the `copy-from-above-command' command." (dotimes (_ n) (insert line "\n"))))) +(declare-function rectangle--duplicate-right "rect" (n)) + +;; `duplicate-dwim' preserves an active region and changes the buffer +;; outside of it: disregard the region when immediately undoing the +;; actions of this command. +(put 'duplicate-dwim 'undo-inhibit-region t) + +;;;###autoload +(defun duplicate-dwim (&optional n) + "Duplicate the current line or region N times. +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." + (interactive "p") + (unless n + (setq n 1)) + (cond + ;; Duplicate rectangle. + ((bound-and-true-p rectangle-mark-mode) + (rectangle--duplicate-right n) + (setq deactivate-mark nil)) + + ;; Duplicate (contiguous) region. + ((use-region-p) + (let* ((beg (region-beginning)) + (end (region-end)) + (text (buffer-substring beg end))) + (save-excursion + (goto-char end) + (dotimes (_ n) + (insert text)))) + (setq deactivate-mark nil)) + + ;; Duplicate line. + (t (duplicate-line n)))) + ;; Variation of `zap-to-char'. ;;;###autoload diff --git a/lisp/rect.el b/lisp/rect.el index 47df95b04e4..eebbf999d40 100644 --- a/lisp/rect.el +++ b/lisp/rect.el @@ -930,6 +930,27 @@ 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." + (let ((cols (rectangle--pos-cols (point) (mark)))) + (apply-on-rectangle + (lambda (startcol endcol) + (let ((lines (list nil))) + (extract-rectangle-line startcol endcol lines) + (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))) + (rectangle--reset-crutches) + (goto-char m) + (move-to-column (cdr cols) t) + (set-mark (point)) + (goto-char p) + (move-to-column (car cols) t)))) + (provide 'rect) ;;; rect.el ends here diff --git a/test/lisp/misc-tests.el b/test/lisp/misc-tests.el index a56feaa0495..f84827ab025 100644 --- a/test/lisp/misc-tests.el +++ b/test/lisp/misc-tests.el @@ -96,5 +96,43 @@ (should (equal (buffer-string) "abc\nabc\n")) (should (equal (point) 2)))) +(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))) + + ;; 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 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 |