Subject: bug#45412: File ... is large (... MiB), really open? (y)es or (n)o or (l)iterally

From: Daniel Martín <[email-masked]>
Date: Sun, 11 Apr 2021 00:32:34 +0200

Juri Linkov <[email-masked]> writes:

>>> Another approach could be to offer a C-h binding that opens a help
>>> window that describes the possible options and briefly links to the
>>> customization entry point that removes the prompt permanently.
>>
>> Yeah, that sounds like a good idea.
>
> I agree, this is really the best idea.

OK, I've implemented it in the attached patch.  Suggestions
welcome.

>From 8ae34d5bca1a8fc13f2388842880d4234ab28165 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= <[email-masked]>
Date: Sun, 11 Apr 2021 00:16:38 +0200
Subject: [PATCH] Add a help option in the open large files prompt

* lisp/files.el (files--ask-user-about-large-file-help): New function
that displays a Help buffer with information about opening large files
in Emacs.  (Bug#45412)
(files--ask-user-about-large-file): Add a ?/C-h option to the prompt.
* etc/NEWS: Advertise the change.
---
 etc/NEWS      |  6 ++++
 lisp/files.el | 76 +++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 61 insertions(+), 21 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index a0f05d8cf1..b1bd9689e2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -261,6 +261,12 @@ commands.  The new keystrokes are 'C-x x g' ('revert-buffer'),
 ** Commands 'set-frame-width' and 'set-frame-height' can now get their
 input using the minibuffer.
 
+---
+** When Emacs prompts before opening a large file, a new help window
+has been added.  This help window describes the available options and
+how to disable the prompt by customizing the
+'large-file-warning-threshold' variable.
+
 
 * Editing Changes in Emacs 28.1
 
diff --git a/lisp/files.el b/lisp/files.el
index 60d6034011..49224e4b1e 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2123,28 +2123,62 @@ out-of-memory-warning-percentage
 
 (declare-function x-popup-dialog "menu.c" (position contents &optional header))
 
+(defun files--ask-user-about-large-file-help (op-type size)
+  "Show a buffer explaining the options to open large files in Emacs."
+  (with-output-to-temp-buffer "*Help*"
+    (with-current-buffer standard-output
+      (insert
+       (format
+        "The file that you want to %s is large (%s).
+
+Large files may be slow to edit or navigate so Emacs asks you
+before you try to open such files.
+
+You can press ‘y‘ to open the file.
+You can press ‘n‘ to abort.
+You can press ‘l‘ to open the file literally, which means that
+Emacs will open the file without doing any format or character code
+conversion and in Fundamental mode, without loading any potentially
+expensive feature.
+
+You can customize the option ‘large-file-warning-threshold‘ to be
+the file size, in bytes, from which Emacs will ask for
+confirmation.  Set it to ‘nil‘ to never request
+confirmation."
+        op-type
+        size))
+      (save-excursion
+	(re-search-backward "\\(customize\\)" nil t)
+	(help-xref-button 1 'help-customize-variable 'large-file-warning-threshold)))))
+
 (defun files--ask-user-about-large-file (size op-type filename offer-raw)
-  (let ((prompt (format "File %s is large (%s), really %s?"
-		        (file-name-nondirectory filename)
-		        (funcall byte-count-to-string-function size) op-type)))
-    (if (not offer-raw)
-        (if (y-or-n-p prompt) nil 'abort)
-      (let* ((use-dialog (and (display-popup-menus-p)
-                              last-input-event
-	                      (listp last-nonmenu-event)
-	                      use-dialog-box))
-             (choice
-              (if use-dialog
-                  (x-popup-dialog t `(,prompt
-                                      ("Yes" . ?y)
-                                      ("No" . ?n)
-                                      ("Open literally" . ?l)))
-                (read-char-choice
-                 (concat prompt " (y)es or (n)o or (l)iterally ")
-                 '(?y ?Y ?n ?N ?l ?L)))))
-        (cond ((memq choice '(?y ?Y)) nil)
-              ((memq choice '(?l ?L)) 'raw)
-              (t 'abort))))))
+  (save-window-excursion
+    (let ((prompt (format "File %s is large (%s), really %s?"
+		          (file-name-nondirectory filename)
+		          (funcall byte-count-to-string-function size) op-type)))
+      (if (not offer-raw)
+          (if (y-or-n-p prompt) nil 'abort)
+        (let* ((use-dialog (and (display-popup-menus-p)
+                                last-input-event
+	                        (listp last-nonmenu-event)
+	                        use-dialog-box))
+               choice)
+          (if use-dialog
+              (setq choice (x-popup-dialog t `(,prompt
+                                               ("Yes" . ?y)
+                                               ("No" . ?n)
+                                               ("Open literally" . ?l))))
+            (while (null choice)
+              (setq choice (read-char-choice
+                            (concat prompt " (y)es, (n)o, (l)iterally, (?)")
+                            '(?y ?Y ?n ?N ?l ?L ?? ?\C-h)))
+              (when (memq choice '(?? ?\C-h))
+                (files--ask-user-about-large-file-help
+                 op-type (funcall byte-count-to-string-function size))
+                (setq choice nil))))
+          (cond ((memq choice '(?y ?Y)) nil)
+                ((memq choice '(?l ?L)) 'raw)
+                (t 'abort)))))))
 
 (defun abort-if-file-too-large (size op-type filename &optional offer-raw)
   "If file SIZE larger than `large-file-warning-threshold', allow user to abort.
-- 
2.31.0