21

When I edit large documents, I would like to see where I'm at by seeing the outline (with no-content) in a separate buffer. Like when you read a PDF file there is a TOC on the left. (see below)

In org-mode it's possible to expand/collapse outline. But is it possible to have a static outline in the left(or right) in a separate buffer so that when you click on the headings, the other buffer moves to that position?

Kinda like this but for org-mode? enter image description here

[Edit]
The clone-indirect-buffer is very close to what I want. The missing piece of the puzzle is to jump to the same location when clicking a heading/(or any point really).

For this I have tried to write some code: Move to other cloned buffer to same point? (sync position of indirect buffers) (org-mode)

But it doesn't function if content is collapsed. If that can be made to work, then the clone-inderect-buffer is a complete solution to this.

[Edit2 Solution]
The code in the link above and in the answer below combine niceley to solve the jumping back and forth.

;first call 'clone-indirect-buffer'. Then...

;This function works between buffer and it's clone.
(defun my/goto-same-spot-in-other-buffer () 
  "Go to the same location in the other buffer. Useful for when you have cloned indirect buffers"
  (interactive)
  (let ((my/goto-current-point (point)))
       (other-window 1)
       (goto-char my/goto-current-point)
       (when (invisible-p (point))
        (org-reveal)))
)

;This function is a clone-to-buffer jump only:
; It does find the other buffer first thou instead of just jumping to the other 
; window as does the function above.
(defun my/jump-to-point-and-show ()
  "Switch to a cloned buffer's base buffer and move point to the
cursor position in the clone."
  (interactive)
  (let ((buf (buffer-base-buffer)))
    (unless buf
      (error "You need to be in a cloned buffer!"))
    (let ((pos (point))
          (win (car (get-buffer-window-list buf))))
      (if win
          (select-window win)
        (other-window 1)
        (switch-to-buffer buf))
      (goto-char pos)
      (when (invisible-p (point))
        (show-branches)))))


