diff --git a/doc-fmt/run.bash b/doc-fmt/run.bash new file mode 100755 index 000000000000..0a9f29547f9f --- /dev/null +++ b/doc-fmt/run.bash @@ -0,0 +1,30 @@ +#!/bin/bash + +if ! [ -d "./.git" ] + then + echo "Should be executed from the repo root." + exit 1 +fi + +#rm #+HTML_HEAD_EXTRA: ... readtheorg.css" /> in the doc. +find ./doc -name "*.org" -type f -exec sed -i '/#+HTML_HEAD_EXTRA.*readtheorg.css.*/d' {} \; + +#rm #+HTML_HEAD_EXTRA: ... readtheorg.css" /> in the layers. +find ./layers -name "*.org" -type f -exec sed -i '/#+HTML_HEAD_EXTRA.*readtheorg.css.*/d' {} \; + + + +#replace :TOC_4_org: with :TOC_4_gh: in the doc. +find ./doc -name "*.org" -type f -exec sed -i 's/:TOC_4_org:/:TOC_4_gh:/' {} \; + +#replace :TOC_4_org: with :TOC_4_gh: in the layers. +find ./layers -name "*.org" -type f -exec sed -i 's/:TOC_4_org:/:TOC_4_gh:/' {} \; + + + +#apply toc-org to doc. +find ./doc -name "*.org" -type f -exec emacs -batch -l ./doc-fmt/toc-org-apply.el '{}' -f toc-apply \; + +#apply toc-org to layers. +find ./layers -name "*.org" -type f -exec emacs -batch -l ./doc-fmt/toc-org-apply.el '{}' -f toc-apply \; + diff --git a/doc-fmt/test.org b/doc-fmt/test.org new file mode 100644 index 000000000000..2d3a108eb5be --- /dev/null +++ b/doc-fmt/test.org @@ -0,0 +1,29 @@ +#+TITLE: FOO + +* Table of Contents :TOC_4_gh:noexport: + - [[#links][Links]] + - [[#foo][FOO]] + - [[#bar][BAR]] + - [[#baz][BAZ]] + +* Links + +[[https://github.com/syl20bnr/spacemacs/blob/master/doc/FAQ.org#os-x][Link to FAQ "OS X" heading]] + +[[https://www.google.com][Link to www.google.com]] + +[[https://github.com/syl20bnr/spacemacs/blob/master/doc/VIMUSERS.org#sessions]] + +[[https://github.com/syl20bnr/spacemacs/blob/master/layers/%2Bfun/emoji/README.org][Link to Emoji layer README.org]] + +* FOO + +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed conseq + +* BAR + +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed conseq + +* BAZ + +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed conseq diff --git a/doc-fmt/toc-org-apply.el b/doc-fmt/toc-org-apply.el new file mode 100644 index 000000000000..6e8e2036695f --- /dev/null +++ b/doc-fmt/toc-org-apply.el @@ -0,0 +1,4 @@ +(load-file "./doc-fmt/toc-org.el") +(defun toc-apply () + (toc-org-insert-toc) + (save-buffer 0)) diff --git a/doc-fmt/toc-org.el b/doc-fmt/toc-org.el new file mode 100644 index 000000000000..30bb306f5540 --- /dev/null +++ b/doc-fmt/toc-org.el @@ -0,0 +1,409 @@ +;;; toc-org.el --- add table of contents to org-mode files (formerly, org-toc) + +;; Copyright (C) 2014 Sergei Nosov + +;; Author: Sergei Nosov +;; Version: 1.0 +;; Keywords: org-mode org-toc toc-org org toc table of contents +;; URL: https://github.com/snosov1/toc-org + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2, or (at +;; your option) any later version. + +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; toc-org helps you to have an up-to-date table of contents in org files +;; without exporting (useful primarily for readme files on GitHub). + +;; NOTE: Previous name of the package is org-toc. It was changed because of a +;; name conflict with one of the org contrib modules. + +;; After installation put into your .emacs file something like + +;; (if (require 'toc-org nil t) +;; (add-hook 'org-mode-hook 'toc-org-enable) +;; (warn "toc-org not found")) + +;; And every time you'll be saving an org file, the first headline with a :TOC: +;; tag will be updated with the current table of contents. + +;; For details, see https://github.com/snosov1/toc-org + +;;; Code: + +(require 'ert) +(require 'org) + +(defgroup toc-org nil + "toc-org is a utility to have an up-to-date table of contents +in the org files without exporting (useful primarily for readme +files on GitHub)" + :group 'org) + +;; just in case, simple regexp "^*.*:toc:\\($\\|[^ ]*:$\\)" +(defconst toc-org-toc-org-regexp "^*.*:toc\\([@_][0-9]\\|\\([@_][0-9][@_][a-zA-Z]+\\)\\)?:\\($\\|[^ ]*:$\\)" + "Regexp to find the heading with the :toc: tag") +(defconst toc-org-tags-regexp "\s*:[[:word:]:@]*:\s*$" + "Regexp to find tags on the line") +(defconst toc-org-states-regexp "^*+\s+\\(TODO\s+\\|DONE\s+\\)" + "Regexp to find states on the line") +(defconst toc-org-links-regexp "\\[\\[\\(.*?\\)\\]\\[\\(.*?\\)\\]\\]" + "Regexp to find states on the line") +(defconst toc-org-special-chars-regexp "[^[:alnum:]_-]" + "Regexp with the special characters (which are omitted in hrefs + by GitHub)") + +(defcustom toc-org-max-depth 2 + "Maximum depth of the headings to use in the table of +contents. The default of 2 uses only the highest level headings +and their subheadings (one and two stars)." + :group 'toc-org) + +(defcustom toc-org-hrefify-default "gh" + "Default hrefify function to use." + :group 'toc-org) + +(defcustom toc-org-enable-links-opening t + "With this option, org-open-at-point (C-c C-o) should work on +the TOC links (even if the style is different from org)." + :group 'toc-org) + +(defvar-local toc-org-hrefify-hash nil + "Buffer local hash-table that is used to enable links +opening. The keys are hrefified headings, the values are original +headings.") + +(defun toc-org-raw-toc () + "Return the \"raw\" table of contents of the current file, +i.e. simply flush everything that's not a heading and strip +tags." + (let ((content (buffer-substring-no-properties + (point-min) (point-max)))) + (with-temp-buffer + (insert content) + (goto-char (point-min)) + (keep-lines "^\*+[ ]") + + ;; don't include the TOC itself + (goto-char (point-min)) + (re-search-forward toc-org-toc-org-regexp nil t) + (beginning-of-line) + (delete-region (point) (progn (forward-line 1) (point))) + + ;; strip states + (goto-char (point-min)) + (while (re-search-forward toc-org-states-regexp nil t) + (replace-match "" nil nil nil 1)) + + ;; strip tags + ;; TODO :export: and :noexport: tags semantic should be probably + ;; implemented + (goto-char (point-min)) + (while (re-search-forward toc-org-tags-regexp nil t) + (replace-match "" nil nil)) + + ;; flatten links + (goto-char (point-min)) + (while (re-search-forward toc-org-links-regexp nil t) + (replace-match "\\2" nil nil)) + + (buffer-substring-no-properties + (point-min) (point-max))))) + +(ert-deftest toc-org-test-raw-toc () + "Test the `toc-org-raw-toc' function" + + (defun toc-org-test-raw-toc-gold-test (content gold) + (should (equal + (with-temp-buffer + (insert content) + (toc-org-raw-toc)) + gold))) + (declare-function toc-org-test-raw-toc-gold-test "toc-org") ;; suppress compiler warning + + (let ((beg "* TODO [[http://somewhere.com][About]]\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n\n* Table of Contents ") + (gold "* About\n")) + + ;; different TOC styles + (toc-org-test-raw-toc-gold-test (concat beg ":TOC:" ) gold) + (toc-org-test-raw-toc-gold-test (concat beg ":TOC_1:" ) gold) + (toc-org-test-raw-toc-gold-test (concat beg ":TOC_1_qqq:" ) gold) + (toc-org-test-raw-toc-gold-test (concat beg ":TOC@1:" ) gold) + (toc-org-test-raw-toc-gold-test (concat beg ":TOC@1@cxv:" ) gold) + (toc-org-test-raw-toc-gold-test (concat beg ":TOC@1_hello:" ) gold) + + ;; trailing symbols + (toc-org-test-raw-toc-gold-test (concat beg ":TOC@1_hello:" "\n\n\n") gold) + (toc-org-test-raw-toc-gold-test (concat beg ":TOC@1_hello:" "\n\n\nsdfd") gold)) + + ;; more complex case + (toc-org-test-raw-toc-gold-test + "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n\n* Table of Contents :TOC:\n - [[#about][About]]\n - [[#use][Use]]\n - [[#different-href-styles][Different href styles]]\n - [[#example][Example]]\n\n* Installation\n** via package.el\nThis is the simplest method if you have the package.el module\n(built-in since Emacs 24.1) you can simply use =M-x package-install=\nand then put the following snippet in your ~/.emacs file\n#+BEGIN_SRC elisp\n (eval-after-load \"toc-org-autoloads\"\n '(progn\n (if (require 'toc-org nil t)\n (add-hook 'org-mode-hook 'toc-org-enable)\n (warn \"toc-org not found\"))))\n#+END_SRC\n** Manual :Hello:\n- Create folder ~/.emacs.d if you don't have it\n- Go to it and clone toc-org there\n #+BEGIN_SRC sh\n git clone https://github.com/snosov1/toc-org.git\n #+END_SRC\n- Put this in your ~/.emacs file\n #+BEGIN_SRC elisp\n (add-to-list 'load-path \"~/.emacs.d/toc-org\")\n (when (require 'toc-org nil t)\n (add-hook 'org-mode-hook 'toc-org-enable))\n #+END_SRC\n\n* Use\n\nAfter the installation, every time you'll be saving an org file, the\nfirst headline with a :TOC: tag will be updated with the current table\nof contents.\n\nTo add a TOC tag, you can use the command =org-set-tags-command=.\n\nIn addition to the simple :TOC: tag, you can also use the following\ntag formats:\n\n- :TOC@2: - sets the max depth of the headlines in the table of\n contents to 2 (the default)\n\n- :TOC@2@gh: - sets the max depth as in above and also uses the\n GitHub-style hrefs in the table of contents (the default). The other\n supported href style is 'org', which is the default org style (you\n can use C-c C-o to go to the headline at point).\n\nYou can also use =_= as separator, instead of =@=.\n\n* Different href styles\n\nCurrently, only 2 href styles are supported: =gh= and =org=. You can easily\ndefine your own styles. If you use the tag =:TOC@2@STYLE:= (=STYLE= being a\nstyle name), then the package will look for a function named\n=toc-org-hrefify-STYLE=, which accepts a heading string and returns a href\ncorresponding to that heading.\n\nE.g. for =org= style it simply returns input as is:\n\n#+BEGIN_SRC emacs-lisp\n (defun toc-org-hrefify-org (str)\n \"Given a heading, transform it into a href using the org-mode\n rules.\"\n str)\n#+END_SRC\n\n* Example\n\n#+BEGIN_SRC org\n * About\n * Table of Contents :TOC:\n - [[#about][About]]\n - [[#installation][Installation]]\n - [[#via-packageel][via package.el]]\n - [[#manual][Manual]]\n - [[#use][Use]]\n * Installation\n ** via package.el\n ** Manual\n * Use\n * Example\n#+END_SRC\n" + "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n")) + +(defun toc-org-hrefify-gh (str) + "Given a heading, transform it into a href using the GitHub +rules." + (let* ((spc-fix (replace-regexp-in-string " " "-" str)) + (upcase-fix (replace-regexp-in-string "[A-Z]" 'downcase spc-fix t)) + (special-chars-fix (replace-regexp-in-string toc-org-special-chars-regexp "" upcase-fix t))) + (concat "#" special-chars-fix))) + +(ert-deftest toc-org-test-hrefify-gh () + "Test the `toc-org-hrefify-gh' function" + (should (equal (toc-org-hrefify-gh "About") "#about")) + (should (equal (toc-org-hrefify-gh "!h@#$%^&*(){}|][:;\"'/?.>,<`~") "#h")) + (should (equal (toc-org-hrefify-gh "!h@#$% ^&*(S){}|][:;\"'/?.>,<`~") "#h-s"))) + +(defun toc-org-hrefify-org (str) + "Given a heading, transform it into a href using the org-mode +rules." + str) + +(defun toc-org-unhrefify (type path) + "Looks for a value in toc-org-hrefify-hash using path as a key." + (let ((ret-type type) + (ret-path path) + (original-path (and (not (eq toc-org-hrefify-hash nil)) + (gethash + (concat + ;; Org 8.3 and above provides type as "custom-id" + ;; and strips the leading hash symbol + (if (equal type "custom-id") "#" "") + (substring-no-properties path)) + toc-org-hrefify-hash + nil)))) + (when toc-org-enable-links-opening + (when original-path + ;; Org 8.2 and below provides type as "thisfile" + (when (equal type "thisfile") + (setq ret-path original-path)) + (when (equal type "custom-id") + (setq ret-type "fuzzy") + (setq ret-path original-path)))) + (cons ret-type ret-path))) + +(defun toc-org-hrefify-toc (toc hrefify &optional hash) + "Format the raw `toc' using the `hrefify' function to transform +each heading into a link." + (with-temp-buffer + (insert toc) + (goto-char (point-min)) + + (while + (progn + (when (looking-at "\\*") + (delete-char 1) + + (while (looking-at "\\*") + (delete-char 1) + (insert (make-string + (+ 2 (or (bound-and-true-p org-list-indent-offset) 0)) + ?\s))) + + (skip-chars-forward " ") + (insert "- ") + + (let* ((beg (point)) + (end (line-end-position)) + (heading (buffer-substring-no-properties + beg end)) + (hrefified (funcall hrefify heading))) + (insert "[[") + (insert hrefified) + (insert "][") + (end-of-line) + (insert "]]") + + ;; maintain the hash table, if provided + (when hash + (puthash hrefified heading hash))) + (= 0 (forward-line 1))))) + + (buffer-substring-no-properties + (point-min) (point-max)))) + +(ert-deftest toc-org-test-hrefify-toc () + (let ((hash (make-hash-table :test 'equal))) + (should (equal (toc-org-hrefify-toc "* About\n" 'upcase hash) + " - [[ABOUT][About]]\n")) + (should (equal (gethash "ABOUT" hash) "About"))) + (let ((hash (make-hash-table :test 'equal))) + (should (equal (toc-org-hrefify-toc "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n" 'upcase hash) + " - [[ABOUT][About]]\n - [[INSTALLATION][Installation]]\n - [[VIA PACKAGE.EL][via package.el]]\n - [[MANUAL][Manual]]\n - [[USE][Use]]\n - [[DIFFERENT HREF STYLES][Different href styles]]\n - [[EXAMPLE][Example]]\n")) + (should (equal (gethash "ABOUT" hash) "About")) + (should (equal (gethash "INSTALLATION" hash) "Installation")) + (should (equal (gethash "VIA PACKAGE.EL" hash) "via package.el")) + (should (equal (gethash "MANUAL" hash) "Manual")) + (should (equal (gethash "USE" hash) "Use")) + (should (equal (gethash "DIFFERENT HREF STYLES" hash) "Different href styles")) + (should (equal (gethash "EXAMPLE" hash) "Example")))) + +(defun toc-org-flush-subheadings (toc max-depth) + "Flush subheadings of the raw `toc' deeper than `max-depth'." + (with-temp-buffer + (insert toc) + (goto-char (point-min)) + + (let ((re "^")) + (dotimes (i (1+ max-depth)) + (setq re (concat re "\\*"))) + (flush-lines re)) + + (buffer-substring-no-properties + (point-min) (point-max)))) + +(ert-deftest toc-org-test-flush-subheadings () + (should (equal (toc-org-flush-subheadings "* About\n" 0) + "")) + (should (equal (toc-org-flush-subheadings "* About\n" 1) + "* About\n")) + (should (equal (toc-org-flush-subheadings "* About\n" 2) + "* About\n")) + + (should (equal (toc-org-flush-subheadings "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n" 0) + "")) + (should (equal (toc-org-flush-subheadings "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n" 1) + "* About\n* Installation\n* Use\n* Different href styles\n* Example\n")) + (should (equal (toc-org-flush-subheadings "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n" 2) + "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n")) + (should (equal (toc-org-flush-subheadings "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n" 3) + "* About\n* Installation\n** via package.el\n** Manual\n* Use\n* Different href styles\n* Example\n"))) + +(defun toc-org-insert-toc (&optional dry-run) + "Looks for a headline with the TOC tag and updates it with the +current table of contents. + +If optional second argument DRY-RUN is provided, then the buffer +is not modified at all. Only the internal hash-table is updated +to enable `org-open-at-point' for TOC links. + +To add a TOC tag, you can use the command +`org-set-tags-command' (C-c C-q). + +In addition to the simple :TOC: tag, you can also use the +following tag formats: + +- :TOC_2: - sets the max depth of the headlines in the table of + contents to 2 (the default) + +- :TOC_2_gh: - sets the max depth as in above and also uses the + GitHub-style hrefs in the table of contents (this style is + default). The other supported href style is 'org', which is the + default org style." + + (interactive) + (when (eq major-mode 'org-mode) + (save-excursion + (goto-char (point-min)) + (let ((case-fold-search t)) + ;; find the first heading with the :TOC: tag + (when (re-search-forward toc-org-toc-org-regexp (point-max) t) + (let* ((tag (match-string 1)) + (depth (if tag + (- (aref tag 1) ?0) ;; is there a better way to convert char to number? + toc-org-max-depth)) + (hrefify-tag (if (and tag (>= (length tag) 4)) + (downcase (substring tag 3)) + toc-org-hrefify-default)) + (hrefify-string (concat "toc-org-hrefify-" hrefify-tag)) + (hrefify (intern-soft hrefify-string))) + (if hrefify + (let ((new-toc + (toc-org-hrefify-toc + (toc-org-flush-subheadings (toc-org-raw-toc) depth) + hrefify + (when toc-org-hrefify-hash + (clrhash toc-org-hrefify-hash))))) + (unless dry-run + (newline (forward-line 1)) + + ;; insert newline if TOC is currently empty + (when (looking-at "^\\*") + (open-line 1)) + + ;; find TOC boundaries + (let ((beg (point)) + (end + (save-excursion + (when (search-forward-regexp "^\\*" (point-max) t) + (forward-line -1)) + (end-of-line) + (point)))) + ;; update the TOC, but only if it's actually different + ;; from the current one + (unless (equal + (buffer-substring-no-properties beg end) + new-toc) + (delete-region beg end) + (insert new-toc))))) + (message (concat "Hrefify function " hrefify-string " is not found"))))))))) + +;;;###autoload +(defun toc-org-enable () + "Enable toc-org in this buffer." + (add-hook 'before-save-hook 'toc-org-insert-toc nil t) + + ;; conservatively set org-link-translation-function + (when (and (equal toc-org-enable-links-opening t) + (or + (not (fboundp org-link-translation-function)) + (equal org-link-translation-function 'toc-org-unhrefify))) + (setq toc-org-hrefify-hash (make-hash-table :test 'equal)) + (setq org-link-translation-function 'toc-org-unhrefify) + (toc-org-insert-toc t))) + +(ert-deftest toc-org-test-insert-toc () + "Test the `toc-org-insert-toc' function" + + (defun toc-org-test-insert-toc-gold-test (content gold) + (with-temp-buffer + (org-mode) + (insert content) + (toc-org-raw-toc) + (toc-org-insert-toc) + (should (equal + (buffer-substring-no-properties + (point-min) (point-max)) + gold)))) + (declare-function toc-org-test-insert-toc-gold-test "toc-org") ;; suppress compiler warning + + (let ((beg "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n* Hello\n** Good-bye\n*** Salut\n* Table of Contents ")) + (toc-org-test-insert-toc-gold-test + (concat beg ":TOC:") + "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n* Hello\n** Good-bye\n*** Salut\n* Table of Contents :TOC:\n - [[#about][About]]\n - [[#hello][Hello]]\n - [[#good-bye][Good-bye]]\n") + + (toc-org-test-insert-toc-gold-test + (concat beg ":TOC_1:") + "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n* Hello\n** Good-bye\n*** Salut\n* Table of Contents :TOC_1:\n - [[#about][About]]\n - [[#hello][Hello]]\n") + + (toc-org-test-insert-toc-gold-test + (concat beg ":TOC_3:") + "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n* Hello\n** Good-bye\n*** Salut\n* Table of Contents :TOC_3:\n - [[#about][About]]\n - [[#hello][Hello]]\n - [[#good-bye][Good-bye]]\n - [[#salut][Salut]]\n") + + (toc-org-test-insert-toc-gold-test + (concat beg ":TOC_1_org:") + "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n* Hello\n** Good-bye\n*** Salut\n* Table of Contents :TOC_1_org:\n - [[About][About]]\n - [[Hello][Hello]]\n") + + (toc-org-test-insert-toc-gold-test + (concat beg ":TOC_3_org:") + "* About\n:TOC:\n drawer\n:END:\n\ntoc-org is a utility to have an up-to-date table of contents in the\norg files without exporting (useful primarily for readme files on\nGitHub).\n\nIt is similar to the [[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works for org files.\n:TOC:\n drawer\n:END:\n* Hello\n** Good-bye\n*** Salut\n* Table of Contents :TOC_3_org:\n - [[About][About]]\n - [[Hello][Hello]]\n - [[Good-bye][Good-bye]]\n - [[Salut][Salut]]\n"))) + +;; Local Variables: +;; compile-command: "emacs -batch -l ert -l *.el -f ert-run-tests-batch-and-exit && emacs -batch -f batch-byte-compile *.el 2>&1 | sed -n '/Warning\|Error/p' | xargs -r ls" +;; End: + +(provide 'toc-org) +;;; toc-org.el ends here