On 19 February 2013 14:53, David Naumann
<naumann@cs.stevens.edu> wrote:
I'm a happy, frequent user of org mode but there's something I can't figure out from the manual.
What I would like to be able to do is insert a new heading at the same
level as current, _following_ all the others. For example, with the
cursor on the A in this tree:
* top
-> ** A
** B
** C
* next
I would like to insert a last sibling and move to it:
* top
** A
** B
** C
-> **
* next
Use case: adding to a very long chronological list. I have not seen a
quick way to do this using the structure motion/editing commands in
the manual, without scrolling in one way or another.
If you have a hint, please reply to my address; I'm not on this
mailing list.
The following is a little long, however only one function is actually
interactive.
#+begin_src emacs-lisp
(require 'org-element)
(defun zin/org-next-element ()
"Move to the end of the current element (start of next)."
(let ((end (org-element-property :end (org-element-at-point))))
(goto-char end)))
(defun zin/org-element-check-element (type &optional name)
"Check if current element is of type TYPE.
If not move to next element using `zin/org-next-element'.
If NAME is non-nil, verify that the :name or :drawer-name
property matches it."
(save-excursion
(save-restriction
(save-excursion
(org-up-element)
(org-narrow-to-element))
(catch 'element
(while (not (eobp))
(let ((cur-type (car (org-element-at-point)))
;; Allows for adaptation to non-headline cases.
(cur-name (or (org-element-property
:name (org-element-at-point))
(org-element-property
:drawer-name (org-element-at-point)))))
(if (and (string= type cur-type)
(string= name cur-name))
(throw 'element (point))
(zin/org-next-element))))))))
(defun zin/org-element-start-or-end (start &optional level)
"Find start or ending point of an element's content.
If START is non-nil, return the beginning of the content, if nil
return the end.
If LEVEL is equal to 1, parse the buffer for level 1 headlines.
Any other value is ignored."
;; If level is 1, looking at top level headlines, there is no
;; containing element.
(if (and level
(= 1 level))
(let* ((map (org-element-map (org-element-parse-buffer) 'headline
(lambda (hl)
;; return a list of beginning and ending
;; points of all level 1 headlines.
(list
(org-element-property :begin hl)
(org-element-property :end hl))) 'nil 'nil 'headline))
;; Find smallest (when start is 't) or largest (when
;; start is 'nil) point.
(point (if start
(apply 'min (mapcar 'car map))
(apply 'max (mapcar 'cadr map)))))
point)
;; Not in a top level headline, deal with contents directly.
(let ((top (org-element-property :contents-begin (org-element-at-point)))
(bottom (org-element-property :contents-end (org-element-at-point))))
(if start
top
bottom))))
(defun zin/org-add-heading (start &optional title)
"Create a new heading at the before or after all headings of current level.
If START is non-nil, the new heading will be the first in the
list. If nil it will be created after all the others.
With optional TITLE, automatically insert the desired title,
leaving the point on the following line."
(interactive "P")
(org-back-to-heading)
(let* ((level (org-element-property :level (org-element-at-point)))
(point (save-excursion
(ignore-errors (org-up-element))
(zin/org-element-start-or-end start level)))
;; Org-element minimal version of a headline at LEVEL with
;; TITLE (or blank)
(headline `(headline (:level ,level :title ,(or title "")))))
(if start
;; If placing headline above existing headlines, ensure you do
;; not place it above the content of the parent headline.
(progn
;; Search from top of content of parent headline. Without
;; this it will put it above the current headline.
(goto-char point)
;; Do not check to make sure content is skipped if in a
;; level 1 headline, just return the start of the top
;; headline.
(unless
(= 1 level)
(goto-char (zin/org-element-check-element "headline"))))
(goto-char point))
(org-save-outline-visibility 't
;; Insert a parsed version of the headline
(newline)
(delete-char -1)
(insert (org-element-interpret-data headline))
;; If there is a title provided, put point on start of next line,
;; otherwise return point to headline to insert title.
(if title
(open-line 1)
(backward-char 1)))))
#+end_src
Calling zin/org-add-heading will insert a new heading at the bottom of
the current set of headlines (of the current level). Calling it with
the prefix argument: C-u M-x zin/org-add-heading will insert it as the
first headline at that level.
Regards,
Jon