2

In Org Mode, is it possible to dynamically number sub headings without dynamically numbering the parent headings? Example:

Dynamic numbering of sub headings

The Org Num minor mode allows you to exclude selected headings from numbering, but I want to achieve the inverse. To achieve this I tried setting org-num-skip-unnumbered to true, then setting the UNNUMBERED property of the parent heading to true and the UNNUMBERED property of the sub-heading to nil. This did not work.

1
  • My initial attempts to accomplish this failed: it seems that the mode is biased towards being able to eliminate the numbering of lower levels while keeping the numbering at higher levels. If nobody comes up with a solution here, you might want to post an RFE to the Org mode mailing list
    – NickD
    Oct 5, 2022 at 10:48

1 Answer 1

0

See also org export - Org-mode do not inheriting Unnumbered property - Emacs Stack Exchange, which is about numbering on the export side.

The code below has nothing to do with org-num-mode: org-num-mode fontifies; that is, it changes headline numbers as and when the headline level changes.

But what you have here is a set of command show-headline-numbers and hide-headline-numbers. If you change the headlines, the numbers won't refresh automatically, or fontification may be "off". In that case, turn off and turn on the numbering again.


  1. emacs -Q
  2. Copy the following snippet to *scratch* buffer, and doM-x eval-buffer. This will install a command show-headline-numbers, and bind that command to C-c C-c. (This key binding is only for quick testing of the snippet)
(require 'ox)

(defvar headline-number)

(put 'headline-number 'face 'bold)
(put 'headline-number 'headline-numbers t)

(define-key org-mode-map (kbd "C-c C-b") 'do-show-headline-numbers)

(defun current-element ()
  (let* ((context (org-with-wide-buffer
                   (org-element-context)))
         (whole-tree
          (org-with-wide-buffer
           (org-element-parse-buffer))))
    (org-element-map whole-tree (org-element-type context)
      (lambda (el)
        (when (and (= (org-element-property :begin el)
                      (org-element-property :begin context)))
          el))
      nil t)))

(defun do-show-headline-numbers (level &optional data)
  (cl-letf (((symbol-function 'org-export-numbered-headline-p)
             ;; Temporariy pretend that all headlines are numbered.
             (lambda (_headline _info)
               t)))
    (let* ((data (or data (org-with-wide-buffer
                           (org-element-parse-buffer))))
           (buf (current-buffer))
           (info
            (org-export--collect-tree-properties
             data
             ;; Ignore in buffer params; use these instead
             `(:headline-levels 10 :section-numbers t)))
           (headline-numbering (org-export--collect-headline-numbering (plist-get info :parse-tree) info)))
      (cl-loop for (h . ns) in headline-numbering
               for num-ns = (length ns)
               for raw-text = (org-element-property :raw-value h)
               for beg = (org-element-property :begin h)
               for displayed-ns = (mapcar #'number-to-string ns)
               for displayed-n = (mapconcat #'identity displayed-ns ".")
               for end = (with-current-buffer buf
                           (save-excursion
                             (goto-char beg)
                             (skip-chars-forward "*")
                             (point)))
               for ov = (with-current-buffer buf
                          (when-let ((ov (cl-some
                                          (lambda (ov)
                                            (when (eq (overlay-get ov 'category) 'headline-number)
                                              ov))
                                          (overlays-at beg))))
                            (message "WARNING: Overwriting existing overlay")
                            (delete-overlay ov))
                          (make-overlay beg end))
               when (<= num-ns level)
               do
               (overlay-put ov 'display displayed-n)
               (overlay-put ov 'category 'headline-number)))))

(defun get-choices (prompt choices)
  (assoc-default (completing-read prompt choices nil t) choices))

(defun show-headline-numbers ()
  (interactive)
  (unless (eq 'headline (org-element-type (org-element-context)))
    (error "Cursor not on a headline"))
  (let* ((showp (get-choices "Operation: " '(("show" . t)
                                             ("hide" . nil))))
         (scope (when showp
                  (get-choices "Scope: " '(("buffer" . buffer)
                                           ("children" . children)
                                           ("siblings (including self)" . siblings)))))
         (n (when showp
              (read-number (format "[Number %s] Upto Depth: " (capitalize (symbol-name scope)))))))
    (cond
     (showp
      (pcase scope
        (`buffer
         (do-show-headline-numbers n))
        (`children
         (let* ((h (current-element)))
           (do-show-headline-numbers n (org-element-contents h))))
        (`siblings
         (let* ((h (current-element))
                (parent-h (org-export-get-parent-headline h)))
           (do-show-headline-numbers n parent-h)))))
     ((not showp)
      (hide-headline-numbers)))))

(defun hide-headline-numbers ()
  (interactive)
  (org-with-wide-buffer
   (remove-overlays (point-min) (point-max) 'headline-numbers t)))

(define-key org-mode-map (kbd "C-c C-c") 'show-headline-numbers)

  1. Download, and visit org-manual.org.

    The buffer looks as below. Note the absence of headline numbers.

Default view of org-manual.org

Default view of org-manual.org 4. Do C-c C-c, answer the prompts as follows

  • Operation is show
  • Scope is buffer
  • Depth is 3

In other words, you are requesting that all headlines up to level 3 be numbered (using overlay)

This is what you will see

Number all headlines in buffer upto level 3

Number all headlines in buffer upto level 3

  1. Do C-c C-c, answer the prompts as follows
  • Operation is hide

    In other words, you are requesting that all headline numbers added in previous step be cleared.

You will see that all the headline numbers added in previous step are cleared.

  1. Navigate to any other headline, say Summary, and do C-c C-c, answer the prompts as follows
  • Operation is show
  • Scope is siblings (including self)
  • Depth is 1

In other words, you are requesting that all sibling headlines of Summary, and their children up to level 1 be numbered (using overlay)

This is what you will see

Number all siblings of Summary up to level 1

Number all siblings up to level 1

  1. Clear the headline numbers. Now navigate to any other headline, say Document Structure, and do C-c C-c, answer the prompts as follows
  • Operation is show
  • Scope is children
  • Depth is 2

In other words, you are requesting that all child headlines of Document Structure (up to a depth of 2) be numbered (using overlay)

Number all child headlines of Document Structure up to level 2 Number all child headlines up to level 2

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.