>> Oops. My patch happens to be exactly the opposite of my changes. (I >> chose wrong direction of the diff range) > > Here is the correct diff.
Applied diff and rebased onto main. Karthik
>From eedcba143b72abdaa93e34d1356616012141c7f4 Mon Sep 17 00:00:00 2001 From: Karthik Chikmagalur <karthikchikmaga...@gmail.com> Date: Fri, 23 Aug 2024 15:46:53 -0700 Subject: [PATCH 1/2] org-link: Customizable preview API for arbitrary link types Add a customizable preview API for arbitrary link types. Make inline image previews a part of the more universal org-link preview feature. Each link type can now be previewed differently based on a new link parameter. * lisp/ol.el (org-link-parameters, org-link-preview-batch-size, org-link-preview-delay, org-link-preview--timer, org-link-preview--queue, org-link-preview-overlays, org-link-preview--get-overlays, org-link-preview--remove-overlay, org-link-preview, org-link-preview-region, org-link-preview--process-queue, org-link-preview-clear, org-link-preview-file, org-display-remote-inline-images, org-image-align, org--create-inline-image, org-display-inline-image--width, org-image--align): Add new commands `org-link-preview', `org-link-preview-region' and `org-link-preview-clear' for creating link previews for any kind of link. Add new org-link parameter `:preview' for specifying how a link type should be previewed. This link parameter is a function called asynchronously to place previes. File links and attachments are previewed using inline image previews as before. Move image handling utilities from lisp/org.el to lisp/ol.el. * testing/lisp/test-org-fold.el: Use `org-link-preview'. * lisp/org.el (org-toggle-inline-images, org-toggle-inline-images-command, org-display-inline-images, org--inline-image-overlays, org-inline-image-overlays, org-redisplay-inline-images, org-image-align, org-display-inline-remove-overlay, org-remove-inline-images, org-startup-with-inline-images, org-startup-with-link-previews, org-startup-options): Obsolete and move `org-toggle-inline-images', `org-display-inline-images' and `org-redisplay-inline-images' to org-compat. These are obsoleted by `org-link-preview' and `org-link-preview-region'. Remove `org-toggle-inline-images-command'. Move the other internal functions to org-link. Rename `org-startup-with-inline-images' to `org-startup-with-link-previews'. Add new STARTUP options for link previews to `org-startup-options': "linkpreviews" and "nolinkpreviews". * lisp/org-plot.el (org-plot/redisplay-img-in-buffer): Modify to use `org-link-preview'. * lisp/org-keys.el: Bind `C-c C-x C-v' to new command `org-link-preview', which has the same prefix arg behaviors as `org-latex-preview'. In addition to these, it supports numeric prefix args 1 and 11 to preview links with descriptions at point/region (with 1) and across the buffer (with 11). * lisp/org-cycle.el (org-cycle-display-inline-images, org-cycle-display-link-previews, org-cycle-inline-images-display, org-cycle-inline-link-previews): Use `org-link-preview' and `org-link-preview-region'. Rename inline-images functions and user options to their link-previews equivalents: - `org-cycle-display-inline-images' to `org-cycle-display-link-previews' - `org-cycle-inline-images-display' to `org-cycle-inline-link-previews' * lisp/org-compat.el (org-display-inline-remove-overlay, org--inline-image-overlays, org-remove-inline-images, org-inline-image-overlays, org-display-inline-images, org-toggle-inline-images): * lisp/org-attach.el (org-attach-preview-file): Add new `:preview' link parameter for links of type "attachment", set to the new function `org-attach-preview-file'. --- lisp/ol.el | 603 +++++++++++++++++++++++++++++++++- lisp/org-attach.el | 11 +- lisp/org-compat.el | 185 +++++++++++ lisp/org-cycle.el | 31 +- lisp/org-keys.el | 7 +- lisp/org-plot.el | 2 +- lisp/org.el | 554 +------------------------------ testing/lisp/test-org-fold.el | 4 +- 8 files changed, 839 insertions(+), 558 deletions(-) diff --git a/lisp/ol.el b/lisp/ol.el index ea01e2254..032610bad 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)) ;;; Customization @@ -171,6 +176,18 @@ (defcustom org-link-parameters nil The default face is `org-link'. +`:preview' + + Function to run to generate an in-buffer preview for the link. It + must accept three arguments: + - an overlay placed from the start to the end of the link + - the link path, as a string + - the syntax node for the link + + This function must return a non-nil value to indicate success. + A return value of nil implies that the preview failed, and the + overlay placed on the link will be removed. + `:help-echo' String or function used as a value for the `help-echo' text @@ -521,6 +538,81 @@ (defcustom org-link-keep-stored-after-insertion nil :type 'boolean :safe #'booleanp) +(defcustom org-link-preview-delay 0.05 + "Idle delay in seconds between link previews when using +`org-link-preview'. Links are previewed in batches (see +`org-link-preview-batch-size') spaced out by this delay. Set +this to a small number for more immediate previews, but at the +expense of higher lag." + :group 'org-link + :type 'number) + +(defcustom org-link-preview-batch-size 6 + "Number of links that are previewed at once with +`org-link-preview'. Links are previewed asynchronously, in +batches spaced out in time (see `org-link-preview-delay'). Set +this to a large integer for more immediate previews, but at the +expense of higher lag." + :group 'org-link + :type 'natnum) + +(defcustom org-display-remote-inline-images 'skip + "How to display remote inline images. +Possible values of this option are: + +skip Don't display remote images. +download Always download and display remote images. +t +cache Display remote images, and open them in separate buffers + for caching. Silently update the image buffer when a file + change is detected." + :group 'org-appearance + :package-version '(Org . "9.7") + :type '(choice + (const :tag "Ignore remote images" skip) + (const :tag "Always display remote images" download) + (const :tag "Display and silently update remote images" cache)) + :safe #'symbolp) + +(defcustom org-image-max-width 'fill-column + "When non-nil, limit the displayed image width. +This setting only takes effect when `org-image-actual-width' is set to +t or when #+ATTR* is set to t. + +Possible values: +- `fill-column' :: limit width to `fill-column' +- `window' :: limit width to window width +- integer :: limit width to number in pixels +- float :: limit width to that fraction of window width +- nil :: do not limit image width" + :group 'org-appearance + :package-version '(Org . "9.7") + :type '(choice + (const :tag "Do not limit image width" nil) + (const :tag "Limit to `fill-column'" fill-column) + (const :tag "Limit to window width" window) + (integer :tag "Limit to a number of pixels") + (float :tag "Limit to a fraction of window width"))) + +(defcustom org-image-align 'left + "How to align images previewed using `org-link-preview-region'. + +Only stand-alone image links are affected by this setting. These +are links without surrounding text. + +Possible values of this option are: + +left Insert image at specified position. +center Center image previews. +right Right-align image previews." + :group 'org-appearance + :package-version '(Org . "9.7") + :type '(choice + (const :tag "Left align (or don\\='t align) image previews" left) + (const :tag "Center image previews" center) + (const :tag "Right align image previews" right)) + :safe #'symbolp) + ;;; Public variables (defconst org-target-regexp (let ((border "[^<>\n\r \t]")) @@ -649,6 +741,29 @@ (defvar org-link--insert-history nil (defvar org-link--search-failed nil "Non-nil when last link search failed.") +(defvar-local org-link-preview-overlays nil) +;; Preserve when switching modes or when restarting Org. +;; If we clear the overlay list and later enable Or mode, the existing +;; image overlays will never be cleared by `org-link-preview' +;; and `org-link-preview-clear'. +(put 'org-link-preview-overlays 'permanent-local t) + +(defvar-local org-link-preview--timer nil + "Timer for previewing Org links in buffer. + +This timer creates previews for specs in +`org-link-preview--queue'.") + +(defvar-local org-link-preview--queue nil + "Queue of pending previews for Org links in buffer. + +Each element of this queue is a list of the form + +(PREVIEW-FUNC OVERLAY PATH LINK) + +where PREVIEW-FUNC places a preview of PATH using OVERLAY. LINK +is the Org element being previewed.") + ;;; Internal Functions @@ -881,7 +996,227 @@ (defun org-link--file-link-to-here () (setq desc search-desc)))) (cons link desc))) +(defun org-link-preview--get-overlays (&optional beg end) + "Return link preview overlays between BEG and END." + (let* ((beg (or beg (point-min))) + (end (or end (point-max))) + (overlays (overlays-in beg end)) + result) + (dolist (ov overlays result) + (when (memq ov org-link-preview-overlays) + (push ov result))))) + +(defun org-link-preview--remove-overlay (ov after _beg _end &optional _len) + "Remove link-preview overlay OV if a corresponding region is modified. + +AFTER is true when this function is called post-change." + (when (and ov after) + (setq org-link-preview-overlays (delq ov org-link-preview-overlays)) + ;; Clear image from cache to avoid image not updating upon + ;; changing on disk. See Emacs bug#59902. + (when-let* ((disp (overlay-get ov 'display)) + ((imagep disp))) + (image-flush disp)) + (delete-overlay ov))) + + +;;;; Utilities for image preview display + +;; For without-x builds. +(declare-function image-flush "image" (spec &optional frame)) + +(defun org--create-inline-image (file width) + "Create image located at FILE, or return nil. +WIDTH is the width of the image. The image may not be created +according to the value of `org-display-remote-inline-images'." + (let* ((remote? (file-remote-p file)) + (file-or-data + (pcase org-display-remote-inline-images + ((guard (not remote?)) file) + (`download (with-temp-buffer + (set-buffer-multibyte nil) + (insert-file-contents-literally file) + (buffer-string))) + ((or `cache `t) + (let ((revert-without-query '("."))) + (with-current-buffer (find-file-noselect file) + (buffer-string)))) + (`skip nil) + (other + (message "Invalid value of `org-display-remote-inline-images': %S" + other) + nil)))) + (when file-or-data + (create-image file-or-data + (and (image-type-available-p 'imagemagick) + width + 'imagemagick) + remote? + :width width + :max-width + (pcase org-image-max-width + (`fill-column (* fill-column (frame-char-width (selected-frame)))) + (`window (window-width nil t)) + ((pred integerp) org-image-max-width) + ((pred floatp) (floor (* org-image-max-width (window-width nil t)))) + (`nil nil) + (_ (error "Unsupported value of `org-image-max-width': %S" + org-image-max-width))) + :scale 1)))) + +(declare-function org-export-read-attribute "ox" + (attribute element &optional property)) +(defvar visual-fill-column-width) ; Silence compiler warning +(defun org-display-inline-image--width (link) + "Determine the display width of the image LINK, in pixels. +- When `org-image-actual-width' is t, the image's pixel width is used. +- When `org-image-actual-width' is a number, that value will is used. +- When `org-image-actual-width' is nil or a list, :width attribute of + #+attr_org or the first #+attr_... (if it exists) is used to set the + image width. A width of X% is divided by 100. If the value is a + float between 0 and 2, it interpreted as that proportion of the text + width in the buffer. + + If no :width attribute is given and `org-image-actual-width' is a + list with a number as the car, then that number is used as the + default value." + ;; Apply `org-image-actual-width' specifications. + ;; Support subtree-level property "ORG-IMAGE-ACTUAL-WIDTH" specified + ;; width. + (let ((org-image-actual-width (org-property-or-variable-value 'org-image-actual-width))) + (cond + ((eq org-image-actual-width t) nil) + ((listp org-image-actual-width) + (require 'ox) + (let* ((par (org-element-lineage link 'paragraph)) + ;; Try to find an attribute providing a :width. + ;; #+ATTR_ORG: :width ... + (attr-width (org-export-read-attribute :attr_org par :width)) + (width-unreadable? + (lambda (value) + (or (not (stringp value)) + (unless (string= value "t") + (or (not (string-match + (rx bos (opt "+") + (or + ;; Number of pixels + ;; must be a lone number, not + ;; things like 4in + (seq (1+ (in "0-9")) eos) + ;; Numbers ending with % + (seq (1+ (in "0-9.")) (group-n 1 "%")) + ;; Fractions + (seq (0+ (in "0-9")) "." (1+ (in "0-9"))))) + value)) + (let ((number (string-to-number value))) + (and (floatp number) + (not (match-string 1 value)) ; X% + (not (<= 0.0 number 2.0))))))))) + ;; #+ATTR_BACKEND: :width ... + (attr-other + (catch :found + (org-element-properties-map + (lambda (prop _) + (when (and + (not (eq prop :attr_org)) + (string-match-p "^:attr_" (symbol-name prop)) + (not (funcall width-unreadable? (org-export-read-attribute prop par :width)))) + (throw :found prop))) + par))) + (attr-width + (if (not (funcall width-unreadable? attr-width)) + attr-width + ;; When #+attr_org: does not have readable :width + (and attr-other + (org-export-read-attribute attr-other par :width)))) + (width + (cond + ;; Treat :width t as if `org-image-actual-width' were t. + ((string= attr-width "t") nil) + ;; Fallback to `org-image-actual-width' if no interprable width is given. + ((funcall width-unreadable? attr-width) + (car org-image-actual-width)) + ;; Convert numeric widths to numbers, converting percentages. + ((string-match-p "\\`[[+]?[0-9.]+%" attr-width) + (/ (string-to-number attr-width) 100.0)) + (t (string-to-number attr-width))))) + (if (and (floatp width) (<= 0.0 width 2.0)) + ;; A float in [0,2] should be interpereted as this portion of + ;; the text width in the window. This works well with cases like + ;; #+attr_latex: :width 0.X\{line,page,column,etc.}width, + ;; as the "0.X" is pulled out as a float. We use 2 as the upper + ;; bound as cases such as 1.2\linewidth are feasible. + (round (* width + (window-pixel-width) + (/ (or (and (bound-and-true-p visual-fill-column-mode) + (or visual-fill-column-width auto-fill-function)) + (when auto-fill-function fill-column) + (- (window-text-width) (line-number-display-width))) + (float (window-total-width))))) + width))) + ((numberp org-image-actual-width) + org-image-actual-width) + (t nil)))) + +(defun org-image--align (link) + "Determine the alignment of the image LINK. +LINK is a link object. + +In decreasing order of priority, this is controlled: +- Per image by the value of `:center' or `:align' in the +affiliated keyword `#+attr_org'. +- By the `#+attr_html' or `#+attr_latex` keywords with valid + `:center' or `:align' values. +- Globally by the user option `org-image-align'. + +The result is either nil or one of the strings \"left\", +\"center\" or \"right\". + +\"center\" will cause the image preview to be centered, \"right\" +will cause it to be right-aligned. A value of \"left\" or nil +implies no special alignment." + (let ((par (org-element-lineage link 'paragraph))) + ;; Only align when image is not surrounded by paragraph text: + (when (and par ; when image is not in paragraph, but in table/headline/etc, do not align + (= (org-element-begin link) + (save-excursion + (goto-char (org-element-contents-begin par)) + (skip-chars-forward "\t ") + (point))) ;account for leading space + ;before link + (<= (- (org-element-contents-end par) + (org-element-end link)) + 1)) ;account for trailing newline + ;at end of paragraph + (save-match-data + ;; Look for a valid ":center t" or ":align left|center|right" + ;; attribute. + ;; + ;; An attr_org keyword has the highest priority, with + ;; any attr.* next. Choosing between these is + ;; unspecified. + (let ((center-re ":\\(center\\)[[:space:]]+t\\b") + (align-re ":align[[:space:]]+\\(left\\|center\\|right\\)\\b") + attr-align) + (catch 'exit + (org-element-properties-mapc + (lambda (propname propval) + (when (and propval + (string-match-p ":attr.*" (symbol-name propname))) + (setq propval (car-safe propval)) + (when (or (string-match center-re propval) + (string-match align-re propval)) + (setq attr-align (match-string 1 propval)) + (when (eq propname :attr_org) + (throw 'exit t))))) + par)) + (if attr-align + (when (member attr-align '("center" "right")) attr-align) + ;; No image-specific keyword, check global alignment property + (when (memq org-image-align '(center right)) + (symbol-name org-image-align)))))))) + ;;; Public API (defun org-link-types () @@ -1573,6 +1908,231 @@ (defun org-link-add-angle-brackets (s) (unless (equal (substring s -1) ">") (setq s (concat s ">"))) s) +;;;###autoload +(defun org-link-preview (&optional arg beg end) + "Toggle display of link previews in the buffer. + +When region BEG..END is active, preview links in the +region. + +When point is at a link, display a preview for that link only. +Otherwise, display previews for links in current entry. + +With numeric prefix ARG 1, also preview links with description in +the active region, at point or in the current section. + +With prefix ARG `\\[universal-argument]', clear link previews at +point or in the current entry. + +With prefix ARG `\\[universal-argument] \\[universal-argument]', + display link previews in the accessible portion of the + buffer. With numeric prefix ARG 11, do the same, but include + links with descriptions. + +With prefix ARG `\\[universal-argument] \\[universal-argument] \\[universal-argument]', +hide all link previews in the accessible portion of the buffer. + +This command is designed for interactive use. From Elisp, you can +also use `org-link-preview-region'." + (interactive (cons current-prefix-arg + (when (use-region-p) + (list (region-beginning) (region-end))))) + (let* ((include-linked + (cond + ((member arg '(nil (4) (16)) ) nil) + ((member arg '(1 11)) 'include-linked) + (t 'include-linked))) + (interactive? (called-interactively-p 'any)) + (toggle-previews + (lambda (&optional beg end scope remove) + (let* ((beg (or beg (point-min))) + (end (or end (point-max))) + (old (org-link-preview--get-overlays beg end)) + (scope (or scope (format "%d:%d" beg end)))) + (if remove + (progn + (org-link-preview-clear beg end) + (when interactive? + (message + "[%s] Inline link previews turned off (removed %d images)" + scope (length old)))) + (org-link-preview-region include-linked t beg end) + (when interactive? + (let ((new (org-link-preview--get-overlays beg end))) + (message + (if new + (format "[%s] Displaying %d images inline %s" + scope (length new) + (if include-linked "(including images with description)" + "")) + (format "[%s] No images to display inline" scope)))))))))) + (cond + ;; Region selected :: display previews in region. + ((and beg end) + (funcall toggle-previews beg end "region" + (and (equal arg '(4)) 'remove))) + ;; C-u argument: clear image at point or in entry + ((equal arg '(4)) + (if-let ((ov (cdr (get-char-property-and-overlay + (point) 'org-image-overlay)))) + ;; clear link preview at point + (funcall toggle-previews + (overlay-start ov) (overlay-end ov) + "preview at point" 'remove) + ;; Clear link previews in entry + (funcall toggle-previews + (if (org-before-first-heading-p) (point-min) + (save-excursion + (org-with-limited-levels (org-back-to-heading t) (point)))) + (org-with-limited-levels (org-entry-end-position)) + "current section" 'remove))) + ;; C-u C-u or C-11 argument :: display images in the whole buffer. + ((member arg '(11 (16))) (funcall toggle-previews nil nil "buffer")) + ;; C-u C-u C-u argument :: unconditionally hide images in the buffer. + ((equal arg '(64)) (funcall toggle-previews nil nil "buffer" 'remove)) + ;; Argument nil or 1, no region selected :: display images in + ;; current section or image link at point. + ((and (member arg '(nil 1)) (null beg) (null end)) + (let ((context (org-element-context))) + ;; toggle display of inline image link at point. + (if (org-element-type-p context 'link) + (let* ((ov (cdr-safe (get-char-property-and-overlay + (point) 'org-image-overlay))) + (remove? (and ov (memq ov org-link-preview-overlays) + 'remove))) + (funcall toggle-previews + (org-element-begin context) + (org-element-end context) + "image at point" remove?)) + (let ((beg (if (org-before-first-heading-p) (point-min) + (save-excursion + (org-with-limited-levels (org-back-to-heading t) (point))))) + (end (org-with-limited-levels (org-entry-end-position)))) + (funcall toggle-previews beg end "current section"))))) + ;; Any other non-nil argument. + ((not (null arg)) (funcall toggle-previews beg end "region"))))) + +;;;###autoload +(defun org-link-preview-refresh () + "Assure display of link previews in buffer and refresh them." + (interactive) + (org-link-preview-region nil t (point-min) (point-max))) + +(defun org-link-preview-region (&optional include-linked refresh beg end) + "Display link previews. + +A previewable link type is one that has a `:preview' link +parameter, see `org-link-parameters'. + +By default, a file link or attachment is previewable if it +follows either of these conventions: + + 1. Its path is a file with an extension matching return value + from `image-file-name-regexp' and it has no contents. + + 2. Its description consists in a single link of the previous + type. In this case, that link must be a well-formed plain + or angle link, i.e., it must have an explicit \"file\" or + \"attachment\" type. + +File links are equipped with the keymap `image-map'. + +When optional argument INCLUDE-LINKED is non-nil, links with a +text description part will also be inlined. This can be nice for +a quick look at those images, but it does not reflect what +exported files will look like. + +When optional argument REFRESH is non-nil, refresh existing +images between BEG and END. This will create new image displays +only if necessary. + +BEG and END define the considered part. They default to the +buffer boundaries with possible narrowing." + (interactive "P") + (when refresh (org-link-preview-clear beg end)) + (org-with-point-at (or beg (point-min)) + (let ((case-fold-search t) + preview-queue) + ;; Collect links to preview + (while (re-search-forward org-link-any-re end t) + (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 @@ -1595,7 +2155,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 d4393324f..4fe06cbf4 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 05fe13457..20caafb2b 100644 --- a/lisp/org-compat.el +++ b/lisp/org-compat.el @@ -1010,6 +1010,191 @@ (define-obsolete-function-alias 'org-open-link-from-string (define-obsolete-function-alias 'org-add-angle-brackets 'org-link-add-angle-brackets "9.3") +(declare-function org-link-preview--remove-overlay "ol" + (ov after beg end &optional len)) +(declare-function org-link-preview--get-overlays "ol" (&optional beg end)) +(declare-function org-link-preview-clear "ol" (&optional beg end)) +(declare-function org-link-preview--remove-overlay "ol" + (ov after beg end &optional len)) +(declare-function org-attach-expand "org-attach" (file)) +(declare-function org-display-inline-image--width "org" (link)) +(declare-function org-image--align "org" (link)) +(declare-function org--create-inline-image "org" (file width)) + +(define-obsolete-function-alias 'org-display-inline-remove-overlay + 'org-link-preview--remove-overlay "9.8") +(define-obsolete-function-alias 'org--inline-image-overlays + 'org-link-preview--get-overlays "9.8") +(define-obsolete-function-alias 'org-remove-inline-images + 'org-link-preview-clear "9.8") +(define-obsolete-function-alias 'org-redisplay-inline-images + 'org-link-preview-refresh "9.8") +(define-obsolete-variable-alias 'org-inline-image-overlays + 'org-link-preview-overlays "9.8") +(defvar org-link-preview-overlays) +(defvar org-link-abbrev-alist-local) +(defvar org-link-abbrev-alist) +(defvar org-link-angle-re) +(defvar org-link-plain-re) + +(make-obsolete 'org-display-inline-images + 'org-link-preview-region "9.8") +;; FIXME: Unused; obsoleted; to be removed +(defun org-display-inline-images (&optional include-linked refresh beg end) + "Display inline images. + +An inline image is a link which follows either of these +conventions: + + 1. Its path is a file with an extension matching return value + from `image-file-name-regexp' and it has no contents. + + 2. Its description consists in a single link of the previous + type. In this case, that link must be a well-formed plain + or angle link, i.e., it must have an explicit \"file\" or + \"attachment\" type. + +Equip each image with the key-map `image-map'. + +When optional argument INCLUDE-LINKED is non-nil, also links with +a text description part will be inlined. This can be nice for +a quick look at those images, but it does not reflect what +exported files will look like. + +When optional argument REFRESH is non-nil, refresh existing +images between BEG and END. This will create new image displays +only if necessary. + +BEG and END define the considered part. They default to the +buffer boundaries with possible narrowing." + (interactive "P") + (when (display-graphic-p) + (when refresh + (org-link-preview-clear beg end) + (when (fboundp 'clear-image-cache) (clear-image-cache))) + (let ((end (or end (point-max)))) + (org-with-point-at (or beg (point-min)) + (let* ((case-fold-search t) + (file-extension-re (image-file-name-regexp)) + (link-abbrevs (mapcar #'car + (append org-link-abbrev-alist-local + org-link-abbrev-alist))) + ;; Check absolute, relative file names and explicit + ;; "file:" links. Also check link abbreviations since + ;; some might expand to "file" links. + (file-types-re + (format "\\[\\[\\(?:file%s:\\|attachment:\\|[./~]\\)\\|\\]\\[\\(<?\\(?:file\\|attachment\\):\\)" + (if (not link-abbrevs) "" + (concat "\\|" (regexp-opt link-abbrevs)))))) + (while (re-search-forward file-types-re end t) + (let* ((link (org-element-lineage + (save-match-data (org-element-context)) + 'link t)) + (linktype (org-element-property :type link)) + (inner-start (match-beginning 1)) + (path + (cond + ;; No link at point; no inline image. + ((not link) nil) + ;; File link without a description. Also handle + ;; INCLUDE-LINKED here since it should have + ;; precedence over the next case. I.e., if link + ;; contains filenames in both the path and the + ;; description, prioritize the path only when + ;; INCLUDE-LINKED is non-nil. + ((or (not (org-element-contents-begin link)) + include-linked) + (and (or (equal "file" linktype) + (equal "attachment" linktype)) + (org-element-property :path link))) + ;; Link with a description. Check if description + ;; is a filename. Even if Org doesn't have syntax + ;; for those -- clickable image -- constructs, fake + ;; them, as in `org-export-insert-image-links'. + ((not inner-start) nil) + (t + (org-with-point-at inner-start + (and (looking-at + (if (char-equal ?< (char-after inner-start)) + org-link-angle-re + org-link-plain-re)) + ;; File name must fill the whole + ;; description. + (= (org-element-contents-end link) + (match-end 0)) + (progn + (setq linktype (match-string 1)) + (match-string 2)))))))) + (when (and path (string-match-p file-extension-re path)) + (let ((file (if (equal "attachment" linktype) + (progn + (require 'org-attach) + (ignore-errors (org-attach-expand path))) + (expand-file-name path)))) + ;; Expand environment variables. + (when file (setq file (substitute-in-file-name file))) + (when (and file (file-exists-p file)) + (let ((width (org-display-inline-image--width link)) + (align (org-image--align link)) + (old (get-char-property-and-overlay + (org-element-begin link) + 'org-image-overlay))) + (if (and (car-safe old) refresh) + (image-flush (overlay-get (cdr old) 'display)) + (let ((image (org--create-inline-image file width))) + (when image + (let ((ov (make-overlay + (org-element-begin link) + (progn + (goto-char + (org-element-end link)) + (unless (eolp) (skip-chars-backward " \t")) + (point))))) + ;; See bug#59902. We cannot rely + ;; on Emacs to update image if the file + ;; has changed. + (image-flush image) + (overlay-put ov 'display image) + (overlay-put ov 'face 'default) + (overlay-put ov 'org-image-overlay t) + (overlay-put + ov 'modification-hooks + (list 'org-link-preview--remove-overlay)) + (when (boundp 'image-map) + (overlay-put ov 'keymap image-map)) + (when align + (overlay-put + ov 'before-string + (propertize + " " 'face 'default + 'display + (pcase align + ("center" `(space :align-to (- center (0.5 . ,image)))) + ("right" `(space :align-to (- right ,image))))))) + (push ov org-inline-image-overlays)))))))))))))))) + +(make-obsolete 'org-toggle-inline-images + 'org-link-preview "9.8") +(declare-function org-link-preview-region "ol") +;; FIXME: Unused; obsoleted; to be removed +(defun org-toggle-inline-images (&optional include-linked beg end) + "Toggle the display of inline images. +INCLUDE-LINKED is passed to `org-display-inline-images'." + (interactive "P") + (if (org-link-preview--get-overlays beg end) + (progn + (org-link-preview-clear beg end) + (when (called-interactively-p 'interactive) + (message "Inline image display turned off"))) + (org-link-preview-region include-linked nil beg end) + (when (called-interactively-p 'interactive) + (let ((new (org-link-preview--get-overlays beg end))) + (message (if new + (format "%d images displayed inline" + (length new)) + "No images to display inline")))))) + + ;; The function was made obsolete by commit 65399674d5 of 2013-02-22. ;; This make-obsolete call was added 2016-09-01. (make-obsolete 'org-capture-import-remember-templates diff --git a/lisp/org-cycle.el b/lisp/org-cycle.el index 8a39bdb8c..97a545393 100644 --- a/lisp/org-cycle.el +++ b/lisp/org-cycle.el @@ -40,14 +40,14 @@ (declare-function org-element-property "org-element-ast" (property node)) (declare-function org-element-post-affiliated "org-element" (node)) (declare-function org-element-lineage "org-element-ast" (datum &optional types with-self)) (declare-function org-element-at-point "org-element" (&optional pom cached-only)) -(declare-function org-display-inline-images "org" (&optional include-linked refresh beg end)) +(declare-function org-link-preview-region "ol" (&optional include-linked refresh beg end)) (declare-function org-get-tags "org" (&optional pos local fontify)) (declare-function org-subtree-end-visible-p "org" ()) (declare-function org-narrow-to-subtree "org" (&optional element)) (declare-function org-next-visible-heading "org" (arg)) (declare-function org-at-property-p "org" ()) (declare-function org-re-property "org" (property &optional literal allow-null value)) -(declare-function org-remove-inline-images "org" (&optional beg end)) +(declare-function org-link-preview-clear "ol" (&optional beg end)) (declare-function org-item-beginning-re "org" ()) (declare-function org-at-heading-p "org" (&optional invisible-not-ok)) (declare-function org-at-item-p "org" ()) @@ -217,7 +217,7 @@ (defcustom org-cycle-pre-hook nil (defcustom org-cycle-hook '(org-cycle-hide-archived-subtrees org-cycle-show-empty-lines org-cycle-optimize-window-after-visibility-change - org-cycle-display-inline-images) + org-cycle-display-link-previews) "Hook that is run after `org-cycle' has changed the buffer visibility. The function(s) in this hook must accept a single argument which indicates the new state that was set by the most recent `org-cycle' command. The @@ -237,11 +237,15 @@ (defcustom org-cycle-open-archived-trees nil :group 'org-cycle :type 'boolean) -(defcustom org-cycle-inline-images-display nil - "Non-nil means auto display inline images under subtree when cycling." +(defvaralias 'org-cycle-inline-images-display + 'org-cycle-link-previews-display + "Non-nil means auto display inline images under subtree when cycling.") + +(defcustom org-cycle-link-previews-display nil + "Non-nil means auto display link previews under subtree when cycling." :group 'org-startup :group 'org-cycle - :package-version '(Org . "9.6") + :package-version '(Org . "9.8") :type 'boolean) (defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook) @@ -804,12 +808,15 @@ (defun org-cycle-hide-archived-subtrees (state) "Subtree is archived and stays closed. Use \ `\\[org-cycle-force-archived]' to cycle it anyway.")))))) -(defun org-cycle-display-inline-images (state) +(defalias 'org-cycle-inline-images-display + 'org-cycle-display-link-previews) + +(defun org-cycle-display-link-previews (state) "Auto display inline images under subtree when cycling. -It works when `org-cycle-inline-images-display' is non-nil. +It works when `org-cycle-link-previews-display' is non-nil. STATE is the current outline visibility state. It should be one of symbols `content', `all', `folded', `children', or `subtree'." - (when org-cycle-inline-images-display + (when org-cycle-link-previews-display (pcase state ('children (org-with-wide-buffer @@ -817,19 +824,19 @@ (defun org-cycle-display-inline-images (state) ;; If has nested headlines, beg,end only from parent headline ;; to first child headline which reference to upper ;; let-binding `org-next-visible-heading'. - (org-display-inline-images + (org-link-preview-region nil nil (point-min) (progn (org-next-visible-heading 1) (point))))) ('subtree (org-with-wide-buffer (org-narrow-to-subtree) ;; If has nested headlines, also inline display images under all sub-headlines. - (org-display-inline-images nil nil (point-min) (point-max)))) + (org-link-preview-region nil nil (point-min) (point-max)))) ('folded (org-with-wide-buffer (org-narrow-to-subtree) (if (numberp (point-max)) - (org-remove-inline-images (point-min) (point-max)) + (org-link-preview-clear (point-min) (point-max)) (ignore))))))) (provide 'org-cycle) diff --git a/lisp/org-keys.el b/lisp/org-keys.el index f6de753af..3f07a8818 100644 --- a/lisp/org-keys.el +++ b/lisp/org-keys.el @@ -218,7 +218,8 @@ (declare-function org-toggle-checkbox "org" (&optional toggle-presence)) (declare-function org-toggle-radio-button "org" (&optional arg)) (declare-function org-toggle-comment "org" ()) (declare-function org-toggle-fixed-width "org" ()) -(declare-function org-toggle-inline-images-command "org" (&optional arg beg end)) +(declare-function org-link-preview "ol" (&optional arg beg end)) +(declare-function org-link-preview-refresh "ol" ()) (declare-function org-latex-preview "org" (&optional arg)) (declare-function org-toggle-narrow-to-subtree "org" ()) (declare-function org-toggle-ordered-property "org" ()) @@ -652,8 +653,8 @@ (org-defkey org-mode-map (kbd "C-c C-x C-d") #'org-clock-display) (org-defkey org-mode-map (kbd "C-c C-x x") #'org-dynamic-block-insert-dblock) (org-defkey org-mode-map (kbd "C-c C-x C-u") #'org-dblock-update) (org-defkey org-mode-map (kbd "C-c C-x C-l") #'org-latex-preview) -(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images-command) -(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-redisplay-inline-images) +(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-link-preview) +(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-link-preview-refresh) (org-defkey org-mode-map (kbd "C-c C-x \\") #'org-toggle-pretty-entities) (org-defkey org-mode-map (kbd "C-c C-x C-b") #'org-toggle-checkbox) (org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-toggle-radio-button) diff --git a/lisp/org-plot.el b/lisp/org-plot.el index b045344f0..836cfaffc 100644 --- a/lisp/org-plot.el +++ b/lisp/org-plot.el @@ -633,7 +633,7 @@ (defun org-plot/gnuplot-script (table data-file num-cols params &optional prefac (defun org-plot/redisplay-img-in-buffer (img-file) "Find any overlays for IMG-FILE in the current Org buffer, and refresh them." - (dolist (img-overlay org-inline-image-overlays) + (dolist (img-overlay org-link-preview-overlays) (when (string= img-file (plist-get (cdr (overlay-get img-overlay 'display)) :file)) (when (and (file-exists-p img-file) (fboundp 'image-flush)) diff --git a/lisp/org.el b/lisp/org.el index 1e9057935..def094a9a 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -1160,14 +1160,22 @@ (defcustom org-startup-shrink-all-tables nil :package-version '(Org . "9.2") :safe #'booleanp) -(defcustom org-startup-with-inline-images nil +(defvaralias 'org-startup-with-inline-images + 'org-startup-with-link-previews "Non-nil means show inline images when loading a new Org file. This can also be configured on a per-file basis by adding one of the following lines anywhere in the buffer: #+STARTUP: inlineimages - #+STARTUP: noinlineimages" + #+STARTUP: noinlineimages") + +(defcustom org-startup-with-link-previews nil + "Non-nil means show link previews when loading a new Org file. +This can also be configured on a per-file basis by adding one of +the following lines anywhere in the buffer: + #+STARTUP: linkpreviews + #+STARTUP: nolinkpreviews" :group 'org-startup - :version "24.1" + :version "29.4" :type 'boolean) (defcustom org-startup-with-latex-preview nil @@ -4153,8 +4161,10 @@ (defconst org-startup-options ("shrink" org-startup-shrink-all-tables t) ("descriptivelinks" org-link-descriptive t) ("literallinks" org-link-descriptive nil) - ("inlineimages" org-startup-with-inline-images t) - ("noinlineimages" org-startup-with-inline-images nil) + ("inlineimages" org-startup-with-link-previews t) + ("noinlineimages" org-startup-with-link-previews nil) + ("linkpreviews" org-startup-with-link-previews t) + ("nolinkpreviews" org-startup-with-link-previews nil) ("latexpreview" org-startup-with-latex-preview t) ("nolatexpreview" org-startup-with-latex-preview nil) ("customtime" org-display-custom-times t) @@ -5079,7 +5089,7 @@ (define-derived-mode org-mode outline-mode "Org" ;; modifications to make cache updates work reliably. (org-unmodified (when org-startup-with-beamer-mode (org-beamer-mode)) - (when org-startup-with-inline-images (org-display-inline-images)) + (when org-startup-with-inline-images (org-link-preview '(16))) (when org-startup-with-latex-preview (org-latex-preview '(16))) (unless org-inhibit-startup-visibility-stuff (org-cycle-set-startup-visibility)) (when org-startup-truncated (setq truncate-lines t)) @@ -15601,26 +15611,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 @@ -16667,518 +16657,6 @@ (defun org-normalize-color (value) (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-speed-command nil) diff --git a/testing/lisp/test-org-fold.el b/testing/lisp/test-org-fold.el index f58642be6..809738f6c 100644 --- a/testing/lisp/test-org-fold.el +++ b/testing/lisp/test-org-fold.el @@ -716,14 +716,14 @@ (ert-deftest test-org-fold/org-fold-display-inline-images () (org-show-subtree) (org-fold-subtree t) (run-hook-with-args 'org-cycle-hook 'folded) - (should-not org-inline-image-overlays) + (should-not org-link-preview-overlays) (should-not (cl-every (lambda (ov) (overlay-get ov 'org-image-overlay)) (overlays-in (point-min) (point-max)))) (org-show-subtree) (run-hook-with-args 'org-cycle-hook 'subtree) - (should org-inline-image-overlays) + (should org-link-preview-overlays) (should (cl-every (lambda (ov) (overlay-get ov 'org-image-overlay)) -- 2.46.1
>From 43df7292f834644aff1cdc8e0c28eaedacba6fe7 Mon Sep 17 00:00:00 2001 From: Karthik Chikmagalur <karthikchikmaga...@gmail.com> Date: Sun, 27 Oct 2024 20:22:59 -0700 Subject: [PATCH 2/2] Org: Document preview API for arbitrary link types * etc/ORG-NEWS: Mention new commands and options for link preview. * doc/org-manual.org: Add sections on - previewing external links (Images and link previews), and - adding the link preview feature to other link types (Adding Hyperlink preview). --- doc/org-manual.org | 293 ++++++++++++++++++++++++++++++--------------- etc/ORG-NEWS | 93 ++++++++++++-- 2 files changed, 281 insertions(+), 105 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index 451fd72cd..95846b475 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -11778,7 +11778,77 @@ ** Literal Examples the end of the current line. Then the label is stored as a link =(label)=, for retrieval with {{{kbd(C-c C-l)}}}. -** Images +** Images and link previews +:PROPERTIES: +:DESCRIPTION: Preview links in the buffer +:END: + +Org mode can display previews of hyperlinks (see [[*Hyperlinks]]) +inside Org buffers. By default, only image links[fn::Image links are +=file:= and =attachment:= links to existing image files; Emacs can +only display the linked images listed in ~image-types~ variable] can +be previewed inline, where the images are displayed in place of the +link path. + +You can customize the previews as described in [[*Adding Hyperlink +preview]]. Link previews do not have to display images -- any kind of +display decoration (see [[info:elisp#Overlay Properties]]) can be +used. + +You can preview the supported link types in the whole buffer, in the +current section, active region or at point with the following commands: + +- {{{kbd(C-c C-x C-v)}}} (~org-link-preview~) :: + + #+kindex: C-c C-x C-v + #+findex: org-link-preview + Create inline previews for external links in the active region, if + any; the link at point, if there is such; or in the current section + otherwise. With a prefix argument, clear link previews at point or + in the current entry. With a double prefix argument, preview all + links in the buffer. With triple prefix argument, hide previews for + all links in the buffer. + + By default, only links without descriptions are previewed. You + can force displaying previews for all supported links (including + links with descriptions) using a numeric argument of ~1~. This + will toggle all previews in the active region, the link at point + or the current section. A numeric prefix argument of ~11~ will + toggle previews in the whole buffer instead. + + #+vindex: org-startup-with-link-previews + Org mode can display link previews automatically when opening + buffers. Either customize ~org-startup-with-link-previews~ or use + the =#+STARTUP: linkpreviews= keyword to enable previews for that + specific buffer when opening it. =#+STARTUP: nolinkpreviews= will + disable previews on startup in the buffer. + +- {{{kbd(C-c C-x C-M-v)}}} (~org-link-preview-refresh~) :: + + #+kindex: C-c C-x C-M-v + #+findex: org-link-preview-refresh + Assure inline display of external link previews in the whole buffer + and refresh them. + +- {{{kbd(M-x org-link-preview-region)}}} :: + + #+findex: org-link-preview-region + Create inline previews for external links in the active region, or + the buffer. With a prefix argument, also preview links with a text + description part. + +- {{{kbd(M-x org-link-preview-clear)}}} :: + + #+findex: org-link-preview-clear + Clear external link previews in the active region, or the buffer. + +#+vindex: org-cycle-inline-images-display +Link previews can also be displayed when cycling the folding state. +When the custom option ~org-cycle-link-previews-display~ is set, +supported link types under the subtree (including images) will be +displayed automatically. + +*** Images :PROPERTIES: :DESCRIPTION: Display an image. :END: @@ -11803,102 +11873,76 @@ ** Images [[./img/a.jpg]] #+end_example -Such images can be displayed within the buffer with the following -command: - -- {{{kbd(C-c C-x C-v)}}} (~org-toggle-inline-images-command~) :: - - #+kindex: C-c C-x C-v - #+findex: org-toggle-inline-images-command - Toggle the inline display of linked images in current section or at - point. With a prefix argument, toggle inline images in the whole - buffer. With double prefix argument, hide all the images in buffer. - - By default, only the image links without description are displayed. - You can force displaying all the images using numeric argument to - toggle all the images in current section (~1~ argument) or the whole - buffer (~11~ argument). - - #+vindex: org-startup-with-inline-images - You can ask for inline images to be displayed at - startup by configuring the variable - ~org-startup-with-inline-images~[fn:: The variable - ~org-startup-with-inline-images~ can be set within a buffer with the - =STARTUP= options =inlineimages= and =noinlineimages=.]. - - - #+vindex: org-image-actual-width - #+vindex: org-image-max-width - #+cindex: @samp{ORG-IMAGE-ACTUAL-WIDTH}, property - By default, Org mode displays inline images according to their - actual width, but no wider than ~fill-column~ characters. - - You can customize the displayed image width using - ~org-image-actual-width~ variable (globally) or - =ORG-IMAGE-ACTUAL-WIDTH= property (subtree-level)[fn:: The width can - be customized in Emacs >= 24.1, built with ImageMagick support.]. - Their value can be the following: - - (default) Non-~nil~, use the actual width of images when inlining - them. If the actual width is too wide, limit it according to - ~org-image-max-width~. - - When set to a number, use ImageMagick (when available) to set the - image's width to this value. - - When set to a number in a list, try to get the width from any - =#+ATTR.*= keyword if it matches a width specification like: - #+begin_example - ,#+ATTR_HTML: :width 300px - #+end_example - and fall back on that number if none is found. - - When set to ~nil~, try to get the width from an =#+ATTR.*= keyword - and fall back on the original width or ~org-image-max-width~ if - none is found. - - ~org-image-max-width~ limits the maximum displayed image width, but - only when the image width is not set explicitly. Possible maximum - width can be set to: - - (default) ~fill-column~, limit width to ~fill-column~ number of - characters. - - ~window~, limit width to current window width. - - integer number, limit width to that specified number of pixels. - - ~nil~, do not limit the width. - - #+vindex: org-image-align - Org mode can left-align, center or right-align the display of inline - images. This setting is controlled (globally) by ~org-image-align~. - Only standalone images are affected, corresponding to links with no - surrounding text in their paragraph except for whitespace. Its - value can be the following: - - (default) The symbol ~left~, which inserts the image where the - link appears in the buffer. - - The symbol ~center~, which will preview links centered in the - Emacs window. - - The symbol ~right~, which will preview links right-aligned in the - Emacs window. - - Inline image alignment can be specified for each link using the - =#+ATTR.*= keyword if it matches an alignment specification like: +When link previews are displayed as images, the image size and +alignment can be further customized. + +#+vindex: org-image-actual-width +#+vindex: org-image-max-width +#+cindex: @samp{ORG-IMAGE-ACTUAL-WIDTH}, property +By default, Org mode displays inline images according to their +actual width, but no wider than ~fill-column~ characters. + +You can customize the displayed image width using +~org-image-actual-width~ variable (globally) or +=ORG-IMAGE-ACTUAL-WIDTH= property (subtree-level)[fn:: The width can +be customized in Emacs >= 24.1, built with ImageMagick support.]. +Their value can be the following: +- (default) Non-~nil~, use the actual width of images when inlining + them. If the actual width is too wide, limit it according to + ~org-image-max-width~. +- When set to a number, use ImageMagick (when available) to set the + image's width to this value. +- When set to a number in a list, try to get the width from any + =#+ATTR.*= keyword if it matches a width specification like: #+begin_example - ,#+ATTR_HTML: :align center + ,#+ATTR_HTML: :width 300px #+end_example - Org will use the alignment specification from any =#+ATTR.*= - keyword, such as =#+ATTR_HTML= or =#+ATTR_LATEX=, but =#+ATTR_ORG= - (if present) will override the others. For instance, this link - #+begin_example - ,#+ATTR_HTML: :align right - ,#+ATTR_ORG: :align center - [[/path/to/image/file.png]] - #+end_example - will be displayed centered in Emacs but exported right-aligned to - HTML. - - When =#+ATTR_ORG= is not set, inline image alignment is also read - from the =:center= attribute supported by some export backends (like - HTML, LaTeX and Beamer.) + and fall back on that number if none is found. +- When set to ~nil~, try to get the width from an =#+ATTR.*= keyword + and fall back on the original width or ~org-image-max-width~ if + none is found. + +~org-image-max-width~ limits the maximum displayed image width, but +only when the image width is not set explicitly. Possible maximum +width can be set to: +- (default) ~fill-column~, limit width to ~fill-column~ number of + characters. +- ~window~, limit width to current window width. +- integer number, limit width to that specified number of pixels. +- ~nil~, do not limit the width. + +#+vindex: org-image-align +Org mode can left-align, center or right-align the display of inline +images. This setting is controlled (globally) by ~org-image-align~. +Only standalone images are affected, corresponding to links with no +surrounding text in their paragraph except for whitespace. Its +value can be the following: +- (default) The symbol ~left~, which inserts the image where the + link appears in the buffer. +- The symbol ~center~, which will preview links centered in the + Emacs window. +- The symbol ~right~, which will preview links right-aligned in the + Emacs window. + +Inline image alignment can be specified for each link using the +=#+ATTR.*= keyword if it matches an alignment specification like: +#+begin_example +,#+ATTR_HTML: :align center +#+end_example +Org will use the alignment specification from any =#+ATTR.*= +keyword, such as =#+ATTR_HTML= or =#+ATTR_LATEX=, but =#+ATTR_ORG= +(if present) will override the others. For instance, this link +#+begin_example +,#+ATTR_HTML: :align right +,#+ATTR_ORG: :align center +[[/path/to/image/file.png]] +#+end_example +will be displayed centered in Emacs but exported right-aligned to +HTML. -#+vindex: org-cycle-inline-images-display -Inline images can also be displayed when cycling the folding state. -When custom option ~org-cycle-inline-images-display~ is set, the -visible inline images under subtree will be displayed automatically. +When =#+ATTR_ORG= is not set, inline image alignment is also read from +the =:center= attribute supported by some export backends (like HTML, +LaTeX and Beamer.) ** Captions :PROPERTIES: @@ -21837,6 +21881,67 @@ ** Adding Hyperlink Types topic. It also provides a default description. The function ~org-insert-link~ can insert it back into an Org buffer later on. +** Adding Hyperlink preview +:PROPERTIES: +:DESCRIPTION: Preview behavior for new hyperlink types. +:END: +#+cindex: hyperlinks, adding preview behavior + +By default, Org supports previewing external links for links ot type +=file= and =attachment= that point to image files. (See [[*Images]].) + +Support for previewing other link types inline can be added to Org in +the following way: + +1. Add a =:preview= link parameter to the link type using + ~org-link-set-parameters~. As an example, here we add previews for + the =docview= link type. + + #+begin_src emacs-lisp +(org-link-set-parameters + "docview" :preview #'org-link-docview-preview) + #+end_src + +2. The value of the =:preview= parameter must be a function that + accepts three arguments: + - an overlay placed from the start to the end of the link, + - the link path, as a string, and + - the syntax node for the link. + It must return a non-nil value to indicate preview success. A + value of =nil= implies that the preview failed, and the overlay + placed on the link will be removed. + + In our example, we use the =convert= program (part of the + =imagemagick= suite of tools) to create the thumbnail that is + displayed inline. + + #+begin_src emacs-lisp +(defun org-link-docview-preview (ov path _elem) + "Preview file at PATH in overlay OV. + +_ELEM is the syntax node of the link element." + (when (executable-find "convert") + (let* ((path (expand-file-name (substitute-in-file-name path))) + (output-file (expand-file-name (concat "org-docview-preview-" + (substring (sha1 path) 0 10) + ".png") + temporary-file-directory))) + ;; Create or find preview for path + (when (or (file-readable-p output-file) + (= 0 (call-process + "convert" + nil (get-buffer-create "*Org Docview Preview Output*") nil + "-thumbnail" "x320" "-colorspace" "rgb" + "-background" "white" "-alpha" "remove" "-strip" + (concat path "[0]") output-file))) + ;; If preview image is available, display it via the overlay + (overlay-put ov 'display (create-image output-file)))))) + #+end_src + +3. Now previews of docview links for supported document types (PDF, + djvu) are generated (along with image file previews) when calling + ~org-link-preview~. + ** Adding Export Backends :PROPERTIES: :DESCRIPTION: How to write new export backends. diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 92bfe3501..08857962b 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -24,26 +24,38 @@ Previously, =C-c C-x C-v= always toggled image display in the whole buffer (or narrowed part of the buffer). With prefix argument, it also forced displaying image links with description. -Now, =C-c C-x C-v= is bound to a new command -~org-toggle-inline-images-command~, which uses different defaults: +Now, =C-c C-x C-v= is bound to a new command ~org-link-preview~, which +uses different defaults: -1. By default, it toggles image at point or, if there is no image at - point, images in current entry +1. When the region is active, images in the region are previewed -2. When region is active, it is honored +2. Otherwise, if there is an image at point, it is toggled. If there + is no image at point, images in the current entry are previewed -3. =C-u= argument changed its meaning. Now, it forces toggling images - in the whole buffer +3. With the =C-u= argument, image previews in the active region or at + point are cleared instead -4. =C-u C-u= argument unconditionally hides all the images in buffer +4. The =C-u C-u= argument unconditionally shows all images in the + accessible portion of the buffer -5. Displaying images over links with description can be forced using +5. The =C-u C-u C-u= argument unconditionally clears all images in the + accessible portion of the buffer + +6. Displaying images over links with description can be forced using numeric argument: - ~C-u 1~ for toggling all images at point/current entry - ~C-u 11~ for toggling all images in buffer -The old ~org-toggle-inline-images~ command is still available. You -can bind it back to =C-c C-x C-v= by adding the following to you config: +(The first five of these prefix arg behaviors are the same as that of +the ~org-latex-preview~ command.) + +In addition to images, ~org-link-preview~ can also be used to preview +Org links of all types for which preview behavior is defined, see +[[#link-preview][previews for arbitrary link types]]. + +The old ~org-toggle-inline-images~ command is obsolete but still +available. You can bind it back to =C-c C-x C-v= by adding the +following to your config: #+begin_src emacs-lisp (eval-after-load 'org-keys (org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images)) @@ -76,6 +88,16 @@ now have diary timestamps included as well. # We list the most important features, and the features that may # require user action to be used. +*** All Org link types can be previewed +:PROPERTIES: +:CUSTOM_ID: link-preview +:END: + +Org links support a new parameter =:preview= that can be used to +preview arbitrary link types. The value of this parameter should be a +function that is called to preview links of the corresponding type +(see ~org-link-parameters~). + *** Alignment of image previews can be customized This is not a new feature. It has been added in Org 9.7, but not @@ -120,6 +142,17 @@ See the new [[info:org#Repeating commands]["Repeating commands"]] section in Org # adding new customizations, or changing the interpretation of the # existing customizations. +*** New option ~org-link-preview-batch-size~ + +Org link previews are generated asynchronously and a few at a time, in +batches. This option controls the number of links that are previewed +in each batch. + +*** New option ~org-link-preview-delay~ + +Org link previews are generated asynchronously. This option controls +the minimum idle time in seconds between previews of batches of links. + *** Allow disabling macro replacement during export New custom option ~org-export-replace-macros~ controls whether Org @@ -162,6 +195,26 @@ bibliography format requires them to be written in title-case. # This also includes changes in function behavior from Elisp perspective. +*** New command ~org-link-preview~ to preview Org links + +This command replaces ~org-toggle-inline-images~, which is now +obsolete. + +*** New command ~org-link-preview-region~ to preview Org links in a region or the buffer + +This command replaces ~org-display-inline-images~, which is now +obsolete. + +*** New command ~org-link-preview-clear~ to clear Org link previews in a region or the buffer + +This command replaces ~org-remove-inline-images~, which is now +obsolete. + +*** New command ~org-link-preview-refresh~ to refresh Org link previews in the buffer + +This command replaces ~org-redisplay-inline-images~, which is now +obsolete. + *** ob-sqlite: Added ability to open a database in readonly mode Added option :readonly to ob-sqlite. @@ -204,6 +257,24 @@ leave extra prompts after evaluation, and skipping the prompt filtering can be more robust for such languages (as this avoids removing false positive prompts). +** Removed or renamed functions and variables + +*** ~org-cycle-display-inline-images~ is renamed to ~org-cycle-display-link-previews~ + +Inline image previews in Org mode are now provided by the more general +link previews feature. The behavior with regards to image links is +unchanged. + +*** ~org-cycle-inline-images-display~ is renamed to ~org-cycle-link-previews-display~ + +The behavior is unchanged, except in that the new variable now affects +previews of supported link types besides image links. + +*** ~org-startup-with-inline-images~ is renamed to ~org-startup-with-link-previews~ + +The behavior is unchanged, except in that the new variable now affects +previews of supported link types besides image links. + ** Miscellaneous *** Org mode no longer prevents =flyspell= from spell-checking inside =LOGBOOK= drawers -- 2.46.1