8

I'm using org-mode with really huge files (what I love in org-mode is having everything in a single file).

I would like to know where I am in the file by having a look at the modeline.

If I have the following :

* Foo
** Bar
*** Dump
[cursor being here]
** Egg

I'd like the modeline to show me something like Foo > Bar > Dump

Any ideas on how to achieve that?

1

3 Answers 3

9

The which-function minor mode by default shows part of the org outline path in the mode line; you can turn it on using M-x which-function-mode. In general, which-function tries to show the current function in the mode line. It works in most languages.

If you want to see the entire path, you just have to add a new function to the variable which-func-functions. The first function in this list that returns non-nil is what which-function uses. Here is a function that uses org-get-outline-path to get the full path, if the current major mode is org-mode

(defun org-which-function ()
  (interactive)
  (when (eq major-mode 'org-mode)
    (mapconcat 'identity (org-get-outline-path t)
               " > ")
  ))


(add-to-list 'which-func-functions #'org-which-function)

You will quickly run out of room in your mode line, so try putting it in the header line instead. For example, this snippet will put the current buffer name and the outline path in the header:

(setq header-line-format
  '(:eval
   (list
    (header-buffer-name)
    " "
    (header-function-name))))

[EDIT] Here, the fuctions header-buffer-name and header-function-name are the author's private functions, and defined in this answer.

Emacs 29.1 NEWS in org-mode, header generated by org-which-function

3
  • Nice. I didn't know about org-get-outline-path: using it would streamline the post I had made considerably.
    – Dan
    Feb 20, 2017 at 19:40
  • @erikstokes I'm using Emacs 24, and getting Symbol's value as variable is void: which-func-functions while trying your code ; does it require recent emacs ? Feb 21, 2017 at 22:45
  • @Jocelyndelalande It's in 24.5 at least. The repo history makes it look likes it's been around a long time. Try running (require which-func) first to make sure it's loaded.
    – erikstokes
    Feb 21, 2017 at 23:06
1

The following is an interactive command that will give you your location within the current org-mode tree:

(defun org-where-am-i ()
  "Returns a string of headers indicating where point is in the
current tree."
  (interactive)
  (let (headers)
    (save-excursion
      (while (condition-case nil
                 (progn
                   (push (nth 4 (org-heading-components)) headers) 
                   (outline-up-heading 1))
               (error nil))))
    (message (mapconcat #'identity headers " > "))))

I'd actually suggest not putting it in the modeline, if for no other reason than the fact that it'll take over the whole thing. Instead, I'd suggest you bind the command to a key and call it whenever you want to know where you are.

(Note: if you really want to put it in the modeline, you'll want to remove the call to message in the last line.)

1
  • The header-line might also be a reasonable place to put this.
    – amitp
    Feb 20, 2017 at 18:08
0

What Dan said is true. The Modeline is already very populated and showing the outline path there overfloods it. Alternatively you could also use the header line:

(defvar-local org+-header-line-format nil
  "Cons with position of last synchronization of outline path
and the header line string.")

(defun org+-list-mapconcat (fun list separator)
  "Replace each non-nil cdr SUBLIST of LIST with `(,(FUN SEPARATOR) . SUBLIST)."
  (setq list (mapcar fun list))
  (let ((sublist list))
    (while (cdr sublist)
      (setcdr sublist (cons separator (cdr sublist)))
      (setq sublist (cddr sublist))))
  list)
;; Test: (assert (equal (org+-list-mapconcat #'1+ '(1 2 3) 0) '(2 0 3 0 4)) "Outch.")
;; (assert (equal (org+-list-mapconcat #'identity '(1) 0) '(1)) "Outch.")

(defun org+-get-outline-path (&optional maxdepth)
  "Return the outline path of the current entry.
MAXDEPTH defaults to 10."
  (unless (numberp maxdepth)
    (setq maxdepth 10))
  (save-excursion
    (let (ret)
      (while (and
          (numberp maxdepth)
          (>= maxdepth 1)
          (re-search-backward (format "^\\*\\{1,%d\\} " maxdepth) nil t))
    (let* ((components (org-heading-components))
           (level (car components))
           (txt (nth 4 components)))
      (setq maxdepth (1- level))
      (push (cons txt (point)) ret)))
      ret)))

(defun org+-header-line ()
  "Outline path in header line of Org."
  (let ((pos (car org+-header-line-format))
    (pt (point)))
    (when (or (null (number-or-marker-p pos))
          (save-excursion
        (goto-char (min pos pt))
        (re-search-forward "^\\*+ " (max pos pt) t)))
      (setq org+-header-line-format
        (cons (point)
          (append
           (list " ")
           (org+-list-mapconcat
            (lambda (txt-pos)
              (list :propertize
                (car txt-pos)
                'local-map
                (let ((map (make-sparse-keymap))
                  (pos (set-marker (make-marker) (cdr txt-pos))))
                  (define-key map [header-line mouse-1]
                (lambda ()
                  (interactive)
                  (switch-to-buffer (marker-buffer pos))
                  (goto-char pos)))
                  map))
              )
            (org+-get-outline-path)
            " ‖ "))))))
  (cdr org+-header-line-format))

(defvar-local org+-heder-old-header nil
  "Old value of `header-line-format'.")

(define-minor-mode org+-header-outline-path-mode
  "Show outline path in header line."
  :ligher nil
  (if org+-header-outline-path-mode
      (setq org+-header-old-header header-line-format
        header-line-format '((:eval (org+-header-line))))
    (setq header-line-format org+-header-old-header)))

(add-hook 'org-mode-hook #'org+-header-outline-path-mode)

Note that this will probably interfere with org-columns. But it is a viable option, if you do not use org-columns extensively.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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