unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: "Daniel Martín via Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Lars Ingebrigtsen <larsi@gnus.org>
Cc: 45412@debbugs.gnu.org, "積丹尼 Dan Jacobson" <jidanni@jidanni.org>,
	"Juri Linkov" <juri@linkov.net>
Subject: bug#45412: File ... is large (... MiB), really open? (y)es or (n)o or (l)iterally
Date: Thu, 22 Apr 2021 00:12:54 +0200	[thread overview]
Message-ID: <m1a6prmgm1.fsf@yahoo.es> (raw)
In-Reply-To: <87mttxqgnv.fsf@gnus.org> (Lars Ingebrigtsen's message of "Sat, 17 Apr 2021 13:44:36 +0200")

[-- Attachment #1: Type: text/plain, Size: 1420 bytes --]

Lars Ingebrigtsen <larsi@gnus.org> writes:

> Daniel Martín <mardani29@yahoo.es> writes:
>
>> I've given `read-multiple-choice' a try and the help text doesn't look
>> very good if any of the descriptions is a bit long.  Another problem I
>> saw is that if the help text contains a reference to a variable, the
>> rendered hyperlink doesn't work.
>>
>> It's a pity because I think this function is a good way to simplify and
>> homogenize this use case in Emacs.
>
> Well, neither problem seem insurmountable to fix.

OK, I've refactored the feature on top of a patched version of
`read-multiple-choice' that adds support for overriding the help text.
I've also handled the `edit' action, so that a user can enter a
recursive edit to select the help link.  This API change is
backwards-compatible.  As one change depends on the previous one, but
they are otherwise orthogonal, I've made two separate commits.

These new patches address the following review comments:

- Simplify the call site by avoiding the character loop.

- The help description now appears on `help-char', ?, or F1.

- Improves and fixes the help text and NEWS description, per Eli's
  suggestions.

The idea to show the help text also for graphical prompts is a good
idea, but as there's no command already in Emacs that supports it,
AFAIK, it could be a separate feature request.  What do you think?

Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Extend-read-multiple-choice-to-support-free-form-hel.patch --]
[-- Type: text/x-patch, Size: 8316 bytes --]

From d6c124930a7b54f222d3cef79e11b66eb4a188f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= <mardani29@yahoo.es>
Date: Wed, 21 Apr 2021 23:14:27 +0200
Subject: [PATCH 1/2] Extend read-multiple-choice to support free-form help
 descriptions

* lisp/emacs-lisp/rmc.el (read-multiple-choice): Add a new argument to
override the default help description in `read-multiple-choice'.  Use
the `help-char' variable instead of ?\C-h.  Also support the `edit'
action from `query-replace-map', so that help links can be visited by
entering a recursive edit.
---
 lisp/emacs-lisp/rmc.el | 119 ++++++++++++++++++++++-------------------
 1 file changed, 65 insertions(+), 54 deletions(-)

diff --git a/lisp/emacs-lisp/rmc.el b/lisp/emacs-lisp/rmc.el
index bedf598d44..2c4c13ae1c 100644
--- a/lisp/emacs-lisp/rmc.el
+++ b/lisp/emacs-lisp/rmc.el
@@ -26,7 +26,7 @@
 (require 'seq)
 
 ;;;###autoload
-(defun read-multiple-choice (prompt choices)
+(defun read-multiple-choice (prompt choices &optional help-string)
   "Ask user a multiple choice question.
 PROMPT should be a string that will be displayed as the prompt.
 
@@ -35,15 +35,18 @@ read-multiple-choice
 be displayed while prompting (if there's room, it might be
 shortened).  DESCRIPTION is an optional longer explanation that
 will be displayed in a help buffer if the user requests more
-help.
+help.  This help description has a fixed format in columns, but
+the user can control the text that is displayed and how it is
+formatted with optional argument HELP-STRING.
 
 This function translates user input into responses by consulting
 the bindings in `query-replace-map'; see the documentation of
 that variable for more information.  In this case, the useful
-bindings are `recenter', `scroll-up', and `scroll-down'.  If the
-user enters `recenter', `scroll-up', or `scroll-down' responses,
-perform the requested window recentering or scrolling and ask
-again.
+bindings are `recenter', `scroll-up', `scroll-down', and `edit'.
+If the user enters `recenter', `scroll-up', or `scroll-down'
+responses, perform the requested window recentering or scrolling
+and ask again.  If the user enters `edit', a recursive edit is
+started.
 
 When `use-dialog-box' is t (the default), this function can pop
 up a dialog window to collect the user input.  That functionality
@@ -133,6 +136,10 @@ read-multiple-choice
                   (ignore-errors (scroll-other-window)) t)
                  ((eq answer 'scroll-other-window-down)
                   (ignore-errors (scroll-other-window-down)) t)
+                 ((eq answer 'edit)
+                  (save-excursion
+		    (save-window-excursion
+		      (recursive-edit))))
                  (t tchar)))
           (when (eq tchar t)
             (setq wrong-char nil
@@ -141,57 +148,61 @@ read-multiple-choice
           ;; help messages.
           (when (and (not (eq tchar nil))
                      (not (assq tchar choices)))
-	    (setq wrong-char (not (memq tchar '(?? ?\C-h)))
+	    (setq wrong-char (not (memq tchar '(?? help-char)))
                   tchar nil)
             (when wrong-char
               (ding))
-            (with-help-window (setq buf (get-buffer-create
-                                         "*Multiple Choice Help*"))
-              (with-current-buffer buf
-                (erase-buffer)
-                (pop-to-buffer buf)
-                (insert prompt "\n\n")
-                (let* ((columns (/ (window-width) 25))
-                       (fill-column 21)
-                       (times 0)
-                       (start (point)))
-                  (dolist (elem choices)
-                    (goto-char start)
-                    (unless (zerop times)
-                      (if (zerop (mod times columns))
-                          ;; Go to the next "line".
-                          (goto-char (setq start (point-max)))
-                        ;; Add padding.
-                        (while (not (eobp))
-                          (end-of-line)
-                          (insert (make-string (max (- (* (mod times columns)
-                                                          (+ fill-column 4))
-                                                       (current-column))
-                                                    0)
-                                               ?\s))
-                          (forward-line 1))))
-                    (setq times (1+ times))
-                    (let ((text
-                           (with-temp-buffer
-                             (insert (format
-                                      "%c: %s\n"
-                                      (car elem)
-                                      (cdr (assq (car elem) altered-names))))
-                             (fill-region (point-min) (point-max))
-                             (when (nth 2 elem)
-                               (let ((start (point)))
-                                 (insert (nth 2 elem))
-                                 (unless (bolp)
-                                   (insert "\n"))
-                                 (fill-region start (point-max))))
-                             (buffer-string))))
-                      (goto-char start)
-                      (dolist (line (split-string text "\n"))
-                        (end-of-line)
-                        (if (bolp)
-                            (insert line "\n")
-                          (insert line))
-                        (forward-line 1)))))))))))
+            (let ((buf (get-buffer-create "*Multiple Choice Help*")))
+              (if help-string
+                  (with-help-window buf
+                    (with-current-buffer buf
+                      (insert help-string)))
+                (with-help-window buf
+                  (with-current-buffer buf
+                    (erase-buffer)
+                    (pop-to-buffer buf)
+                    (insert prompt "\n\n")
+                    (let* ((columns (/ (window-width) 25))
+                           (fill-column 21)
+                           (times 0)
+                           (start (point)))
+                      (dolist (elem choices)
+                        (goto-char start)
+                        (unless (zerop times)
+                          (if (zerop (mod times columns))
+                              ;; Go to the next "line".
+                              (goto-char (setq start (point-max)))
+                            ;; Add padding.
+                            (while (not (eobp))
+                              (end-of-line)
+                              (insert (make-string (max (- (* (mod times columns)
+                                                              (+ fill-column 4))
+                                                           (current-column))
+                                                        0)
+                                                   ?\s))
+                              (forward-line 1))))
+                        (setq times (1+ times))
+                        (let ((text
+                               (with-temp-buffer
+                                 (insert (format
+                                          "%c: %s\n"
+                                          (car elem)
+                                          (cdr (assq (car elem) altered-names))))
+                                 (fill-region (point-min) (point-max))
+                                 (when (nth 2 elem)
+                                   (let ((start (point)))
+                                     (insert (nth 2 elem))
+                                     (unless (bolp)
+                                       (insert "\n"))
+                                     (fill-region start (point-max))))
+                                 (buffer-string))))
+                          (goto-char start)
+                          (dolist (line (split-string text "\n"))
+                            (end-of-line)
+                            (if (bolp)
+                                (insert line "\n")
+                              (insert line))
+                            (forward-line 1)))))))))))))
     (when (buffer-live-p buf)
       (kill-buffer buf))
     (assq tchar choices)))
-- 
2.31.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Add-a-help-option-to-the-open-large-files-prompt.patch --]
[-- Type: text/x-patch, Size: 4367 bytes --]

From 4f3e308b372cfea728211421679ad00d5a8b64df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= <mardani29@yahoo.es>
Date: Wed, 21 Apr 2021 23:27:13 +0200
Subject: [PATCH 2/2] Add a help option to the open large files prompt

* lisp/files.el (files--ask-user-about-large-file-help-text): New
function that returns information about opening large files in
Emacs.  (Bug#45412)
(files--ask-user-about-large-file): Use read-multiple-choice to
display the available actions.
* etc/NEWS: Advertise the new feature.
---
 etc/NEWS      |  4 ++++
 lisp/files.el | 57 ++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b641e8d95f..7905c936a3 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -272,6 +272,10 @@ 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.
 
+---
+** New help window when Emacs prompts before opening a large file.
++Press '?' or 'help-char' (by default, 'C-h') to display this new help window.
+
 \f
 * Editing Changes in Emacs 28.1
 
diff --git a/lisp/files.el b/lisp/files.el
index 7440c11a21..fe3722b07d 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2123,27 +2123,56 @@ out-of-memory-warning-percentage
 
 (declare-function x-popup-dialog "menu.c" (position contents &optional header))
 
+(defun files--ask-user-about-large-file-help-text (op-type size)
+  "Format the text that explains the options to open large files in Emacs.
+OP-TYPE contains the kind of file operation that will be
+performed.  SIZE is the size of the large file."
+  (format
+   "The file that you want to %s is large (%s), which exceeds the
+ threshold above which Emacs asks for confirmation (%s).
+
+ Large files may be slow to edit or navigate so Emacs asks you
+ before you try to %s such files.
+
+ You can press:
+ 'y' to %s the file.
+ 'n' to abort, and not %s the file.
+ 'l' (the letter ell) to %s the file literally, which means that
+ Emacs will %s the file without doing any format or character code
+ conversion and in Fundamental mode, without loading any potentially
+ expensive features.
+
+ 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
+   (funcall byte-count-to-string-function large-file-warning-threshold)
+   op-type
+   op-type
+   op-type
+   op-type
+   op-type))
+
 (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))
+      (let* ((prompt (format "File %s is large (%s), really %s?"
+		             (file-name-nondirectory filename)
+		             (funcall byte-count-to-string-function size) op-type))
              (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)
+              (car
+               (read-multiple-choice prompt '((?y "yes")
+                                              (?n "no")
+                                              (?l "literally"))
+                                     (files--ask-user-about-large-file-help-text
+                                      op-type
+                                      (funcall byte-count-to-string-function size))))))
+        (cond ((eq choice ?y) nil)
+              ((eq choice ?l) 'raw)
               (t 'abort))))))
 
 (defun abort-if-file-too-large (size op-type filename &optional offer-raw)
-- 
2.31.0


  reply	other threads:[~2021-04-21 22:12 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-24 14:44 bug#45412: File ... is large (... MiB), really open? (y)es or (n)o or (l)iterally 積丹尼 Dan Jacobson
2020-12-25  1:00 ` Unknown
2020-12-25  1:29   ` 積丹尼 Dan Jacobson
2020-12-25  5:52   ` Lars Ingebrigtsen
2020-12-25  9:32     ` Juri Linkov
2021-04-10 22:32       ` Daniel Martín via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-04-10 22:52         ` Juri Linkov
2021-04-11  7:03         ` Eli Zaretskii
2021-04-12  8:06         ` Lars Ingebrigtsen
2021-04-16 23:50           ` Daniel Martín via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-04-17 11:44             ` Lars Ingebrigtsen
2021-04-21 22:12               ` Daniel Martín via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2021-04-22 10:04                 ` Eli Zaretskii
2021-04-25 19:14                   ` Lars Ingebrigtsen
2021-04-25 21:23                     ` Daniel Martín via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-04-26  2:28                       ` Eli Zaretskii
2021-04-27  1:08                       ` Lars Ingebrigtsen
2021-05-05 23:10                         ` Daniel Martín via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-05-06  8:28                           ` Lars Ingebrigtsen
2021-05-06  8:30                           ` Eli Zaretskii
2021-05-07 14:11                             ` Eli Zaretskii
2021-05-08 11:37                               ` Lars Ingebrigtsen
2021-05-08 12:22                                 ` Eli Zaretskii
2021-05-09  9:57                                   ` Lars Ingebrigtsen
2021-05-09 10:04                                     ` Eli Zaretskii
2021-04-22 22:09                 ` Juri Linkov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m1a6prmgm1.fsf@yahoo.es \
    --to=bug-gnu-emacs@gnu.org \
    --cc=45412@debbugs.gnu.org \
    --cc=jidanni@jidanni.org \
    --cc=juri@linkov.net \
    --cc=larsi@gnus.org \
    --cc=mardani29@yahoo.es \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).