emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Jonathan Leech-Pepin <jonathan.leechpepin@gmail.com>
To: David Naumann <naumann@cs.stevens.edu>
Cc: emacs-orgmode@gnu.org
Subject: Re: how can I insert a new heading after all at this level?
Date: Mon, 25 Feb 2013 11:28:44 -0500	[thread overview]
Message-ID: <CAEWDx5crfXs42SHWKGWJwAiXdhvPoJgfqqiWMikfL_+Zuc=iwA@mail.gmail.com> (raw)
In-Reply-To: <alpine.DEB.2.02.1302191451040.9441@eloi>

[-- Attachment #1: Type: text/plain, Size: 5678 bytes --]

Hello David,

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

[-- Attachment #2: Type: text/html, Size: 7551 bytes --]

  parent reply	other threads:[~2013-02-25 16:28 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-19 19:53 how can I insert a new heading after all at this level? David Naumann
2013-02-19 22:36 ` Nick Dokos
2013-02-25 16:28 ` Jonathan Leech-Pepin [this message]
2013-03-03 17:08 ` Bastien

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAEWDx5crfXs42SHWKGWJwAiXdhvPoJgfqqiWMikfL_+Zuc=iwA@mail.gmail.com' \
    --to=jonathan.leechpepin@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    --cc=naumann@cs.stevens.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).