branch: elpa/toc-org
commit acca680bc5b148214d71c42517aa500292e6e80c
Author: Sergei Nosov <[email protected]>
Commit: Sergei Nosov <[email protected]>
Add somewhat working TOC generation for markdown files
---
toc-org-test.el | 6 +-
toc-org.el | 175 ++++++++++++++++++++++++++++++++------------------------
2 files changed, 105 insertions(+), 76 deletions(-)
diff --git a/toc-org-test.el b/toc-org-test.el
index a3b8d12e09..edbf1b4d71 100644
--- a/toc-org-test.el
+++ b/toc-org-test.el
@@ -8,7 +8,7 @@
(should (equal
(with-temp-buffer
(insert content)
- (toc-org-raw-toc))
+ (toc-org-raw-toc nil))
gold)))
(declare-function test-toc-org-raw-toc-gold-test "toc-org") ;; suppress
compiler warning
@@ -144,6 +144,7 @@
(let ((hash (make-hash-table :test 'equal)))
(should (equal (toc-org-hrefify-toc "* About\n"
(lambda (str &optional hash) (upcase
str))
+ nil
hash)
"- [[ABOUT][About]]\n"))
(should (equal (gethash "ABOUT" hash) "About")))
@@ -151,11 +152,12 @@
(let ((hash (make-hash-table :test 'equal)))
(should (equal (toc-org-hrefify-toc "* About \n"
(lambda (str &optional hash) (upcase
str))
+ nil
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" (lambda
(str &optional hash) (upcase str)) hash)
+ (should (equal (toc-org-hrefify-toc "* About\n* Installation\n** via
package.el\n** Manual\n* Use\n* Different href styles\n* Example\n" (lambda
(str &optional hash) (upcase str)) nil 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"))
diff --git a/toc-org.el b/toc-org.el
index 4c52f234b0..03d69e209b 100644
--- a/toc-org.el
+++ b/toc-org.el
@@ -52,9 +52,9 @@ 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-quote-tag-regexp "^*.*:quote:\\($\\|[^ ]*?:$\\)"
+(defconst toc-org-toc-org-regexp ".*?\\(<--
\\)?:toc\\([@_][0-9]\\|\\([@_][0-9][@_][a-zA-Z]+\\)\\)?:\\(\\( -->\\)?$\\|[^
]*?:\\( -->\\)?$\\)"
+ "Regexp to find the heading with the :toc: tag. It misses the heading symbol
which must be added depending on the markup style (org vs markdown).")
+(defconst toc-org-quote-tag-regexp ":quote:\\(\\( -->\\)?$\\|[^ ]*?:\\(
-->\\)?$\\)"
"Regexp to find the heading with the :quote: tag")
(defconst toc-org-noexport-regexp
"\\(^*+\\)\s+.*:noexport\\([@_][0-9]\\)?:\\($\\|[^ ]*?:$\\)"
"Regexp to find the extended version of :noexport: tag")
@@ -104,7 +104,7 @@ the TOC links (even if the style is different from org)."
opening. The keys are hrefified headings, the values are original
headings.")
-(defun toc-org-raw-toc ()
+(defun toc-org-raw-toc (markdown-syntax-p)
"Return the \"raw\" table of contents of the current file,
i.e. simply flush everything that's not a heading and strip
auxiliary text."
@@ -116,6 +116,19 @@ auxiliary text."
(with-temp-buffer
(insert content)
+ ;; preprocess markdown-style headings
+ (when markdown-syntax-p
+ (save-excursion
+ (let ((case-fold-search t))
+ (goto-char (point-min))
+ (while (re-search-forward "^#+ " nil t)
+ (replace-match (concat
+ (make-string (1- (length (match-string 0))) ?*)
+ " ") nil nil))
+ (goto-char (point-min))
+ (while (re-search-forward "\\(^*.*\\)<-- \\(:toc[^ ]*:\\)
-->\\($\\)" nil t)
+ (replace-match (concat (match-string 1) (match-string 2)
(match-string 3)) nil nil)))))
+
;; set leave-states-p variable
(goto-char (point-min))
(when (re-search-forward toc-org-leave-todo-regexp nil t)
@@ -139,7 +152,7 @@ auxiliary text."
;; don't include the TOC itself
(goto-char (point-min))
- (re-search-forward toc-org-toc-org-regexp nil t)
+ (re-search-forward (concat "^\\*" toc-org-toc-org-regexp) nil t)
(beginning-of-line)
(delete-region (point) (progn (forward-line 1) (point)))
@@ -251,13 +264,12 @@ rules."
(setq ret-path original-path))))
(cons ret-type ret-path)))
-(defun toc-org-hrefify-toc (toc hrefify &optional hash)
+(defun toc-org-hrefify-toc (toc hrefify markdown-syntax-p &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 "\\*")
@@ -280,15 +292,28 @@ each heading into a link."
(heading (buffer-substring-no-properties
beg end))
(hrefified (funcall hrefify heading hash)))
- (insert "[[")
- (insert hrefified)
- (insert "][")
- (insert
- (toc-org-format-visible-link
- (buffer-substring-no-properties
- (point) (line-end-position))))
- (delete-region (point) (line-end-position))
- (insert "]]")
+
+ (if markdown-syntax-p
+ (progn
+ (insert "[")
+ (insert
+ (toc-org-format-visible-link
+ (buffer-substring-no-properties
+ (point) (line-end-position))))
+ (delete-region (point) (line-end-position))
+ (insert "]")
+ (insert "(")
+ (insert hrefified)
+ (insert ")"))
+ (insert "[[")
+ (insert hrefified)
+ (insert "][")
+ (insert
+ (toc-org-format-visible-link
+ (buffer-substring-no-properties
+ (point) (line-end-position))))
+ (delete-region (point) (line-end-position))
+ (insert "]]"))
;; maintain the hash table, if provided
(when hash
@@ -342,64 +367,66 @@ Note that :noexport: is also used by Org-mode's exporter,
but
not :noexport_#:."
(interactive)
- (when (derived-mode-p 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))
- (put-quote (save-match-data (string-match
toc-org-quote-tag-regexp (match-string 0))))
- (toc-prefix (if put-quote "#+BEGIN_QUOTE\n" ""))
- (toc-suffix (if put-quote "#+END_QUOTE\n" "")))
- (if hrefify
- (let ((new-toc
- (concat toc-prefix
- (toc-org-hrefify-toc
- (toc-org-flush-subheadings (toc-org-raw-toc)
depth)
- hrefify
- (when toc-org-hrefify-hash
- (clrhash toc-org-hrefify-hash)))
- toc-suffix)))
- (unless dry-run
- (newline (forward-line 1))
-
- ;; skip drawers
- (let ((end
- (save-excursion ;; limit to next heading
- (search-forward-regexp "^\\*" (point-max)
'skip))))
- (while (re-search-forward toc-org-drawer-regexp end t)
- (skip-chars-forward "[:space:]")))
- (beginning-of-line)
-
- ;; 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)
'skip)
- (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")))))))))
+ (save-excursion
+ (goto-char (point-min))
+ (let* ((case-fold-search t)
+ (markdown-syntax-p (derived-mode-p 'markdown-mode))
+ (heading-symbol-regexp (if markdown-syntax-p "^#" "^\\*")))
+ ;; find the first heading with the :TOC: tag
+ (when (re-search-forward (concat heading-symbol-regexp
toc-org-toc-org-regexp) (point-max) t)
+ (let* ((tag (match-string 2))
+ (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))
+ (put-quote (save-match-data (string-match
toc-org-quote-tag-regexp (match-string 0))))
+ (toc-prefix (if put-quote (if markdown-syntax-p "```\n"
"#+BEGIN_QUOTE\n") ""))
+ (toc-suffix (if put-quote (if markdown-syntax-p "```\n"
"#+END_QUOTE\n") "")))
+ (if hrefify
+ (let ((new-toc
+ (concat toc-prefix
+ (toc-org-hrefify-toc
+ (toc-org-flush-subheadings (toc-org-raw-toc
markdown-syntax-p) depth)
+ hrefify
+ markdown-syntax-p
+ (when toc-org-hrefify-hash
+ (clrhash toc-org-hrefify-hash)))
+ toc-suffix)))
+ (unless dry-run
+ (newline (forward-line 1))
+
+ ;; skip drawers
+ (let ((end
+ (save-excursion ;; limit to next heading
+ (search-forward-regexp heading-symbol-regexp
(point-max) 'skip))))
+ (while (re-search-forward toc-org-drawer-regexp end t)
+ (skip-chars-forward "[:space:]")))
+ (beginning-of-line)
+
+ ;; insert newline if TOC is currently empty
+ (when (looking-at heading-symbol-regexp)
+ (open-line 1))
+
+ ;; find TOC boundaries
+ (let ((beg (point))
+ (end
+ (save-excursion
+ (when (search-forward-regexp heading-symbol-regexp
(point-max) 'skip)
+ (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 ()