> A few more comments.
>
> > +*** New custom variable ~org-link-default-file-link-description~
> > +
> > +This option, nil by default, allows you to choose the description
> > +that will be created for file links.  See its docstring for more
information.
>
> Please expand a bit. Otherwise, people reading the news will have no
> clue whether this option is useful or not. It would be best if the news
> entry shows how the new option can be useful.
>

Done.

> > +(defcustom org-link-default-file-link-description nil
> > +  "How the description of file links should be stored.
> > +The value of this variable is only respected when
> > +`org-store-link' can not create a link from the link
>
> *cannot
>

Fixed.

> > +context, i.e. in `Dired' or non-org-mode files.
>
> I think `Dired' quote is not necessary.
>

Removed the quotes.

> > +Valid values are:
> > +
> > +nil        No description will be created.
> > +filename   Description will be the filename of the link.
> > +file-path  Description will be the full filepath of the link.
> > +           Respects the value of `org-link-file-path-type'.
> > +
> > +Alternatively, users may supply a custom function that takes the
>
> "users may supply" reads weird - a user will be reading this!
> Maybe "you can supply".
>

Fixed the language here.

> > +path of the link as an argument and returns the description."
> > +  :group 'org-link
> > +  :type '(choice (const nil)
> > +                 (const filename)
> > +                 (const file-path)
> > +                 (function))
> > +  :package-version '(Org . "10.0")
> > +  :safe #'symbolp)
>
> (symbolp #'delete-file) ; -> t
> `symbolp' won't cut it.
>

Replace with a lambda.

> > -     (let ((file (dired-get-filename nil t)))
> > +     (let ((file (or (dired-get-filename nil t) )))
>
> What is this change about?
>

My bad, I removed this change.

> > +    ;; Doesn't changed behavior of links
>
> *change
>

Fixed.

> > +    ;; The value of `org-link-file-path-type' is always respectedd.
>
> *respected
>

Fixed.

Le sam. 4 juil. 2026 à 04:48, Ihor Radchenko <[email protected]> a écrit :

> Earl Chase <[email protected]> writes:
>
> > Subject: [PATCH] lisp/ol.el: Custom variable for file link description
>
> A few more comments.
>
> > +*** New custom variable ~org-link-default-file-link-description~
> > +
> > +This option, nil by default, allows you to choose the description
> > +that will be created for file links.  See its docstring for more
> information.
>
> Please expand a bit. Otherwise, people reading the news will have no
> clue whether this option is useful or not. It would be best if the news
> entry shows how the new option can be useful.
>
> > +(defcustom org-link-default-file-link-description nil
> > +  "How the description of file links should be stored.
> > +The value of this variable is only respected when
> > +`org-store-link' can not create a link from the link
>
> *cannot
>
> > +context, i.e. in `Dired' or non-org-mode files.
>
> I think `Dired' quote is not necessary.
>
> > +Valid values are:
> > +
> > +nil        No description will be created.
> > +filename   Description will be the filename of the link.
> > +file-path  Description will be the full filepath of the link.
> > +           Respects the value of `org-link-file-path-type'.
> > +
> > +Alternatively, users may supply a custom function that takes the
>
> "users may supply" reads weird - a user will be reading this!
> Maybe "you can supply".
>
> > +path of the link as an argument and returns the description."
> > +  :group 'org-link
> > +  :type '(choice (const nil)
> > +                 (const filename)
> > +                 (const file-path)
> > +                 (function))
> > +  :package-version '(Org . "10.0")
> > +  :safe #'symbolp)
>
> (symbolp #'delete-file) ; -> t
> `symbolp' won't cut it.
>
> > -     (let ((file (dired-get-filename nil t)))
> > +     (let ((file (or (dired-get-filename nil t) )))
>
> What is this change about?
>
> > +    ;; Doesn't changed behavior of links
>
> *change
>
> > +    ;; The value of `org-link-file-path-type' is always respectedd.
>
> *respected
>
>
> --
> Ihor Radchenko // yantar92,
> Org mode maintainer,
> Learn more about Org mode at <https://orgmode.org/>.
> Support Org development at <https://liberapay.com/org-mode>,
> or support my work at <https://liberapay.com/yantar92>
>
From 6220b7df3d0c0db665100656a4cdf7d0c82ed8d7 Mon Sep 17 00:00:00 2001
From: ApollonDeParnasse <[email protected]>
Date: Sun, 7 Jun 2026 09:14:15 -0500
Subject: [PATCH] lisp/ol.el: Custom variable for file link description

* lisp/ol.el (org-link-default-file-link-description): New custom
variable for file link descriptions.
(org-link--file-link-description): Create the description of a
file link based on the value of
`org-link-default-file-link-description'.
(org-link--file-link-to-here): Use
`org-link-default-file-link-description' to create the
description of a file link when a description can not
be created from the context.
(org-store-link): Use `org-link--file-link-description'
to create the description of file links created in dired.

* testing/lisp/test-ol.el (test-org-link/store-link/filename-description):
New tests for `org-store-link'.
(test-org-link/store-link/filepath-description):
New tests for `org-store-link'.
(test-org-link/store-link/function-creates-description):
New tests for `org-store-link'.
(test-org-link/store-link/dired):
New tests for `org-store-link'.
---
 etc/ORG-NEWS            |   8 +
 lisp/ol.el              |  56 +++++--
 testing/lisp/test-ol.el | 321 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 373 insertions(+), 12 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 50a46c6c7..9263bfeb0 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -225,6 +225,14 @@ environment for descriptive lists when exporting to LaTeX.
 Use it when the default ~description~ environment does not fit your
 needs. The recommended alternative value is ~itemize~.
 
+*** New custom variable ~org-link-default-file-link-description~
+
+This option, nil by default, allows you to choose the description
+that will be created for file links that are created in Dired,
+non-org-mode files, or any other place where org can not use the
+link context to create a description.  See its docstring for more
+information.
+
 ** New functions and changes in function arguments
 
 # This also includes changes in function behavior from Elisp perspective.
diff --git a/lisp/ol.el b/lisp/ol.el
index d3ac5e56f..068d25e06 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -279,6 +279,28 @@ filename in the link as an argument and returns the path."
   :package-version '(Org . "9.5")
   :safe #'symbolp)
 
+(defcustom org-link-default-file-link-description nil
+  "How the description of file links should be stored.
+The value of this variable is only respected when
+`org-store-link' cannot create a link from the link
+context, i.e. in Dired or non-org-mode files.
+Valid values are:
+
+nil        No description will be created.
+filename   Description will be the filename of the link.
+file-path  Description will be the full filepath of the link.
+           Respects the value of `org-link-file-path-type'.
+
+Alternatively, you can supply a custom function that takes the
+path of the link as an argument and returns the description."
+  :group 'org-link
+  :type '(choice (const nil)
+                 (const filename)
+                 (const file-path)
+                 (function))
+  :package-version '(Org . "10.0")
+  :safe (lambda (x) (member x (list nil 'filename 'file-path))))
+
 (defcustom org-link-abbrev-alist nil
   "Alist of link abbreviations.
 The car of each element is a string, to be replaced at the start of a link.
@@ -1035,19 +1057,28 @@ Return t when a link has been stored in `org-link-store-props'."
     (push (list link desc) org-stored-links)
     (message "Link moved to front: %s" (or desc link)))))
 
