From 9ef2b32a6c2fc7b5ced9060b684e29e8d9bd54dc Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 17 May 2020 12:41:24 +0200 Subject: [PATCH] Follow link in toc: M-x markdown-toc-follow-toc-link-at-point Related #42 --- markdown-toc.el | 43 +++++++++++++++++++- test/markdown-toc-test.el | 85 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/markdown-toc.el b/markdown-toc.el index c70c2c3..ed3becd 100644 --- a/markdown-toc.el +++ b/markdown-toc.el @@ -217,7 +217,7 @@ Return the end position if it exists, nil otherwise." markdown-toc-header-toc-end)) (defcustom markdown-toc-indentation-space 4 - "Let the user decide the indentation level") + "Let the user decide the indentation level.") (defcustom markdown-toc-user-toc-structure-manipulation-fn (lambda (toc-structure) toc-structure) @@ -264,6 +264,8 @@ If called interactively with prefix arg REPLACE-TOC-P, replaces previous TOC." markdown-toc--generate-toc insert))) +(defalias 'markdown-toc/generate-toc 'markdown-toc-generate-toc) + ;;;###autoload (defun markdown-toc-generate-or-refresh-toc () "Generate a TOC for markdown file at current point or refreshes an already generated TOC." @@ -284,7 +286,44 @@ If called interactively with prefix arg REPLACE-TOC-P, replaces previous TOC." (save-excursion (markdown-toc--delete-toc t))) -(defalias 'markdown-toc/generate-toc 'markdown-toc-generate-toc) +(defun markdown-toc--read-title-out-of-link (link) + "Extract the link title out of a markdown LINK title. +This assumes no funky stuff in the markdown link format ` - [](...) ` " + (->> link + s-trim + (s-chop-prefix "- [") + (s-split "]") + car)) + +(defun markdown-toc--title-level (link) + "Determine the markdown title LINK out of its indentation. +If misindented or not prefixed by `-`, it's considered not a link +and returns nil. Otherwise, returns the level number." + (when (s-prefix? "-" (-> link s-trim)) ;; if not, it's not a link title + (let ((indent (->> link + (s-split "-") + car ;; first string contains a string with empty spaces + ;; which should be a multiple of + ;; `markdown-toc-indentation-space` + length))) + (when (zerop (% indent markdown-toc-indentation-space)) + (+ 1 (/ indent markdown-toc-indentation-space)))))) + +;;;###autoload +(defun markdown-toc-follow-toc-link-at-point () + "On a given toc link, navigate to the current markdown header. +If the toc is misindented (according to +markdown-toc-indentation-space`) or if not on a toc link, this +does nothing. +" + (interactive) + (let* ((full-title (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) + (let ((level (markdown-toc--title-level full-title))) + (when level ;; nil if misindented or not on a title + (let ((title (markdown-toc--read-title-out-of-link full-title))) + (goto-char (point-min)) + (search-forward-regexp (format "%s %s" (s-repeat level "#") title)))) + (message "markdown-toc: Not on a link (or misindented), nothing to do")))) (defun markdown-toc--bug-report () "Compute the bug report for the user to include." diff --git a/test/markdown-toc-test.el b/test/markdown-toc-test.el index 816c520..c93154b 100755 --- a/test/markdown-toc-test.el +++ b/test/markdown-toc-test.el @@ -716,5 +716,90 @@ System information: ((:input '((:message2)) :output :res2)))) (markdown-toc-bug-report))))) +(ert-deftest markdown-toc--read-title-out-of-link () + (should (string= "this is the title" + (markdown-toc--read-title-out-of-link " - [this is the title](#this-is-the-link) "))) + (should (string= "another title" + (markdown-toc--read-title-out-of-link " - [another title](#this-is-the-link) +with multiple line +should not matter ")))) + +(ert-deftest markdown-toc--title-level () + (should (eq 1 + (markdown-toc--title-level "- [this is the title](#this-is-the-link)"))) + (should (eq 4 + (let ((markdown-toc-indentation-space 4)) + (markdown-toc--title-level " - [this is the title](#this-is-the-link)")))) + (should (eq 2 + (let ((markdown-toc-indentation-space 2)) + (markdown-toc--title-level " - [another title](#this-is-the-link) +with multiple line +should not matter ")))) + (should (eq 2 + (let ((markdown-toc-indentation-space 3)) + (markdown-toc--title-level " - [another title](#this-is-the-link) +with multiple line +should not matter ")))) + ;; no - as prefix so considered not a title + (should-not (markdown-toc--title-level "[this is the title](#this-is-the-link)")) + ;; prefixed with a dash but misaligned, title should be indented with a + ;; multiple of `markdown-toc-indentation-space` blank spaces + (should-not (markdown-toc--title-level " - [title](#this-is-the-link)"))) + +(ert-deftest markdown-toc-follow-toc-link-at-point() + "Follow a correct toc link should follow to the title" + (should (string= "## Sources" + (with-temp-buffer + (insert "- [some markdown page title](#some-markdown-page-title) +- [main title](#main-title) + - [Sources](#sources) + - [Marmalade (recommended)](#marmalade-recommended) + +# main title +## Sources +### marmalade +... +") + (search-backward "- [Sources]") + (call-interactively 'markdown-toc-follow-toc-link-at-point) + (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) + +(ert-deftest markdown-toc-follow-toc-link-at-point-failures() + "Follow a misindented toc link should do nothing" + (should + ;; not move + (string= " - [Sources](#sources) <- misindented 3 instead of 4 here" + (with-temp-buffer + (insert "- [some markdown page title](#some-markdown-page-title) +- [main title](#main-title) + - [Sources](#sources) <- misindented 3 instead of 4 here + +# main title +## Sources +... +") + (search-backward "- [Sources]") + (call-interactively 'markdown-toc-follow-toc-link-at-point) + (buffer-substring-no-properties (point-at-bol) (point-at-eol))))) + + (should + ;; not move as well because + (string= "not a title" + (with-temp-buffer + (insert "- [some markdown page title](#some-markdown-page-title) +- [main title](#main-title) + - [Sources](#sources) + - [Marmalade (recommended)](#marmalade-recommended) + +# main title +## Sources +### marmalade +not a title +... +") + (search-backward "not a title") + (call-interactively 'markdown-toc-follow-toc-link-at-point) + (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) + (provide 'markdown-toc-tests) ;;; markdown-toc-tests.el ends here