Dear Org developers,

At the moment, `org-yank-image-save-method' can only save an image
into a single location, or query "org-attach".

This change allows customising its behaviour, allowing
`org-yank-image-save-method' to be a function returning a place to
save the file.

Patch attached.

-- 
Your sincerely,
Vladimir Nikishkin (MiEr, lockywolf)
(Laptop)

Attachment: signature.asc
Description: PGP signature

>From 52b31c47b0a49b6fa624f4f1e67c7a945ceb69eb Mon Sep 17 00:00:00 2001
From: Lockywolf <>
Date: Sun, 13 Jul 2025 20:11:50 +0800
Subject: [PATCH] lisp/org.el: generalise org-yank-image-save-method to be a
 function

* lisp/org.el (yank-media): Allow `org-yank-image-save-method' to be
a function to be called to produce the directory path.  Also make
sure that there just a single `(insert)'.
(org-yank-image-save-method): Allow function as a value.
* doc/org-manual.org (Drag and Drop & ~yank-media~): Synchronise
documentation with the changes in org.el.
* etc/ORG-NEWS (New and changed options): Document the fact that
org-yank-image-save-method can now be a function.

`org-yank-image-save-method' had no way to obtain a directory to save
clipboard contents dynamically.  It could either specify that the
yank location should be determined by the attachment machinery, or
it could be a global fixed directory name.

Current change allows setting up `org-yank-image-save-method' to a
function which would be called to determine where to save an image,
and could take into account buffer's name, whether it is remote or
local, et cetera.
---
 doc/org-manual.org | 15 ++++++++-------
 etc/ORG-NEWS       |  8 ++++++++
 lisp/org.el        | 29 ++++++++++++++++-------------
 3 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 15fc24712..a8b049f14 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -773,10 +773,10 @@ The following commands jump to other headlines in the buffer.
   where you can use the following keys to find your destination:
 
   #+attr_texinfo: :columns 0.3 0.7
-  | {{{kbd(TAB)}}}            | Cycle visibility.               |
+  | {{{kbd(TAB)}}}                  | Cycle visibility.               |
   | {{{kbd(DOWN)}}} / {{{kbd(UP)}}} | Next/previous visible headline. |
-  | {{{kbd(RET)}}}            | Select this location.           |
-  | {{{kbd(/)}}}              | Do a Sparse-tree search         |
+  | {{{kbd(RET)}}}                  | Select this location.           |
+  | {{{kbd(/)}}}                    | Do a Sparse-tree search         |
 
   #+texinfo: @noindent
   The following keys work if you turn off ~org-goto-auto-isearch~
@@ -784,9 +784,9 @@ The following commands jump to other headlines in the buffer.
   #+attr_texinfo: :columns 0.3 0.7
   | {{{kbd(n)}}} / {{{kbd(p)}}}   | Next/previous visible headline.    |
   | {{{kbd(f)}}} / {{{kbd(b)}}}   | Next/previous headline same level. |
-  | {{{kbd(u)}}}            | One level up.                      |
+  | {{{kbd(u)}}}                  | One level up.                      |
   | {{{kbd(0)}}} ... {{{kbd(9)}}} | Digit argument.                    |
-  | {{{kbd(q)}}}            | Quit.                              |
+  | {{{kbd(q)}}}                  | Quit.                              |
 
   #+vindex: org-goto-interface
   #+texinfo: @noindent
@@ -12116,7 +12116,7 @@ The following command handles footnotes:
   #+attr_texinfo: :columns 0.1 0.9
   | {{{kbd(s)}}} | Sort the footnote definitions by reference sequence.               |
   | {{{kbd(r)}}} | Renumber the simple =fn:N= footnotes.                              |
-  | {{{kbd(S)}}} | Short for first {{{kbd(r)}}}, then {{{kbd(s)}}} action.                        |
+  | {{{kbd(S)}}} | Short for first {{{kbd(r)}}}, then {{{kbd(s)}}} action.            |
   | {{{kbd(n)}}} | Rename all footnotes into a =fn:1= ... =fn:n= sequence.            |
   | {{{kbd(d)}}} | Delete the footnote at point, including definition and references. |
 