+(defun org-link--file-link-description (path)
+  "Use PATH to create a description for file link.
+PATH will be transformed according to the value
+of `org-link-default-file-link-description'."
+  (pcase-exhaustive org-link-default-file-link-description
+    (`nil nil)
+    (`filename (file-name-nondirectory path))
+    (`file-path (org-link--normalize-filename path))
+    ((pred functionp) (funcall org-link-default-file-link-description path))
+    (_ (error "Invalid `org-link-default-file-link-description' value"))))
+
 (defun org-link--file-link-to-here ()
   "Return as (LINK . DESC) a file link with search string to here."
-  (let ((link (concat "file:"
-                      (abbreviate-file-name
-                       (buffer-file-name (buffer-base-buffer)))))
-        desc)
-    (when org-link-context-for-files
-      (pcase (org-link-precise-link-target)
-        (`nil nil)
-        (`(,search-string ,search-desc ,_position)
-         (setq link (format "%s::%s" link search-string))
-         (setq desc search-desc))))
-    (cons link desc)))
+  (let* ((path (buffer-file-name (buffer-base-buffer)))
+         (link (concat "file:" (abbreviate-file-name path)))
+         (context (and org-link-context-for-files (org-link-precise-link-target)))
+         (search-string (car context))
+         (search-desc (cadr context)))
+    (cond
+     ((and search-string search-desc) (cons (format "%s::%s" link search-string) search-desc))
+     (search-string (cons (format "%s::%s" link search-string) (org-link--file-link-description path)))
+     (t (cons link (org-link--file-link-description path))))))
 
 (defun org-link-preview--get-overlays (&optional beg end)
   "Return link preview overlays between BEG and END."
@@ -2726,7 +2757,8 @@ NAME."
 			  (expand-file-name (dired-get-filename nil t)))
 		       ;; Otherwise, no file so use current directory.
 		       default-directory))
-	  (setq link (concat "file:" file))))
+	  (setq link (concat "file:" file)
+                desc (org-link--file-link-description file))))
 
        ;; Try `org-create-file-search-functions`.  If any are
        ;; successful, create a file link to the current buffer with
diff --git a/testing/lisp/test-ol.el b/testing/lisp/test-ol.el
index 405e59a95..1487f7d38 100644
--- a/testing/lisp/test-ol.el
+++ b/testing/lisp/test-ol.el
@@ -390,6 +390,327 @@ See https://github.com/yantar92/org/issues/4.";
 	 (equal (format "[[file:%s::*foo bar][foo bar]]" file)
 		(org-store-link nil)))))))
 
