From ab9166ad29abb7d48273f961afc0ccd30b0d537a Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Fri, 14 Feb 2020 10:00:15 +0100 Subject: [PATCH] Extend export tooling in link parameters * lisp/ol.el (org-link-parameters): Allow a fourth "info" argument for `:export' property. Expound docstring. * lisp/ox.el (org-export-custom-protocol-maybe): Accept a fourth optional argument. * lisp/ox-ascii.el (org-ascii--describe-links): (org-ascii-link): * lisp/ox-beamer.el (org-beamer-link): * lisp/ox-html.el (org-html-link): * lisp/ox-latex.el (org-latex-link): * lisp/ox-man.el (org-man-link): * lisp/ox-md.el (org-md-link): * lisp/ox-odt.el (org-odt-link): * lisp/ox-org.el (org-org-link): * lisp/ox-texinfo.el (org-texinfo-link): * contrib/lisp/ox-groff.el (org-groff-link): Provide expected fourth argument. * lisp/ox.el (org-export-link-as-file): New function. * lisp/ol.el (org-link-parameters): Add reference to new function in docstring. * testing/lisp/test-ox.el (test-org-export/link-as-file): Add tests. (test-org-export/custom-protocol-maybe): Update tests. --- contrib/lisp/ox-groff.el | 2 +- lisp/ol.el | 100 +++++++++++++++++++++++++++++---------- lisp/ox-ascii.el | 4 +- lisp/ox-beamer.el | 2 +- lisp/ox-html.el | 2 +- lisp/ox-latex.el | 2 +- lisp/ox-man.el | 4 +- lisp/ox-md.el | 2 +- lisp/ox-odt.el | 2 +- lisp/ox-org.el | 4 +- lisp/ox-texinfo.el | 2 +- lisp/ox.el | 37 +++++++++++---- testing/lisp/test-ox.el | 32 +++++++++++-- 13 files changed, 147 insertions(+), 48 deletions(-) diff --git a/contrib/lisp/ox-groff.el b/contrib/lisp/ox-groff.el index 9f7d3f11f..9f0a32432 100644 --- a/contrib/lisp/ox-groff.el +++ b/contrib/lisp/ox-groff.el @@ -1248,7 +1248,7 @@ INFO is a plist holding contextual information. See ((string= type "file") (org-export-file-uri raw-path)) (t raw-path)))) (cond - ((org-export-custom-protocol-maybe link desc 'groff)) + ((org-export-custom-protocol-maybe link desc 'groff info)) ;; Image file. (imagep (org-groff-link--inline-image link info)) ;; import groff files diff --git a/lisp/ol.el b/lisp/ol.el index 9edf87c9a..40cc3b740 100644 --- a/lisp/ol.el +++ b/lisp/ol.el @@ -86,42 +86,94 @@ :group 'org) (defcustom org-link-parameters nil - "An alist of properties that defines all the links in Org mode. + "Alist of properties that defines all the links in Org mode. + The key in each association is a string of the link type. -Subsequent optional elements make up a plist of link properties. +Subsequent optional elements make up a property list for that +type. -:follow - A function that takes the link path as an argument. +All properties ar optional. However, the most important ones +are, in this order, `:follow', `:export', and `:store', described +below. -:export - A function that takes the link path, description and -export-backend as arguments. +`:follow' -:store - A function responsible for storing the link. See the -function `org-store-link-functions'. + Function that takes the link path (a string) as an argument and + \"opens\" the link. -:complete - A function that inserts a link with completion. The -function takes one optional prefix argument. +`:export' -:face - A face for the link, or a function that returns a face. -The function takes one argument which is the link path. The -default face is `org-link'. + Function that accepts four arguments: + - the path, as a string, + - the description as a string, or nil, + - the export back-end, + - the export communication channel, as a plist. -:mouse-face - The mouse-face. The default is `highlight'. + If the new link type is meant to be exported as a \"file\"-link + (or as an image), consider using `org-export-link-as-file', + either as an helper function, or as a value for this parameter. -:display - `full' will not fold the link in descriptive -display. Default is `org-link'. + When nil, export for that type of link is delegated to the + back-end. -:help-echo - A string or function that takes (window object position) -as arguments and returns a string. +`:store' -:keymap - A keymap that is active on the link. The default is -`org-mouse-map'. + Function responsible for storing the link. See the function + `org-store-link-functions' for a description of the expected + arguments. -:htmlize-link - A function for the htmlize-link. Defaults -to (list :uri \"type:path\") +Additional properties provide more specific control over the +link. -:activate-func - A function to run at the end of font-lock -activation. The function must accept (link-start link-end path bracketp) -as arguments." +`:activate-func' + + Function to run at the end of Font Lock activation. It must + accept four arguments: + - the buffer position at the start of the link, + - the buffer position at its end, + - the path, as a string, + - a boolean, non-nil when the link has brackets. + +`:complete' + + Function that inserts a link with completion. The function + takes one optional prefix argument. + +`:display' + + Value for `invisible' text property on the hidden parts of the + link. The most useful value is `full', which will not fold the + link in descriptive display. Default is `org-link'. + +`:face' + + Face for the link, or a function returning a face. The + function takes one argument, which is the path. + + The default face is `org-link'. + +`:help-echo' + + String or function used as a value for the `help-echo' text + property. The function is called with one argument, the help + string to display, and should return a string. + +`:htmlize-link' + + Function or plist for the `htmlize-link' text property. The + function takes no argument. + + Default is (:uri \"type:path\") + +`:keymap' + + Active keymap when point is on the link. Default is + `org-mouse-map'. + +`:mouse-face' + + Face used when hovering over the link. Default is + `highlight'." :group 'org-link :package-version '(Org . "9.1") :type '(alist :tag "Link display parameters" diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el index 8927730cb..981a7660e 100644 --- a/lisp/ox-ascii.el +++ b/lisp/ox-ascii.el @@ -957,7 +957,7 @@ channel." ((not (org-element-contents link)) nil) ;; Do not add a link already handled by custom export ;; functions. - ((org-export-custom-protocol-maybe link anchor 'ascii) nil) + ((org-export-custom-protocol-maybe link anchor 'ascii info) nil) (t (concat (org-ascii--fill-string @@ -1579,7 +1579,7 @@ INFO is a plist holding contextual information." (concat type ":" raw-path)) (t (concat type ":" raw-path))))) (cond - ((org-export-custom-protocol-maybe link desc 'ascii)) + ((org-export-custom-protocol-maybe link desc 'ascii info)) ((string= type "coderef") (format (org-export-get-coderef-format path desc) (org-export-resolve-coderef path info))) diff --git a/lisp/ox-beamer.el b/lisp/ox-beamer.el index 23656db44..66589fac5 100644 --- a/lisp/ox-beamer.el +++ b/lisp/ox-beamer.el @@ -731,7 +731,7 @@ channel." "Transcode a LINK object into Beamer code. CONTENTS is the description part of the link. INFO is a plist used as a communication channel." - (or (org-export-custom-protocol-maybe link contents 'beamer) + (or (org-export-custom-protocol-maybe link contents 'beamer info) ;; Fall-back to LaTeX export. However, prefer "\hyperlink" over ;; "\hyperref" since the former handles overlay specifications. (let ((latex-link (org-export-with-backend 'latex link contents info))) diff --git a/lisp/ox-html.el b/lisp/ox-html.el index 62043b30c..e77cd3b12 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -3049,7 +3049,7 @@ INFO is a plist holding contextual information. See (if (org-string-nw-p attr) (concat " " attr) "")))) (cond ;; Link type is handled by a special function. - ((org-export-custom-protocol-maybe link desc 'html)) + ((org-export-custom-protocol-maybe link desc 'html info)) ;; Image file. ((and (plist-get info :html-inline-images) (org-export-inline-image-p diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index d904d562f..6540d1e70 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -2541,7 +2541,7 @@ INFO is a plist holding contextual information. See raw-path))))) (cond ;; Link type is handled by a special function. - ((org-export-custom-protocol-maybe link desc 'latex)) + ((org-export-custom-protocol-maybe link desc 'latex info)) ;; Image file. (imagep (org-latex--inline-image link info)) ;; Radio link: Transcode target's contents and use them as link's diff --git a/lisp/ox-man.el b/lisp/ox-man.el index b8dc54b54..1de8d522c 100644 --- a/lisp/ox-man.el +++ b/lisp/ox-man.el @@ -603,7 +603,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;; Link -(defun org-man-link (link desc _info) +(defun org-man-link (link desc info) "Transcode a LINK object from Org to Man. DESC is the description part of the link, or the empty string. @@ -623,7 +623,7 @@ INFO is a plist holding contextual information. See (t raw-path)))) (cond ;; Link type is handled by a special function. - ((org-export-custom-protocol-maybe link desc 'man)) + ((org-export-custom-protocol-maybe link desc 'man info)) ;; External link with a description part. ((and path desc) (format "%s \\fBat\\fP \\fI%s\\fP" path desc)) ;; External link without a description part. diff --git a/lisp/ox-md.el b/lisp/ox-md.el index e800f98aa..1933d9e9f 100644 --- a/lisp/ox-md.el +++ b/lisp/ox-md.el @@ -412,7 +412,7 @@ INFO is a plist holding contextual information. See (t raw-path)))) (cond ;; Link type is handled by a special function. - ((org-export-custom-protocol-maybe link desc 'md)) + ((org-export-custom-protocol-maybe link desc 'md info)) ((member type '("custom-id" "id" "fuzzy")) (let ((destination (if (string= type "fuzzy") (org-export-resolve-fuzzy-link link info) diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el index 49e37cc1d..64bb97811 100644 --- a/lisp/ox-odt.el +++ b/lisp/ox-odt.el @@ -2715,7 +2715,7 @@ INFO is a plist holding contextual information. See (path (replace-regexp-in-string "&" "&" path))) (cond ;; Link type is handled by a special function. - ((org-export-custom-protocol-maybe link desc 'odt)) + ((org-export-custom-protocol-maybe link desc 'odt info)) ;; Image file. ((and (not desc) imagep) (org-odt-link--inline-image link info)) ;; Formula file. diff --git a/lisp/ox-org.el b/lisp/ox-org.el index 97d8d0e92..740419e0e 100644 --- a/lisp/ox-org.el +++ b/lisp/ox-org.el @@ -165,11 +165,11 @@ CONTENTS is nil. INFO is ignored." '("AUTHOR" "CREATOR" "DATE" "EMAIL" "OPTIONS" "TITLE")) (org-element-keyword-interpreter keyword nil)))) -(defun org-org-link (link contents _info) +(defun org-org-link (link contents info) "Transcode LINK object back into Org syntax. CONTENTS is the description of the link, as a string, or nil. INFO is a plist containing current export state." - (or (org-export-custom-protocol-maybe link contents 'org) + (or (org-export-custom-protocol-maybe link contents 'org info) (org-element-link-interpreter link contents))) (defun org-org-template (contents info) diff --git a/lisp/ox-texinfo.el b/lisp/ox-texinfo.el index 346518933..4e7b575a7 100644 --- a/lisp/ox-texinfo.el +++ b/lisp/ox-texinfo.el @@ -1065,7 +1065,7 @@ INFO is a plist holding contextual information. See (org-export-file-uri raw-path)) (t raw-path)))) (cond - ((org-export-custom-protocol-maybe link desc 'texinfo)) + ((org-export-custom-protocol-maybe link desc 'texinfo info)) ((org-export-inline-image-p link org-texinfo-inline-image-rules) (org-texinfo--inline-image link info)) ((equal type "radio") diff --git a/lisp/ox.el b/lisp/ox.el index feef0b6f8..4e22eed71 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -4185,8 +4185,23 @@ meant to be translated with `org-export-data' or alike." (org-define-error 'org-link-broken "Unable to resolve link; aborting") -(defun org-export-custom-protocol-maybe (link desc backend) - "Try exporting LINK with a dedicated function. +(defun org-export-link-as-file (path description backend info) + "Pretend PATH is a file name, and export it. + +DESCRIPTION, when non-nil, is the description of the link, as +a string. BACKEND is the symbol representing the back-end used +for export. INFO is the communication channel, as a plist. + +This function is meant to be used as a possible tool for +`:export' property in `org-link-parameters'." + (org-export-data-with-backend + (org-element-parse-secondary-string + (org-link-make-string (concat "file:" path) description) '(link)) + backend + info)) + +(defun org-export-custom-protocol-maybe (link desc backend &optional info) + "Try exporting LINK object with a dedicated function. DESC is its description, as a string, or nil. BACKEND is the back-end used for export, as a symbol. @@ -4197,14 +4212,20 @@ A custom protocol has precedence over regular back-end export. The function ignores links with an implicit type (e.g., \"custom-id\")." (let ((type (org-element-property :type link))) - (unless (or (member type '("coderef" "custom-id" "fuzzy" "radio")) + (unless (or (member type '("coderef" "custom-id" "fuzzy" "radio" nil)) (not backend)) - (let ((protocol (org-link-get-parameter type :export))) + (let ((protocol (org-link-get-parameter type :export)) + (path (org-element-property :path link))) (and (functionp protocol) - (funcall protocol - (org-element-property :path link) - desc - backend)))))) + (condition-case nil + (funcall protocol path desc backend info) + ;; XXX: The function used (< Org 9.4) to accept only + ;; three mandatory arguments. Type-specific `:export' + ;; functions in the wild may not handle current + ;; signature. Provide backward compatibility support + ;; for them. + (wrong-number-of-arguments + (funcall protocol path desc backend)))))))) (defun org-export-get-coderef-format (path desc) "Return format string for code reference link. diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index 29916b4f6..551d9b717 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -2965,6 +2965,32 @@ Para2" ;;; Links +(ert-deftest test-org-export/link-as-file () + "Test `org-export-link-as-file' specifications." + ;; Export path as a "file"-type link. + (should + (equal "success" + (let ((backend + (org-export-create-backend + :name 'test + :transcoders + '((link . (lambda (l _c _i) + (if (equal "file" (org-element-property :type l)) + "success" + "failure"))))))) + (org-export-link-as-file "foo.org" nil backend nil)))) + ;; Exported path handles "file"-type specific properties, + ;; e.g., :search-option. + (should + (equal "bar" + (let ((backend + (org-export-create-backend + :name 'test + :transcoders + '((link . (lambda (l _c _i) + (org-element-property :search-option l))))))) + (org-export-link-as-file "foo.org::bar" nil backend nil))))) + (ert-deftest test-org-export/custom-protocol-maybe () "Test `org-export-custom-protocol-maybe' specifications." (should @@ -2980,7 +3006,7 @@ Para2" '((section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)) (link . (lambda (l c i) - (or (org-export-custom-protocol-maybe l c 'test) + (or (org-export-custom-protocol-maybe l c 'test i) "failure"))))))))) (should-not (string-match @@ -2996,7 +3022,7 @@ Para2" '((section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)) (link . (lambda (l c i) - (or (org-export-custom-protocol-maybe l c 'no-test) + (or (org-export-custom-protocol-maybe l c 'no-test i) "failure"))))))))) ;; Ignore anonymous back-ends. (should-not @@ -3012,7 +3038,7 @@ Para2" '((section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)) (link . (lambda (l c i) - (or (org-export-custom-protocol-maybe l c nil) + (or (org-export-custom-protocol-maybe l c nil i) "failure")))))))))) (ert-deftest test-org-export/get-coderef-format ()