Function: markdown-ts--build-heading-ids

markdown-ts--build-heading-ids is a byte-compiled function defined in markdown-ts-mode.el.gz.

Signature

(markdown-ts--build-heading-ids)

Documentation

Walk all headings in the buffer and return a hash table.

Which maps each slug to the buffer position of its heading. Each heading contributes its GitHub slug, its Pandoc slug, and its explicit
{#id} if present. Both slug algorithms are stored in the same buffer
because a markdown file is often previewed by several renderers (GitHub web UI, Pandoc, mdBook, Hugo, and so on) and the same source should resolve regardless of which one the author wrote the link for. For most headings the two algorithms produce the same slug anyway. Duplicate slugs (within one algorithm) are disambiguated by appending
-1, -2, and so on, in document order, matching GitHub's behavior.
When two distinct headings would otherwise share a slug, the first occurrence in document order wins.

Source Code

;; Defined in /usr/src/emacs/lisp/textmodes/markdown-ts-mode.el.gz
(defun markdown-ts--build-heading-ids ()
  "Walk all headings in the buffer and return a hash table.
Which maps each slug to the buffer position of its heading.  Each
heading contributes its GitHub slug, its Pandoc slug, and its explicit
`{#id}' if present.  Both slug algorithms are stored in the same buffer
because a markdown file is often previewed by several renderers (GitHub
web UI, Pandoc, mdBook, Hugo, and so on) and the same source should
resolve regardless of which one the author wrote the link for.  For most
headings the two algorithms produce the same slug anyway.
Duplicate slugs (within one algorithm) are disambiguated by appending
`-1', `-2', and so on, in document order, matching GitHub's behavior.
When two distinct headings would otherwise share a slug, the first
occurrence in document order wins."
  (let ((table (make-hash-table :test #'equal))
        ;; Per-algorithm dedupe counters: how many times this base slug
        ;; has been seen so far in document order.
        (gh-counts (make-hash-table :test #'equal))
        (pd-counts (make-hash-table :test #'equal))
        (root (treesit-buffer-root-node 'markdown)))
    (dolist (cap (treesit-query-capture
                  root
                  '(((atx_heading) @h)
                    ((setext_heading) @h))))
      (let* ((node (cdr cap))
             (pos (treesit-node-start node))
             (raw (string-trim (treesit-node-text node t)))
             ;; Strip the leading marker (### or ====/----).
             ;; For atx, drop leading #'s and following space.
             ;; For setext, drop the trailing underline line.
             (text (cond
                    ((string-match "\\`#+[ \t]*\\(.*?\\)[ \t]*#*[ \t]*\\'" raw)
                     (match-string 1 raw))
                    ((string-match "\\`\\(.*?\\)\n[=-]+[ \t]*\\'" raw)
                     (match-string 1 raw))
                    (t raw)))
             (split (markdown-ts--heading-text-and-id text))
             (visible (car split))
             (explicit (cdr split)))
        ;; Explicit {#id} wins outright; first occurrence keeps it.
        (when (and explicit (not (gethash explicit table)))
          (puthash explicit pos table))
        ;; Auto slugs (GitHub + Pandoc), deduped per algorithm.
        (let* ((gh-base (markdown-ts--slug-github visible))
               (pd-base (markdown-ts--slug-pandoc visible))
               (gh-n (gethash gh-base gh-counts 0))
               (pd-n (gethash pd-base pd-counts 0))
               (gh-id (if (zerop gh-n) gh-base
                        (format "%s-%d" gh-base gh-n)))
               (pd-id (if (zerop pd-n) pd-base
                        (format "%s-%d" pd-base pd-n))))
          (puthash gh-base (1+ gh-n) gh-counts)
          (puthash pd-base (1+ pd-n) pd-counts)
          ;; First-writer-wins so document order tiebreaks collisions.
          (unless (gethash gh-id table) (puthash gh-id pos table))
          (unless (gethash pd-id table) (puthash pd-id pos table)))))
    table))