+(ert-deftest test-org-link/store-link/filename-description ()
+  "Test `org-store-link' with `org-link-default-file-link-description' set to `filename'."
+
+  (let ((org-link-default-file-link-description 'filename))
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (file-name-nondirectory file))
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (file-name-nondirectory file))
+		  (org-store-link '(16)))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files t))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file (file-name-nondirectory file))
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (file-name-nondirectory file))
+		  (org-store-link nil))))))
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file (file-name-nondirectory file))
+		  (org-store-link '(4)))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (file-name-nondirectory file))
+		  (org-store-link '(16)))))))
+    ;; Doesn't change behavior of links
+    ;; who get their name from an org-element
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::*h1][h1]]" file)
+		  (org-store-link '(4)))))))))
+
+(ert-deftest test-org-link/store-link/filepath-description ()
+  "Test `org-store-link' with `org-link-default-file-link-description' set to `filepath'."
+  (let ((org-link-default-file-link-description 'file-path))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link '(16)))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files t))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link '(4)))))))
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link '(16)))))))
+    ;; The value of `org-link-file-path-type' is always respected.
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil)
+           (org-link-file-path-type 'relative))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link nil))))))
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil)
+           (org-link-file-path-type 'absolute))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link '(16)))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil)
+           (org-link-file-path-type 'adaptive))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil)
+           (org-link-file-path-type #'identity))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file (org-link--normalize-filename file))
+		  (org-store-link '(4)))))))
+    ;; Doesn't change behavior of links
+    ;; who get their name from an org-element
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files t))
+       (org-test-with-temp-text-in-file "* TODO [#A] COMMENT foo :bar:"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::*foo][foo]]" file)
+		  (org-store-link nil))))))))
+
+(ert-deftest test-org-link/store-link/function-creates-description ()
+  "Test `org-store-link' with `org-link-default-file-link-description' set to `function'."
+  (let ((org-link-default-file-link-description #'identity))
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file file)
+		  (org-store-link nil))))))
+    (should
+     (let ((org-stored-links nil)
+	   (org-id-link-to-org-use-id nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "* h1"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file file)
+		  (org-store-link '(16)))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files t))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file file)
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file file)
+		  (org-store-link nil))))))
+
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::two][%s]]" file file)
+		  (org-store-link '(4)))))))
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files nil))
+       (org-test-with-temp-text-in-file "one\n<point>two"
+         (fundamental-mode)
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s][%s]]" file file)
+		  (org-store-link '(16)))))))
+    ;; Doesn't change behavior of links
+    ;; who get their name from an org-element
+    (should
+     (let ((org-stored-links nil)
+	   (org-link-context-for-files t))
+       (org-test-with-temp-text-in-file "* foo[33%]bar"
+         (let ((file (buffer-file-name)))
+	   (equal (format "[[file:%s::*foo bar][foo bar]]" file)
+		  (org-store-link nil))))))))
+
+(ert-deftest test-org-link/store-link/dired ()
+  "Test `org-store-link' in Dired."
+  ;; no file link description
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description nil))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s]]" test-file)))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value)))))
+
+  ;; filename as file link description
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description 'filename))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s][%s]]" test-file (file-name-nondirectory test-file))))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value)))))
+
+  ;; file-path as file link description
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description 'file-path))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s][%s]]" test-file (org-link--normalize-filename test-file))))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value)))))
+
+  ;; file-path as file link description and
+  ;; `org-link-file-path-type' set to `relative'
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description 'file-path)
+         (org-link-file-path-type 'relative))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s][%s]]" test-file (org-link--normalize-filename test-file))))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value)))))
+
+  ;; file-path as file link description and
+  ;; `org-link-file-path-type' set to `absolute'
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description 'file-path)
+         (org-link-file-path-type 'absolute))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s][%s]]" test-file (org-link--normalize-filename test-file))))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value)))))
+
+  ;; file-path as file link description and
+  ;; `org-link-file-path-type' set to a function
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description 'file-path)
+         (org-link-file-path-type #'identity))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s][%s]]" test-file test-file)))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value)))))
+
+  ;; function creates file link description
+  (let* ((org-stored-links nil)
+         (org-link-default-file-link-description #'identity))
+    (org-test-with-temp-text-in-file ""
+      (let* ((test-file (buffer-file-name))
+             (expected-value (format "[[file:%s][%s]]" test-file test-file)))
+        (dired (file-name-directory test-file))
+        (revert-buffer)
+        (dired-goto-file test-file)
+        (should (equal (org-store-link nil) expected-value))))))
+
 (ert-deftest test-org-link/precise-link-target ()
   "Test `org-link-precise-link-target` specifications."
   (org-test-with-temp-text "* H1<point>\n* H2\n"
-- 
2.54.0

Reply via email to