Subject: Re: bug#64185: proposal for new function: copy-line

From: Juri Linkov <[email-masked]>
Date: Thu, 22 Jun 2023 20:27:04 +0300

>> > Or we could have a user option to do that regardless of the argument,
>> > but then we should decide on which of the N new lines to put point:
>> > the first one copied or the last one.
>>
>> A user option looks preferable.  In case of a prefix numeric argument,
>> it also makes sense to have 3 choices:
>>
>> 1. leave point on the old line
>> 2. move point to the first new line
>> 3. move point to the last new line
>
> Patches welcome, but if you want to have this in Emacs 29, please
> hurry up with the patches.

This is both simple and general allowing to specify any number
for the new line position counting from the first/last line,
but only values 0, 1, and -1 are really useful:

diff --git a/lisp/misc.el b/lisp/misc.el
index ca013d5f72f..a0c8a1f620e 100644
--- a/lisp/misc.el
+++ b/lisp/misc.el
@@ -63,6 +63,14 @@ copy-from-above-command
 				 (+ n (point)))))))
     (insert string)))

+(defcustom duplicate-line-pos 0
+  "Where to put point after copying the line.
+When 0, leave point on the original line.
+When 1, move point to the first new line.
+When -1, move point to the last new line."
+  :type 'integer
+  :version "29.1")
+
 ;;;###autoload
 (defun duplicate-line (&optional n)
   "Duplicate the current line N times.
@@ -71,13 +79,19 @@ duplicate-line
   (interactive "p")
   (unless n
     (setq n 1))
-  (let ((line (buffer-substring (line-beginning-position) (line-end-position))))
-    (save-excursion
-      (forward-line 1)
-      (unless (bolp)
-        (insert "\n"))
-      (dotimes (_ n)
-        (insert line "\n")))))
+  (let ((line (buffer-substring (line-beginning-position) (line-end-position)))
+        (pos (unless (< duplicate-line-pos 0) (point)))
+        (column (unless (eq duplicate-line-pos 0) (current-column))))
+    (forward-line 1)
+    (unless (bolp)
+      (insert "\n"))
+    (dotimes (_ n)
+      (insert line "\n"))
+    (unless (< duplicate-line-pos 0)
+      (goto-char pos))
+    (unless (eq duplicate-line-pos 0)
+      (forward-line duplicate-line-pos)
+      (move-to-column column))))

 (declare-function rectangle--duplicate-right "rect" (n))