From 5b0b7f2924c013c945c1891fd6338a211912ed97 Mon Sep 17 00:00:00 2001 From: Ihor Radchenko Date: Sun, 21 Apr 2024 14:59:37 +0300 Subject: [PATCH] org-paste-subtree: With single/double prefix, force inserting sibling/child * lisp/org.el (org-paste-subtree): When called with single or double universal argument, force inserting sibling heading or child heading accordingly. * testing/lisp/test-org.el (test-org/paste-subtree): Add tests for the new behavior. * doc/org-manual.org (Structure Editing): Update the command description. * etc/ORG-NEWS (~org-paste-subtree~ now handles =C-u= and =C-u C-u= prefix arguments specially): Announce the change. Link: https://orgmode.org/list/878rhxtszb.fsf@localhost --- doc/org-manual.org | 4 +++- etc/ORG-NEWS | 5 +++++ lisp/org.el | 25 ++++++++++++++++++++----- testing/lisp/test-org.el | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index 343841b53..ca1c9b482 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -921,7 +921,9 @@ The following commands jump to other headlines in the buffer. Yank subtree from kill ring. This does modify the level of the subtree to make sure the tree fits in nicely at the yank position. The yank level can also be specified with a numeric prefix argument, - or by yanking after a headline marker like =****=. + or by yanking after a headline marker like =****=. With + {{{kbd(C-u)}}} prefix, force inserting as a sibling. With + {{{kbd(C-u C-u)}}} prefix argument, force inserting as a child. - {{{kbd(C-y)}}} (~org-yank~) :: diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index e61bd6988..dbf849422 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -1000,6 +1000,11 @@ properties, links to headlines in the file can also be made more robust by using the file id instead of the file path. ** New features +*** ~org-paste-subtree~ now handles =C-u= and =C-u C-u= prefix arguments specially + +With =C-u= prefix argument, force inserting a sibling heading below. +With =C-u C-u= prefix argument, force inserting a child heading. + *** =colview= dynamic block now writes column width specifications When column format contains width specifications, =colview= dynamic diff --git a/lisp/org.el b/lisp/org.el index c24c67f89..ed814ded4 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -7294,7 +7294,13 @@ The entire subtree is promoted or demoted in order to match a new headline level. If the cursor is at the beginning of a headline, the same level as -that headline is used to paste the tree. +that headline is used to paste the tree before current headline. + +With `\\[universal-argument]' prefix, force inserting at the same level +as current headline, after subtree at point. + +With `\\[universal-argument]' `\\[universal-argument]' prefix, force +inserting as a child headline, as the first child. If not, the new level is derived from the *visible* headings before and after the insertion point, and taken to be the inferior headline @@ -7330,12 +7336,15 @@ When REMOVE is non-nil, remove the subtree from the clipboard." level-indicator? (force-level (cond - (level (prefix-numeric-value level)) ;; When point is after the stars in an otherwise empty ;; headline, use the number of stars as the forced level. - ((and (org-match-line "^\\*+[ \t]*$") + ((and (or (not level) (member level '((4) (16)))) + (org-match-line "^\\*+[ \t]*$") (not (eq ?* (char-after)))) (setq level-indicator? (org-outline-level))) + ((equal level '(4)) (org-outline-level)) + ((equal level '(16)) nil) ; handle later + (level (prefix-numeric-value level)) ((looking-at-p org-outline-regexp-bol) (org-outline-level)))) (previous-level (save-excursion @@ -7345,7 +7354,12 @@ When REMOVE is non-nil, remove the subtree from the clipboard." (save-excursion (org-next-visible-heading 1) (if (org-at-heading-p) (org-outline-level) 1))) - (new-level (or force-level (max previous-level next-level))) + (new-level (or force-level + (max + ;; C-u C-u forces child. + (if (equal level '(16)) (1+ previous-level) 0) + previous-level + next-level))) (shift (if (or (= old-level -1) (= new-level -1) (= old-level new-level)) @@ -7360,7 +7374,8 @@ When REMOVE is non-nil, remove the subtree from the clipboard." (delete-region (line-beginning-position) (line-beginning-position 2))) ;; Paste before the next visible heading or at end of buffer, ;; unless point is at the beginning of a headline. - (unless (and (bolp) (org-at-heading-p)) + (unless (and (bolp) (org-at-heading-p) (not (member level '((4) (16))))) + (when (equal level '(4)) (org-end-of-subtree t)) (org-next-visible-heading 1) (unless (bolp) (insert "\n"))) (setq beg (point)) diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 36f4d796b..23d12b26a 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -9356,6 +9356,18 @@ CLOSED: %s (org-test-with-temp-text "* H1\nParagraph\n* H2" (org-paste-subtree nil "* Text") (buffer-string)))) + ;; With prefix argument, move to the end of subtree. + (should + (equal "* H1\nParagraph\n** H1.1\n* Text\n* H2" + (org-test-with-temp-text "* H1\nParagraph\n** H1.1\n* H2" + (org-paste-subtree '(4) "* Text") + (buffer-string)))) + ;; With double prefix argument, move to first sibling + (should + (equal "* H1\nParagraph\n** Text\n** H1.1\n* H2" + (org-test-with-temp-text "* H1\nParagraph\n** H1.1\n* H2" + (org-paste-subtree '(16) "* Text") + (buffer-string)))) ;; If point is between two headings, use the deepest level. (should (equal "* H1\n\n* Text\n* H2" @@ -9378,6 +9390,12 @@ CLOSED: %s (org-test-with-temp-text "* H1\n** H2" (org-paste-subtree nil "*** Text") (buffer-string)))) + ;; With prefix argument, ignore that we are at bol + (should + (equal "* H1\n** H2\n* Text\n" + (org-test-with-temp-text "* H1\n** H2" + (org-paste-subtree '(4) "*** Text") + (buffer-string)))) ;; When point is on heading but not at bol, use smallest level among ;; current heading and next, inserting before the next heading. (should