请教:如何使org-mode可以inline显示link中的图片,如果link的类型不是file:

在配置 https://github.com/wdavew/org-excalidraw/ 时遇到的问题。

org-excalidraw会生成定制的link类型,类似[[excalidraw:/path-to-base-directory/uuid.excalidraw.svg]], 作者定义了特定的点击函数,可以直接打开excalidraw的网站编辑,并同步到本地。

这些功能在我目前的配置里面是生效的。出问题的是图片的预览。

从作者展示的gif来看,这个定制的[[excalidraw:]]类型是可以像[[file:]]类型一样inline显示的,但是在我这里却是不行的。

我最早猜测这个预览的实现有两种:

  1. 为excalidraw的类型特殊定义了一个“显示”函数。
  2. emacs可以直接支持定制link类型的显示。

我查看了org-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” type.

看到这个说明,我理解org-mode是可以忽略link类型(file:或者是其他),完全靠正则判断“这个link是不是图片,是不是可以用inline-display去显示”。于是就把image-file-name-regexp,加入了(".png" ".jpg" ".svg"),结果也是不行。

希望大家指点一下,是我哪里理解错了吗。

我试了下也不行,看它的代码,链接需要匹配这个正则:

(file-types-re
 (format "\\[\\[\\(?:file%s:\\|attachment:\\|[./~]\\)\\|\\]\\[\\(<?file:\\)"
	 (if (not link-abbrevs) ""
	   (concat "\\|" (regexp-opt link-abbrevs))))))

这么看来它的描述是不准确的,其实只会匹配 file 和 attach 以及 ./~ 这种模式

魔改一下它的源码,把其中的 attachment 全部替换成 excalidraw 就可以了:

(defun my/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\" 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:\\|excalidraw:\\|[./~]\\)\\|\\]\\[\\(<?file:\\)"
			(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-property :contents-begin link))
			  include-linked)
		      (and (or (equal "file" linktype)
                               (equal "excalidraw" 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-property :contents-end link)
				(match-end 0))
			     (match-string 2)))))))
	      (when (and path (string-match-p file-extension-re path))
		(let ((file (if (equal "excalidraw" linktype)
				(progn
                                  (require 'org-attach)
				  (ignore-errors (org-attach-expand path)))
                              (expand-file-name path))))
		  (when (and file (file-exists-p file))
		    (let ((width (org-display-inline-image--width link))
			  (old (get-char-property-and-overlay
				(org-element-property :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-property :begin link)
				       (progn
					 (goto-char
					  (org-element-property :end link))
					 (skip-chars-backward " \t")
					 (point)))))
                              ;; FIXME: 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))
			      (push ov org-inline-image-overlays))))))))))))))))

多谢回复。确实跟你说的一样,所以很好奇作者是不是也是这么魔改的。

我还发现iimage-mode,也可以显示图片,无视link类型,暂时先这么用了。

@dvorak Hi, 请教下我看org-excalidraw要enable chrome flag里 File Handling API 。但我在chrome flag里面没有找到这个选项,是这个flag换名了么,还是说这个flag在某个版本后被删掉了?