(global-set-key (kbd "<s-mouse-1>") 'my/goto-same-spot-in-other-buffer)
(global-set-key (kbd "s-m") 'my/goto-same-spot-in-other-buffer)
(global-set-key (kbd "<C-s-mouse-1>") 'my/jump-to-point-and-show)
(global-set-key (kbd "C-s-m") 'my/jump-to-point-and-show)
  • 2
    Maybe try C-c C-x b, or org-tree-to-indirect-buffer. – Samuel Flint Aug 23 '15 at 0:46
  • @SWFlint After a short test, it looks like it does something different from what the OP wants: it copies the current subtree to an indirect buffer. What we'd need would be an org-sparse-tree-to-indirect-buffer function, for example, but it does not seem to exist. – T. Verron Aug 24 '15 at 8:10
  • After looking a little further, try something along the lines of org-panes – Samuel Flint Sep 1 '15 at 1:57
  • I think it's also worth mentioning imenu-list. It lacks some of the requirements but addresses others. – incandescentman Nov 14 '15 at 10:43
  • org-tree-to-indirect-buffer is awesome. – ninrod Apr 2 '17 at 0:28
9

A few options come to mind. The first two are speedbar, which allegedly plays well with org-mode, and minimap, although I've not used them so can't vouch for them personally.

What might be the simplest option (and also the most flexible), would be to use an indirect buffer.

In practice, you would go to the org buffer for which you'd like an outline, hit M-x clone-indirect-buffer (use C-u M-x clone-indirect-buffer if you'd like to control what the clone is called), and then boom, there's another copy of the buffer for you to use. Put that clone in a window or frame side-by-side with the original buffer and, in the clone, adjust the outline to your tastes.

It doesn't get you the "click on heading in outline" functionality you mentioned, but gets you the spirit of the sidebar.

Edit: in response to your comment, here's a simple command that, when invoked from the buffer clone, will switch to the base buffer and move point to wherever the cursor was in the buffer clone:

(defun jump-to-point-and-show ()
  "Switch to a cloned buffer's base buffer and move point to the
cursor position in the clone."
  (interactive)
  (let ((buf (buffer-base-buffer)))
    (unless buf
      (error "You need to be in a cloned buffer!"))
    (let ((pos (point))
          (win (car (get-buffer-window-list buf))))
      (if win
          (select-window win)
        (other-window 1)
        (switch-to-buffer buf))
      (goto-char pos)
      (when (invisible-p (point))
        (show-branches)))))
  • I tried out speedbar/Sr-Speedbar. It is very nice but it only shows the first 2 levels. I asked if it's possible to have more levels here: emacs.stackexchange.com/questions/9533/… The indirect buffer is a nice option. I wonder if it's possible to somehow move the cursor in the other buffer to the same position as in the cloned buffer.. – Leo Ufimtsev Feb 24 '15 at 17:14
  • I tried to write code that would jump to the same location in the other buffer, but it only works if everything is expanded. If it can be made to work so that it auto-expands, then the clone-buffer is a perfect solution. Post: emacs.stackexchange.com/questions/9536/… – Leo Ufimtsev Feb 24 '15 at 17:38
  • Thank you for the snippet of code above. Much appreciated ^_^. – Leo Ufimtsev Feb 24 '15 at 18:20
14

How about: M-x occur RET ^*+ RET (note that there's a space at the end of the regexp).

  • I don't know why someone downvoted this answer, I think this is actually a pretty neat build-in hassle free solution actually. The only quirck is that it doesn't follow you around. I.e it's not obvious in the occur buffer where you are, (highlight the current heading for instance) and it doesn't scroll for long long documents. But for small documents it works. – Leo Ufimtsev Feb 25 '15 at 21:42
  • I like this simple, built-in solution. If one needs highlighting, use helm-occur instead of occur. An added benefit is to be able to use a single navigation for multiple org file buffers by using multi-occur. Lastly, with occur-edit, one can edit both views, the outline view and the expanded view. In any case, I'm up-voting this. – Emacs User Nov 19 '15 at 4:32
  • Going to upvote this because its awesome, simple and efficient – Gambo Aug 8 '17 at 12:20
  • So simple. So clever. So hackish. So awesome. – dangom Aug 1 '18 at 12:42
5

After reading Dan's answer and your solution based on it, I put this together. It opens a new, narrow, read-only clone to the left of the current buffer, and it binds <mouse-1> and RET locally in the clone to jump to that position in the base buffer.

(defun my/open-tree-view ()
  "Open a clone of the current buffer to the left, resize it to 30 columns, and bind <mouse-1> to jump to the same position in the base buffer."
  (interactive)
  (let ((new-buffer-name (concat "<tree>" (buffer-name))))
    ;; Create tree buffer
    (split-window-right 30)
    (if (get-buffer new-buffer-name)
        (switch-to-buffer new-buffer-name)  ; Use existing tree buffer
      ;; Make new tree buffer
      (progn  (clone-indirect-buffer new-buffer-name nil t)
              (switch-to-buffer new-buffer-name)
              (read-only-mode)
              (hide-body)
              (toggle-truncate-lines)

              ;; Do this twice in case the point is in a hidden line
              (dotimes (_ 2 (forward-line 0)))

              ;; Map keys
              (use-local-map (copy-keymap outline-mode-map))
              (local-set-key (kbd "q") 'delete-window)
              (mapc (lambda (key) (local-set-key (kbd key) 'my/jump-to-point-and-show))
                    '("<mouse-1>" "RET"))))))

(defun my/jump-to-point-and-show ()
  "Switch to a cloned buffer's base buffer and move point to the cursor position in the clone."
  (interactive)
  (let ((buf (buffer-base-buffer)))
    (unless buf
      (error "You need to be in a cloned buffer!"))
    (let ((pos (point))
          (win (car (get-buffer-window-list buf))))
      (if win
          (select-window win)
        (other-window 1)
        (switch-to-buffer buf))
      (goto-char pos)
      (when (invisible-p (point))
        (show-branches)))))

This will work with outline-mode and outline-minor-mode, as well as modes that build on them, such as org-mode. I found some info about getting the local key maps, but I don't know how to choose which one to copy. There's also this page which has a function to automatically make buffer-specific minor modes for setting buffer-local keys, but that seems out-of-scope for this issue.

  • Thank you for sharing this. By using outline-mode-map instead of org-mode-map I managed to make it working for AUCTeX with outline-minor-mode, which is nice. – Oleg Domanov Nov 3 '15 at 15:08
  • By the way, the outline-mode's function hide-body hides everything except headlines. – Oleg Domanov Nov 3 '15 at 15:14
  • @OlegDomanov Thanks for that info! I improved the code to use the outline-mode-map and hide-body, and a few other minor improvements. – blujay Nov 14 '15 at 7:07
0

two low rent suggestions (what i do) from a non-programmer who likes to use aquamacs for text editor:

  1. toggle back and forth with in-buffer commands:

STARTUP: indent (to show the waterfall type outline easier to see as whole)

this is for tab-driven viewing of buffer

and

+OPTIONS: H:N, where N = =the number of levels you wish to see in html export, which is suggestion #2

see: https://emacsclub.github.io/html/org_tutorial.html

  1. export as html (C-C C-E h h) to see TOC. i can find no one who can easily get the text output of html or text (C-C C-E t a) to indent
0

I needed something similar, so I put together outline-toc mode. It should work for org-mode docs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.