> Thanks for the patch!
>
> > +(defcustom org-link-default-file-link-description nil
> > +  "How the description of file links should be stored.
> > +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
> > +path of the link as an argument and returns the description."
>
> I think we need to clarify that this variable is not always respected,
> but only when description is not set by other means, like
> org-link-precise-link-target.

Done.

> > +  :group 'org-link
> > +  :type '(choice (const nil)
> > +                 (const filename)
> > +                 (const file-path)
> > +                 (function))
> > +  :package-version '(Org . "10.0")
> > +  :safe #'null)
>
> Lll the possible values except function are safe.

Fixed.

Le dim. 21 juin 2026 à 06:19, Ihor Radchenko <[email protected]> a écrit :

> Earl Chase <[email protected]> writes:
>
> > I added a new custom variable that allows you to select the description
> > that will be created for file links when no description can be created
> from
> > the context.
>
> Thanks for the patch!
>
> > +(defcustom org-link-default-file-link-description nil
> > +  "How the description of file links should be stored.
> > +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
> > +path of the link as an argument and returns the description."
>
> I think we need to clarify that this variable is not always respected,
> but only when description is not set by other means, like
> org-link-precise-link-target.
>
> > +  :group 'org-link
> > +  :type '(choice (const nil)
> > +                 (const filename)
> > +                 (const file-path)
> > +                 (function))
> > +  :package-version '(Org . "10.0")
> > +  :safe #'null)
>
> Lll the possible values except function are safe.
>
> --
> 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 9d57e79b15ea39efc8d8016c7e77de65466ac3fe 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            |   5 +
 lisp/ol.el              |  58 ++++++--
 testing/lisp/test-ol.el | 321 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 371 insertions(+), 13 deletions(-)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 8e8700972..bc994a05d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -213,6 +213,11 @@ 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.  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..fe7078cd6 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' can not 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, users may 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 #'symbolp)
+
 (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."
@@ -2720,13 +2751,14 @@ NAME."
 
        ;; In dired, store a link to the file of the current line
        ((derived-mode-p 'dired-mode)
-	(let ((file (dired-get-filename nil t)))
+	(let ((file (or (dired-get-filename nil t) )))
 	  (setq file (if file
 			 (abbreviate-file-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..b3ba37983 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 changed 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 respectedd.
+    (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 changed 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 changed 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