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'.
This commit is contained in:
parent
4ff4828944
commit
9cce40c94f
603
lisp/ol.el
603
lisp/ol.el
|
@ -82,6 +82,11 @@
|
||||||
(declare-function org-src-source-type "org-src" ())
|
(declare-function org-src-source-type "org-src" ())
|
||||||
(declare-function org-time-stamp-format "org" (&optional long inactive))
|
(declare-function org-time-stamp-format "org" (&optional long inactive))
|
||||||
(declare-function outline-next-heading "outline" ())
|
(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))
|
||||||
|
|
||||||
|
|
||||||
;;; Customization
|
;;; Customization
|
||||||
|
@ -171,6 +176,18 @@ link.
|
||||||
|
|
||||||
The default face is `org-link'.
|
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'
|
`:help-echo'
|
||||||
|
|
||||||
String or function used as a value for the `help-echo' text
|
String or function used as a value for the `help-echo' text
|
||||||
|
@ -521,6 +538,81 @@ links more efficient."
|
||||||
:type 'boolean
|
:type 'boolean
|
||||||
:safe #'booleanp)
|
: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
|
;;; Public variables
|
||||||
|
|
||||||
(defconst org-target-regexp (let ((border "[^<>\n\r \t]"))
|
(defconst org-target-regexp (let ((border "[^<>\n\r \t]"))
|
||||||
|
@ -649,6 +741,29 @@ exact and fuzzy text search.")
|
||||||
(defvar org-link--search-failed nil
|
(defvar org-link--search-failed nil
|
||||||
"Non-nil when last link search failed.")
|
"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.")
|
||||||
|
|
||||||
|
|
||||||
;;; Internal Functions
|
;;; Internal Functions
|
||||||
|
|
||||||
|
@ -881,7 +996,227 @@ Return t when a link has been stored in `org-link-store-props'."
|
||||||
(setq desc search-desc))))
|
(setq desc search-desc))))
|
||||||
(cons link 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)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;;; 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
|
;;; Public API
|
||||||
|
|
||||||
(defun org-link-types ()
|
(defun org-link-types ()
|
||||||
|
@ -1573,6 +1908,231 @@ If there is no description, use the link target."
|
||||||
(unless (equal (substring s -1) ">") (setq s (concat s ">")))
|
(unless (equal (substring s -1) ">") (setq s (concat s ">")))
|
||||||
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)
|
||||||
|
(forward-char -1) ;ensure we are on the link
|
||||||
|
(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))))))
|
||||||
|
|
||||||
|
|
||||||
;;; Built-in link types
|
;;; Built-in link types
|
||||||
|
|
||||||
|
@ -1595,7 +2155,48 @@ PATH is the sexp to evaluate, as a string."
|
||||||
(org-link-set-parameters "elisp" :follow #'org-link--open-elisp)
|
(org-link-set-parameters "elisp" :follow #'org-link--open-elisp)
|
||||||
|
|
||||||
;;;; "file" link type
|
;;;; "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
|
;;;; "help" link type
|
||||||
(defun org-link--open-help (path _)
|
(defun org-link--open-help (path _)
|
||||||
|
|
|
@ -797,9 +797,18 @@ It is meant to be added to `org-export-before-parsing-hook'."
|
||||||
See `org-open-file' for details about ARG."
|
See `org-open-file' for details about ARG."
|
||||||
(org-link-open-as-file (org-attach-expand file) 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"
|
(org-link-set-parameters "attachment"
|
||||||
:follow #'org-attach-follow
|
:follow #'org-attach-follow
|
||||||
:complete #'org-attach-complete-link)
|
:complete #'org-attach-complete-link
|
||||||
|
:preview #'org-attach-preview-file)
|
||||||
|
|
||||||
(defun org-attach-complete-link ()
|
(defun org-attach-complete-link ()
|
||||||
"Advise the user with the available files in the attachment directory."
|
"Advise the user with the available files in the attachment directory."
|
||||||
|
|
|
@ -1010,6 +1010,191 @@ use of this function is for the stuck project list."
|
||||||
(define-obsolete-function-alias 'org-add-angle-brackets
|
(define-obsolete-function-alias 'org-add-angle-brackets
|
||||||
'org-link-add-angle-brackets "9.3")
|
'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.
|
;; The function was made obsolete by commit 65399674d5 of 2013-02-22.
|
||||||
;; This make-obsolete call was added 2016-09-01.
|
;; This make-obsolete call was added 2016-09-01.
|
||||||
(make-obsolete 'org-capture-import-remember-templates
|
(make-obsolete 'org-capture-import-remember-templates
|
||||||
|
|
|
@ -40,14 +40,14 @@
|
||||||
(declare-function org-element-post-affiliated "org-element" (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-lineage "org-element-ast" (datum &optional types with-self))
|
||||||
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
|
(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-get-tags "org" (&optional pos local fontify))
|
||||||
(declare-function org-subtree-end-visible-p "org" ())
|
(declare-function org-subtree-end-visible-p "org" ())
|
||||||
(declare-function org-narrow-to-subtree "org" (&optional element))
|
(declare-function org-narrow-to-subtree "org" (&optional element))
|
||||||
(declare-function org-next-visible-heading "org" (arg))
|
(declare-function org-next-visible-heading "org" (arg))
|
||||||
(declare-function org-at-property-p "org" ())
|
(declare-function org-at-property-p "org" ())
|
||||||
(declare-function org-re-property "org" (property &optional literal allow-null value))
|
(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-item-beginning-re "org" ())
|
||||||
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
|
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
|
||||||
(declare-function org-at-item-p "org" ())
|
(declare-function org-at-item-p "org" ())
|
||||||
|
@ -217,7 +217,7 @@ the values `folded', `children', or `subtree'."
|
||||||
(defcustom org-cycle-hook '(org-cycle-hide-archived-subtrees
|
(defcustom org-cycle-hook '(org-cycle-hide-archived-subtrees
|
||||||
org-cycle-show-empty-lines
|
org-cycle-show-empty-lines
|
||||||
org-cycle-optimize-window-after-visibility-change
|
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.
|
"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 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
|
the new state that was set by the most recent `org-cycle' command. The
|
||||||
|
@ -237,11 +237,15 @@ normal outline commands like `show-all', but not with the cycling commands."
|
||||||
:group 'org-cycle
|
:group 'org-cycle
|
||||||
:type 'boolean)
|
:type 'boolean)
|
||||||
|
|
||||||
(defcustom org-cycle-inline-images-display nil
|
(defvaralias 'org-cycle-inline-images-display
|
||||||
"Non-nil means auto display inline images under subtree when cycling."
|
'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-startup
|
||||||
:group 'org-cycle
|
:group 'org-cycle
|
||||||
:package-version '(Org . "9.6")
|
:package-version '(Org . "9.8")
|
||||||
:type 'boolean)
|
:type 'boolean)
|
||||||
|
|
||||||
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
|
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
|
||||||
|
@ -804,12 +808,15 @@ STATE should be one of the symbols listed in the docstring of
|
||||||
"Subtree is archived and stays closed. Use \
|
"Subtree is archived and stays closed. Use \
|
||||||
`\\[org-cycle-force-archived]' to cycle it anyway."))))))
|
`\\[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.
|
"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
|
STATE is the current outline visibility state. It should be one of
|
||||||
symbols `content', `all', `folded', `children', or `subtree'."
|
symbols `content', `all', `folded', `children', or `subtree'."
|
||||||
(when org-cycle-inline-images-display
|
(when org-cycle-link-previews-display
|
||||||
(pcase state
|
(pcase state
|
||||||
('children
|
('children
|
||||||
(org-with-wide-buffer
|
(org-with-wide-buffer
|
||||||
|
@ -817,19 +824,19 @@ symbols `content', `all', `folded', `children', or `subtree'."
|
||||||
;; If has nested headlines, beg,end only from parent headline
|
;; If has nested headlines, beg,end only from parent headline
|
||||||
;; to first child headline which reference to upper
|
;; to first child headline which reference to upper
|
||||||
;; let-binding `org-next-visible-heading'.
|
;; let-binding `org-next-visible-heading'.
|
||||||
(org-display-inline-images
|
(org-link-preview-region
|
||||||
nil nil
|
nil nil
|
||||||
(point-min) (progn (org-next-visible-heading 1) (point)))))
|
(point-min) (progn (org-next-visible-heading 1) (point)))))
|
||||||
('subtree
|
('subtree
|
||||||
(org-with-wide-buffer
|
(org-with-wide-buffer
|
||||||
(org-narrow-to-subtree)
|
(org-narrow-to-subtree)
|
||||||
;; If has nested headlines, also inline display images under all sub-headlines.
|
;; 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
|
('folded
|
||||||
(org-with-wide-buffer
|
(org-with-wide-buffer
|
||||||
(org-narrow-to-subtree)
|
(org-narrow-to-subtree)
|
||||||
(if (numberp (point-max))
|
(if (numberp (point-max))
|
||||||
(org-remove-inline-images (point-min) (point-max))
|
(org-link-preview-clear (point-min) (point-max))
|
||||||
(ignore)))))))
|
(ignore)))))))
|
||||||
|
|
||||||
(provide 'org-cycle)
|
(provide 'org-cycle)
|
||||||
|
|
|
@ -218,7 +218,8 @@
|
||||||
(declare-function org-toggle-radio-button "org" (&optional arg))
|
(declare-function org-toggle-radio-button "org" (&optional arg))
|
||||||
(declare-function org-toggle-comment "org" ())
|
(declare-function org-toggle-comment "org" ())
|
||||||
(declare-function org-toggle-fixed-width "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-latex-preview "org" (&optional arg))
|
||||||
(declare-function org-toggle-narrow-to-subtree "org" ())
|
(declare-function org-toggle-narrow-to-subtree "org" ())
|
||||||
(declare-function org-toggle-ordered-property "org" ())
|
(declare-function org-toggle-ordered-property "org" ())
|
||||||
|
@ -652,8 +653,8 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
|
||||||
(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 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-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-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 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 \\") #'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-b") #'org-toggle-checkbox)
|
||||||
(org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-toggle-radio-button)
|
(org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-toggle-radio-button)
|
||||||
|
|
|
@ -633,7 +633,7 @@ manner suitable for prepending to a user-specified script."
|
||||||
|
|
||||||
(defun org-plot/redisplay-img-in-buffer (img-file)
|
(defun org-plot/redisplay-img-in-buffer (img-file)
|
||||||
"Find any overlays for IMG-FILE in the current Org buffer, and refresh them."
|
"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 (string= img-file (plist-get (cdr (overlay-get img-overlay 'display)) :file))
|
||||||
(when (and (file-exists-p img-file)
|
(when (and (file-exists-p img-file)
|
||||||
(fboundp 'image-flush))
|
(fboundp 'image-flush))
|
||||||
|
|
554
lisp/org.el
554
lisp/org.el
|
@ -1160,14 +1160,22 @@ the following lines anywhere in the buffer:
|
||||||
:package-version '(Org . "9.2")
|
:package-version '(Org . "9.2")
|
||||||
:safe #'booleanp)
|
: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.
|
"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
|
This can also be configured on a per-file basis by adding one of
|
||||||
the following lines anywhere in the buffer:
|
the following lines anywhere in the buffer:
|
||||||
#+STARTUP: inlineimages
|
#+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
|
:group 'org-startup
|
||||||
:version "24.1"
|
:version "29.4"
|
||||||
:type 'boolean)
|
:type 'boolean)
|
||||||
|
|
||||||
(defcustom org-startup-with-latex-preview nil
|
(defcustom org-startup-with-latex-preview nil
|
||||||
|
@ -4153,8 +4161,10 @@ After a match, the following groups carry important information:
|
||||||
("shrink" org-startup-shrink-all-tables t)
|
("shrink" org-startup-shrink-all-tables t)
|
||||||
("descriptivelinks" org-link-descriptive t)
|
("descriptivelinks" org-link-descriptive t)
|
||||||
("literallinks" org-link-descriptive nil)
|
("literallinks" org-link-descriptive nil)
|
||||||
("inlineimages" org-startup-with-inline-images t)
|
("inlineimages" org-startup-with-link-previews t)
|
||||||
("noinlineimages" org-startup-with-inline-images nil)
|
("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)
|
("latexpreview" org-startup-with-latex-preview t)
|
||||||
("nolatexpreview" org-startup-with-latex-preview nil)
|
("nolatexpreview" org-startup-with-latex-preview nil)
|
||||||
("customtime" org-display-custom-times t)
|
("customtime" org-display-custom-times t)
|
||||||
|
@ -5079,7 +5089,7 @@ The following commands are available:
|
||||||
;; modifications to make cache updates work reliably.
|
;; modifications to make cache updates work reliably.
|
||||||
(org-unmodified
|
(org-unmodified
|
||||||
(when org-startup-with-beamer-mode (org-beamer-mode))
|
(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)))
|
(when org-startup-with-latex-preview (org-latex-preview '(16)))
|
||||||
(unless org-inhibit-startup-visibility-stuff (org-cycle-set-startup-visibility))
|
(unless org-inhibit-startup-visibility-stuff (org-cycle-set-startup-visibility))
|
||||||
(when org-startup-truncated (setq truncate-lines t))
|
(when org-startup-truncated (setq truncate-lines t))
|
||||||
|
@ -15601,26 +15611,6 @@ This requires Emacs >= 24.1, built with imagemagick support."
|
||||||
(list :tag "Use #+ATTR* or a number of pixels" (integer))
|
(list :tag "Use #+ATTR* or a number of pixels" (integer))
|
||||||
(const :tag "Use #+ATTR* or don't resize" nil)))
|
(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
|
(defcustom org-agenda-inhibit-startup nil
|
||||||
"Inhibit startup when preparing agenda buffers.
|
"Inhibit startup when preparing agenda buffers.
|
||||||
When this variable is t, the initialization of the Org agenda
|
When this variable is t, the initialization of the Org agenda
|
||||||
|
@ -16667,518 +16657,6 @@ SNIPPETS-P indicates if this is run to create snippet images for HTML."
|
||||||
(format "%g" (/ value 65535.0)))
|
(format "%g" (/ value 65535.0)))
|
||||||
|
|
||||||
|
|
||||||
;; 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-self-insert-command-undo-counter 0)
|
||||||
(defvar org-speed-command nil)
|
(defvar org-speed-command nil)
|
||||||
|
|
||||||
|
|
|
@ -716,14 +716,14 @@ Unfolded Paragraph.
|
||||||
(org-show-subtree)
|
(org-show-subtree)
|
||||||
(org-fold-subtree t)
|
(org-fold-subtree t)
|
||||||
(run-hook-with-args 'org-cycle-hook 'folded)
|
(run-hook-with-args 'org-cycle-hook 'folded)
|
||||||
(should-not org-inline-image-overlays)
|
(should-not org-link-preview-overlays)
|
||||||
(should-not
|
(should-not
|
||||||
(cl-every
|
(cl-every
|
||||||
(lambda (ov) (overlay-get ov 'org-image-overlay))
|
(lambda (ov) (overlay-get ov 'org-image-overlay))
|
||||||
(overlays-in (point-min) (point-max))))
|
(overlays-in (point-min) (point-max))))
|
||||||
(org-show-subtree)
|
(org-show-subtree)
|
||||||
(run-hook-with-args 'org-cycle-hook 'subtree)
|
(run-hook-with-args 'org-cycle-hook 'subtree)
|
||||||
(should org-inline-image-overlays)
|
(should org-link-preview-overlays)
|
||||||
(should
|
(should
|
||||||
(cl-every
|
(cl-every
|
||||||
(lambda (ov) (overlay-get ov 'org-image-overlay))
|
(lambda (ov) (overlay-get ov 'org-image-overlay))
|
||||||
|
|
Loading…
Reference in New Issue