emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/toc-org 3315ef6c96 106/128: Merge pull request #66 from sn


From: ELPA Syncer
Subject: [nongnu] elpa/toc-org 3315ef6c96 106/128: Merge pull request #66 from snosov1/enable-toc-org-for-markdown
Date: Sun, 2 Jan 2022 09:59:14 -0500 (EST)

branch: elpa/toc-org
commit 3315ef6c96a380882f2b32fa0db64b4491b7dcf1
Merge: 2539b4be40 d52f95fae8
Author: Sergei Nosov <sergei.nosov@gmail.com>
Commit: GitHub <noreply@github.com>

    Merge pull request #66 from snosov1/enable-toc-org-for-markdown
    
    Enable toc org for markdown
---
 .travis.yml     |   1 +
 README.org      |  41 ++++++++++-
 toc-org-test.el |  44 +++++++++---
 toc-org.el      | 217 ++++++++++++++++++++++++++++++++++++--------------------
 4 files changed, 217 insertions(+), 86 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index f157a2504b..3c4814a77e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,5 +8,6 @@ before_install:
   - emacs --version
   - emacs-25.3 --version
 script:
+  - emacs -batch -f batch-byte-compile toc-org.el 2>&1
   - emacs      -batch -l ert -l toc-org.el -l toc-org-test.el -f 
ert-run-tests-batch-and-exit && emacs -batch -f batch-byte-compile toc-org.el 
2>&1 | sed -n '/Warning\|Error/p' | xargs -r ls
   - emacs-25.3 -batch -l ert -l toc-org.el -l toc-org-test.el -f 
ert-run-tests-batch-and-exit && emacs -batch -f batch-byte-compile toc-org.el 
2>&1 | sed -n '/Warning\|Error/p' | xargs -r ls
diff --git a/README.org b/README.org
index fb302c305d..3044c5460b 100644
--- a/README.org
+++ b/README.org
@@ -2,10 +2,11 @@
 
 * About
 
-toc-org helps you to have an up-to-date table of contents in org files without
+=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).
 