@@ -21824,7 +21824,8 @@ from LibreOffice Calc documents.
 #+vindex: org-yank-image-save-method
 When yanking images from clipboard, Org saves the image on disk and
 inserts the image link to Org buffer.  Images are either saved as
-attachments to heading (default) or to a globally defined directory.
+attachments to heading (default), or to a globally defined directory,
+or to a directory, path to which is produced by a function call.
 The save location is controlled by ~org-yank-image-save-method~.
 
 #+vindex: org-yank-image-file-name-function
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 62502a678..2cfdbea1e 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -362,6 +362,14 @@ This option makes it possible to disable mapping of linked org files
 to markdown during export to Markdown. This is analogous to how
 ~org-html-link-org-files-as-html~ works in export to HTML.
 
+*** org-yank-image-save-method: allow to be a procedure producing file name
+
+In previous versions ~org-yank-image-save-method~ could be either
+a symbol ~attach~ or a string, directory name.  Now it can also be
+a procedure, which will be called and its return value will be used
+as if it was originally the string value -- as a directory to save
+the file to.
+
 ** New functions and changes in function arguments
 
 # This also includes changes in function behavior from Elisp perspective.
diff --git a/lisp/org.el b/lisp/org.el
index 0a406d7cc..7e5a1a1ff 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20485,7 +20485,8 @@ directory name to copy/cut the image to that directory."
   :group 'org
   :package-version '(Org . "9.7")
   :type '(choice (const :tag "Add it as attachment" attach)
-                 (directory :tag "Save it in directory"))
+                 (directory :tag "Save it in directory")
+                 (function : tag "Save it in a directory returned from the function call."))
   :safe (lambda (x) (eq x 'attach)))
 
 (defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename
@@ -20525,26 +20526,28 @@ end."
          (iname (funcall org-yank-image-file-name-function))
          (filename (with-no-warnings ; Suppress warning in Emacs <28
                      (file-name-with-extension iname ext)))
+         (dirname (cond ((eq org-yank-image-save-method 'attach) temporary-file-directory)
+                        ((stringp org-yank-image-save-method) org-yank-image-save-method)
+                        ((functionp org-yank-image-save-method) (funcall org-yank-image-save-method))
+                        (t (error "%s must be a string or a callable" org-yank-image-save-method))))
          (absname (expand-file-name
                    filename
-                   (if (eq org-yank-image-save-method 'attach)
-                       temporary-file-directory
-                     org-yank-image-save-method))))
+                   dirname)))
     (when (and (not (eq org-yank-image-save-method 'attach))
-               (not (file-directory-p org-yank-image-save-method)))
-      (make-directory org-yank-image-save-method t))
+               (not (file-directory-p dirname)))
+      (make-directory dirname t))
     ;; DATA is a raw image.  Tell Emacs to write it raw, without
     ;; trying to auto-detect the coding system.
     (let ((coding-system-for-write 'emacs-internal))
       (with-temp-file absname
         (insert data)))
-    (if (null (eq org-yank-image-save-method 'attach))
-        (insert (org-link-make-string
-                 (concat "file:"
-                         (org-link--normalize-filename absname))))
-      (require 'org-attach)
-      (org-attach-attach absname nil 'mv)
-      (insert (org-link-make-string (concat "attachment:" filename))))))
+    (insert (if (null (eq org-yank-image-save-method 'attach))
+        (org-link-make-string (concat "file:" (org-link--normalize-filename absname)))
+        (progn
+          (require 'org-attach)
+          (org-attach-attach absname nil 'mv)
+          (org-link-make-string (concat "attachment:" filename)))))
+    ))
 
 ;; I cannot find a spec for this but
 ;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm
-- 
2.46.4

Reply via email to