From 91a25f58df4d28a916989331eee0527ae25d7123 Mon Sep 17 00:00:00 2001 From: Shohei YOSHIDA Date: Sat, 19 Dec 2020 23:41:14 +0900 Subject: [PATCH] Fix creating imenu index when there is no level1 header --- CHANGES.md | 4 +- markdown-mode.el | 83 +++++++++++++++++++++++------------------- tests/markdown-test.el | 20 ++++++++++ 3 files changed, 69 insertions(+), 38 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d987cd7f..04ebeeb6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,6 +33,7 @@ - Fix wrong fontification words between strong markups [GH-534][] - Fix wrong italic fontification just after code block [GH-548][] - Fix too indended list face issue [GH-569][] + - Fix creating imenu index issue when there is no level-1 header too[GH-571][] [gh-290]: https://github.com/jrblevin/markdown-mode/issues/290 [gh-311]: https://github.com/jrblevin/markdown-mode/issues/311 @@ -52,7 +53,8 @@ [gh-548]: https://github.com/jrblevin/markdown-mode/issues/548 [gh-553]: https://github.com/jrblevin/markdown-mode/issues/553 [gh-560]: https://github.com/jrblevin/markdown-mode/issues/560 - [gh-560]: https://github.com/jrblevin/markdown-mode/issues/569 + [gh-569]: https://github.com/jrblevin/markdown-mode/issues/569 + [gh-571]: https://github.com/jrblevin/markdown-mode/issues/571 # Markdown Mode 2.4 diff --git a/markdown-mode.el b/markdown-mode.el index 2e472475..f70aedb2 100644 --- a/markdown-mode.el +++ b/markdown-mode.el @@ -5533,52 +5533,61 @@ See also `markdown-mode-map'.") "Create and return a nested imenu index alist for the current buffer. See `imenu-create-index-function' and `imenu--index-alist' for details." (let* ((root '(nil . nil)) - cur-alist - (cur-level 0) - (empty-heading "-") - (self-heading ".") - hashes pos level heading) + (min-level 9999) + hashes headers) (save-excursion ;; Headings (goto-char (point-min)) (while (re-search-forward markdown-regex-header (point-max) t) - (unless (markdown-code-block-at-point-p) + (unless (or (markdown-code-block-at-point-p) + (and (match-beginning 3) + (get-text-property (match-beginning 3) 'markdown-yaml-metadata-end))) (cond ((match-string-no-properties 2) ;; level 1 setext - (setq heading (match-string-no-properties 1)) - (setq pos (match-beginning 1) - level 1)) + (setq min-level 1) + (push (list :heading (match-string-no-properties 1) + :point (match-beginning 1) + :level 1) headers)) ((match-string-no-properties 3) ;; level 2 setext - (setq heading (match-string-no-properties 1)) - (setq pos (match-beginning 1) - level 2)) + (setq min-level (min min-level 2)) + (push (list :heading (match-string-no-properties 1) + :point (match-beginning 1) + :level (- 2 (1- min-level))) headers)) ((setq hashes (markdown-trim-whitespace (match-string-no-properties 4))) - (setq heading (match-string-no-properties 5) - pos (match-beginning 4) - level (length hashes)))) - (let ((alist (list (cons heading pos)))) - (cond - ((= cur-level level) ; new sibling - (setcdr cur-alist alist) - (setq cur-alist alist)) - ((< cur-level level) ; first child - (dotimes (_ (- level cur-level 1)) - (setq alist (list (cons empty-heading alist)))) - (if cur-alist - (let* ((parent (car cur-alist)) - (self-pos (cdr parent))) - (setcdr parent (cons (cons self-heading self-pos) alist))) - (setcdr root alist)) ; primogenitor - (setq cur-alist alist) - (setq cur-level level)) - (t ; new sibling of an ancestor - (let ((sibling-alist (last (cdr root)))) - (dotimes (_ (1- level)) - (setq sibling-alist (last (cdar sibling-alist)))) - (setcdr sibling-alist alist) - (setq cur-alist alist)) - (setq cur-level level)))))) + (setq min-level (min min-level (length hashes))) + (push (list :heading (match-string-no-properties 5) + :point (match-beginning 4) + :level (- (length hashes) (1- min-level))) headers))))) + (cl-loop with cur-level = 0 + with cur-alist = nil + with empty-heading = "-" + with self-heading = "." + for header in (reverse headers) + for level = (plist-get header :level) + do + (let ((alist (list (cons (plist-get header :heading) (plist-get header :point))))) + (cond + ((= cur-level level) ; new sibling + (setcdr cur-alist alist) + (setq cur-alist alist)) + ((< cur-level level) ; first child + (dotimes (_ (- level cur-level 1)) + (setq alist (list (cons empty-heading alist)))) + (if cur-alist + (let* ((parent (car cur-alist)) + (self-pos (cdr parent))) + (setcdr parent (cons (cons self-heading self-pos) alist))) + (setcdr root alist)) ; primogenitor + (setq cur-alist alist) + (setq cur-level level)) + (t ; new sibling of an ancestor + (let ((sibling-alist (last (cdr root)))) + (dotimes (_ (1- level)) + (setq sibling-alist (last (cdar sibling-alist)))) + (setcdr sibling-alist alist) + (setq cur-alist alist)) + (setq cur-level level))))) ;; Footnotes (let ((fn (markdown-get-defined-footnotes))) (if (or (zerop (length fn)) diff --git a/tests/markdown-test.el b/tests/markdown-test.el index 16e8ebb3..99b9ad32 100644 --- a/tests/markdown-test.el +++ b/tests/markdown-test.el @@ -6510,6 +6510,26 @@ https://github.com/jrblevin/markdown-mode/issues/235" (should-not (member "^fn1" entries)) (should-not (member "^fn2" entries))))) +(ert-deftest test-markdown-imenu/no-level-one-header () + "Create imenu entries if there is no level-1 header +https://github.com/jrblevin/markdown-mode/issues/571" + (markdown-test-string "--- +title: Wat +--- + +## Section a + +### Sub 1 + +### Sub 11 + +## Section b + +### Sub 2" + (let ((entries (markdown-imenu-create-nested-index))) + (should (string= (car (nth 0 entries)) "Section a")) + (should (string= (car (nth 1 entries)) "Section b"))))) + (ert-deftest test-markdown-command/function () "Test ‘markdown’ with ‘markdown-command’ being a function." (markdown-test-string "foo"