6

Q: is there a way to indicate the depth of an org header by number?

I often use deep nests of org headers, e.g.:

* header 1
** more
*** still more
**** yet more
***** still going
****** don't judge me
** another subheader
*** here we go again
**** sweet cuppin' cakes
***** puppies

It's easy to lose track of the depth of the header after third or fourth level. Is there a way to get a visual indicator of the depth (maybe with an overlay?) somewhere on the header? For example, an overlay over the first * to indicate the numerical level, as with the following hypothetical:

1 header 1
2* more
3** still more
4*** yet more
5**** still going
6***** don't judge me
2* another subheader
3** here we go again
4*** sweet cuppin' cakes
5**** puppies
8

I would suggest taking a look at the org-bullets package for reference on precisely how to do this. The essential component is the minor mode definition. The level can be judged from the length of the org-outline-regexp-bol match ((- (match-end 0) (match-beginning 0) 1)). I prefer to use the 'display property rather than the composition mechanism.

I might also suggest using circled unicode numbers to make your outline really "pop" ;-)

① header 1
 ② more
  ③ still more
...

I've included my function to look up those characters.

(require 'org-bullets)
(require 'nxml-mode)

(define-minor-mode org-my-bullets-level-mode
  "UTF-8 bullets for `org-mode' indicating declarative sort of headings."
  nil nil nil
  (let* ((keyword
          `((,org-outline-regexp-bol
             (0 (progn
                  ;; Set the bullet display character
                  (put-text-property (- (match-end 0) 2)
                                     (- (match-end 0) 1)
                                     'display
                                     (circled-number-unicode-char (- (match-end 0) (match-beginning 0) 1)))
                  (when (facep org-bullets-face-name)
                    ;; Set bullet face
                    (put-text-property (- (match-end 0) 2)
                                       (- (match-end 0) 1)
                                       'face
                                       org-bullets-face-name))
                  ;; "Hide" leading '*'s
                  (put-text-property (match-beginning 0)
                                     (- (match-end 0) 2)
                                     'face (list :foreground
                                                 (face-attribute
                                                  'default :background)))
                  ;; Attach the keymap to the entire segment
                  (put-text-property (match-beginning 0)
                                     (match-end 0)
                                     'keymap
                                     org-bullets-bullet-map)
                  nil))))))
    (if org-my-bullets-level-mode
        (progn (font-lock-add-keywords nil keyword)
               (font-lock-fontify-buffer))
      (save-excursion
        (goto-char (point-min))
        (font-lock-remove-keywords nil keyword)))))

(defun circled-number-unicode-char (num)
  "Return the corresponding circled digit unicode character for NUM.  Number range: -20 to 50."
  (let ((charcode (cond
                   ((and (>= num -20)
                         (<= num -11))
                    (+ (abs num) 9440))
                   ((and (>= num -10)
                         (<= num -1))
                    (+ (abs num) 10101))
                   ((= num 0)
                    9450)
                   ((and (>= num 1)
                         (<= num 20))
                    (+ 9311 num))
                   ((and (>= num 21)
                         (<= num 35))
                    (+ 12860 num))
                   ((and (>= num 36)
                         (<= num 50))
                    (+ 12941 num)))))
    (char-to-string (xmltok-unicode-to-char charcode))))

It looks like this on my system:

Example screenshot

  • Hmm, when I turn on your minor mode, it depropertizes everything, and does not show the bullets as you defined them. – Dan Sep 8 '17 at 16:32
  • @Dan just a guess: xmltok-unicode-to-char is undefined and the lack of error handling might be breaking your fontification. Try (require 'nxml) and reloading. If that's not it do you get any messages? – ebpa Sep 8 '17 at 16:40
  • Yup, it was (require 'nxml-mode) that was missing. I've added it to your code. Thank you! – Dan Sep 8 '17 at 17:10

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.