From: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
To: Ihor Radchenko <yantar92@posteo.net>
Cc: stardiviner <numbchild@gmail.com>, Org mode <emacs-orgmode@gnu.org>
Subject: Re: [PATCH v9] Inline image display as part of a new org-link-preview system
Date: Mon, 28 Oct 2024 14:53:06 -0700 [thread overview]
Message-ID: <87a5enevy5.fsf@gmail.com> (raw)
In-Reply-To: <875xpcks8a.fsf@localhost>
[-- Attachment #1: Type: text/plain, Size: 6789 bytes --]
New patches attached.
>> -** Images
>> +** Images and link previews
>> +
>> +Org mode can preview hyperlinks in the buffer as images or with other
>> +decorations.
>
> May put a link to [*Hyperlinks] here.
Done.
> Also, this reads awkward. Maybe something like
>
> Org mode can display previews of [[*Hyperlinks][hyperlinks]] inside
> Org buffers. By default, only image links[fn::previews are
> available =file:= and =attachment:= links to image files; Emacs
> should be able to display the linked images (see ~image-types~
> variable)] can be previewed - the images are displayed in place of
> the link path.
>
> You can customize the previews as described in [*Adding Hyperlink
> preview] section. The custom previews do not have to display images
> - any kind of [[info:elisp#Overlay Properties][display decoration]]
> can be used.
Done.
>> +*** External link previews
>> +:PROPERTIES:
>> +:DESCRIPTION: Preview links in the buffer.
>> +:END:
>> +
>> +Org mode can preview [[* External links][External links]] as inline
>> +images or with other decorations. This requires support for the
>> +corresponding link types to be available[fn:: To add support for or to
>> +modify how links of a certain type are previewed, see .]. By default
> ^^^^^
>> +Org mode includes support for previewing file and attachment links for
>> +image files (see [[* Images][Images]]).
>
> The first sentence in the parent section and the first sentence here are
> basically identical. As I suggested above, let's move these things
> up. (Feel free to modify my version as you see fit.)
This is the new organization:
--8<---------------cut here---------------start------------->8---
* Images and link previews
[Explanation of link previews and org-link-preview]
** Images
[Image-preview-specific options]
--8<---------------cut here---------------end--------------->8---
The section "External link previews" is gone, moved up into the parent.
>> +Supported link types can be previewed within the buffer with the
>> +following commands:
>
> If possible, try to use active voice. We are talking to users in the
> manual:
>
> You can preview the supported link types in the whole buffer, in
> current section, region, or at point.
Used active voice.
>> + By default, only image links without a description are displayed.
>> + You can force displaying previews for all supported links using
>> + numeric argument to toggle all the images in current section (~1~
>> + argument) or the whole buffer (~11~ argument).
>
> This is not accurate. C-1 argument will act just as no argument: display
> links at point/region/section depending on the context.
>
> Looks like the docstring wrt C-1/C-11 args should be improved.
Improved both in the manual and in the command documentation in ol.el.
> Org mode can display link previews automatically when opening
> buffers. Either customize ~org-startup-with-inline-images~ or use
> in-buffer =#+STARTUP: inlineimages= keyword to enable the previews.
> =#+STARTUP: noinlineimages= will disable the previews in specific
> buffer.
>
> Also, since we are generalizing things away from images, maybe we should
> introduce some aliases to ~org-startup-with-inline-images~ and to the
> startup option values. Something like ~org-startup-with-link-previews~
> and similar to startup option.
Okay. I've added `org-startup-with-link-previews' and the "linkpreview",
"nolinkpreview" STARTUP options.
`org-startup-with-inline-images' is an alias to the new variable, and
the "inlineimages" and "noinlineimages" options are still respected.
There are two other changes. In org-cycle, I've renamed
`org-cycle-inline-images-display' and `org-cycle-display-inline-images'
to `org-cycle-link-previews-display' and
`org-cycle-display-link-previews'. The older user option and function
are aliased to the new ones.
There are new entries in ORG-NEWS documenting these renames.
None of the above have been obsoleted. Let me know if I should make
them all obsolete aliases instead.
>> +- {{{kbd(C-c C-x C-M-v)}}} (~org-link-preview-refresh~) ::
>> +
>> + #+kindex: C-c C-x C-M-v
>> + #+findex: org-link-preview-refresh
>> + Assure inline display of external link previews in the buffer and
>> + refresh them.
>
> "the whole buffer" maybe.
Done.
>> +Such images can be displayed within the buffer with the
>> +~org-link-preview~ and associated command, see [[*External link previews]].
>
> This sentence reads awkwardly after the previous section that already
> talked about previews and ~org-link-preview~ command.
>
> We can write
>
> When [[*External link previews][link previews]] are displayed as
> images, the image size and alignment can be further customized.
Done.
>> #+vindex: org-cycle-inline-images-display
>> Inline images can also be displayed when cycling the folding state.
>> @@ -21794,6 +21833,65 @@ ** Adding Hyperlink Types
>> topic. It also provides a default description. The function
>> ~org-insert-link~ can insert it back into an Org buffer later on.
>
> I suspect that it belongs to the previous section - cycling should work
> for all the link previews, not just images.
Done.
>> +2. The value of the =:preview= parameter must be a function that
>> + accepts three arguments:
>> + - an overlay placed from the start to the end of the link,
>> + - the link path, as a string, and
>> + - the link element.
>
> element -> syntax node
Done.
>> + It must return a non-nil value to indicate preview success.
>
> ... and your example can sometimes return nil, which is confusing.
> We should explain what nil means as a return value as well.
Done.
>> --- a/etc/ORG-NEWS
>> +++ b/etc/ORG-NEWS
>> @@ -24,26 +24,38 @@ Previously, =C-c C-x C-v= always toggled image display in the whole
>> buffer (or narrowed part of the buffer). With prefix argument, it
>> also forced displaying image links with description.
>> ...
>> +2. Otherwise, if there is an image at point, it is toggled. If there
>> + is no image at point, images in the current entry are previewed
>
> Aside: I am wondering if we should also attach preview toggle to <TAB>
> when ~org-cycle-inline-images-display~ is non-nil and there is a link at
> point.
I haven't added this. I think it's better not to do this, since cycling
is expected to only involve hiding/showing buffer contents. Generating
a preview might cause new external processes to be started. I can add
it if you think we should.
>> +*** New option ~org-link-preview-batch-size~
>> +
>> +Org link previews are generated a few at a time, in batches. This
>
> asynchronously
Done, both in the manual and in the variable docstring.
Karthik
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-org-link-Customizable-preview-API-for-arbitrary-link.patch --]
[-- Type: text/x-patch, Size: 75484 bytes --]
From 9809676310228a927ddea84aced6d26bb9f9dc8e Mon Sep 17 00:00:00 2001
From: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Date: Fri, 23 Aug 2024 15:46:53 -0700
Subject: [PATCH 1/2] org-link: Customizable preview API for arbitrary link
types
Add a customizable preview API for arbitrary link types. Make
inline image previews a part of the more universal org-link
preview feature. Each link type can now be previewed differently
based on a new link parameter.
* lisp/ol.el (org-link-parameters, org-link-preview-batch-size,
org-link-preview-delay, org-link-preview--timer,
org-link-preview--queue, org-link-preview-overlays,
org-link-preview--get-overlays, org-link-preview--remove-overlay,
org-link-preview, org-link-preview-region,
org-link-preview--process-queue, org-link-preview-clear,
org-link-preview-file, org-display-remote-inline-images,
org-image-align, org--create-inline-image,
org-display-inline-image--width, org-image--align): Add new
commands `org-link-preview', `org-link-preview-region' and
`org-link-preview-clear' for creating link previews for any kind
of link. Add new org-link parameter `:preview' for specifying how
a link type should be previewed. This link parameter is a
function called asynchronously to place previes. File links and
attachments are previewed using inline image previews as before.
Move image handling utilities from lisp/org.el to lisp/ol.el.
* testing/lisp/test-org-fold.el: Use `org-link-preview'.
* lisp/org.el (org-toggle-inline-images,
org-toggle-inline-images-command, org-display-inline-images,
org--inline-image-overlays, org-inline-image-overlays,
org-redisplay-inline-images, org-image-align,
org-display-inline-remove-overlay, org-remove-inline-images,
org-startup-with-inline-images, org-startup-with-link-previews,
org-startup-options): Obsolete and move
`org-toggle-inline-images', `org-display-inline-images' and
`org-redisplay-inline-images' to org-compat. These are obsoleted
by `org-link-preview' and `org-link-preview-region'. Remove
`org-toggle-inline-images-command'. Move the other internal
functions to org-link. Rename `org-startup-with-inline-images' to
`org-startup-with-link-previews'. Add new STARTUP options for
link previews to `org-startup-options': "linkpreviews" and
"nolinkpreviews".
* lisp/org-plot.el (org-plot/redisplay-img-in-buffer): Modify to
use `org-link-preview'.
* lisp/org-keys.el: Bind `C-c C-x C-v' to new command
`org-link-preview', which has the same prefix arg behaviors as
`org-latex-preview'. In addition to these, it supports numeric
prefix args 1 and 11 to preview links with descriptions at
point/region (with 1) and across the buffer (with 11).
* lisp/org-cycle.el (org-cycle-display-inline-images,
org-cycle-display-link-previews, org-cycle-inline-images-display,
org-cycle-inline-link-previews): Use `org-link-preview' and
`org-link-preview-region'. Rename inline-images functions and user
options to their link-previews equivalents:
- `org-cycle-display-inline-images' to `org-cycle-display-link-previews'
- `org-cycle-inline-images-display' to `org-cycle-inline-link-previews'
* lisp/org-compat.el (org-display-inline-remove-overlay,
org--inline-image-overlays, org-remove-inline-images,
org-inline-image-overlays, org-display-inline-images,
org-toggle-inline-images):
* lisp/org-attach.el (org-attach-preview-file): Add new `:preview'
link parameter for links of type "attachment", set to the new
function `org-attach-preview-file'.
---
lisp/ol.el | 602 +++++++++++++++++++++++++++++++++-
lisp/org-attach.el | 11 +-
lisp/org-compat.el | 185 +++++++++++
lisp/org-cycle.el | 31 +-
lisp/org-keys.el | 7 +-
lisp/org-plot.el | 2 +-
lisp/org.el | 554 +------------------------------
testing/lisp/test-org-fold.el | 4 +-
8 files changed, 838 insertions(+), 558 deletions(-)
diff --git a/lisp/ol.el b/lisp/ol.el
index 52ea62d69..2c0b80dfe 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -82,6 +82,11 @@ (declare-function org-src-source-buffer "org-src" ())
(declare-function org-src-source-type "org-src" ())
(declare-function org-time-stamp-format "org" (&optional long inactive))
(declare-function outline-next-heading "outline" ())
+(declare-function image-flush "image" (spec &optional frame))
+(declare-function org-entry-end-position "org" ())
+(declare-function org-element-contents-begin "org-element" (node))
+(declare-function org-element-contents-end "org-element" (node))
+(declare-function org-property-or-variable-value "org" (var &optional inherit))
\f
;;; Customization
@@ -171,6 +176,18 @@ (defcustom org-link-parameters nil
The default face is `org-link'.
+`:preview'
+
+ Function to run to generate an in-buffer preview for the link. It
+ must accept three arguments:
+ - an overlay placed from the start to the end of the link
+ - the link path, as a string
+ - the syntax node for the link
+
+ This function must return a non-nil value to indicate success.
+ A return value of nil implies that the preview failed, and the
+ overlay placed on the link will be removed.
+
`:help-echo'
String or function used as a value for the `help-echo' text
@@ -521,6 +538,81 @@ (defcustom org-link-keep-stored-after-insertion nil
:type 'boolean
:safe #'booleanp)
+(defcustom org-link-preview-delay 0.05
+ "Idle delay in seconds between link previews when using
+`org-link-preview'. Links are previewed in batches (see
+`org-link-preview-batch-size') spaced out by this delay. Set
+this to a small number for more immediate previews, but at the
+expense of higher lag."
+ :group 'org-link
+ :type 'number)
+
+(defcustom org-link-preview-batch-size 6
+ "Number of links that are previewed at once with
+`org-link-preview'. Links are previewed asynchronously, in
+batches spaced out in time (see `org-link-preview-delay'). Set
+this to a large integer for more immediate previews, but at the
+expense of higher lag."
+ :group 'org-link
+ :type 'natnum)
+
+(defcustom org-display-remote-inline-images 'skip
+ "How to display remote inline images.
+Possible values of this option are:
+
+skip Don't display remote images.
+download Always download and display remote images.
+t
+cache Display remote images, and open them in separate buffers
+ for caching. Silently update the image buffer when a file
+ change is detected."
+ :group 'org-appearance
+ :package-version '(Org . "9.7")
+ :type '(choice
+ (const :tag "Ignore remote images" skip)
+ (const :tag "Always display remote images" download)
+ (const :tag "Display and silently update remote images" cache))
+ :safe #'symbolp)
+
+(defcustom org-image-max-width 'fill-column
+ "When non-nil, limit the displayed image width.
+This setting only takes effect when `org-image-actual-width' is set to
+t or when #+ATTR* is set to t.
+
+Possible values:
+- `fill-column' :: limit width to `fill-column'
+- `window' :: limit width to window width
+- integer :: limit width to number in pixels
+- float :: limit width to that fraction of window width
+- nil :: do not limit image width"
+ :group 'org-appearance
+ :package-version '(Org . "9.7")
+ :type '(choice
+ (const :tag "Do not limit image width" nil)
+ (const :tag "Limit to `fill-column'" fill-column)
+ (const :tag "Limit to window width" window)
+ (integer :tag "Limit to a number of pixels")
+ (float :tag "Limit to a fraction of window width")))
+
+(defcustom org-image-align 'left
+ "How to align images previewed using `org-link-preview-region'.
+
+Only stand-alone image links are affected by this setting. These
+are links without surrounding text.
+
+Possible values of this option are:
+
+left Insert image at specified position.
+center Center image previews.
+right Right-align image previews."
+ :group 'org-appearance
+ :package-version '(Org . "9.7")
+ :type '(choice
+ (const :tag "Left align (or don\\='t align) image previews" left)
+ (const :tag "Center image previews" center)
+ (const :tag "Right align image previews" right))
+ :safe #'symbolp)
+
;;; Public variables
(defconst org-target-regexp (let ((border "[^<>\n\r \t]"))
@@ -649,6 +741,29 @@ (defvar org-link--insert-history nil
(defvar org-link--search-failed nil
"Non-nil when last link search failed.")
+(defvar-local org-link-preview-overlays nil)
+;; Preserve when switching modes or when restarting Org.
+;; If we clear the overlay list and later enable Or mode, the existing
+;; image overlays will never be cleared by `org-link-preview'
+;; and `org-link-preview-clear'.
+(put 'org-link-preview-overlays 'permanent-local t)
+
+(defvar-local org-link-preview--timer nil
+ "Timer for previewing Org links in buffer.
+
+This timer creates previews for specs in
+`org-link-preview--queue'.")
+
+(defvar-local org-link-preview--queue nil
+ "Queue of pending previews for Org links in buffer.
+
+Each element of this queue is a list of the form
+
+(PREVIEW-FUNC OVERLAY PATH LINK)
+
+where PREVIEW-FUNC places a preview of PATH using OVERLAY. LINK
+is the Org element being previewed.")
+
\f
;;; Internal Functions
@@ -881,7 +996,227 @@ (defun org-link--file-link-to-here ()
(setq desc search-desc))))
(cons link desc)))
+(defun org-link-preview--get-overlays (&optional beg end)
+ "Return link preview overlays between BEG and END."
+ (let* ((beg (or beg (point-min)))
+ (end (or end (point-max)))
+ (overlays (overlays-in beg end))
+ result)
+ (dolist (ov overlays result)
+ (when (memq ov org-link-preview-overlays)
+ (push ov result)))))
+
+(defun org-link-preview--remove-overlay (ov after _beg _end &optional _len)
+ "Remove link-preview overlay OV if a corresponding region is modified.
+
+AFTER is true when this function is called post-change."
+ (when (and ov after)
+ (setq org-link-preview-overlays (delq ov org-link-preview-overlays))
+ ;; Clear image from cache to avoid image not updating upon
+ ;; changing on disk. See Emacs bug#59902.
+ (when-let* ((disp (overlay-get ov 'display))
+ ((imagep disp)))
+ (image-flush disp))
+ (delete-overlay ov)))
+
\f
+
+;;;; Utilities for image preview display
+
+;; For without-x builds.
+(declare-function image-flush "image" (spec &optional frame))
+
+(defun org--create-inline-image (file width)
+ "Create image located at FILE, or return nil.
+WIDTH is the width of the image. The image may not be created
+according to the value of `org-display-remote-inline-images'."
+ (let* ((remote? (file-remote-p file))
+ (file-or-data
+ (pcase org-display-remote-inline-images
+ ((guard (not remote?)) file)
+ (`download (with-temp-buffer
+ (set-buffer-multibyte nil)
+ (insert-file-contents-literally file)
+ (buffer-string)))
+ ((or `cache `t)
+ (let ((revert-without-query '(".")))
+ (with-current-buffer (find-file-noselect file)
+ (buffer-string))))
+ (`skip nil)
+ (other
+ (message "Invalid value of `org-display-remote-inline-images': %S"
+ other)
+ nil))))
+ (when file-or-data
+ (create-image file-or-data
+ (and (image-type-available-p 'imagemagick)
+ width
+ 'imagemagick)
+ remote?
+ :width width
+ :max-width
+ (pcase org-image-max-width
+ (`fill-column (* fill-column (frame-char-width (selected-frame))))
+ (`window (window-width nil t))
+ ((pred integerp) org-image-max-width)
+ ((pred floatp) (floor (* org-image-max-width (window-width nil t))))
+ (`nil nil)
+ (_ (error "Unsupported value of `org-image-max-width': %S"
+ org-image-max-width)))
+ :scale 1))))
+
+(declare-function org-export-read-attribute "ox"
+ (attribute element &optional property))
+(defvar visual-fill-column-width) ; Silence compiler warning
+(defun org-display-inline-image--width (link)
+ "Determine the display width of the image LINK, in pixels.
+- When `org-image-actual-width' is t, the image's pixel width is used.
+- When `org-image-actual-width' is a number, that value will is used.
+- When `org-image-actual-width' is nil or a list, :width attribute of
+ #+attr_org or the first #+attr_... (if it exists) is used to set the
+ image width. A width of X% is divided by 100. If the value is a
+ float between 0 and 2, it interpreted as that proportion of the text
+ width in the buffer.
+
+ If no :width attribute is given and `org-image-actual-width' is a
+ list with a number as the car, then that number is used as the
+ default value."
+ ;; Apply `org-image-actual-width' specifications.
+ ;; Support subtree-level property "ORG-IMAGE-ACTUAL-WIDTH" specified
+ ;; width.
+ (let ((org-image-actual-width (org-property-or-variable-value 'org-image-actual-width)))
+ (cond
+ ((eq org-image-actual-width t) nil)
+ ((listp org-image-actual-width)
+ (require 'ox)
+ (let* ((par (org-element-lineage link 'paragraph))
+ ;; Try to find an attribute providing a :width.
+ ;; #+ATTR_ORG: :width ...
+ (attr-width (org-export-read-attribute :attr_org par :width))
+ (width-unreadable?
+ (lambda (value)
+ (or (not (stringp value))
+ (unless (string= value "t")
+ (or (not (string-match
+ (rx bos (opt "+")
+ (or
+ ;; Number of pixels
+ ;; must be a lone number, not
+ ;; things like 4in
+ (seq (1+ (in "0-9")) eos)
+ ;; Numbers ending with %
+ (seq (1+ (in "0-9.")) (group-n 1 "%"))
+ ;; Fractions
+ (seq (0+ (in "0-9")) "." (1+ (in "0-9")))))
+ value))
+ (let ((number (string-to-number value)))
+ (and (floatp number)
+ (not (match-string 1 value)) ; X%
+ (not (<= 0.0 number 2.0)))))))))
+ ;; #+ATTR_BACKEND: :width ...
+ (attr-other
+ (catch :found
+ (org-element-properties-map
+ (lambda (prop _)
+ (when (and
+ (not (eq prop :attr_org))
+ (string-match-p "^:attr_" (symbol-name prop))
+ (not (funcall width-unreadable? (org-export-read-attribute prop par :width))))
+ (throw :found prop)))
+ par)))
+ (attr-width
+ (if (not (funcall width-unreadable? attr-width))
+ attr-width
+ ;; When #+attr_org: does not have readable :width
+ (and attr-other
+ (org-export-read-attribute attr-other par :width))))
+ (width
+ (cond
+ ;; Treat :width t as if `org-image-actual-width' were t.
+ ((string= attr-width "t") nil)
+ ;; Fallback to `org-image-actual-width' if no interprable width is given.
+ ((funcall width-unreadable? attr-width)
+ (car org-image-actual-width))
+ ;; Convert numeric widths to numbers, converting percentages.
+ ((string-match-p "\\`[[+]?[0-9.]+%" attr-width)
+ (/ (string-to-number attr-width) 100.0))
+ (t (string-to-number attr-width)))))
+ (if (and (floatp width) (<= 0.0 width 2.0))
+ ;; A float in [0,2] should be interpereted as this portion of
+ ;; the text width in the window. This works well with cases like
+ ;; #+attr_latex: :width 0.X\{line,page,column,etc.}width,
+ ;; as the "0.X" is pulled out as a float. We use 2 as the upper
+ ;; bound as cases such as 1.2\linewidth are feasible.
+ (round (* width
+ (window-pixel-width)
+ (/ (or (and (bound-and-true-p visual-fill-column-mode)
+ (or visual-fill-column-width auto-fill-function))
+ (when auto-fill-function fill-column)
+ (- (window-text-width) (line-number-display-width)))
+ (float (window-total-width)))))
+ width)))
+ ((numberp org-image-actual-width)
+ org-image-actual-width)
+ (t nil))))
+
+(defun org-image--align (link)
+ "Determine the alignment of the image LINK.
+LINK is a link object.
+
+In decreasing order of priority, this is controlled:
+- Per image by the value of `:center' or `:align' in the
+affiliated keyword `#+attr_org'.
+- By the `#+attr_html' or `#+attr_latex` keywords with valid
+ `:center' or `:align' values.
+- Globally by the user option `org-image-align'.
+
+The result is either nil or one of the strings \"left\",
+\"center\" or \"right\".
+
+\"center\" will cause the image preview to be centered, \"right\"
+will cause it to be right-aligned. A value of \"left\" or nil
+implies no special alignment."
+ (let ((par (org-element-lineage link 'paragraph)))
+ ;; Only align when image is not surrounded by paragraph text:
+ (when (and par ; when image is not in paragraph, but in table/headline/etc, do not align
+ (= (org-element-begin link)
+ (save-excursion
+ (goto-char (org-element-contents-begin par))
+ (skip-chars-forward "\t ")
+ (point))) ;account for leading space
+ ;before link
+ (<= (- (org-element-contents-end par)
+ (org-element-end link))
+ 1)) ;account for trailing newline
+ ;at end of paragraph
+ (save-match-data
+ ;; Look for a valid ":center t" or ":align left|center|right"
+ ;; attribute.
+ ;;
+ ;; An attr_org keyword has the highest priority, with
+ ;; any attr.* next. Choosing between these is
+ ;; unspecified.
+ (let ((center-re ":\\(center\\)[[:space:]]+t\\b")
+ (align-re ":align[[:space:]]+\\(left\\|center\\|right\\)\\b")
+ attr-align)
+ (catch 'exit
+ (org-element-properties-mapc
+ (lambda (propname propval)
+ (when (and propval
+ (string-match-p ":attr.*" (symbol-name propname)))
+ (setq propval (car-safe propval))
+ (when (or (string-match center-re propval)
+ (string-match align-re propval))
+ (setq attr-align (match-string 1 propval))
+ (when (eq propname :attr_org)
+ (throw 'exit t)))))
+ par))
+ (if attr-align
+ (when (member attr-align '("center" "right")) attr-align)
+ ;; No image-specific keyword, check global alignment property
+ (when (memq org-image-align '(center right))
+ (symbol-name org-image-align))))))))
+
;;; Public API
(defun org-link-types ()
@@ -1573,6 +1908,230 @@ (defun org-link-add-angle-brackets (s)
(unless (equal (substring s -1) ">") (setq s (concat s ">")))
s)
+;;;###autoload
+(defun org-link-preview (&optional arg beg end)
+ "Toggle display of link previews in the buffer.
+
+When region BEG..END is active, preview links in the
+region.
+
+When point is at a link, display a preview for that link only.
+Otherwise, display previews for links in current entry.
+
+With numeric prefix ARG 1, also preview links with description in
+the active region, at point or in the current section.
+
+With prefix ARG `\\[universal-argument]', clear link previews at
+point or in the current entry.
+
+With prefix ARG `\\[universal-argument] \\[universal-argument]',
+ display link previews in the accessible portion of the
+ buffer. With numeric prefix ARG 11, do the same, but include
+ links with descriptions.
+
+With prefix ARG `\\[universal-argument] \\[universal-argument] \\[universal-argument]',
+hide all link previews in the accessible portion of the buffer.
+
+This command is designed for interactive use. From Elisp, you can
+also use `org-link-preview-region'."
+ (interactive (cons current-prefix-arg
+ (when (use-region-p)
+ (list (region-beginning) (region-end)))))
+ (let* ((include-linked
+ (cond
+ ((member arg '(nil (4) (16)) ) nil)
+ ((member arg '(1 11)) 'include-linked)
+ (t 'include-linked)))
+ (interactive? (called-interactively-p 'any))
+ (toggle-previews
+ (lambda (&optional beg end scope remove)
+ (let* ((beg (or beg (point-min)))
+ (end (or end (point-max)))
+ (old (org-link-preview--get-overlays beg end))
+ (scope (or scope (format "%d:%d" beg end))))
+ (if remove
+ (progn
+ (org-link-preview-clear beg end)
+ (when interactive?
+ (message
+ "[%s] Inline link previews turned off (removed %d images)"
+ scope (length old))))
+ (org-link-preview-region include-linked t beg end)
+ (when interactive?
+ (let ((new (org-link-preview--get-overlays beg end)))
+ (message
+ (if new
+ (format "[%s] Displaying %d images inline %s"
+ scope (length new)
+ (if include-linked "(including images with description)"
+ ""))
+ (format "[%s] No images to display inline" scope))))))))))
+ (cond
+ ;; Region selected :: display previews in region.
+ ((and beg end)
+ (funcall toggle-previews beg end "region"
+ (and (equal arg '(4)) 'remove)))
+ ;; C-u argument: clear image at point or in entry
+ ((equal arg '(4))
+ (if-let ((ov (cdr (get-char-property-and-overlay
+ (point) 'org-image-overlay))))
+ ;; clear link preview at point
+ (funcall toggle-previews
+ (overlay-start ov) (overlay-end ov)
+ "preview at point" 'remove)
+ ;; Clear link previews in entry
+ (funcall toggle-previews
+ (if (org-before-first-heading-p) (point-min)
+ (save-excursion
+ (org-with-limited-levels (org-back-to-heading t) (point))))
+ (org-with-limited-levels (org-entry-end-position))
+ "current section" 'remove)))
+ ;; C-u C-u or C-11 argument :: display images in the whole buffer.
+ ((member arg '(11 (16))) (funcall toggle-previews nil nil "buffer"))
+ ;; C-u C-u C-u argument :: unconditionally hide images in the buffer.
+ ((equal arg '(64)) (funcall toggle-previews nil nil "buffer" 'remove))
+ ;; Argument nil or 1, no region selected :: display images in
+ ;; current section or image link at point.
+ ((and (member arg '(nil 1)) (null beg) (null end))
+ (let ((context (org-element-context)))
+ ;; toggle display of inline image link at point.
+ (if (org-element-type-p context 'link)
+ (let* ((ov (cdr-safe (get-char-property-and-overlay
+ (point) 'org-image-overlay)))
+ (remove? (and ov (memq ov org-link-preview-overlays)
+ 'remove)))
+ (funcall toggle-previews
+ (org-element-begin context)
+ (org-element-end context)
+ "image at point" remove?))
+ (let ((beg (if (org-before-first-heading-p) (point-min)
+ (save-excursion
+ (org-with-limited-levels (org-back-to-heading t) (point)))))
+ (end (org-with-limited-levels (org-entry-end-position))))
+ (funcall toggle-previews beg end "current section")))))
+ ;; Any other non-nil argument.
+ ((not (null arg)) (funcall toggle-previews beg end "region")))))
+
+;;;###autoload
+(defun org-link-preview-refresh ()
+ "Assure display of link previews in buffer and refresh them."
+ (interactive)
+ (org-link-preview-region nil t (point-min) (point-max)))
+
+(defun org-link-preview-region (&optional include-linked refresh beg end)
+ "Display link previews.
+
+A previewable link type is one that has a `:preview' link
+parameter, see `org-link-parameters'.
+
+By default, a file link or attachment is previewable if it
+follows either of these conventions:
+
+ 1. Its path is a file with an extension matching return value
+ from `image-file-name-regexp' and it has no contents.
+
+ 2. Its description consists in a single link of the previous
+ type. In this case, that link must be a well-formed plain
+ or angle link, i.e., it must have an explicit \"file\" or
+ \"attachment\" type.
+
+File links are equipped with the keymap `image-map'.
+
+When optional argument INCLUDE-LINKED is non-nil, links with a
+text description part will also be inlined. This can be nice for
+a quick look at those images, but it does not reflect what
+exported files will look like.
+
+When optional argument REFRESH is non-nil, refresh existing
+images between BEG and END. This will create new image displays
+only if necessary.
+
+BEG and END define the considered part. They default to the
+buffer boundaries with possible narrowing."
+ (interactive "P")
+ (when refresh (org-link-preview-clear beg end))
+ (org-with-point-at (or beg (point-min))
+ (let ((case-fold-search t)
+ preview-queue)
+ ;; Collect links to preview
+ (while (re-search-forward org-link-any-re end t)
+ (when-let*
+ ((link (org-element-lineage (org-element-context) 'link t))
+ (linktype (org-element-property :type link))
+ (preview-func (org-link-get-parameter linktype :preview))
+ (path (and (or include-linked
+ (not (org-element-contents-begin link)))
+ (org-element-property :path link))))
+ ;; Create an overlay to hold the preview
+ (let ((ov (make-overlay
+ (org-element-begin link)
+ (progn
+ (goto-char
+ (org-element-end link))
+ (unless (eolp) (skip-chars-backward " \t"))
+ (point)))))
+ (overlay-put ov 'modification-hooks
+ (list 'org-link-preview--remove-overlay))
+ (push ov org-link-preview-overlays)
+ (push (list preview-func ov path link) preview-queue))))
+ ;; Collect previews in buffer-local LIFO preview queue
+ (setq org-link-preview--queue
+ (nconc (nreverse preview-queue) org-link-preview--queue))
+ ;; Run preview possibly asynchronously
+ (when org-link-preview--queue
+ (org-link-preview--process-queue (current-buffer))))))
+
+(defun org-link-preview--process-queue (org-buffer)
+ "Preview pending Org link previews in ORG-BUFFER.
+
+Previews are generated from the specs in
+`org-link-preview--queue', which see."
+ (and (buffer-live-p org-buffer)
+ (with-current-buffer org-buffer
+ (cl-loop
+ for spec in org-link-preview--queue
+ for ov = (cadr spec) ;SPEC is (preview-func ov path link)
+ for count from org-link-preview-batch-size above 0
+ do (pop org-link-preview--queue)
+ if (overlay-buffer ov) do
+ (if (apply spec)
+ (overlay-put ov 'org-image-overlay t)
+ ;; Preview was unsuccessful, delete overlay
+ (delete-overlay ov)
+ (setq org-link-preview-overlays
+ (delq ov org-link-preview-overlays)))
+ else do (cl-incf count) end
+ finally do
+ (setq org-link-preview--timer
+ (and org-link-preview--queue
+ (run-with-idle-timer
+ (time-add (or (current-idle-time) 0)
+ org-link-preview-delay)
+ nil #'org-link-preview--process-queue org-buffer)))))))
+
+(defun org-link-preview-clear (&optional beg end)
+ "Clear link previews in region BEG to END."
+ (interactive (and (use-region-p) (list (region-beginning) (region-end))))
+ (let* ((beg (or beg (point-min)))
+ (end (or end (point-max)))
+ (overlays (overlays-in beg end)))
+ (dolist (ov overlays)
+ (when (memq ov org-link-preview-overlays)
+ ;; Remove pending preview tasks between BEG and END
+ (when-let ((spec (cl-find ov org-link-preview--queue
+ :key #'cadr)))
+ (setq org-link-preview--queue (delq spec org-link-preview--queue)))
+ ;; Remove placed overlays between BEG and END
+ (when-let ((image (overlay-get ov 'display))
+ ((imagep image)))
+ (image-flush image))
+ (setq org-link-preview-overlays (delq ov org-link-preview-overlays))
+ (delete-overlay ov)))
+ ;; Clear removed overlays.
+ (dolist (ov org-link-preview-overlays)
+ (unless (overlay-buffer ov)
+ (setq org-link-preview-overlays (delq ov org-link-preview-overlays))))))
+
\f
;;; Built-in link types
@@ -1595,7 +2154,48 @@ (defun org-link--open-elisp (path _)
(org-link-set-parameters "elisp" :follow #'org-link--open-elisp)
;;;; "file" link type
-(org-link-set-parameters "file" :complete #'org-link-complete-file)
+(org-link-set-parameters "file"
+ :complete #'org-link-complete-file
+ :preview #'org-link-preview-file)
+
+(defun org-link-preview-file (ov path link)
+ "Display image file PATH in overlay OV for LINK.
+
+LINK is the Org element being previewed.
+
+Equip each image with the keymap `image-map'.
+
+This is intended to be used as the `:preview' link property of
+file links, see `org-link-parameters'."
+ (if (not (display-graphic-p))
+ (prog1 nil
+ (message "Your Emacs does not support displaying images!"))
+ (require 'image)
+ (when-let* ((file-full (expand-file-name path))
+ (file (substitute-in-file-name file-full))
+ ((string-match-p (image-file-name-regexp) file))
+ ((file-exists-p file)))
+ (let* ((width (org-display-inline-image--width link))
+ (align (org-image--align link))
+ (image (org--create-inline-image file width)))
+ (when image ; Add image to overlay
+ ;; See bug#59902. We cannot rely
+ ;; on Emacs to update image if the file
+ ;; has changed.
+ (image-flush image)
+ (overlay-put ov 'display image)
+ (overlay-put ov 'face 'default)
+ (overlay-put ov 'keymap image-map)
+ (when align
+ (overlay-put
+ ov 'before-string
+ (propertize
+ " " 'face 'default
+ 'display
+ (pcase align
+ ("center" `(space :align-to (- center (0.5 . ,image))))
+ ("right" `(space :align-to (- right ,image)))))))
+ t)))))
;;;; "help" link type
(defun org-link--open-help (path _)
diff --git a/lisp/org-attach.el b/lisp/org-attach.el
index 7a03d170e..3c4e1bfb6 100644
--- a/lisp/org-attach.el
+++ b/lisp/org-attach.el
@@ -797,9 +797,18 @@ (defun org-attach-follow (file arg)
See `org-open-file' for details about ARG."
(org-link-open-as-file (org-attach-expand file) arg))
+(defun org-attach-preview-file (ov path link)
+ "Preview attachment with PATH in overlay OV.
+
+LINK is the Org link element being previewed."
+ (org-with-point-at (org-element-begin link)
+ (org-link-preview-file
+ ov (org-attach-expand path) link)))
+
(org-link-set-parameters "attachment"
:follow #'org-attach-follow
- :complete #'org-attach-complete-link)
+ :complete #'org-attach-complete-link
+ :preview #'org-attach-preview-file)
(defun org-attach-complete-link ()
"Advise the user with the available files in the attachment directory."
diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index d843216f3..885a5245d 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -1010,6 +1010,191 @@ (define-obsolete-function-alias 'org-open-link-from-string
(define-obsolete-function-alias 'org-add-angle-brackets
'org-link-add-angle-brackets "9.3")
+(declare-function org-link-preview--remove-overlay "ol"
+ (ov after beg end &optional len))
+(declare-function org-link-preview--get-overlays "ol" (&optional beg end))
+(declare-function org-link-preview-clear "ol" (&optional beg end))
+(declare-function org-link-preview--remove-overlay "ol"
+ (ov after beg end &optional len))
+(declare-function org-attach-expand "org-attach" (file))
+(declare-function org-display-inline-image--width "org" (link))
+(declare-function org-image--align "org" (link))
+(declare-function org--create-inline-image "org" (file width))
+
+(define-obsolete-function-alias 'org-display-inline-remove-overlay
+ 'org-link-preview--remove-overlay "9.8")
+(define-obsolete-function-alias 'org--inline-image-overlays
+ 'org-link-preview--get-overlays "9.8")
+(define-obsolete-function-alias 'org-remove-inline-images
+ 'org-link-preview-clear "9.8")
+(define-obsolete-function-alias 'org-redisplay-inline-images
+ 'org-link-preview-refresh "9.8")
+(define-obsolete-variable-alias 'org-inline-image-overlays
+ 'org-link-preview-overlays "9.8")
+(defvar org-link-preview-overlays)
+(defvar org-link-abbrev-alist-local)
+(defvar org-link-abbrev-alist)
+(defvar org-link-angle-re)
+(defvar org-link-plain-re)
+
+(make-obsolete 'org-display-inline-images
+ 'org-link-preview-region "9.8")
+;; FIXME: Unused; obsoleted; to be removed
+(defun org-display-inline-images (&optional include-linked refresh beg end)
+ "Display inline images.
+
+An inline image is a link which follows either of these
+conventions:
+
+ 1. Its path is a file with an extension matching return value
+ from `image-file-name-regexp' and it has no contents.
+
+ 2. Its description consists in a single link of the previous
+ type. In this case, that link must be a well-formed plain
+ or angle link, i.e., it must have an explicit \"file\" or
+ \"attachment\" type.
+
+Equip each image with the key-map `image-map'.
+
+When optional argument INCLUDE-LINKED is non-nil, also links with
+a text description part will be inlined. This can be nice for
+a quick look at those images, but it does not reflect what
+exported files will look like.
+
+When optional argument REFRESH is non-nil, refresh existing
+images between BEG and END. This will create new image displays
+only if necessary.
+
+BEG and END define the considered part. They default to the
+buffer boundaries with possible narrowing."
+ (interactive "P")
+ (when (display-graphic-p)
+ (when refresh
+ (org-link-preview-clear beg end)
+ (when (fboundp 'clear-image-cache) (clear-image-cache)))
+ (let ((end (or end (point-max))))
+ (org-with-point-at (or beg (point-min))
+ (let* ((case-fold-search t)
+ (file-extension-re (image-file-name-regexp))
+ (link-abbrevs (mapcar #'car
+ (append org-link-abbrev-alist-local
+ org-link-abbrev-alist)))
+ ;; Check absolute, relative file names and explicit
+ ;; "file:" links. Also check link abbreviations since
+ ;; some might expand to "file" links.
+ (file-types-re
+ (format "\\[\\[\\(?:file%s:\\|attachment:\\|[./~]\\)\\|\\]\\[\\(<?\\(?:file\\|attachment\\):\\)"
+ (if (not link-abbrevs) ""
+ (concat "\\|" (regexp-opt link-abbrevs))))))
+ (while (re-search-forward file-types-re end t)
+ (let* ((link (org-element-lineage
+ (save-match-data (org-element-context))
+ 'link t))
+ (linktype (org-element-property :type link))
+ (inner-start (match-beginning 1))
+ (path
+ (cond
+ ;; No link at point; no inline image.
+ ((not link) nil)
+ ;; File link without a description. Also handle
+ ;; INCLUDE-LINKED here since it should have
+ ;; precedence over the next case. I.e., if link
+ ;; contains filenames in both the path and the
+ ;; description, prioritize the path only when
+ ;; INCLUDE-LINKED is non-nil.
+ ((or (not (org-element-contents-begin link))
+ include-linked)
+ (and (or (equal "file" linktype)
+ (equal "attachment" linktype))
+ (org-element-property :path link)))
+ ;; Link with a description. Check if description
+ ;; is a filename. Even if Org doesn't have syntax
+ ;; for those -- clickable image -- constructs, fake
+ ;; them, as in `org-export-insert-image-links'.
+ ((not inner-start) nil)
+ (t
+ (org-with-point-at inner-start
+ (and (looking-at
+ (if (char-equal ?< (char-after inner-start))
+ org-link-angle-re
+ org-link-plain-re))
+ ;; File name must fill the whole
+ ;; description.
+ (= (org-element-contents-end link)
+ (match-end 0))
+ (progn
+ (setq linktype (match-string 1))
+ (match-string 2))))))))
+ (when (and path (string-match-p file-extension-re path))
+ (let ((file (if (equal "attachment" linktype)
+ (progn
+ (require 'org-attach)
+ (ignore-errors (org-attach-expand path)))
+ (expand-file-name path))))
+ ;; Expand environment variables.
+ (when file (setq file (substitute-in-file-name file)))
+ (when (and file (file-exists-p file))
+ (let ((width (org-display-inline-image--width link))
+ (align (org-image--align link))
+ (old (get-char-property-and-overlay
+ (org-element-begin link)
+ 'org-image-overlay)))
+ (if (and (car-safe old) refresh)
+ (image-flush (overlay-get (cdr old) 'display))
+ (let ((image (org--create-inline-image file width)))
+ (when image
+ (let ((ov (make-overlay
+ (org-element-begin link)
+ (progn
+ (goto-char
+ (org-element-end link))
+ (unless (eolp) (skip-chars-backward " \t"))
+ (point)))))
+ ;; See bug#59902. We cannot rely
+ ;; on Emacs to update image if the file
+ ;; has changed.
+ (image-flush image)
+ (overlay-put ov 'display image)
+ (overlay-put ov 'face 'default)
+ (overlay-put ov 'org-image-overlay t)
+ (overlay-put
+ ov 'modification-hooks
+ (list 'org-link-preview--remove-overlay))
+ (when (boundp 'image-map)
+ (overlay-put ov 'keymap image-map))
+ (when align
+ (overlay-put
+ ov 'before-string
+ (propertize
+ " " 'face 'default
+ 'display
+ (pcase align
+ ("center" `(space :align-to (- center (0.5 . ,image))))
+ ("right" `(space :align-to (- right ,image)))))))
+ (push ov org-inline-image-overlays))))))))))))))))
+
+(make-obsolete 'org-toggle-inline-images
+ 'org-link-preview "9.8")
+(declare-function org-link-preview-region "ol")
+;; FIXME: Unused; obsoleted; to be removed
+(defun org-toggle-inline-images (&optional include-linked beg end)
+ "Toggle the display of inline images.
+INCLUDE-LINKED is passed to `org-display-inline-images'."
+ (interactive "P")
+ (if (org-link-preview--get-overlays beg end)
+ (progn
+ (org-link-preview-clear beg end)
+ (when (called-interactively-p 'interactive)
+ (message "Inline image display turned off")))
+ (org-link-preview-region include-linked nil beg end)
+ (when (called-interactively-p 'interactive)
+ (let ((new (org-link-preview--get-overlays beg end)))
+ (message (if new
+ (format "%d images displayed inline"
+ (length new))
+ "No images to display inline"))))))
+
+
;; The function was made obsolete by commit 65399674d5 of 2013-02-22.
;; This make-obsolete call was added 2016-09-01.
(make-obsolete 'org-capture-import-remember-templates
diff --git a/lisp/org-cycle.el b/lisp/org-cycle.el
index 8a39bdb8c..97a545393 100644
--- a/lisp/org-cycle.el
+++ b/lisp/org-cycle.el
@@ -40,14 +40,14 @@ (declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-post-affiliated "org-element" (node))
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
-(declare-function org-display-inline-images "org" (&optional include-linked refresh beg end))
+(declare-function org-link-preview-region "ol" (&optional include-linked refresh beg end))
(declare-function org-get-tags "org" (&optional pos local fontify))
(declare-function org-subtree-end-visible-p "org" ())
(declare-function org-narrow-to-subtree "org" (&optional element))
(declare-function org-next-visible-heading "org" (arg))
(declare-function org-at-property-p "org" ())
(declare-function org-re-property "org" (property &optional literal allow-null value))
-(declare-function org-remove-inline-images "org" (&optional beg end))
+(declare-function org-link-preview-clear "ol" (&optional beg end))
(declare-function org-item-beginning-re "org" ())
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
(declare-function org-at-item-p "org" ())
@@ -217,7 +217,7 @@ (defcustom org-cycle-pre-hook nil
(defcustom org-cycle-hook '(org-cycle-hide-archived-subtrees
org-cycle-show-empty-lines
org-cycle-optimize-window-after-visibility-change
- org-cycle-display-inline-images)
+ org-cycle-display-link-previews)
"Hook that is run after `org-cycle' has changed the buffer visibility.
The function(s) in this hook must accept a single argument which indicates
the new state that was set by the most recent `org-cycle' command. The
@@ -237,11 +237,15 @@ (defcustom org-cycle-open-archived-trees nil
:group 'org-cycle
:type 'boolean)
-(defcustom org-cycle-inline-images-display nil
- "Non-nil means auto display inline images under subtree when cycling."
+(defvaralias 'org-cycle-inline-images-display
+ 'org-cycle-link-previews-display
+ "Non-nil means auto display inline images under subtree when cycling.")
+
+(defcustom org-cycle-link-previews-display nil
+ "Non-nil means auto display link previews under subtree when cycling."
:group 'org-startup
:group 'org-cycle
- :package-version '(Org . "9.6")
+ :package-version '(Org . "9.8")
:type 'boolean)
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
@@ -804,12 +808,15 @@ (defun org-cycle-hide-archived-subtrees (state)
"Subtree is archived and stays closed. Use \
`\\[org-cycle-force-archived]' to cycle it anyway."))))))
-(defun org-cycle-display-inline-images (state)
+(defalias 'org-cycle-inline-images-display
+ 'org-cycle-display-link-previews)
+
+(defun org-cycle-display-link-previews (state)
"Auto display inline images under subtree when cycling.
-It works when `org-cycle-inline-images-display' is non-nil.
+It works when `org-cycle-link-previews-display' is non-nil.
STATE is the current outline visibility state. It should be one of
symbols `content', `all', `folded', `children', or `subtree'."
- (when org-cycle-inline-images-display
+ (when org-cycle-link-previews-display
(pcase state
('children
(org-with-wide-buffer
@@ -817,19 +824,19 @@ (defun org-cycle-display-inline-images (state)
;; If has nested headlines, beg,end only from parent headline
;; to first child headline which reference to upper
;; let-binding `org-next-visible-heading'.
- (org-display-inline-images
+ (org-link-preview-region
nil nil
(point-min) (progn (org-next-visible-heading 1) (point)))))
('subtree
(org-with-wide-buffer
(org-narrow-to-subtree)
;; If has nested headlines, also inline display images under all sub-headlines.
- (org-display-inline-images nil nil (point-min) (point-max))))
+ (org-link-preview-region nil nil (point-min) (point-max))))
('folded
(org-with-wide-buffer
(org-narrow-to-subtree)
(if (numberp (point-max))
- (org-remove-inline-images (point-min) (point-max))
+ (org-link-preview-clear (point-min) (point-max))
(ignore)))))))
(provide 'org-cycle)
diff --git a/lisp/org-keys.el b/lisp/org-keys.el
index 1daedaae8..d2e4a6993 100644
--- a/lisp/org-keys.el
+++ b/lisp/org-keys.el
@@ -218,7 +218,8 @@ (declare-function org-toggle-checkbox "org" (&optional toggle-presence))
(declare-function org-toggle-radio-button "org" (&optional arg))
(declare-function org-toggle-comment "org" ())
(declare-function org-toggle-fixed-width "org" ())
-(declare-function org-toggle-inline-images-command "org" (&optional arg beg end))
+(declare-function org-link-preview "ol" (&optional arg beg end))
+(declare-function org-link-preview-refresh "ol" ())
(declare-function org-latex-preview "org" (&optional arg))
(declare-function org-toggle-narrow-to-subtree "org" ())
(declare-function org-toggle-ordered-property "org" ())
@@ -618,8 +619,8 @@ (org-defkey org-mode-map (kbd "C-c C-x C-d") #'org-clock-display)
(org-defkey org-mode-map (kbd "C-c C-x x") #'org-dynamic-block-insert-dblock)
(org-defkey org-mode-map (kbd "C-c C-x C-u") #'org-dblock-update)
(org-defkey org-mode-map (kbd "C-c C-x C-l") #'org-latex-preview)
-(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images-command)
-(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-redisplay-inline-images)
+(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-link-preview)
+(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-link-preview-refresh)
(org-defkey org-mode-map (kbd "C-c C-x \\") #'org-toggle-pretty-entities)
(org-defkey org-mode-map (kbd "C-c C-x C-b") #'org-toggle-checkbox)
(org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-toggle-radio-button)
diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index b045344f0..836cfaffc 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -633,7 +633,7 @@ (defun org-plot/gnuplot-script (table data-file num-cols params &optional prefac
(defun org-plot/redisplay-img-in-buffer (img-file)
"Find any overlays for IMG-FILE in the current Org buffer, and refresh them."
- (dolist (img-overlay org-inline-image-overlays)
+ (dolist (img-overlay org-link-preview-overlays)
(when (string= img-file (plist-get (cdr (overlay-get img-overlay 'display)) :file))
(when (and (file-exists-p img-file)
(fboundp 'image-flush))
diff --git a/lisp/org.el b/lisp/org.el
index df58b47be..c2a125000 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -1160,14 +1160,22 @@ (defcustom org-startup-shrink-all-tables nil
:package-version '(Org . "9.2")
:safe #'booleanp)
-(defcustom org-startup-with-inline-images nil
+(defvaralias 'org-startup-with-inline-images
+ 'org-startup-with-link-previews
"Non-nil means show inline images when loading a new Org file.
This can also be configured on a per-file basis by adding one of
the following lines anywhere in the buffer:
#+STARTUP: inlineimages
- #+STARTUP: noinlineimages"
+ #+STARTUP: noinlineimages")
+
+(defcustom org-startup-with-link-previews nil
+ "Non-nil means show link previews when loading a new Org file.
+This can also be configured on a per-file basis by adding one of
+the following lines anywhere in the buffer:
+ #+STARTUP: linkpreviews
+ #+STARTUP: nolinkpreviews"
:group 'org-startup
- :version "24.1"
+ :version "29.4"
:type 'boolean)
(defcustom org-startup-with-latex-preview nil
@@ -4153,8 +4161,10 @@ (defconst org-startup-options
("shrink" org-startup-shrink-all-tables t)
("descriptivelinks" org-link-descriptive t)
("literallinks" org-link-descriptive nil)
- ("inlineimages" org-startup-with-inline-images t)
- ("noinlineimages" org-startup-with-inline-images nil)
+ ("inlineimages" org-startup-with-link-previews t)
+ ("noinlineimages" org-startup-with-link-previews nil)
+ ("linkpreviews" org-startup-with-link-previews t)
+ ("nolinkpreviews" org-startup-with-link-previews nil)
("latexpreview" org-startup-with-latex-preview t)
("nolatexpreview" org-startup-with-latex-preview nil)
("customtime" org-display-custom-times t)
@@ -5079,7 +5089,7 @@ (define-derived-mode org-mode outline-mode "Org"
;; modifications to make cache updates work reliably.
(org-unmodified
(when org-startup-with-beamer-mode (org-beamer-mode))
- (when org-startup-with-inline-images (org-display-inline-images))
+ (when org-startup-with-inline-images (org-link-preview '(16)))
(when org-startup-with-latex-preview (org-latex-preview '(16)))
(unless org-inhibit-startup-visibility-stuff (org-cycle-set-startup-visibility))
(when org-startup-truncated (setq truncate-lines t))
@@ -15583,26 +15593,6 @@ (defcustom org-image-actual-width t
(list :tag "Use #+ATTR* or a number of pixels" (integer))
(const :tag "Use #+ATTR* or don't resize" nil)))
-(defcustom org-image-max-width 'fill-column
- "When non-nil, limit the displayed image width.
-This setting only takes effect when `org-image-actual-width' is set to
-t or when #+ATTR* is set to t.
-
-Possible values:
-- `fill-column' :: limit width to `fill-column'
-- `window' :: limit width to window width
-- integer :: limit width to number in pixels
-- float :: limit width to that fraction of window width
-- nil :: do not limit image width"
- :group 'org-appearance
- :package-version '(Org . "9.7")
- :type '(choice
- (const :tag "Do not limit image width" nil)
- (const :tag "Limit to `fill-column'" fill-column)
- (const :tag "Limit to window width" window)
- (integer :tag "Limit to a number of pixels")
- (float :tag "Limit to a fraction of window width")))
-
(defcustom org-agenda-inhibit-startup nil
"Inhibit startup when preparing agenda buffers.
When this variable is t, the initialization of the Org agenda
@@ -16649,518 +16639,6 @@ (defun org-normalize-color (value)
(format "%g" (/ value 65535.0)))
\f
-;; Image display
-
-(defvar-local org-inline-image-overlays nil)
-;; Preserve when switching modes or when restarting Org.
-;; If we clear the overlay list and later enable Or mode, the existing
-;; image overlays will never be cleared by `org-toggle-inline-images'
-;; and `org-toggle-inline-images-command'.
-(put 'org-inline-image-overlays 'permanent-local t)
-
-(defun org--inline-image-overlays (&optional beg end)
- "Return image overlays between BEG and END."
- (let* ((beg (or beg (point-min)))
- (end (or end (point-max)))
- (overlays (overlays-in beg end))
- result)
- (dolist (ov overlays result)
- (when (memq ov org-inline-image-overlays)
- (push ov result)))))
-
-(defun org-toggle-inline-images-command (&optional arg beg end)
- "Toggle display of inline images without description at point.
-
-When point is at an image link, toggle displaying that image.
-Otherwise, toggle displaying images in current entry.
-
-When region BEG..END is active, toggle displaying images in the
-region.
-
-With numeric prefix ARG 1, display images with description as well.
-
-With prefix ARG `\\[universal-argument]', toggle displaying images in
-the accessible portion of the buffer. With numeric prefix ARG 11, do
-the same, but include images with description.
-
-With prefix ARG `\\[universal-argument] \\[universal-argument]', hide
-all the images in accessible portion of the buffer.
-
-This command is designed for interactive use. From Elisp, you can
-also use `org-toggle-inline-images'."
- (interactive (cons current-prefix-arg
- (when (use-region-p)
- (list (region-beginning) (region-end)))))
- (let* ((include-linked
- (cond
- ((member arg '(nil (4) (16)) ) nil)
- ((member arg '(1 11)) 'include-linked)
- (t 'include-linked)))
- (interactive? (called-interactively-p 'any))
- (toggle-images
- (lambda (&optional beg end scope force-remove)
- (let* ((beg (or beg (point-min)))
- (end (or end (point-max)))
- (old (org--inline-image-overlays beg end))
- (scope (or scope (format "%d:%d" beg end))))
- (if (or old force-remove)
- (progn
- (org-remove-inline-images beg end)
- (when interactive?
- (message
- "[%s] Inline image display turned off (removed %d images)"
- scope (length old))))
- (org-display-inline-images include-linked t beg end)
- (when interactive?
- (let ((new (org--inline-image-overlays beg end)))
- (message
- (if new
- (format "[%s] %d images displayed inline %s"
- scope (length new)
- (if include-linked "(including images with description)"
- ""))
- (format "[%s] No images to display inline" scope))))))))))
- (cond
- ((not (display-graphic-p))
- (message "Your Emacs does not support displaying images!"))
- ;; Region selected :: toggle images in region.
- ((and beg end) (funcall toggle-images beg end "region"))
- ;; C-u or C-11 argument :: toggle images in the whole buffer.
- ((member arg '(11 (4))) (funcall toggle-images nil nil "buffer"))
- ;; C-u C-u argument :: unconditionally hide images in the buffer.
- ((equal arg '(16)) (funcall toggle-images nil nil "buffer" 'remove))
- ;; Argument nil or 1, no region selected :: toggle (display or hide
- ;; dwim) images in current section or image link at point.
- ((and (member arg '(nil 1)) (null beg) (null end))
- (let ((context (org-element-context)))
- ;; toggle display of inline image link at point.
- (if (org-element-type-p context 'link)
- (funcall toggle-images
- (org-element-begin context)
- (org-element-end context)
- "image at point")
- (let ((beg (if (org-before-first-heading-p) (point-min)
- (save-excursion
- (org-with-limited-levels (org-back-to-heading t) (point)))))
- (end (org-with-limited-levels (org-entry-end-position))))
- (funcall toggle-images beg end "current section")))))
- ;; Any other non-nil argument.
- ((not (null arg)) (funcall toggle-images beg end "region")))))
-
-(defun org-toggle-inline-images (&optional include-linked beg end)
- "Toggle the display of inline images.
-INCLUDE-LINKED is passed to `org-display-inline-images'."
- (interactive "P")
- (if (org--inline-image-overlays beg end)
- (progn
- (org-remove-inline-images beg end)
- (when (called-interactively-p 'interactive)
- (message "Inline image display turned off")))
- (org-display-inline-images include-linked nil beg end)
- (when (called-interactively-p 'interactive)
- (let ((new (org--inline-image-overlays beg end)))
- (message (if new
- (format "%d images displayed inline"
- (length new))
- "No images to display inline"))))))
-
-(defun org-redisplay-inline-images ()
- "Assure display of inline images and refresh them."
- (interactive)
- (org-toggle-inline-images)
- (unless org-inline-image-overlays
- (org-toggle-inline-images)))
-
-;; For without-x builds.
-(declare-function image-flush "image" (spec &optional frame))
-
-(defcustom org-display-remote-inline-images 'skip
- "How to display remote inline images.
-Possible values of this option are:
-
-skip Don't display remote images.
-download Always download and display remote images.
-t
-cache Display remote images, and open them in separate buffers
- for caching. Silently update the image buffer when a file
- change is detected."
- :group 'org-appearance
- :package-version '(Org . "9.7")
- :type '(choice
- (const :tag "Ignore remote images" skip)
- (const :tag "Always display remote images" download)
- (const :tag "Display and silently update remote images" cache))
- :safe #'symbolp)
-
-(defcustom org-image-align 'left
- "How to align images previewed using `org-display-inline-images'.
-
-Only stand-alone image links are affected by this setting. These
-are links without surrounding text.
-
-Possible values of this option are:
-
-left Insert image at specified position.
-center Center image previews.
-right Right-align image previews."
- :group 'org-appearance
- :package-version '(Org . "9.7")
- :type '(choice
- (const :tag "Left align (or don\\='t align) image previews" left)
- (const :tag "Center image previews" center)
- (const :tag "Right align image previews" right))
- :safe #'symbolp)
-
-(defun org--create-inline-image (file width)
- "Create image located at FILE, or return nil.
-WIDTH is the width of the image. The image may not be created
-according to the value of `org-display-remote-inline-images'."
- (let* ((remote? (file-remote-p file))
- (file-or-data
- (pcase org-display-remote-inline-images
- ((guard (not remote?)) file)
- (`download (with-temp-buffer
- (set-buffer-multibyte nil)
- (insert-file-contents-literally file)
- (buffer-string)))
- ((or `cache `t)
- (let ((revert-without-query '(".")))
- (with-current-buffer (find-file-noselect file)
- (buffer-string))))
- (`skip nil)
- (other
- (message "Invalid value of `org-display-remote-inline-images': %S"
- other)
- nil))))
- (when file-or-data
- (create-image file-or-data
- (and (image-type-available-p 'imagemagick)
- width
- 'imagemagick)
- remote?
- :width width
- :max-width
- (pcase org-image-max-width
- (`fill-column (* fill-column (frame-char-width (selected-frame))))
- (`window (window-width nil t))
- ((pred integerp) org-image-max-width)
- ((pred floatp) (floor (* org-image-max-width (window-width nil t))))
- (`nil nil)
- (_ (error "Unsupported value of `org-image-max-width': %S"
- org-image-max-width)))
- :scale 1))))
-
-(defun org-display-inline-images (&optional include-linked refresh beg end)
- "Display inline images.
-
-An inline image is a link which follows either of these
-conventions:
-
- 1. Its path is a file with an extension matching return value
- from `image-file-name-regexp' and it has no contents.
-
- 2. Its description consists in a single link of the previous
- type. In this case, that link must be a well-formed plain
- or angle link, i.e., it must have an explicit \"file\" or
- \"attachment\" type.
-
-Equip each image with the key-map `image-map'.
-
-When optional argument INCLUDE-LINKED is non-nil, also links with
-a text description part will be inlined. This can be nice for
-a quick look at those images, but it does not reflect what
-exported files will look like.
-
-When optional argument REFRESH is non-nil, refresh existing
-images between BEG and END. This will create new image displays
-only if necessary.
-
-BEG and END define the considered part. They default to the
-buffer boundaries with possible narrowing."
- (interactive "P")
- (when (display-graphic-p)
- (when refresh
- (org-remove-inline-images beg end)
- (when (fboundp 'clear-image-cache) (clear-image-cache)))
- (let ((end (or end (point-max))))
- (org-with-point-at (or beg (point-min))
- (let* ((case-fold-search t)
- (file-extension-re (image-file-name-regexp))
- (link-abbrevs (mapcar #'car
- (append org-link-abbrev-alist-local
- org-link-abbrev-alist)))
- ;; Check absolute, relative file names and explicit
- ;; "file:" links. Also check link abbreviations since
- ;; some might expand to "file" links.
- (file-types-re
- (format "\\[\\[\\(?:file%s:\\|attachment:\\|[./~]\\)\\|\\]\\[\\(<?\\(?:file\\|attachment\\):\\)"
- (if (not link-abbrevs) ""
- (concat "\\|" (regexp-opt link-abbrevs))))))
- (while (re-search-forward file-types-re end t)
- (let* ((link (org-element-lineage
- (save-match-data (org-element-context))
- 'link t))
- (linktype (org-element-property :type link))
- (inner-start (match-beginning 1))
- (path
- (cond
- ;; No link at point; no inline image.
- ((not link) nil)
- ;; File link without a description. Also handle
- ;; INCLUDE-LINKED here since it should have
- ;; precedence over the next case. I.e., if link
- ;; contains filenames in both the path and the
- ;; description, prioritize the path only when
- ;; INCLUDE-LINKED is non-nil.
- ((or (not (org-element-contents-begin link))
- include-linked)
- (and (or (equal "file" linktype)
- (equal "attachment" linktype))
- (org-element-property :path link)))
- ;; Link with a description. Check if description
- ;; is a filename. Even if Org doesn't have syntax
- ;; for those -- clickable image -- constructs, fake
- ;; them, as in `org-export-insert-image-links'.
- ((not inner-start) nil)
- (t
- (org-with-point-at inner-start
- (and (looking-at
- (if (char-equal ?< (char-after inner-start))
- org-link-angle-re
- org-link-plain-re))
- ;; File name must fill the whole
- ;; description.
- (= (org-element-contents-end link)
- (match-end 0))
- (progn
- (setq linktype (match-string 1))
- (match-string 2))))))))
- (when (and path (string-match-p file-extension-re path))
- (let ((file (if (equal "attachment" linktype)
- (progn
- (require 'org-attach)
- (ignore-errors (org-attach-expand path)))
- (expand-file-name path))))
- ;; Expand environment variables.
- (when file (setq file (substitute-in-file-name file)))
- (when (and file (file-exists-p file))
- (let ((width (org-display-inline-image--width link))
- (align (org-image--align link))
- (old (get-char-property-and-overlay
- (org-element-begin link)
- 'org-image-overlay)))
- (if (and (car-safe old) refresh)
- (image-flush (overlay-get (cdr old) 'display))
- (let ((image (org--create-inline-image file width)))
- (when image
- (let ((ov (make-overlay
- (org-element-begin link)
- (progn
- (goto-char
- (org-element-end link))
- (unless (eolp) (skip-chars-backward " \t"))
- (point)))))
- ;; See bug#59902. We cannot rely
- ;; on Emacs to update image if the file
- ;; has changed.
- (image-flush image)
- (overlay-put ov 'display image)
- (overlay-put ov 'face 'default)
- (overlay-put ov 'org-image-overlay t)
- (overlay-put
- ov 'modification-hooks
- (list 'org-display-inline-remove-overlay))
- (when (boundp 'image-map)
- (overlay-put ov 'keymap image-map))
- (when align
- (overlay-put
- ov 'before-string
- (propertize
- " " 'face 'default
- 'display
- (pcase align
- ("center" `(space :align-to (- center (0.5 . ,image))))
- ("right" `(space :align-to (- right ,image)))))))
- (push ov org-inline-image-overlays))))))))))))))))
-
-(declare-function org-export-read-attribute "ox"
- (attribute element &optional property))
-(defvar visual-fill-column-width) ; Silence compiler warning
-(defun org-display-inline-image--width (link)
- "Determine the display width of the image LINK, in pixels.
-- When `org-image-actual-width' is t, the image's pixel width is used.
-- When `org-image-actual-width' is a number, that value will is used.
-- When `org-image-actual-width' is nil or a list, :width attribute of
- #+attr_org or the first #+attr_... (if it exists) is used to set the
- image width. A width of X% is divided by 100. If the value is a
- float between 0 and 2, it interpreted as that proportion of the text
- width in the buffer.
-
- If no :width attribute is given and `org-image-actual-width' is a
- list with a number as the car, then that number is used as the
- default value."
- ;; Apply `org-image-actual-width' specifications.
- ;; Support subtree-level property "ORG-IMAGE-ACTUAL-WIDTH" specified
- ;; width.
- (let ((org-image-actual-width (org-property-or-variable-value 'org-image-actual-width)))
- (cond
- ((eq org-image-actual-width t) nil)
- ((listp org-image-actual-width)
- (require 'ox)
- (let* ((par (org-element-lineage link 'paragraph))
- ;; Try to find an attribute providing a :width.
- ;; #+ATTR_ORG: :width ...
- (attr-width (org-export-read-attribute :attr_org par :width))
- (width-unreadable?
- (lambda (value)
- (or (not (stringp value))
- (unless (string= value "t")
- (or (not (string-match
- (rx bos (opt "+")
- (or
- ;; Number of pixels
- ;; must be a lone number, not
- ;; things like 4in
- (seq (1+ (in "0-9")) eos)
- ;; Numbers ending with %
- (seq (1+ (in "0-9.")) (group-n 1 "%"))
- ;; Fractions
- (seq (0+ (in "0-9")) "." (1+ (in "0-9")))))
- value))
- (let ((number (string-to-number value)))
- (and (floatp number)
- (not (match-string 1 value)) ; X%
- (not (<= 0.0 number 2.0)))))))))
- ;; #+ATTR_BACKEND: :width ...
- (attr-other
- (catch :found
- (org-element-properties-map
- (lambda (prop _)
- (when (and
- (not (eq prop :attr_org))
- (string-match-p "^:attr_" (symbol-name prop))
- (not (funcall width-unreadable? (org-export-read-attribute prop par :width))))
- (throw :found prop)))
- par)))
- (attr-width
- (if (not (funcall width-unreadable? attr-width))
- attr-width
- ;; When #+attr_org: does not have readable :width
- (and attr-other
- (org-export-read-attribute attr-other par :width))))
- (width
- (cond
- ;; Treat :width t as if `org-image-actual-width' were t.
- ((string= attr-width "t") nil)
- ;; Fallback to `org-image-actual-width' if no interprable width is given.
- ((funcall width-unreadable? attr-width)
- (car org-image-actual-width))
- ;; Convert numeric widths to numbers, converting percentages.
- ((string-match-p "\\`[[+]?[0-9.]+%" attr-width)
- (/ (string-to-number attr-width) 100.0))
- (t (string-to-number attr-width)))))
- (if (and (floatp width) (<= 0.0 width 2.0))
- ;; A float in [0,2] should be interpereted as this portion of
- ;; the text width in the window. This works well with cases like
- ;; #+attr_latex: :width 0.X\{line,page,column,etc.}width,
- ;; as the "0.X" is pulled out as a float. We use 2 as the upper
- ;; bound as cases such as 1.2\linewidth are feasible.
- (round (* width
- (window-pixel-width)
- (/ (or (and (bound-and-true-p visual-fill-column-mode)
- (or visual-fill-column-width auto-fill-function))
- (when auto-fill-function fill-column)
- (- (window-text-width) (line-number-display-width)))
- (float (window-total-width)))))
- width)))
- ((numberp org-image-actual-width)
- org-image-actual-width)
- (t nil))))
-
-(defun org-image--align (link)
- "Determine the alignment of the image LINK.
-LINK is a link object.
-
-In decreasing order of priority, this is controlled:
-- Per image by the value of `:center' or `:align' in the
-affiliated keyword `#+attr_org'.
-- By the `#+attr_html' or `#+attr_latex` keywords with valid
- `:center' or `:align' values.
-- Globally by the user option `org-image-align'.
-
-The result is either nil or one of the strings \"left\",
-\"center\" or \"right\".
-
-\"center\" will cause the image preview to be centered, \"right\"
-will cause it to be right-aligned. A value of \"left\" or nil
-implies no special alignment."
- (let ((par (org-element-lineage link 'paragraph)))
- ;; Only align when image is not surrounded by paragraph text:
- (when (and par ; when image is not in paragraph, but in table/headline/etc, do not align
- (= (org-element-begin link)
- (save-excursion
- (goto-char (org-element-contents-begin par))
- (skip-chars-forward "\t ")
- (point))) ;account for leading space
- ;before link
- (<= (- (org-element-contents-end par)
- (org-element-end link))
- 1)) ;account for trailing newline
- ;at end of paragraph
- (save-match-data
- ;; Look for a valid ":center t" or ":align left|center|right"
- ;; attribute.
- ;;
- ;; An attr_org keyword has the highest priority, with
- ;; any attr.* next. Choosing between these is
- ;; unspecified.
- (let ((center-re ":\\(center\\)[[:space:]]+t\\b")
- (align-re ":align[[:space:]]+\\(left\\|center\\|right\\)\\b")
- attr-align)
- (catch 'exit
- (org-element-properties-mapc
- (lambda (propname propval)
- (when (and propval
- (string-match-p ":attr.*" (symbol-name propname)))
- (setq propval (car-safe propval))
- (when (or (string-match center-re propval)
- (string-match align-re propval))
- (setq attr-align (match-string 1 propval))
- (when (eq propname :attr_org)
- (throw 'exit t)))))
- par))
- (if attr-align
- (when (member attr-align '("center" "right")) attr-align)
- ;; No image-specific keyword, check global alignment property
- (when (memq org-image-align '(center right))
- (symbol-name org-image-align))))))))
-
-
-(defun org-display-inline-remove-overlay (ov after _beg _end &optional _len)
- "Remove inline-display overlay if a corresponding region is modified."
- (when (and ov after)
- (setq org-inline-image-overlays (delete ov org-inline-image-overlays))
- ;; Clear image from cache to avoid image not updating upon
- ;; changing on disk. See Emacs bug#59902.
- (when (overlay-get ov 'org-image-overlay)
- (image-flush (overlay-get ov 'display)))
- (delete-overlay ov)))
-
-(defun org-remove-inline-images (&optional beg end)
- "Remove inline display of images."
- (interactive)
- (let* ((beg (or beg (point-min)))
- (end (or end (point-max)))
- (overlays (overlays-in beg end)))
- (dolist (ov overlays)
- (when (memq ov org-inline-image-overlays)
- (setq org-inline-image-overlays (delq ov org-inline-image-overlays))
- (delete-overlay ov)))
- ;; Clear removed overlays.
- (dolist (ov org-inline-image-overlays)
- (unless (overlay-buffer ov)
- (setq org-inline-image-overlays (delq ov org-inline-image-overlays))))))
-
(defvar org-self-insert-command-undo-counter 0)
(defvar org-speed-command nil)
diff --git a/testing/lisp/test-org-fold.el b/testing/lisp/test-org-fold.el
index f58642be6..809738f6c 100644
--- a/testing/lisp/test-org-fold.el
+++ b/testing/lisp/test-org-fold.el
@@ -716,14 +716,14 @@ (ert-deftest test-org-fold/org-fold-display-inline-images ()
(org-show-subtree)
(org-fold-subtree t)
(run-hook-with-args 'org-cycle-hook 'folded)
- (should-not org-inline-image-overlays)
+ (should-not org-link-preview-overlays)
(should-not
(cl-every
(lambda (ov) (overlay-get ov 'org-image-overlay))
(overlays-in (point-min) (point-max))))
(org-show-subtree)
(run-hook-with-args 'org-cycle-hook 'subtree)
- (should org-inline-image-overlays)
+ (should org-link-preview-overlays)
(should
(cl-every
(lambda (ov) (overlay-get ov 'org-image-overlay))
--
2.46.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Org-Document-preview-API-for-arbitrary-link-types.patch --]
[-- Type: text/x-patch, Size: 19709 bytes --]
From 7dc0be9790a6abcba37554b8a26bcd751e7ad31d Mon Sep 17 00:00:00 2001
From: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Date: Sun, 27 Oct 2024 20:22:59 -0700
Subject: [PATCH 2/2] Org: Document preview API for arbitrary link types
* etc/ORG-NEWS: Mention new commands and options for link preview.
* doc/org-manual.org: Add sections on
- previewing external links (Images and link previews), and
- adding the link preview feature to other link types (Adding
Hyperlink preview).
---
doc/org-manual.org | 290 ++++++++++++++++++++++++++++++---------------
etc/ORG-NEWS | 93 +++++++++++++--
2 files changed, 278 insertions(+), 105 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 94fabde3b..6ed5fac93 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -11778,7 +11778,74 @@ ** Literal Examples
the end of the current line. Then the label is stored as a link
=(label)=, for retrieval with {{{kbd(C-c C-l)}}}.
-** Images
+** Images and link previews
+:PROPERTIES:
+:DESCRIPTION: Preview links in the buffer.
+:END:
+
+Org mode can display previews of [[*Hyperlinks][hyperlinks]] inside Org buffers. By
+default, only image links[fn::Image links are =file:= and
+=attachment:= links to existing image files; Emacs should be able to
+display the linked images (see ~image-types~ variable)] can be
+previewed inline, where the images are displayed in place of the link
+path.
+
+You can customize the previews as described in the [[*Adding Hyperlink
+preview]] section. Link previews do not have to display images -- any
+kind of [[info:elisp#Overlay Properties][display decoration]] can be used.
+
+You can preview the supported link types in the whole buffer, in the
+current section, active region or at point with the following commands:
+
+- {{{kbd(C-c C-x C-v)}}} (~org-link-preview~) ::
+
+ #+kindex: C-c C-x C-v
+ #+findex: org-link-preview
+ Create inline previews for external links in the active region, the
+ link at point or in the current section. With a prefix argument,
+ clear link previews at point or in the current entry. With a double
+ prefix argument, preview all links in the buffer. With triple
+ prefix argument, hide previews for all links in the buffer.
+
+ By default, only image links without a description are displayed.
+ You can force displaying previews for all supported links using a
+ numeric argument of ~1~ to toggle all previews in the active
+ region, the link at point or the current section. A numeric prefix
+ argument of ~11~ will toggle previews in the whole buffer.
+
+ #+vindex: org-startup-with-link-previews
+ Org mode can display link previews automatically when opening
+ buffers. Either customize ~org-startup-with-link-previews~ or use
+ the =#+STARTUP: linkpreviews= keyword to enable previews for that
+ specific buffer when opening it. =#+STARTUP: nolinkpreviews= will
+ disable previews on startup in the buffer.
+
+- {{{kbd(C-c C-x C-M-v)}}} (~org-link-preview-refresh~) ::
+
+ #+kindex: C-c C-x C-M-v
+ #+findex: org-link-preview-refresh
+ Assure inline display of external link previews in the whole buffer
+ and refresh them.
+
+- (~org-link-preview-region~) ::
+
+ #+findex: org-link-preview-region
+ Create inline previews for external links in the active region, or
+ the buffer. With a prefix argument, also preview links with a text
+ description part.
+
+- (~org-link-preview-clear~) ::
+
+ #+findex: org-link-preview-clear
+ Clear external link previews in the active region, or the buffer.
+
+#+vindex: org-cycle-inline-images-display
+Link previews can also be displayed when cycling the folding state.
+When the custom option ~org-cycle-link-previews-display~ is set,
+supported link types under the subtree (including images) will be
+displayed automatically.
+
+*** Images
:PROPERTIES:
:DESCRIPTION: Display an image.
:END:
@@ -11803,102 +11870,76 @@ ** Images
[[./img/a.jpg]]
#+end_example
-Such images can be displayed within the buffer with the following
-command:
-
-- {{{kbd(C-c C-x C-v)}}} (~org-toggle-inline-images-command~) ::
-
- #+kindex: C-c C-x C-v
- #+findex: org-toggle-inline-images-command
- Toggle the inline display of linked images in current section or at
- point. With a prefix argument, toggle inline images in the whole
- buffer. With double prefix argument, hide all the images in buffer.
-
- By default, only the image links without description are displayed.
- You can force displaying all the images using numeric argument to
- toggle all the images in current section (~1~ argument) or the whole
- buffer (~11~ argument).
-
- #+vindex: org-startup-with-inline-images
- You can ask for inline images to be displayed at
- startup by configuring the variable
- ~org-startup-with-inline-images~[fn:: The variable
- ~org-startup-with-inline-images~ can be set within a buffer with the
- =STARTUP= options =inlineimages= and =noinlineimages=.].
-
-
- #+vindex: org-image-actual-width
- #+vindex: org-image-max-width
- #+cindex: @samp{ORG-IMAGE-ACTUAL-WIDTH}, property
- By default, Org mode displays inline images according to their
- actual width, but no wider than ~fill-column~ characters.
-
- You can customize the displayed image width using
- ~org-image-actual-width~ variable (globally) or
- =ORG-IMAGE-ACTUAL-WIDTH= property (subtree-level)[fn:: The width can
- be customized in Emacs >= 24.1, built with ImageMagick support.].
- Their value can be the following:
- - (default) Non-~nil~, use the actual width of images when inlining
- them. If the actual width is too wide, limit it according to
- ~org-image-max-width~.
- - When set to a number, use ImageMagick (when available) to set the
- image's width to this value.
- - When set to a number in a list, try to get the width from any
- =#+ATTR.*= keyword if it matches a width specification like:
- #+begin_example
- ,#+ATTR_HTML: :width 300px
- #+end_example
- and fall back on that number if none is found.
- - When set to ~nil~, try to get the width from an =#+ATTR.*= keyword
- and fall back on the original width or ~org-image-max-width~ if
- none is found.
-
- ~org-image-max-width~ limits the maximum displayed image width, but
- only when the image width is not set explicitly. Possible maximum
- width can be set to:
- - (default) ~fill-column~, limit width to ~fill-column~ number of
- characters.
- - ~window~, limit width to current window width.
- - integer number, limit width to that specified number of pixels.
- - ~nil~, do not limit the width.
-
- #+vindex: org-image-align
- Org mode can left-align, center or right-align the display of inline
- images. This setting is controlled (globally) by ~org-image-align~.
- Only standalone images are affected, corresponding to links with no
- surrounding text in their paragraph except for whitespace. Its
- value can be the following:
- - (default) The symbol ~left~, which inserts the image where the
- link appears in the buffer.
- - The symbol ~center~, which will preview links centered in the
- Emacs window.
- - The symbol ~right~, which will preview links right-aligned in the
- Emacs window.
-
- Inline image alignment can be specified for each link using the
- =#+ATTR.*= keyword if it matches an alignment specification like:
- #+begin_example
- ,#+ATTR_HTML: :align center
- #+end_example
- Org will use the alignment specification from any =#+ATTR.*=
- keyword, such as =#+ATTR_HTML= or =#+ATTR_LATEX=, but =#+ATTR_ORG=
- (if present) will override the others. For instance, this link
+When [[*Images and link previews][link previews]] are displayed as images, the image size and
+alignment can be further customized.
+
+#+vindex: org-image-actual-width
+#+vindex: org-image-max-width
+#+cindex: @samp{ORG-IMAGE-ACTUAL-WIDTH}, property
+By default, Org mode displays inline images according to their
+actual width, but no wider than ~fill-column~ characters.
+
+You can customize the displayed image width using
+~org-image-actual-width~ variable (globally) or
+=ORG-IMAGE-ACTUAL-WIDTH= property (subtree-level)[fn:: The width can
+be customized in Emacs >= 24.1, built with ImageMagick support.].
+Their value can be the following:
+- (default) Non-~nil~, use the actual width of images when inlining
+ them. If the actual width is too wide, limit it according to
+ ~org-image-max-width~.
+- When set to a number, use ImageMagick (when available) to set the
+ image's width to this value.
+- When set to a number in a list, try to get the width from any
+ =#+ATTR.*= keyword if it matches a width specification like:
#+begin_example
- ,#+ATTR_HTML: :align right
- ,#+ATTR_ORG: :align center
- [[/path/to/image/file.png]]
+ ,#+ATTR_HTML: :width 300px
#+end_example
- will be displayed centered in Emacs but exported right-aligned to
- HTML.
-
- When =#+ATTR_ORG= is not set, inline image alignment is also read
- from the =:center= attribute supported by some export backends (like
- HTML, LaTeX and Beamer.)
+ and fall back on that number if none is found.
+- When set to ~nil~, try to get the width from an =#+ATTR.*= keyword
+ and fall back on the original width or ~org-image-max-width~ if
+ none is found.
+
+~org-image-max-width~ limits the maximum displayed image width, but
+only when the image width is not set explicitly. Possible maximum
+width can be set to:
+- (default) ~fill-column~, limit width to ~fill-column~ number of
+ characters.
+- ~window~, limit width to current window width.
+- integer number, limit width to that specified number of pixels.
+- ~nil~, do not limit the width.
+
+#+vindex: org-image-align
+Org mode can left-align, center or right-align the display of inline
+images. This setting is controlled (globally) by ~org-image-align~.
+Only standalone images are affected, corresponding to links with no
+surrounding text in their paragraph except for whitespace. Its
+value can be the following:
+- (default) The symbol ~left~, which inserts the image where the
+ link appears in the buffer.
+- The symbol ~center~, which will preview links centered in the
+ Emacs window.
+- The symbol ~right~, which will preview links right-aligned in the
+ Emacs window.
+
+Inline image alignment can be specified for each link using the
+=#+ATTR.*= keyword if it matches an alignment specification like:
+#+begin_example
+,#+ATTR_HTML: :align center
+#+end_example
+Org will use the alignment specification from any =#+ATTR.*=
+keyword, such as =#+ATTR_HTML= or =#+ATTR_LATEX=, but =#+ATTR_ORG=
+(if present) will override the others. For instance, this link
+#+begin_example
+,#+ATTR_HTML: :align right
+,#+ATTR_ORG: :align center
+[[/path/to/image/file.png]]
+#+end_example
+will be displayed centered in Emacs but exported right-aligned to
+HTML.
-#+vindex: org-cycle-inline-images-display
-Inline images can also be displayed when cycling the folding state.
-When custom option ~org-cycle-inline-images-display~ is set, the
-visible inline images under subtree will be displayed automatically.
+When =#+ATTR_ORG= is not set, inline image alignment is also read from
+the =:center= attribute supported by some export backends (like HTML,
+LaTeX and Beamer.)
** Captions
:PROPERTIES:
@@ -21794,6 +21835,67 @@ ** Adding Hyperlink Types
topic. It also provides a default description. The function
~org-insert-link~ can insert it back into an Org buffer later on.
+** Adding Hyperlink preview
+:PROPERTIES:
+:DESCRIPTION: Preview behavior for new hyperlink types.
+:END:
+#+cindex: hyperlinks, adding preview behavior
+
+By default, Org supports previewing external links for links ot type
+=file= and =attachment= that point to image files. (See [[*Images]].)
+
+Support for previewing other link types inline can be added to Org in
+the following way:
+
+1. Add a =:preview= link parameter to the link type using
+ ~org-link-set-parameters~. As an example, here we add previews for
+ the =docview= link type.
+
+ #+begin_src emacs-lisp
+(org-link-set-parameters
+ "docview" :preview #'org-link-docview-preview)
+ #+end_src
+
+2. The value of the =:preview= parameter must be a function that
+ accepts three arguments:
+ - an overlay placed from the start to the end of the link,
+ - the link path, as a string, and
+ - the syntax node for the link.
+ It must return a non-nil value to indicate preview success. A
+ value of =nil= implies that the preview failed, and the overlay
+ placed on the link will be removed.
+
+ In our example, we use the =convert= program (part of the
+ =imagemagick= suite of tools) to create the thumbnail that is
+ displayed inline.
+
+ #+begin_src emacs-lisp
+(defun org-link-docview-preview (ov path _elem)
+ "Preview file at PATH in overlay OV.
+
+_ELEM is the syntax node of the link element."
+ (when (executable-find "convert")
+ (let* ((path (expand-file-name (substitute-in-file-name path)))
+ (output-file (expand-file-name (concat "org-docview-preview-"
+ (substring (sha1 path) 0 10)
+ ".png")
+ temporary-file-directory)))
+ ;; Create or find preview for path
+ (when (or (file-readable-p output-file)
+ (= 0 (call-process
+ "convert"
+ nil (get-buffer-create "*Org Docview Preview Output*") nil
+ "-thumbnail" "x320" "-colorspace" "rgb"
+ "-background" "white" "-alpha" "remove" "-strip"
+ (concat path "[0]") output-file)))
+ ;; If preview image is available, display it via the overlay
+ (overlay-put ov 'display (create-image output-file))))))
+ #+end_src
+
+3. Now previews of docview links for supported document types (PDF,
+ djvu) are generated (along with image file previews) when calling
+ ~org-link-preview~.
+
** Adding Export Backends
:PROPERTIES:
:DESCRIPTION: How to write new export backends.
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index d8018690f..a033fafcd 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -24,26 +24,38 @@ Previously, =C-c C-x C-v= always toggled image display in the whole
buffer (or narrowed part of the buffer). With prefix argument, it
also forced displaying image links with description.
-Now, =C-c C-x C-v= is bound to a new command
-~org-toggle-inline-images-command~, which uses different defaults:
+Now, =C-c C-x C-v= is bound to a new command ~org-link-preview~, which
+uses different defaults:
-1. By default, it toggles image at point or, if there is no image at
- point, images in current entry
+1. When the region is active, images in the region are previewed
-2. When region is active, it is honored
+2. Otherwise, if there is an image at point, it is toggled. If there
+ is no image at point, images in the current entry are previewed
-3. =C-u= argument changed its meaning. Now, it forces toggling images
- in the whole buffer
+3. With the =C-u= argument, image previews in the active region or at
+ point are cleared instead
-4. =C-u C-u= argument unconditionally hides all the images in buffer
+4. The =C-u C-u= argument unconditionally shows all images in the
+ accessible portion of the buffer
-5. Displaying images over links with description can be forced using
+5. The =C-u C-u C-u= argument unconditionally clears all images in the
+ accessible portion of the buffer
+
+6. Displaying images over links with description can be forced using
numeric argument:
- ~C-u 1~ for toggling all images at point/current entry
- ~C-u 11~ for toggling all images in buffer
-The old ~org-toggle-inline-images~ command is still available. You
-can bind it back to =C-c C-x C-v= by adding the following to you config:
+(The first five of these prefix arg behaviors are the same as that of
+the ~org-latex-preview~ command.)
+
+In addition to images, ~org-link-preview~ can also be used to preview
+Org links of all types for which preview behavior is defined, see
+[[#link-preview][previews for arbitrary link types]].
+
+The old ~org-toggle-inline-images~ command is obsolete but still
+available. You can bind it back to =C-c C-x C-v= by adding the
+following to your config:
#+begin_src emacs-lisp
(eval-after-load 'org-keys
(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images))
@@ -76,6 +88,16 @@ now have diary timestamps included as well.
# We list the most important features, and the features that may
# require user action to be used.
+*** All Org link types can be previewed
+:PROPERTIES:
+:CUSTOM_ID: link-preview
+:END:
+
+Org links support a new parameter =:preview= that can be used to
+preview arbitrary link types. The value of this parameter should be a
+function that is called to preview links of the corresponding type
+(see ~org-link-parameters~).
+
*** Alignment of image previews can be customized
This is not a new feature. It has been added in Org 9.7, but not
@@ -97,6 +119,17 @@ or newer.
# adding new customizations, or changing the interpretation of the
# existing customizations.
+*** New option ~org-link-preview-batch-size~
+
+Org link previews are generated asynchronously and a few at a time, in
+batches. This option controls the number of links that are previewed
+in each batch.
+
+*** New option ~org-link-preview-delay~
+
+Org link previews are generated asynchronously. This option controls
+the minimum idle time in seconds between previews of batches of links.
+
*** Allow disabling macro replacement during export
New custom option ~org-export-replace-macros~ controls whether Org
@@ -139,6 +172,26 @@ bibliography format requires them to be written in title-case.
# This also includes changes in function behavior from Elisp perspective.
+*** New command ~org-link-preview~ to preview Org links
+
+This command replaces ~org-toggle-inline-images~, which is now
+obsolete.
+
+*** New command ~org-link-preview-region~ to preview Org links in a region or the buffer
+
+This command replaces ~org-display-inline-images~, which is now
+obsolete.
+
+*** New command ~org-link-preview-clear~ to clear Org link previews in a region or the buffer
+
+This command replaces ~org-remove-inline-images~, which is now
+obsolete.
+
+*** New command ~org-link-preview-refresh~ to refresh Org link previews in the buffer
+
+This command replaces ~org-redisplay-inline-images~, which is now
+obsolete.
+
*** ob-sqlite: Added ability to open a database in readonly mode
Added option :readonly to ob-sqlite.
@@ -163,6 +216,24 @@ accept the INFO channel and return a string. This makes it possible
to dynamically generate the content of the resulting ~<head>~ tag in
the resulting HTML document.
+** Removed or renamed functions and variables
+
+*** ~org-cycle-display-inline-images~ is renamed to ~org-cycle-display-link-previews~
+
+Inline image previews in Org mode are now provided by the more general
+link previews feature. The behavior with regards to image links is
+unchanged.
+
+*** ~org-cycle-inline-images-display~ is renamed to ~org-cycle-link-previews-display~
+
+The behavior is unchanged, except in that the new variable now affects
+previews of supported link types besides image links.
+
+*** ~org-startup-with-inline-images~ is renamed to ~org-startup-with-link-previews~
+
+The behavior is unchanged, except in that the new variable now affects
+previews of supported link types besides image links.
+
** Miscellaneous
*** Org mode no longer prevents =flyspell= from spell-checking inside =LOGBOOK= drawers
--
2.46.1
next prev parent reply other threads:[~2024-10-28 22:36 UTC|newest]
Thread overview: 72+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-05-15 3:28 [PATCH] add a function to only refresh inline images under current headline instead of global buffer Christopher M. Miles
2023-05-15 11:08 ` Ihor Radchenko
2023-05-15 13:01 ` Christopher M. Miles
2023-05-15 14:00 ` [PATCH v2] " Christopher M. Miles
2023-05-16 9:17 ` Ihor Radchenko
2023-05-16 12:18 ` Christopher M. Miles
2023-07-24 11:25 ` Ihor Radchenko
2023-08-01 4:40 ` [PATCH v3] " Christopher M. Miles
2023-08-01 8:04 ` Ihor Radchenko
2023-08-01 12:17 ` [PATCH v3.1] " Christopher M. Miles
2023-08-01 14:09 ` Ihor Radchenko
2023-08-01 15:22 ` Christopher M. Miles
2023-08-01 15:46 ` Christopher M. Miles
2023-08-02 16:08 ` Ihor Radchenko
2023-08-04 6:30 ` Christopher M. Miles
2023-08-02 7:26 ` Ihor Radchenko
2023-08-02 15:44 ` Christopher M. Miles
2023-08-04 8:20 ` Ihor Radchenko
2023-08-05 5:28 ` [PATCH v3.2] " Christopher M. Miles
2024-07-22 10:46 ` Ihor Radchenko
2024-08-01 22:58 ` [PATCH v4.0] " stardiviner
[not found] ` <66a8b73b.170a0220.383476.996e@mx.google.com>
2024-08-12 10:18 ` Ihor Radchenko
2024-08-14 2:04 ` stardiviner
2024-08-18 10:27 ` Ihor Radchenko
2024-08-20 2:02 ` Karthik Chikmagalur
2024-08-20 15:43 ` Karthik Chikmagalur
2024-08-20 18:19 ` Ihor Radchenko
2024-08-20 19:46 ` Karthik Chikmagalur
2024-08-22 13:19 ` Ihor Radchenko
2024-08-23 6:04 ` Karthik Chikmagalur
2024-08-23 23:36 ` [PATCH v1] Inline image display as part of a new org-link-preview system Karthik Chikmagalur
2024-08-24 1:00 ` Karthik Chikmagalur
2024-08-31 14:22 ` Ihor Radchenko
2024-08-31 16:41 ` Karthik Chikmagalur
2024-08-31 16:53 ` Ihor Radchenko
2024-08-31 22:37 ` [PATCH v2] " Karthik Chikmagalur
2024-09-01 13:06 ` Ihor Radchenko
2024-09-02 20:13 ` [PATCH v3] " Karthik Chikmagalur
2024-09-08 7:43 ` Ihor Radchenko
2024-09-09 3:21 ` Karthik Chikmagalur
2024-09-09 6:06 ` Ihor Radchenko
2024-09-09 6:30 ` Karthik Chikmagalur
2024-09-09 16:47 ` Ihor Radchenko
2024-09-09 19:14 ` Karthik Chikmagalur
2024-09-10 16:57 ` Ihor Radchenko
2024-09-10 19:53 ` Karthik Chikmagalur
2024-09-15 7:51 ` Ihor Radchenko
2024-09-09 21:45 ` Karthik Chikmagalur
2024-09-10 16:58 ` Ihor Radchenko
2024-09-10 17:38 ` Karthik Chikmagalur
2024-09-10 18:34 ` Ihor Radchenko
2024-09-10 19:43 ` Karthik Chikmagalur
2024-09-15 8:12 ` Ihor Radchenko
2024-09-15 20:50 ` Karthik Chikmagalur
2024-09-15 21:57 ` [PATCH v4] " Karthik Chikmagalur
2024-09-17 18:16 ` Ihor Radchenko
2024-09-18 1:44 ` [PATCH v5] " Karthik Chikmagalur
2024-09-21 10:11 ` Ihor Radchenko
2024-09-22 22:00 ` [PATCH v6] " Karthik Chikmagalur
2024-10-03 17:03 ` Ihor Radchenko
2024-10-08 0:07 ` [PATCH v7] " Karthik Chikmagalur
2024-10-10 17:15 ` Ihor Radchenko
2024-10-10 21:23 ` Karthik Chikmagalur
2024-10-12 8:15 ` Ihor Radchenko
2024-10-28 6:13 ` [PATCH v8] " Karthik Chikmagalur
2024-10-28 18:16 ` Ihor Radchenko
2024-10-28 21:53 ` Karthik Chikmagalur [this message]
2024-10-29 18:46 ` [PATCH v9] " Ihor Radchenko
2024-10-29 20:45 ` [PATCH v10] " Karthik Chikmagalur
2024-08-18 10:34 ` [FR] Automatically display images in resutls of evaluation (was: [PATCH v4.0] Re: [PATCH] add a function to only refresh inline images under current headline instead of global buffer) Ihor Radchenko
[not found] ` <66c54dfc.a70a0220.3c823a.2899@mx.google.com>
2024-08-22 13:06 ` [FR] Automatically display images in resutls of evaluation Ihor Radchenko
[not found] ` <66c89411.170a0220.3255c1.0cd5@mx.google.com>
[not found] ` <87zfor7b04.fsf@localhost>
[not found] ` <CAL1eYuLOsaS43ahueN4uWiCn+Ykp=p_-t9dzAypKdy1en_53BQ@mail.gmail.com>
2024-09-15 13:33 ` [PATCH v3] " Ihor Radchenko
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87a5enevy5.fsf@gmail.com \
--to=karthikchikmagalur@gmail.com \
--cc=emacs-orgmode@gnu.org \
--cc=numbchild@gmail.com \
--cc=yantar92@posteo.net \
/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 external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.