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

From: Zachary Kanfer <[email-masked]>
Date: Tue, 20 Jun 2023 01:07:54 -0400

This proposal is a function that copies the line point is on, duplicating
it in the buffer.

I find it useful for both text and code. It makes writing certain types of
repetitive things a lot simpler.
From 883f4dac31f4c78cdc5dd4622a0b110045840bd9 Mon Sep 17 00:00:00 2001
From: Zachary Kanfer <[email-masked]>
Date: Tue, 20 Jun 2023 01:04:47 -0400
Subject: [PATCH] Add copy-line function

* lisp/simple.el (copy-line): Add the copy-line function itself.
* test/lisp/simple-tests.el (simple-tests-zap-to-char): Tests for this
new function.
* etc/NEWS: Update
---
 etc/NEWS                  |  4 ++++
 lisp/simple.el            | 20 ++++++++++++++++++
 test/lisp/simple-tests.el | 44 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)

diff --git a/etc/NEWS b/etc/NEWS
index 61e6e161665..fe3d68b1b9e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -665,6 +665,10 @@ Their 'noerror' arguments have no effect and are therefore obsolete.
 This function evaluates a command's interactive form and returns the
 resultant list.
 
+** New function 'copy-line' in the simple library.
+This function copies the current line, inserting it before (or, with
+prefix argument, after) point.
+
 
 * Changes in Emacs 30.1 on Non-Free Operating Systems
 
diff --git a/lisp/simple.el b/lisp/simple.el
index e08bf4fdd64..0c2cd2ba93a 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10977,6 +10977,26 @@ lax-plist-put
   (plist-put plist prop val #'equal))
 
 
+(defun copy-line (&optional copy-above)
+  "Copy the line point is on, placing the copy above the current line.
+
+With prefix argument COPY-ABOVE, put the copy below the current line."
+  (interactive "*P")
+  (let ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
+        (where-in-line (- (point) (line-beginning-position))))
+    (if copy-above
+        (progn (beginning-of-line)
+               (open-line 1))
+      (progn (end-of-line)
+             (insert "\n")))
+    (insert line)
+    (goto-char (line-beginning-position))
+    (forward-char where-in-line))
+  (set-transient-map
+   (define-keymap "c" (lambda () (interactive) (copy-line copy-above))))
+  (message "press c to copy again?"))
+
+
 (provide 'simple)
 
 ;;; simple.el ends here
diff --git a/test/lisp/simple-tests.el b/test/lisp/simple-tests.el
index 7dabb735522..d419e3493ab 100644
--- a/test/lisp/simple-tests.el
+++ b/test/lisp/simple-tests.el
@@ -1046,5 +1046,49 @@ simple-tests-zap-to-char
     (with-zap-to-char-test "abcdeCXYZ" "XYZ"
       (zap-to-char 1 ?C 'interactive))))
 
+;;; Tests for `copy-line'
+
+(ert-deftest copy-test--copy-down--check-text ()
+  (should (equal (with-temp-buffer
+                   (insert "a\nb\nc")
+                   (previous-line)
+                   (copy-line)
+                   (buffer-string))
+                 "a\nb\nb\nc")))
+
+(ert-deftest copy-test--copy-down--check-point ()
+  (should (equal (with-temp-buffer
+                   (insert "a\nb\nc")
+                   (previous-line)
+                   (copy-line)
+                   (point))
+                 6)))
+
+(ert-deftest copy-test--copy-up--check-point ()
+  (should (equal (with-temp-buffer
+                   (insert "a\nb\nc")
+                   (previous-line)
+                   (copy-line t)
+                   (point))
+                 4)))
+
+(ert-deftest copy-test--copy-down--middle-of-line--check-text ()
+  (should (equal (with-temp-buffer
+                   (insert "a\nb c d e\nf")
+                   (beginning-of-line -1)
+                   (forward-char 4)
+                   (copy-line)
+                   (buffer-string))
+                 "a\nb c d e\nb c d e\nf")))
+
+(ert-deftest copy-test--copy-down--middle-of-line--check-point ()
+  (should (equal (with-temp-buffer
+                   (insert "a\nb c d e\nf")
+                   (beginning-of-line 0)
+                   (forward-char 4)
+                   (copy-line)
+                   (point))
+                 15)))
+
 (provide 'simple-test)
 ;;; simple-tests.el ends here
-- 
2.40.1