emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
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 v3] Inline image display as part of a new org-link-preview system
Date: Tue, 10 Sep 2024 12:43:47 -0700	[thread overview]
Message-ID: <87r09rxpjg.fsf@gmail.com> (raw)
In-Reply-To: <87o74ypp3b.fsf@localhost>

[-- Attachment #1: Type: text/plain, Size: 4473 bytes --]

Latest iteration of patch attached.

>>>> +BEG and END define the considered part.  They default to the
>>>> +buffer boundaries with possible narrowing."
>>>> +  (interactive "P")
>>>> +  (when (display-graphic-p)
>>>
>>> Do we need it here? You check graphics both here and later in the
>>> preview function. We may probably drop the check herein.
>>
>> I moved the `refresh' clause out of the display-graphic-p check, but I
>> think it's needed.  Otherwise this line will run once per previewed
>> image instead of once per call to `org-link-preview':
>>
>> (when (fboundp 'clear-image-cache) (clear-image-cache))
>>
>> clearing newly placed previews (within the same run) from the cache.
>
> Sorry, I was not clear. I only referred to `display-graphic-p' check. I
> did not mean `clear-image-cache'.

Removed the display-graphic-p check.

>> That said, I have a question about `clear-image-cache' here.  Why does

[...]

>> LaTeX previews.  Images in unrelated buffers/major-modes are also
>> affected.
>
> Here is the original code:
>
> (when refresh
>       (org-remove-inline-images beg end)
>       (when (fboundp 'clear-image-cache) (clear-image-cache)))
>
> Image cache is cleared _only_ with REFRESH argument.
> I think that makes sense, right?

Yes.  But `org-link-preview-region' is always called with the REFRESH
argument set to `t' though.

>>> I am wondering why you left these functions in org.el. Why not moving
>>> them here?
>>
>> They seemed out of place in ol.el, so my plan was the following:
>> 1. This patch: Implement org-link-preview.
>> 2. Another patch: Move image creation/manipulation/geometry functions
>> from org into an org-image (or org-image-utils) library.  Require it in
>> ol.
>>
>> However, I can move them wherever you want, let me know.
>
> I'd rather see them moved.
> You can later send another patch moving them into the new utils library,
> if you want.

Okay, I moved them to ol.el.  They seem out of place here, but I can
send another patch after merging this one to create org-image.el.

>> I don't know how create-image can fail.  Suppose a .jpg file exists but
>> is corrupted or does not have image/jpeg data.  Then create-image
>> returns nil, as does `org--create-inline-image'.  This check guards
>> against trying to preview an "image" that's nil.
>
> Let me elaborate.
>
> In `org-link-preview-region', you set org-image-overlay property:
>
> 			     +            (overlay-put ov 'org-image-overlay t)
> 			     +            (overlay-put ov 'modification-hooks
> 			     +                         (list 'org-link-preview--remove-overlay))
> 			     +            ;; call preview function for link type
> 			     +            (if (funcall preview-func ov path link)
> 			     +              (when (overlay-buffer ov)
>
> Then, you do it yet again in `org-link-preview-file', when the property
> is already there - this part is redundant.

Fixed.

>>>> +	  (when (boundp 'image-map)
>>>
>>> What if not bound? Why not simply (require 'image)?
>>
>> Just unmodified old code.  I've require'd image in ol now, let me know
>> if I should do it differently.
>
> Let's not require it on top level. Maybe better do it within the preview
> function.

Moved (require 'image) to inside the `org-link-preview-file'.  I know it
doesn't affect performance in any reasonable way, but I'm usually
hesitant to do this in functions that run inside loops.

> I mostly meant calling preview-func asynchronously, while idle, spaced
> out, spending not longer than a fraction of second to call several
> preview-funcs.
> Spacing might then be controlled by the users.
>
> We might go further, and let the preview functions return a
> process. Then, we may explicitly control running sentinels just for that
> process via `accept-process-output'. But I am not sure if we need to go
> that far.

Following our other discussion in this thread, I've now implemented
batched previews via idle-timers, controlled by `org-link-preview-delay'
and `org-link-preview-batch-size'.  The default values of these
parameters needs to be tuned.  I tested a few combinations but couldn't
perceive a difference in lag or in the profiler.

The only unhandled case is when an asynchronous preview (in the sense
that preview-func is itself asynchronous) fails.  It is then the
responsibility of preview-func's callback to clean up the overlay.  This
can be done by calling org-link-preview-clear on the bounds of the link.
I think this is sufficient for now.


[-- 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: 69629 bytes --]

From 135735097a4ab9d53f3a7ffef79b34c604cb64b9 Mon Sep 17 00:00:00 2001
From: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Date: Fri, 23 Aug 2024 15:46:53 -0700
Subject: [PATCH] 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-overlays,
org-link-preview--get-overlays, org-link-preview--remove-overlay,
org-link-previeworg-link-preview-region, 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):
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.

* 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): Use
`org-link-preview'.

* 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                    | 565 +++++++++++++++++++++++++++++++++-
 lisp/org-attach.el            |  11 +-
 lisp/org-compat.el            | 189 ++++++++++++
 lisp/org-cycle.el             |  10 +-
 lisp/org-keys.el              |   4 +-
 lisp/org-plot.el              |   2 +-
 lisp/org.el                   | 534 +-------------------------------
 testing/lisp/test-org-fold.el |   4 +-
 8 files changed, 774 insertions(+), 545 deletions(-)

diff --git a/lisp/ol.el b/lisp/ol.el
index 52ea62d69..dad84216b 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,16 @@ (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 link element
+
+  This function must return a non-nil value to indicate success.
+
 `:help-echo'
 
   String or function used as a value for the `help-echo' text
@@ -521,6 +536,80 @@ (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 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 +738,13 @@ (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)
+
 \f
 ;;; Internal Functions
 
@@ -881,7 +977,228 @@ (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* (((overlay-get ov 'org-image-overlay))
+                (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 +1890,211 @@ (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, preview links with description as
+well.
+
+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")))))
+
+(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)
+    (when (fboundp 'clear-image-cache) (clear-image-cache)))
+  (org-with-point-at (or beg (point-min))
+    (let ((case-fold-search t)
+          (preview-queue (list nil))
+          (preview-queue-size 1))
+      (while (re-search-forward org-link-any-re end t)
+        (when-let* ((link (org-element-lineage
+                           (save-match-data (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 'org-image-overlay t)
+            (overlay-put ov 'modification-hooks
+                         (list 'org-link-preview--remove-overlay))
+            (push ov org-link-preview-overlays)
+            ;; Collect previews to run asynchronously, in batches
+            (if (>= (length (car-safe preview-queue)) org-link-preview-batch-size)
+                (progn (cl-incf preview-queue-size)
+                       (push (list (list preview-func ov path link)) preview-queue))
+              (push (list preview-func ov path link) (car preview-queue))))))
+      ;; Run preview asynchronously in batches:
+      ;; preview-queue is a list of preview-batch, which is a list of preview-spec
+      (when (car preview-queue)
+        (dolist (preview-batch (nreverse preview-queue))
+          (run-with-idle-timer
+           org-link-preview-delay nil
+           (lambda (previews)
+             ;; (message "queue: %d" preview-queue-size)
+             (cl-decf preview-queue-size)
+             (dolist (preview-spec (nreverse previews)) ;spec is (preview-func overlay path link)
+               (when-let* ((ov (cadr preview-spec))
+                           (buf (overlay-buffer ov)))
+                 (with-current-buffer buf
+                   (unless (apply preview-spec)
+                     ;; Preview was unsuccessful, delete overlay
+                     (delete-overlay ov)
+                     (setq org-link-preview-overlays (delq ov org-link-preview-overlays)))))))
+           preview-batch))))))
+
+(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)
+        (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 +2117,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..242b46a86 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -783,6 +783,195 @@ (defun org-add-link-type (type &optional follow export)
 
 (make-obsolete 'org-add-link-type "use `org-link-set-parameters' instead." "9.0")
 
+(declare-function org-link-preview--remove-overlay "ol")
+(declare-function org-link-preview--get-overlays "ol")
+(declare-function org-link-preview-clear "ol")
+(declare-function org-link-preview--remove-overlay "ol")
+
+(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-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)
+(declare-function org-attach-expand "org-attach")
+(declare-function org-display-inline-image--width "org")
+(declare-function org-image--align "org")
+(declare-function org--create-inline-image "org")
+
+(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")
+;; 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-display-inline-images 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"))))))
+
+(make-obsolete 'org-redisplay-inline-images
+               'org-link-preview "9.8")
+;; FIXME: Unused; obsoleted; to be removed
+(defun org-redisplay-inline-images ()
+  "Assure display of inline images and refresh them."
+  (interactive)
+  (org-toggle-inline-images)
+  (unless org-link-preview-overlays
+    (org-toggle-inline-images)))
+
 ;;;; Functions unused in Org core.
 (defun org-table-recognize-table.el ()
   "If there is a table.el table nearby, recognize it and move into it."
diff --git a/lisp/org-cycle.el b/lisp/org-cycle.el
index 8a39bdb8c..1a1c916bd 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" ())
@@ -817,19 +817,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..77cbe5c0f 100644
--- a/lisp/org-keys.el
+++ b/lisp/org-keys.el
@@ -218,7 +218,7 @@ (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-latex-preview "org" (&optional arg))
 (declare-function org-toggle-narrow-to-subtree "org" ())
 (declare-function org-toggle-ordered-property "org" ())
@@ -618,7 +618,7 @@ (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-v") #'org-link-preview)
 (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 \\") #'org-toggle-pretty-entities)
 (org-defkey org-mode-map (kbd "C-c C-x C-b") #'org-toggle-checkbox)
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 d5c1dcb35..e5cc9308f 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -5079,7 +5079,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 +15583,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 +16629,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.44.1


  parent reply	other threads:[~2024-09-10 19:44 UTC|newest]

Thread overview: 60+ 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 [this message]
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-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

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87r09rxpjg.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 public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).