diff options
Diffstat (limited to 'lisp/misc.el')
-rw-r--r-- | lisp/misc.el | 65 |
1 files changed, 50 insertions, 15 deletions
diff --git a/lisp/misc.el b/lisp/misc.el index b36ca6416d9..fb40d1c16a3 100644 --- a/lisp/misc.el +++ b/lisp/misc.el @@ -76,6 +76,10 @@ The same column is preserved after moving to a new line." :group 'editing :version "29.1") +(defun duplicate--insert-copies (n string) + "Insert N copies of STRING at point." + (insert (mapconcat #'identity (make-list n string)))) + ;;;###autoload (defun duplicate-line (&optional n) "Duplicate the current line N times. @@ -86,21 +90,33 @@ Also see the `copy-from-above-command' command." (interactive "p") (unless n (setq n 1)) - (let ((line (buffer-substring (line-beginning-position) (line-end-position))) + (let ((line (concat (buffer-substring (line-beginning-position) + (line-end-position)) + "\n")) (pos (point)) (col (current-column))) (forward-line 1) (unless (bolp) (insert "\n")) - (dotimes (_ n) - (insert line "\n")) + (duplicate--insert-copies n line) (unless (< duplicate-line-final-position 0) (goto-char pos)) (unless (eq duplicate-line-final-position 0) (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 "30.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 @@ -113,25 +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) - (dotimes (_ n) - (insert 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. @@ -187,18 +218,22 @@ is an upper-case character." (upcase-region (point) (progn (forward-char arg) (point))))) ;;;###autoload -(defun forward-to-word (arg) - "Move forward until encountering the beginning of a word. -With argument, do this that many times." +(defun forward-to-word (&optional arg) + "Move forward until encountering the beginning of the ARGth word. +ARG defaults to 1. When called interactively, ARG is the prefix +numeric argument." (interactive "^p") + (unless arg (setq arg 1)) (or (re-search-forward (if (> arg 0) "\\W\\b" "\\b\\W") nil t arg) (goto-char (if (> arg 0) (point-max) (point-min))))) ;;;###autoload -(defun backward-to-word (arg) - "Move backward until encountering the end of a word. -With argument, do this that many times." +(defun backward-to-word (&optional arg) + "Move backward until encountering the end of the ARGth word. +ARG defaults to 1. When called interactively, ARG is the prefix +numeric argument." (interactive "^p") + (unless arg (setq arg 1)) (forward-to-word (- arg))) ;;;###autoload |