From 827af5987348665edd4a632e827925b947c00f96 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 14 Oct 2017 12:29:52 +0200 Subject: [PATCH] Fix links without contents in table of contents' entries * lisp/ox.el (org-export-toc-entry-backend): New function. * lisp/ox-html.el (org-html--format-toc-headline): * lisp/ox-md.el (org-md--build-toc): * lisp/ox-odt.el (org-odt-toc): Use new function. * lisp/ox-texinfo.el (org-texinfo--sanitize-title): (org-texinfo--wrap-float): Handle links without contents. * testing/lisp/test-ox.el (test-org-export/toc-entry-backend): New test. Reported-by: ConcreteVitamin --- lisp/ox-html.el | 10 +------ lisp/ox-md.el | 11 +------- lisp/ox-odt.el | 8 ++---- lisp/ox-texinfo.el | 33 ++++++++++++++++------- lisp/ox.el | 31 +++++++++++++++++++++- testing/lisp/test-ox.el | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 36 deletions(-) diff --git a/lisp/ox-html.el b/lisp/ox-html.el index 8ce4fb6ad..fe8209997 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -2328,15 +2328,7 @@ INFO is a plist used as a communication channel." (org-element-property :priority headline))) (text (org-export-data-with-backend (org-export-get-alt-title headline info) - ;; Create an anonymous back-end that will ignore any - ;; footnote-reference, link, radio-target and target - ;; in table of contents. - (org-export-create-backend - :parent 'html - :transcoders '((footnote-reference . ignore) - (link . (lambda (object c i) c)) - (radio-target . (lambda (object c i) c)) - (target . ignore))) + (org-export-toc-entry-backend 'html) info)) (tags (and (eq (plist-get info :with-tags) t) (org-export-get-tags headline info)))) diff --git a/lisp/ox-md.el b/lisp/ox-md.el index 8a25f1bd5..121883873 100644 --- a/lisp/ox-md.el +++ b/lisp/ox-md.el @@ -582,16 +582,7 @@ contents according to the current headline." (format "[%s](#%s)" (org-export-data-with-backend (org-export-get-alt-title headline info) - ;; Create an anonymous back-end that will - ;; ignore any footnote-reference, link, - ;; radio-target and target in table of - ;; contents. - (org-export-create-backend - :parent 'md - :transcoders '((footnote-reference . ignore) - (link . (lambda (object c i) c)) - (radio-target . (lambda (object c i) c)) - (target . ignore))) + (org-export-toc-entry-backend 'md) info) (or (org-element-property :CUSTOM_ID headline) (org-export-get-reference headline info)))) diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el index f00fd99fc..d58563662 100644 --- a/lisp/ox-odt.el +++ b/lisp/ox-odt.el @@ -1159,12 +1159,8 @@ table of contents as a string, or nil." ;; Likewise, links, footnote references and regular targets are also ;; suppressed. (let* ((headlines (org-export-collect-headlines info depth scope)) - (backend (org-export-create-backend - :parent (org-export-backend-name (plist-get info :back-end)) - :transcoders '((footnote-reference . ignore) - (link . (lambda (object c i) c)) - (radio-target . (lambda (object c i) c)) - (target . ignore))))) + (backend (org-export-toc-entry-backend + (org-export-backend-name (plist-get info :back-end))))) (when headlines (org-odt--format-toc (and (not scope) (org-export-translate "Table of Contents" :utf-8 info)) diff --git a/lisp/ox-texinfo.el b/lisp/ox-texinfo.el index b5903a521..e6aa3e5bc 100644 --- a/lisp/ox-texinfo.el +++ b/lisp/ox-texinfo.el @@ -499,8 +499,12 @@ export state, as a plist." (org-export-create-backend :parent 'texinfo :transcoders '((footnote-reference . ignore) - (link . (lambda (object c i) c)) - (radio-target . (lambda (object c i) c)) + (link . (lambda (l c i) + (or c + (org-export-data + (org-element-property :raw-link l) + i)))) + (radio-target . (lambda (_r c _i) c)) (target . ignore))) info)) @@ -519,18 +523,27 @@ strings (e.g., returned by `org-export-get-caption')." (let* ((backend (org-export-create-backend :parent 'texinfo - :transcoders '((link . (lambda (object c i) c)) - (radio-target . (lambda (object c i) c)) + :transcoders '((link . (lambda (l c i) + (or c + (org-export-data + (org-element-property :raw-link l) + i)))) + (radio-target . (lambda (_r c _i) c)) (target . ignore)))) (short-backend (org-export-create-backend :parent 'texinfo - :transcoders '((footnote-reference . ignore) - (inline-src-block . ignore) - (link . (lambda (object c i) c)) - (radio-target . (lambda (object c i) c)) - (target . ignore) - (verbatim . ignore)))) + :transcoders + '((footnote-reference . ignore) + (inline-src-block . ignore) + (link . (lambda (l c i) + (or c + (org-export-data + (org-element-property :raw-link l) + i)))) + (radio-target . (lambda (_r c _i) c)) + (target . ignore) + (verbatim . ignore)))) (short-str (if (and short caption) (format "@shortcaption{%s}\n" diff --git a/lisp/ox.el b/lisp/ox.el index 1c43577cd..82778747f 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -5176,7 +5176,7 @@ return nil." info 'first-match))) -;;;; For Tables Of Contents +;;;; For Tables of Contents ;; ;; `org-export-collect-headlines' builds a list of all exportable ;; headline elements, maybe limited to a certain depth. One can then @@ -5186,6 +5186,9 @@ return nil." ;; Once the generic function `org-export-collect-elements' is defined, ;; `org-export-collect-tables', `org-export-collect-figures' and ;; `org-export-collect-listings' can be derived from it. +;; +;; `org-export-toc-entry-backend' builds a special anonymous back-end +;; useful to export table of contents' entries. (defun org-export-collect-headlines (info &optional n scope) "Collect headlines in order to build a table of contents. @@ -5271,6 +5274,32 @@ INFO is a plist used as a communication channel. Return a list of src-block elements with a caption." (org-export-collect-elements 'src-block info)) +(defun org-export-toc-entry-backend (parent &rest transcoders) + "Return an export back-end appropriate for table of contents entries. + +PARENT is an export back-end the returned back-end should inherit +from. + +By default, the back-end removes footnote references and targets. +It also changes links and radio targets into regular text. +TRANSCODERS optional argument, when non-nil, specifies additional +transcoders. A transcoder follows the pattern (TYPE . FUNCTION) +where type is an element or object type and FUNCTION the function +transcoding it." + (declare (indent 1)) + (org-export-create-backend + :parent parent + :transcoders + (append transcoders + `((footnote-reference . ,#'ignore) + (link . ,(lambda (l c i) + (or c + (org-export-data + (org-element-property :raw-link l) + i)))) + (radio-target . ,(lambda (_r c _) c)) + (target . ,#'ignore))))) + ;;;; Smart Quotes ;; diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index 72b6c8ccd..6b5e3a30f 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -4320,6 +4320,76 @@ Another text. (ref:text) (let ((scope (org-element-map tree 'headline #'identity info t))) (length (org-export-collect-headlines info 1 scope))))))) +(ert-deftest test-org-export/toc-entry-backend () + "Test `org-export-toc-entry-backend' specifications." + ;; Ignore targets. + (should + (equal "H \n" + (org-test-with-temp-text "* H <>" + (let (org-export-registered-backends) + (org-export-define-backend 'test + '((headline . (lambda (h _c i) (org-export-data-with-backend + (org-element-property :title h) + (org-export-toc-entry-backend 'test) + i))))) + (org-export-as 'test))))) + ;; Ignore footnote references. + (should + (equal "H \n" + (org-test-with-temp-text "[fn:1] Definition\n* H [fn:1]" + (let (org-export-registered-backends) + (org-export-define-backend 'test + '((headline . (lambda (h _c i) (org-export-data-with-backend + (org-element-property :title h) + (org-export-toc-entry-backend 'test) + i))))) + (org-export-as 'test))))) + ;; Replace plain links with contents, or with path. + (should + (equal "H Org mode\n" + (org-test-with-temp-text "* H [[http://orgmode.org][Org mode]]" + (let (org-export-registered-backends) + (org-export-define-backend 'test + '((headline . (lambda (h _c i) (org-export-data-with-backend + (org-element-property :title h) + (org-export-toc-entry-backend 'test) + i))))) + (org-export-as 'test))))) + (should + (equal "H http://orgmode.org\n" + (org-test-with-temp-text "* H [[http://orgmode.org]]" + (let (org-export-registered-backends) + (org-export-define-backend 'test + '((headline . (lambda (h _c i) (org-export-data-with-backend + (org-element-property :title h) + (org-export-toc-entry-backend 'test) + i))))) + (org-export-as 'test))))) + ;; Replace radio targets with contents. + (should + (equal "H radio\n" + (org-test-with-temp-text "* H <<>>" + (let (org-export-registered-backends) + (org-export-define-backend 'test + '((headline . (lambda (h _c i) (org-export-data-with-backend + (org-element-property :title h) + (org-export-toc-entry-backend 'test) + i))))) + (org-export-as 'test))))) + ;; With optional argument TRANSCODERS, specify other + ;; transformations. + (should + (equal "H bold\n" + (org-test-with-temp-text "* H *bold*" + (let (org-export-registered-backends) + (org-export-define-backend 'test + '((headline . (lambda (h _c i) (org-export-data-with-backend + (org-element-property :title h) + (org-export-toc-entry-backend 'test + '(bold . (lambda (_b c _i) c))) + i))))) + (org-export-as 'test)))))) + ;;; Templates -- 2.11.4.GIT