2
votes

Say you have a tree of the following form:

* Top
** Item A
*** Lower
** Item B
** Item C

What I would like to do is to be able to make a region containing Item A and Item B, and have a command run to change it into this:

* Top
** 
*** Item A
**** Lower
*** Item B
** Item C

With the cursor at the blank item. I am wondering if something similar is already written into org-mode. If not, I can write it myself, but in that case I am wondering how I should loop through the lines in my region to shift them, avoiding items which are on a lower level than my starting one.

2

2 Answers

1
votes

A naive first try

Starting with the outline

* Top
** Item A
*** Lower
** Item B
** Item C

If I place my cursor after Top and press Alt + Return (org-insert-heading) followed by Alt + Shift + right (org-demote-subtree) I get the structure

* Top
** 
*** Item A
**** Lower
*** Item B
*** Item C

Unfortunately this demotes Item C, which you don't seem to want. However, you can just navigate to this item and press Alt + Shift + left. However, I imagine that this is a simplified example and want a more powerful method which doesn't involve moving around the file too much. Can we do better?

Transient mark mode

Reading through structure editing in the Org mode manual I found the note

When there is an active region (Transient Mark mode), promotion and demotion work on all headlines in the region. To select a region of headlines, it is best to place both point and mark at the beginning of a line, mark at the beginning of the first headline, and point at the line just after the last headline to change.

So perhaps this gives us a way forward. However, I haven't been able to do anything sensible with this information. Hopefully someone else can step and and show us how it is done.

Narrowing to a region

One other way we can move more is to "narrow" the buffer to only the region we want to work on.

Select the lines * Top, ** Item B in full and everything in between (with your mouse or by, for example, using Ctrl + Space and Ctrl + c + Ctrl + n (outline-next-visible-heading) a few times). We can now focus our attention on only this highlighted (or marked) region by "narrowing" the buffer to only this region, to the exclusion of all other text in the buffer (we won't delete the text, even though it will look that way, we'll just hide it for a bit. We narrow to the region by using Ctrl + x + n + n (narrow-to-region) (note the helpful message that appears if you haven't explicitly enabled this feature). We can always get back to the full buffer by using Ctrl + x + n + w (widen).

In our narrowed buffer we can now only see:

* Top
** Item A
*** Lower
** Item B

If we now repeat the steps from our naive try (see above) and then returning to the full (widened) buffer we see that we have the outline:

* Top
** 
*** Item A
**** Lower
*** Item B
** Item C

which is the final result we were after! My apologise if this is an unnecessarily long answer. I learnt a lot researching it and wanted to document it all here.

1
votes

Based off Chris's answer, I came up with the following code:

(defun org-file-region (start end)
  "Moves the lines in region to be underneath a new header"
  (interactive "r")
  (extend-region-to-lines start end)
  (narrow-to-region (save-excursion (goto-char start)
                                    (line-beginning-position))
                    (save-excursion (goto-char end)
                                    (line-end-position)))
  (org-do-demote)
  (org-insert-heading)
  (setq mark-active nil)
  (org-do-promote)
  (setq mark-active t)
  (widen))

Note that only org-do-demote will operate on the region; the other demotion functions do not.