emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Orgmode] Synopsis view - moving trees around based on a synopsis


From: Cassio Koshikumo
Subject: [Orgmode] Synopsis view - moving trees around based on a synopsis
Date: Sun, 12 Dec 2010 04:56:57 -0200

Hi there,

So, after fiddling a lot, I finally came up with a nice (I think)
solution to my synopsis-associated-with-text problem
(http://article.gmane.org/gmane.emacs.orgmode/34279/). A little
hackish, maybe, but it works fine.

Funny thing is, this solution utilizes the first method I had
discarded: using a drawer to keep the synopsis.

Here's what I wanted to achieve:

I'm writing a long text. I'd like to divide it into more manageable
chunks (using subtrees), and associate a small synopsis with each of
them. Then, I'd like to have a view of the synopsis only, and be able
to move the chunks based on those synopsis -- without the main text
getting on the way.

Here's the solution:

1. Create a new drawer and name it SYNOPSIS. (Of course, you can
choose whatever name you like.) Put the synopsis text inside this
drawer.

    * Heading
    :SYNOPSIS:
    The synopsis goes here.
    :END:

    And the real text goes where it normally goes: on the entry body.

2. On your .emacs, paste the following function:

(defun k-synopsis-view ()
  "Show all headings (contents view) and, if any, their synopsis."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (hide-sublevels 1)
    (while (re-search-forward "\\(^[ \t]*:SYNOPSIS:[. \t]*$\\)" nil t)
      (org-flag-drawer nil)
      (re-search-forward "^[ \t]*:END:[ \t]*" nil t)
      (outline-flag-region (match-beginning 0) (match-end 0) t))
    (org-content)
    ))

Notice that the name of the drawer is hard-coded into the function.
So, if you chose another name, change it accordingly.

(I /think/ this function is not too stupid, but, if anyone knows how
to improve it, please do. Calling "hide-sublevels" and then
"org-content" was the only way I found to show all the headings and
still keep the text entry invisible when moving trees around.)

Now you can M-x k-synopsis-view and see only the synopsis, without the
real text.

3. For even better results, I recommend setting org-ellipsis to " ":

(setq org-ellipsis " ")

The "..." makes the view a little dirty. Of course, only do this if
you don't mind losing the indicator that there's something hidden (I
don't).

4. Now, if you're in the synopsis view and try to move trees around --
which is a primary objective of this setup -- things will not work as
expected; the synopsis of the subtree you moved and a few others
around it will collapse, leaving you with only the headings.
(Promoting and demoting is no problem.) To solve this, I changed the
functions "org-move-subtree-up" and "org-move-subtree-down" just a
tad:

(defun k-move-synopsis-subtree-up (&optional arg)
  "Move the current subtree up past ARG headlines of the same level,
and keep (or enter) synopsis view."
  (interactive "p")
  (k-move-synopsis-subtree-down (- (prefix-numeric-value arg))))

(defun k-move-synopsis-subtree-down (&optional arg)
  "Move the current subtree down past ARG headlines of the same level,
and keep (or enter) synopsis view."
  (interactive "p")
  (setq arg (prefix-numeric-value arg))
  (let ((movfunc (if (> arg 0) 'org-get-next-sibling
                   'org-get-last-sibling))
        (ins-point (make-marker))
        (cnt (abs arg))
        beg beg0 end txt folded ne-beg ne-end ne-ins ins-end)
    ;; Select the tree
    (org-back-to-heading)
    (setq beg0 (point))
    (save-excursion
      (setq ne-beg (org-back-over-empty-lines))
      (setq beg (point)))
    (save-match-data
      (save-excursion (outline-end-of-heading)
                      (setq folded (org-invisible-p)))
      (outline-end-of-subtree))
    (outline-next-heading)
    (setq ne-end (org-back-over-empty-lines))
    (setq end (point))
    (goto-char beg0)
    (when (and (> arg 0) (org-first-sibling-p) (< ne-end ne-beg))
      ;; include less whitespace
      (save-excursion
        (goto-char beg)
        (forward-line (- ne-beg ne-end))
        (setq beg (point))))
    ;; Find insertion point, with error handling
    (while (> cnt 0)
      (or (and (funcall movfunc) (looking-at outline-regexp))
          (progn (goto-char beg0)
                 (error "Cannot move past superior level or buffer limit")))
      (setq cnt (1- cnt)))
    (if (> arg 0)
        ;; Moving forward - still need to move over subtree
        (progn (org-end-of-subtree t t)
               (save-excursion
                 (org-back-over-empty-lines)
                 (or (bolp) (newline)))))
    (setq ne-ins (org-back-over-empty-lines))
    (move-marker ins-point (point))
    (setq txt (buffer-substring beg end))
    (org-save-markers-in-region beg end)
    (delete-region beg end)
    (org-remove-empty-overlays-at beg)
    (or (= beg (point-min)) (outline-flag-region (1- beg) beg nil))
    (or (bobp) (outline-flag-region (1- (point)) (point) nil))
    (and (not (bolp)) (looking-at "\n") (forward-char 1))
    (let ((bbb (point)))
      (insert-before-markers txt)
      (org-reinstall-markers-in-region bbb)
      (move-marker ins-point bbb))
    (or (bolp) (insert "\n"))
    (setq ins-end (point))
    (goto-char ins-point)
    (org-skip-whitespace)
    (when (and (< arg 0)
               (org-first-sibling-p)
               (> ne-ins ne-beg))
      ;; Move whitespace back to beginning
      (save-excursion
        (goto-char ins-end)
        (let ((kill-whole-line t))
          (kill-line (- ne-ins ne-beg)) (point)))
      (insert (make-string (- ne-ins ne-beg) ?\n)))
    (move-marker ins-point nil)
    (k-synopsis-view)))

The only differences from the normal functions are:

- The names and descriptions, obviously;

- At the end of the function, various visibility settings were removed
and replaced with a call to "k-synopsis-view". This guarantees that,
after a move, you'll still be in synopsis view (or enter it, if you
weren't already.)

5. Now you just need to define some key bindings. Mine are:

(add-hook 'org-mode-hook
          (lambda()
            (local-set-key (kbd "C-M-n") 'k-move-synopsis-subtree-down)
            (local-set-key (kbd "C-M-p") 'k-move-synopsis-subtree-up)
            (local-set-key (kbd "C-\\") 'k-synopsis-view)))

And that's it.

The cool thing is that, since the synopsis are inside drawers, they
won't be exported by default. And, if you want to export only the
synopsis to have an overview of the project, just "limit export to
visible part of outline tree" when in synopsis view.

One thing to notice: when you're in synopsis view, any text you enter
will go /inside/ the :SYNOPSIS: drawer -- unless you move the point
(cursor) to /after the ellipsis/. (Extra care when using a space as
ellipsis, as I do.) So, if you want to insert a new heading when in
synopsis view, you can; just make sure to go to the right place with
the cursor. (I think the same applies to other kinds of sparse trees,
but it's specially relevant here.)

Again: I'm no elisp master (oh my, far from it). If you can improve
these functions, please do.

I hope this is useful to someone else. It sure is for me.

Cheers,

-- 
Cássio Koshikumo



reply via email to

[Prev in Thread] Current Thread [Next in Thread]