From: Max Nikulin <manikulin@gmail.com>
To: emacs-orgmode@gnu.org
Subject: [PATCH v2] ol-info: Define :insert-description function
Date: Sun, 14 Aug 2022 23:41:47 +0700 [thread overview]
Message-ID: <tdb8kd$7mo$1@ciao.gmane.io> (raw)
In-Reply-To: <87a68hn9es.fsf@localhost>
[-- Attachment #1: Type: text/plain, Size: 640 bytes --]
On 06/08/2022 14:00, Ihor Radchenko wrote:
> Max Nikulin writes:
>
>> +If LINK is not an info link then DESC is returned."
>> + (or (org-string-nw-p desc)
>> + (let* ((file-node (org-info-link-file-node link))
>> + (file (car file-node))
>> + (node (cadr file-node)))
>
> pcase-let would be shorter here.
I have rewritten the patch to use `pcase' and to fix allowed separators
between file name and node.
I have realized that unlike other type specific functions,
:insert-description receives link including "scheme:" prefix. However
attempt to achieve consistency at this point may cause more problems.
[-- Attachment #2: v2-0001-ol-info-Define-insert-description-function.patch --]
[-- Type: text/x-patch, Size: 11156 bytes --]
From 7aeaa36ee1b30aa1c9711fe76e151ec956199f27 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Sat, 30 Jul 2022 19:13:01 +0700
Subject: [PATCH v2] ol-info: Define :insert-description function
* lisp/ol-info.el (org-info--link-file-node): New helper to parse info
link info file (manual) name and node.
(org-info-follow-link, org-info-export): Use `org-info--link-file-node'.
(org-info-description-as-command): New function to create description
for info links that may executed to view the manual.
(org-link-parameters): Specify `org-info-description-as-command' as
`:insert-description' for info links.
(org-info-other-documents): Add URL of directory index.
* testing/lisp/test-org-info.el (test-org-info/export): Add cases for
texinfo export with link description.
(test-org-info/link-file-node, test-org-info/description-as-command):
New tests for new functions `org-info--link-file-node' and
`org-info-description-as-command'.
Use recently added :insert-description feature of `org-link'.
Alternative separators between file name and node ":", "::", "#:"
are preserved. Added interpretation of empty path as dir index,
Org manual is assumed if file is not specified for given node.
---
lisp/ol-info.el | 85 ++++++++++++++++++++++++++---------
testing/lisp/test-org-info.el | 82 ++++++++++++++++++++++++++++++++-
2 files changed, 146 insertions(+), 21 deletions(-)
diff --git a/lisp/ol-info.el b/lisp/ol-info.el
index dc5f6d5ba..0584e6f8c 100644
--- a/lisp/ol-info.el
+++ b/lisp/ol-info.el
@@ -30,6 +30,7 @@
;;; Code:
+(require 'subr-x) ; `string-trim', `string-remove-prefix'
(require 'ol)
;; Declare external functions and variables
@@ -43,7 +44,8 @@
(org-link-set-parameters "info"
:follow #'org-info-open
:export #'org-info-export
- :store #'org-info-store-link)
+ :store #'org-info-store-link
+ :insert-description #'org-info-description-as-command)
;; Implementation
(defun org-info-store-link ()
@@ -63,24 +65,68 @@
"Follow an Info file and node link specified by PATH."
(org-info-follow-link path))
+(defun org-info--link-file-node (path)
+ "Extract file name and node from info link PATH.
+
+Return cons consisting of file name and node name or \"Top\" if node
+part is not specified. Components may be separated by \":\" or by \"#\"."
+ (if (not path)
+ '("dir" . "Top")
+ (string-match "\\`\\([^#:]*\\)\\(?:[#:]:?\\(.*\\)\\)?\\'" path)
+ (let* ((node (match-string 2 path))
+ ;; `string-trim' modifies match
+ (file (string-trim (match-string 1 path)))
+ (has-file (org-string-nw-p file))
+ (has-node (org-string-nw-p node)))
+ (cons
+ ;; Fallback to "org" is an arbirtrary choice
+ ;; and added because "(dir)filename" does not work as "filename".
+ (if has-file file (if has-node "org" "dir"))
+ (if has-node (string-trim node) "Top")))))
+
+(defun org-info-description-as-command (link desc)
+ "Info link description that can be pasted as command.
+
+For the following LINK
+
+ \"info:elisp#Non-ASCII in Strings\"
+
+the result is
+
+ info \"(elisp) Non-ASCII in Strings\"
+
+that may be executed as shell command or evaluated by
+\\[eval-expression] (wrapped with parenthesis) to read the manual
+in Emacs.
+
+Calling convention is similar to `org-link-make-description-function'.
+DESC has higher priority and returned when it is not nil or empty string.
+If LINK is not an info link then DESC is returned."
+ (let* ((prefix "info:")
+ (need-file-node (and (not (org-string-nw-p desc))
+ (string-prefix-p prefix link))))
+ (pcase (and need-file-node
+ (org-info--link-file-node (string-remove-prefix prefix link)))
+ ;; Unlike (info "dir"), "info dir" shell command opens "(coreutils)dir invocation"
+ (`("dir" . "Top") "info \"(dir)\"")
+ (`(,file . "Top") (format "info %s" file))
+ (`(,file . ,node) (format "info \"(%s) %s\"" file node))
+ (_ desc))))
(defun org-info-follow-link (name)
"Follow an Info file and node link specified by NAME."
- (if (or (string-match "\\(.*\\)\\(?:#\\|::\\)\\(.*\\)" name)
- (string-match "\\(.*\\)" name))
- (let ((filename (match-string 1 name))
- (nodename-or-index (or (match-string 2 name) "Top")))
- (require 'info)
- ;; If nodename-or-index is invalid node name, then look it up
- ;; in the index.
- (condition-case nil
- (Info-find-node filename nodename-or-index)
- (user-error (Info-find-node filename "Top")
- (condition-case nil
- (Info-index nodename-or-index)
- (user-error "Could not find '%s' node or index entry"
- nodename-or-index)))))
- (user-error "Could not open: %s" name)))
+ (pcase-let ((`(,filename . ,nodename-or-index)
+ (org-info--link-file-node name)))
+ (require 'info)
+ ;; If nodename-or-index is invalid node name, then look it up
+ ;; in the index.
+ (condition-case nil
+ (Info-find-node filename nodename-or-index)
+ (user-error (Info-find-node filename "Top")
+ (condition-case nil
+ (Info-index nodename-or-index)
+ (user-error "Could not find '%s' node or index entry"
+ nodename-or-index))))))
(defconst org-info-emacs-documents
'("ada-mode" "auth" "autotype" "bovine" "calc" "ccmode" "cl" "dbus" "dired-x"
@@ -95,7 +141,8 @@
Taken from <https://www.gnu.org/software/emacs/manual/html_mono/.>")
(defconst org-info-other-documents
- '(("libc" . "https://www.gnu.org/software/libc/manual/html_mono/libc.html")
+ '(("dir" . "https://www.gnu.org/manual/manual.html") ; index
+ ("libc" . "https://www.gnu.org/software/libc/manual/html_mono/libc.html")
("make" . "https://www.gnu.org/software/make/manual/make.html"))
"Alist of documents generated from Texinfo source.
When converting info links to HTML, links to any one of these manuals are
@@ -129,9 +176,7 @@ See `org-info-emacs-documents' and `org-info-other-documents' for details."
(defun org-info-export (path desc format)
"Export an info link.
See `org-link-parameters' for details about PATH, DESC and FORMAT."
- (let* ((parts (split-string path "#\\|::"))
- (manual (car parts))
- (node (or (nth 1 parts) "Top")))
+ (pcase-let ((`(,manual . ,node) (org-info--link-file-node path)))
(pcase format
(`html
(format "<a href=\"%s#%s\">%s</a>"
diff --git a/testing/lisp/test-org-info.el b/testing/lisp/test-org-info.el
index 94923169c..3b8f85a2b 100644
--- a/testing/lisp/test-org-info.el
+++ b/testing/lisp/test-org-info.el
@@ -28,6 +28,11 @@
(should
(equal (org-info-export "filename" nil 'html)
"<a href=\"filename.html#Top\">filename</a>"))
+ ;; Directory index. Top anchor actually should not be added,
+ ;; but it should be rather rare case to add special code path
+ (should
+ (equal (org-info-export "dir" nil 'html)
+ "<a href=\"https://www.gnu.org/manual/manual.html#Top\">dir</a>"))
;; When exporting to HTML, ensure node names are expanded according
;; to (info "(texinfo) HTML Xref Node Name Expansion").
(should
@@ -56,9 +61,84 @@
"@ref{Top,,,filename,}"))
(should
(equal (org-info-export "filename#node" nil 'texinfo)
- "@ref{node,,,filename,}")))
+ "@ref{node,,,filename,}"))
+ ;; "Top" is preserved, "::" as node separator.
+ (should
+ (equal "@ref{Top,,,emacs,}"
+ (org-info-export "emacs::Top" nil 'texinfo)))
+
+ ;; Description.
+ (should
+ (equal "@ref{Top,Emacs,,emacs,}"
+ (org-info-export "emacs" "Emacs" 'texinfo)))
+ (should
+ (equal "@ref{Destructuring with pcase Patterns,pcase-let,,emacs,}"
+ (org-info-export "emacs#Destructuring with pcase Patterns"
+ "pcase-let" 'texinfo))))
+(ert-deftest test-org-info/link-file-node ()
+ "Test parse info links by `org-info--link-file-node'."
+ (should (equal '("success" . "Hash Separator")
+ (org-info--link-file-node "success#Hash Separator")))
+ ;; Other separators
+ (should (equal '("success" . "Single Colon Separator")
+ (org-info--link-file-node "success:Single Colon Separator")))
+ (should (equal '("success" . "Double Colon Separator")
+ (org-info--link-file-node "success::Double Colon Separator")))
+ (should (equal '("success" . "Hash Colon Separator")
+ (org-info--link-file-node "success#:Hash Colon Separator")))
+ ;; Partial specification
+ (should (equal '("nodeless" . "Top")
+ (org-info--link-file-node "nodeless")))
+ (should (equal '("dir" . "Top")
+ (org-info--link-file-node "")))
+ (should (equal '("dir" . "Top")
+ (org-info--link-file-node nil)))
+ (should (equal '("org" . "Introduction")
+ (org-info--link-file-node "#Introduction")))
+ ;; Trailing separator
+ (should (equal '("trailing-hash" . "Top")
+ (org-info--link-file-node "trailing-hash#")))
+ (should (equal '("trailing-single-colon" . "Top")
+ (org-info--link-file-node "trailing-single-colon:")))
+ (should (equal '("trailing-double-colon" . "Top")
+ (org-info--link-file-node "trailing-double-colon::")))
+ (should (equal '("trailing-hash-colon" . "Top")
+ (org-info--link-file-node "trailing-hash-colon#:")))
+ ;; Trim spaces
+ (should (equal '("trim" . "Spaces")
+ (org-info--link-file-node " trim # Spaces \t"))))
+(ert-deftest test-org-info/description-as-command ()
+ "Test `org-info-description-as-command'."
+ (let ((cases
+ '(("info file" "info:file")
+ ("info strip-top-hash" "info:strip-top-hash#Top")
+ ("info strip-top-single-colon" "info:strip-top-single-colon:Top")
+ ("info strip-top-double-colon" "info:strip-top-double-colon::Top")
+ ("info \"(pass) Hash\"" "info:pass#Hash")
+ ("info \"(pass) Double Colon\"" "info:pass:: Double Colon")
+ ("info \"(info) Advanced\"" "info:info:Advanced")
+ ("info \"(dir)\"" "info:")
+ ("info \"(org) Tables\"" "info::Tables")
+ (nil "http://orgmode.org/index.html#Not-info-link"))))
+ (dolist (expectation-input cases)
+ (let ((expectation (car expectation-input))
+ (input (cadr expectation-input)))
+ (should (equal
+ expectation
+ (org-info-description-as-command input nil))))))
+ (let ((cases
+ '(("Override link" "info:ignored#Link" "Override link")
+ ("Fallback description" "http://not.info/link" "Fallback description")
+ ("Link is nil" nil "Link is nil"))))
+ (dolist (expectation-input-desc cases)
+ (let ((expectation (car expectation-input-desc))
+ (input (cadr expectation-input-desc))
+ (desc (nth 2 expectation-input-desc)))
+ (should (equal
+ expectation
+ (org-info-description-as-command input desc)))))))
(provide 'test-org-info)
;;; test-org-info.el ends here
--
2.25.1
next prev parent reply other threads:[~2022-08-14 16:42 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-28 23:15 ol.el: add description format parameter to org-link-parameters Hugo Heagren
2022-03-28 23:15 ` [PATCH] " Hugo Heagren
2022-04-04 9:49 ` Ihor Radchenko
2022-04-05 19:29 ` [PATCH v2] " Hugo Heagren
2022-04-07 5:13 ` Ihor Radchenko
2022-06-21 12:03 ` [PATCH v3] " Hugo Heagren
2022-06-21 13:41 ` Robert Pluim
2022-07-07 19:57 ` [PATCH v4] " Hugo Heagren
2022-07-09 3:31 ` Ihor Radchenko
2022-07-14 13:08 ` [PATCH v5] " Hugo Heagren
2022-07-16 9:09 ` Ihor Radchenko
2022-07-16 21:20 ` Hugo Heagren
2022-07-17 6:11 ` Max Nikulin
2022-07-17 10:27 ` Ihor Radchenko
2022-07-17 10:18 ` Ihor Radchenko
2022-07-17 20:59 ` [PATCH v6] " Hugo Heagren
2022-07-18 10:55 ` Max Nikulin
2022-07-23 7:48 ` [PATCH v7] " Hugo Heagren
2022-07-23 7:59 ` Max Nikulin
2022-07-23 13:06 ` Ihor Radchenko
2022-07-23 15:46 ` Max Nikulin
2022-07-24 10:34 ` Max Nikulin
2022-07-24 13:15 ` Ihor Radchenko
2022-07-25 11:55 ` [PATCH v8] " Hugo Heagren
2022-07-29 12:54 ` Max Nikulin
2022-07-29 19:05 ` [PATCH v9] " Hugo Heagren
2022-07-30 6:29 ` Ihor Radchenko
[not found] ` <87tu6zf2o1.fsf@heagren.com>
2022-07-30 8:02 ` Ihor Radchenko
2022-07-30 12:34 ` [PATCH] ol-info: Enable :insert-description feature Max Nikulin
2022-08-06 7:00 ` Ihor Radchenko
2022-08-14 16:41 ` Max Nikulin [this message]
2022-08-19 4:28 ` [PATCH v2] ol-info: Define :insert-description function Ihor Radchenko
2022-08-19 12:26 ` Max Nikulin
2022-08-20 7:29 ` Ihor Radchenko
2022-08-21 14:49 ` Max Nikulin
2022-08-22 4:10 ` Ihor Radchenko
2022-08-24 14:37 ` [PATCH v3] " Max Nikulin
2022-08-26 13:15 ` Ihor Radchenko
2022-09-04 15:05 ` [PATCH] ol-info: Enable :insert-description feature Max Nikulin
2022-09-05 6:36 ` Ihor Radchenko
2022-08-06 6:06 ` [PATCH v9] ol.el: add description format parameter to org-link-parameters Ihor Radchenko
2022-07-29 1:47 ` [PATCH v7] " Ihor Radchenko
2022-07-29 7:05 ` Bastien Guerry
2022-07-10 10:26 ` [PATCH v4] " Max Nikulin
2022-06-21 15:01 ` [PATCH v3] " Max Nikulin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.orgmode.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='tdb8kd$7mo$1@ciao.gmane.io' \
--to=manikulin@gmail.com \
--cc=emacs-orgmode@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).