-It is similar to the 
[[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works 
for org files.
+It is similar to the 
[[https://github.com/ardumont/markdown-toc][markdown-toc]] package, but works 
for org files. Since
+recently, =toc-org=, actually, works in [[Markdown support][markdown]], too!
 
 *NOTE:* Previous name of the package is =org-toc=. It was changed because of a
 name conflict with one of the org contrib modules.
@@ -21,6 +22,7 @@ name conflict with one of the org contrib modules.
   - [[#exclude-headings][Exclude headings]]
   - [[#quote-table-of-contents][Quote table of contents]]
   - [[#shortcut-for-toc-tag][Shortcut for TOC tag]]
+- [[#markdown-support][Markdown support]]
 - [[#different-href-styles][Different href styles]]
 - [[#example][Example]]
 #+END_QUOTE
@@ -37,6 +39,10 @@ repository and then put the following snippet in your 
~/.emacs file
 #+BEGIN_SRC elisp
   (if (require 'toc-org nil t)
       (add-hook 'org-mode-hook 'toc-org-mode)
+
+      ;; enable in markdown, too
+      (add-hook 'markdown-mode-hook 'toc-org-mode)
+      (define-key markdown-mode-map (kbd "\C-c\C-o") 
'toc-org-markdown-follow-thing-at-point))
     (warn "toc-org not found"))
 #+END_SRC
 
@@ -52,6 +58,10 @@ repository and then put the following snippet in your 
~/.emacs file
     (add-to-list 'load-path "~/.emacs.d/toc-org")
     (if (require 'toc-org nil t)
         (add-hook 'org-mode-hook 'toc-org-mode)
+
+        ;; enable in markdown, too
+        (add-hook 'markdown-mode-hook 'toc-org-mode)
+        (define-key markdown-mode-map (kbd "\C-c\C-o") 
'toc-org-markdown-follow-thing-at-point))
       (warn "toc-org not found"))
   #+END_SRC
 
@@ -112,6 +122,33 @@ In your emacs' setup, you can bind a tag =:TOC:= to a 
binding =T=:
 
 Now =C-c C-q T RET= and you are done putting the =:TOC:= entry.
 
+* Markdown support
+
+You can also enable the mode in Markdown files and get pretty much the same
+functionality. The package will
+1. Look for '#'s instead of '*'s as heading markers.
+2. Expect the =:TOC:= tag to appear as comment, like, =<-- :TOC: -->=
+3. Format the links and the quote block according to Markdown syntax
+
+Example:
+
+#+BEGIN_SRC markdown
+  # About
+  # Table of Contents                                    <-- :TOC: -->
+  - [About](#about)
+  - [Installation](#installation)
+    - [via package.el](#via-packageel)
+    - [Manual](#manual)
+  - [Use](#use)
+  - [Example](#example)
+
+  # Installation
+  ## via package.el
+  ## Manual
+  # Use
+  # Example
+#+END_SRC
+
 * Different href styles
 
 Currently, only 2 href styles are supported: =gh= and =org=. You can easily
diff --git a/toc-org-test.el b/toc-org-test.el
index a3b8d12e09..d927befeea 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
 
@@ -141,21 +141,25 @@
   (should (equal (toc-org-format-visible-link "Context Extraction Service 
[60%]") "Context Extraction Service")))
 
 (ert-deftest test-toc-org-hrefify-toc ()
-  (let ((hash (make-hash-table :test 'equal)))
-    (should (equal (toc-org-hrefify-toc "* About\n"
-                                        (lambda (str &optional hash) (upcase 
str))
-                                        hash)
-                   "- [[ABOUT][About]]\n"))
-    (should (equal (gethash "ABOUT" hash) "About")))
+  (dolist (params '((nil . "- [[ABOUT][About]]\n") ('t . "- 
[About](ABOUT)\n")))
+    (let ((hash (make-hash-table :test 'equal)))
+      (should (equal (toc-org-hrefify-toc "* About\n"
+                                          (lambda (str &optional hash) (upcase 
str))
+                                          (car params)
+                                          hash)
+                     (cdr params)))
+      (should (equal (gethash "ABOUT" hash) "About"))))
+
   ;; check trailing space: https://github.com/snosov1/toc-org/pull/31
   (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"))
@@ -237,3 +241,27 @@
      "* H1\n* TODO H2\n* TOC           :TOC:\n \n"
      "* H1\n* TODO H2\n* TOC           :TOC:\n- [[#h1][H1]]\n- [[#h2][H2]]\n")
     ))
+
+(ert-deftest test-toc-org-insert-toc-markdown ()
+  "Test the `toc-org-insert-toc' function"
+
+  (define-derived-mode markdown-mode text-mode "Markdown")
+
+  (defun test-toc-org-insert-toc-gold-test-markdown (content gold)
+    (with-temp-buffer
+      (markdown-mode)
+      (insert content)
+      (toc-org-insert-toc)
+      (should (equal
+               (buffer-substring-no-properties
+                (point-min) (point-max))
+               gold))))
+  ;; suppress compiler warnings
+  (declare-function test-toc-org-insert-toc-gold-test-markdown "toc-org")
+  (declare-function markdown-mode "toc-org")
+
+  (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                                                     "))
+    (test-toc-org-insert-toc-gold-test-markdown
+     (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  -  [...]
+    ))
diff --git a/toc-org.el b/toc-org.el
index 4c52f234b0..f519b20f8b 100644
--- a/toc-org.el
+++ b/toc-org.el
@@ -24,8 +24,8 @@
 
 ;;; 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).
+;; toc-org helps you to have an up-to-date table of contents in org or markdown
+;; 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.
@@ -33,7 +33,11 @@
 ;; After installation put into your .emacs file something like
 
 ;; (if (require 'toc-org nil t)
-;;     (add-hook 'org-mode-hook 'toc-org-enable)
+;;     (add-hook 'org-mode-hook 'toc-org-mode)
+
+;;     ;; enable in markdown, too
+;;     (add-hook 'markdown-mode-hook 'toc-org-mode)
+;;     (define-key markdown-mode-map (kbd "\C-c\C-o") 
'toc-org-markdown-follow-thing-at-point)
 ;;   (warn "toc-org not found"))
 
 ;; And every time you'll be saving an org file, the first headline with a :TOC:
@@ -44,6 +48,7 @@
 ;;; Code:
 
 (require 'org)
+(require 'thingatpt)
 
 (defgroup toc-org nil
   "toc-org is a utility to have an up-to-date table of contents
@@ -52,9 +57,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 
".*?\\(<--\s+\\)?:toc\\([@_][0-9]\\|\\([@_][0-9][@_][a-zA-Z]+\\)\\)?:\\(\\(\s+-->\\)?$\\|[^
 ]*?:\\(\s+-->\\)?$\\)"
+  "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:\\(\\(\s+-->\\)?$\\|[^ 
]*?:\\(\s+-->\\)?$\\)"
   "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")
@@ -80,6 +85,17 @@ files on GitHub)"
 equal to `org-drawer-regexp'. However, some older versions of
 org (notably, 8.2.10) restrict the values that can be placed
 between the colons. So, the value here is set explicitly.")
+(defconst toc-org-markdown-link-regexp ;; copy-paste from markdown-mode
+  
"\\(!\\)?\\(\\[\\)\\([^]^][^]]*\\|\\)\\(\\]\\)\\((\\)\\([^)]*?\\)\\(?:\\s-+\\(\"[^\"]*\"\\)\\)?\\()\\)"
+  "Regular expression for a [text](file) or an image link ![text](file).
+Group 1 matches the leading exclamation point (optional).
+Group 2 matches the opening square bracket.
+Group 3 matches the text inside the square brackets.
+Group 4 matches the closing square bracket.
+Group 5 matches the opening parenthesis.
+Group 6 matches the URL.
+Group 7 matches the title (optional).
+Group 8 matches the closing parenthesis.")
 
 (defcustom toc-org-max-depth 2
   "Maximum depth of the headings to use in the table of
@@ -104,7 +120,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 +132,22 @@ 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 "\s+#+$" nil t)
+              (replace-match "" 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 +171,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 +283,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 +311,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 +386,85 @@ 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
+  (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"))))))))
+
+(defun toc-org-follow-markdown-link ()
+  "Follow the markdown link (mimics `org-open-at-point')"
+  (interactive)
+  (when (thing-at-point-looking-at toc-org-markdown-link-regexp)
+    (let ((pos (point)))
       (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")))))))))
+      (if (re-search-forward (concat "^#+\s+" (match-string-no-properties 3)) 
(point-max) t)
+          (beginning-of-line)
+        (goto-char pos)))))
+
+(defun toc-org-markdown-follow-thing-at-point (arg)
+  "Try to follow the link with `toc-org-follow-markdown-link',
+fallback to `markdown-follow-thing-at-point' on failure"
+  (interactive "P")
+  (let ((pos (point)))
+    (toc-org-follow-markdown-link)
+    (when (and (equal pos (point)) (fboundp 'markdown-follow-thing-at-point))
+      (markdown-follow-thing-at-point arg))))
 
 ;;;###autoload
 (defun toc-org-enable ()



reply via email to

[Prev in Thread] Current Thread [Next in Thread]