Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

auto-updated meaningful anchors and TOC #26

Open
VladimirAlexiev opened this issue Mar 28, 2024 · 1 comment
Open

auto-updated meaningful anchors and TOC #26

VladimirAlexiev opened this issue Mar 28, 2024 · 1 comment

Comments

@VladimirAlexiev
Copy link
Collaborator

It's very important in HTML (#25) to have meaningful anchors generated from heading titles (like github does).
Here is some code to automatically add and update such anchors, if the file has #+options: anchor:t

;; Meaningful # anchors for headings

(defun va/org-add-headline-anchor (pom overwrite)
  "Add a meaningful anchor to one headline (entry at point-or-marker POM).
If overwrite is not null and not 'maybe then overwrite existing CUSTOM_IDs."
  (org-with-point-at pom
    (let ((x (cadr (org-element-headline-parser (buffer-end 1)))))
      (if (or (and overwrite (not (eq overwrite 'maybe)))
              (not (plist-get x :CUSTOM_ID)))
          (let ((title (plist-get x :raw-value)))
            (setq title (replace-regexp-in-string "\\W+" "-" (downcase title)))
            (setq title (replace-regexp-in-string "^-" "" title))
            (setq title (replace-regexp-in-string "-$" "" title))
            (org-entry-put pom "CUSTOM_ID" title)
            (org-id-add-location title (buffer-file-name (buffer-base-buffer))))))))

(defun va/org-add-headline-anchors-maybe ()
  (require 'ox)
  "Read in-buffer #+OPTIONS: anchor:* and if set, add anchors to all headlines."
  (let ((overwrite (plist-get (org-export--get-inbuffer-options) :auto-anchor)))
    (when overwrite
      (org-map-entries (lambda () (va/org-add-headline-anchor (point) overwrite))))))

(defun va/org-before-save-hook-add-headline-anchors ()
  "Hook to add meaningful anchors upon save."
  (if (and (eq major-mode 'org-mode)
           (eq buffer-read-only nil))
      (va/org-add-headline-anchors-maybe)))

(defun va/org-add-headline-anchors ()
  "Add meaningful anchors to all headlines, overwrite existing CUSTOM_ID, add #+OPTION: anchor:t."
  (interactive)
  (when (eq major-mode 'org-mode)
    (require 'ox)
    (save-excursion
      (beginning-of-buffer)
      (unless (re-search-forward "^#\\+options:" nil 'noerror)
        (beginning-of-buffer)
        (org-export-insert-default-template 'default))
      (unless (plist-get (org-export--get-inbuffer-options) :auto-anchor)
        ;; force anchor:t
        (beginning-of-buffer)
        (re-search-forward "^\\(#\\+options:.*anchor\\):\\(\\w+\\)")
        (replace-match "\\1:t" 'fixedcase))
      (va/org-add-headline-anchors-maybe))))

;;(remove-hook 'before-save-hook 'va/org-before-save-hook-add-ids-to-headlines)
(add-hook 'before-save-hook 'va/org-before-save-hook-add-headline-anchors)

I bind it like this:

(add-hook 'org-mode-hook
  (defun va/org-mode-hook ()
    (local-set-key (kbd "C-c # s") 'org-update-statistics-cookies)
    (local-set-key (kbd "C-c # a") 'va/org-add-headline-anchors)
    (local-set-key (kbd "C-c # t") 'va/org-toc-insert)))

I use a module to make TOC on top, and wrote some functions to control its update automatically

;;;;;;;;;;;;;;;;;;;; Generate TOC on top, useful for github preview
;; https://github.com/alphapapa/org-make-toc

;; https://github.com/alphapapa/org-make-toc/issues/14
(require 'org-make-toc)
(remove-hook 'org-mode-hook 'org-make-toc-mode)
(remove-hook 'before-save-hook 'org-make-toc)
(add-hook 'before-save-hook 'va/org-make-toc)

(defun va/org-make-toc ()
  "Make or update table of contents in current buffer."
  (interactive)
  (when (eq major-mode 'org-mode)
    (save-excursion
      (goto-char (point-min))
      (cl-loop for pos = (org-make-toc--next-toc-position)
               while pos
               do (progn
                    (goto-char pos)
                    (org-make-toc--update-toc-at-point))))))

(defun va/org-toc-insert ()
  "Make TOC at current position. org-make-toc-insert asks too many questions"
  (interactive)
  (unless (org-find-exact-headline-in-buffer "Table of Contents")
    (org-first-headline-recenter)
    (beginning-of-line)
    (insert "* Table of Contents                                 :TOC:noexport:
:PROPERTIES:
:TOC:      :include all
:END:

:CONTENTS:

:END:

")))
@VladimirAlexiev VladimirAlexiev changed the title meaningful anchors auto-updated meaningful anchors and TOC Mar 28, 2024
@johanwk
Copy link
Owner

johanwk commented Apr 4, 2024

I agree that this is a crucial issue, especially as HTML will be the primary target format for document exports.

As an initial question: what do you think about the naming your functions, with the va/ prefix? It is of course possible to include them in the ELOT elisp file, but maybe it would be preferable to rename them with an elot- prefix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants