From 036659c63341f1d895825e02c90883eb8f2f856e Mon Sep 17 00:00:00 2001
From: Rick Lupton <mail@ricklupton.name>
Date: Wed, 3 Jan 2024 22:37:38 +0000
Subject: [PATCH 1/2] lisp/org.el (org-insert-heading): allow specifying
 heading level

* lisp/org.el (org-insert-heading): Change optional argument TOP to
LEVEL, accepting a number to force a specific heading level.
* testing/lisp/test-org.el (test-org/insert-heading): Add tests
* etc/ORG-NEWS: Document changes
---
 etc/ORG-NEWS             |  6 ++++++
 lisp/org.el              | 21 ++++++++++++++-------
 testing/lisp/test-org.el | 26 ++++++++++++++++++++++++--
 3 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index ad5a0684f..adda55532 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -991,6 +991,12 @@ as the function can also act on objects.
 
 *** ~org-export-get-parent-element~ is renamed to ~org-element-parent-element~ and moved to =lisp/org-element.el=
 
+*** ~org-insert-heading~ optional argument =TOP= is now =LEVEL=
+
+A numeric value forces a heading at that level to be inserted.  For
+backwards compatibility, non-numeric non-nil values insert level 1
+headings as before.
+
 ** Miscellaneous
 *** =org-crypt.el= now applies initial visibility settings to decrypted entries
 
diff --git a/lisp/org.el b/lisp/org.el
index 2e53d98d3..96d8fad3a 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -6360,7 +6360,7 @@ headline instead of current one."
     (`(heading . ,value) value)
     (_ nil)))
 
-(defun org-insert-heading (&optional arg invisible-ok top)
+(defun org-insert-heading (&optional arg invisible-ok level)
   "Insert a new heading or an item with the same depth at point.
 
 If point is at the beginning of a heading, insert a new heading
@@ -6389,12 +6389,19 @@ When INVISIBLE-OK is set, stop at invisible headlines when going
 back.  This is important for non-interactive uses of the
 command.
 
-When optional argument TOP is non-nil, insert a level 1 heading,
-unconditionally."
+When optional argument LEVEL is a number, insert a heading at
+that level.  For backwards compatibility, when LEVEL is non-nil
+but not a number, insert a level-1 heading."
   (interactive "P")
   (let* ((blank? (org--blank-before-heading-p (equal arg '(16))))
-	 (level (org-current-level))
-	 (stars (make-string (if (and level (not top)) level 1) ?*)))
+         (current-level (org-current-level))
+         (num-stars (or
+                     ;; Backwards compat: if LEVEL non-nil, level is 1
+                     (and level (if (wholenump level) level 1))
+                     current-level
+                     ;; This `1' is for when before first headline
+                     1))
+         (stars (make-string num-stars ?*)))
     (cond
      ((or org-insert-heading-respect-content
 	  (member arg '((4) (16)))
@@ -6403,7 +6410,7 @@ unconditionally."
       ;; Position point at the location of insertion.  Make sure we
       ;; end up on a visible headline if INVISIBLE-OK is nil.
       (org-with-limited-levels
-       (if (not level) (outline-next-heading) ;before first headline
+       (if (not current-level) (outline-next-heading) ;before first headline
 	 (org-back-to-heading invisible-ok)
 	 (when (equal arg '(16)) (org-up-heading-safe))
 	 (org-end-of-subtree invisible-ok 'to-heading)))
@@ -6416,7 +6423,7 @@ unconditionally."
                           (org-before-first-heading-p)))
         (insert "\n")
         (backward-char))
-      (when (and (not level) (not (eobp)) (not (bobp)))
+      (when (and (not current-level) (not (eobp)) (not (bobp)))
         (when (org-at-heading-p) (insert "\n"))
         (backward-char))
       (unless (and blank? (org-previous-line-empty-p))
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 822cbc67a..fc50dc787 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -1980,8 +1980,30 @@ CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] =>  0:46"
 	    (let ((org-insert-heading-respect-content nil))
 	      (org-insert-heading '(16)))
 	    (buffer-string))))
-  ;; When optional TOP-LEVEL argument is non-nil, always insert
-  ;; a level 1 heading.
+  ;; When optional LEVEL argument is a number, insert a heading at
+  ;; that level.
+  (should
+   (equal "* H1\n** H2\n* "
+	  (org-test-with-temp-text "* H1\n** H2<point>"
+	    (org-insert-heading nil nil 1)
+	    (buffer-string))))
+  (should
+   (equal "* H1\n** H2\n** "
+	  (org-test-with-temp-text "* H1\n** H2<point>"
+	    (org-insert-heading nil nil 2)
+	    (buffer-string))))
+  (should
+   (equal "* H1\n** H2\n*** "
+	  (org-test-with-temp-text "* H1\n** H2<point>"
+	    (org-insert-heading nil nil 3)
+	    (buffer-string))))
+  (should
+   (equal "* H1\n- item\n* "
+	  (org-test-with-temp-text "* H1\n- item<point>"
+	    (org-insert-heading nil nil 1)
+	    (buffer-string))))
+  ;; When optional LEVEL argument is non-nil, always insert a level 1
+  ;; heading.
   (should
    (equal "* H1\n** H2\n* "
 	  (org-test-with-temp-text "* H1\n** H2<point>"
-- 
2.37.1 (Apple Git-137.1)

