3

read-from-minibuffer is a great way to prompt a user for a single line of text. How do I prompt a user for a large block of multi-line text in elisp?

This is what I'm thinking, but I don't know if it's the smoothest approach:

  1. create a temporary buffer (via with-temporary-buffer?)
  2. seed the buffer with some default text
  3. display the buffer
  4. tell the user, "edit the text as you see fit, then hit <some key sequence> to indicate that you are done" (perhaps via header-line-format)
  5. wait for the user to hit the key sequence
  6. collect the buffer text and put it in a variable (via buffer-string)
  7. destroy the temporary buffer and restore the window layout as it was before
  8. do stuff with the text
Richard Hansen
  • 51,690
  • 20
  • 90
  • 97

3 Answers3

3
(defun my-read-mb-lines (prompt some-keyseq)
  (let ((keymap (copy-keymap minibuffer-local-map)))
    (define-key keymap (kbd "RET") 'newline)
    (define-key keymap some-keyseq 'exit-minibuffer)
    (read-from-minibuffer prompt nil keymap)))

Calling example:

(my-read-mb-lines "Insert text (C-s to submit): " (kbd "C-s"))

The 'let' block creates a local copy of the minibuffer's default keymap. The next two calls to "define-key" modify the keymap copy. Afterward, "read-from-minibuffer" passes the modified keymap for the minibuffer to use while prompting the user (instead of its default keymap, "minibuffer-local-map").

FWIW, C-j is mapped to "exit-minibuffer" by default and a simplified version can be written:

(defun my-simplified-read-mb-lines (prompt)
  (let ((keymap (copy-keymap minibuffer-local-map)))
    (define-key keymap (kbd "RET") 'newline)
    (read-from-minibuffer prompt nil keymap)))

Calling example:

(my-simplified-read-mb-lines "Insert text (C-j to submit): ")
gbrener
  • 5,365
  • 2
  • 18
  • 20
  • Richard - in emacs version 24.2 there's a variable called "resize-mini-windows" that might be set differently for you. The default is 'grow-only but if you set it to true then you might get nicer behavior: (let ((keymap (copy-keymap minibuffer-local-map)) (resize-mini-windows t)) etc... – gbrener Jun 09 '13 at 00:30
  • Playing with the "max-mini-window-height" variable might also get you there but it's relatively new. – gbrener Jun 09 '13 at 00:38
  • I'm running 23.4. With `max-mini-window-height` set to 0.25 and `resize-mini-windows` set to `t`, it still shows only one line. Maybe this was fixed in v24? – Richard Hansen Jun 09 '13 at 02:19
  • I don't think so - I just tested the above code in a bare session of v23.2 and it worked. It might be one of your customizations/config settings or the way that you're invoking the function. If you startup emacs with the -q flag and evaluate the above code inside the scratch buffer you'll be able to rule out whether your configurations are the cause. – gbrener Jun 09 '13 at 17:43
  • You're right: the problem was `(custom-set-variables '(truncate-lines t))` – Richard Hansen Jun 09 '13 at 19:00
  • This was fixed in Emacs v24 (minibuffers always set `truncate-lines` to `nil`). For Emacs v23 users, see http://stackoverflow.com/q/1775898/712605 – Richard Hansen Jun 09 '13 at 19:14
2

I suppose it depends on the exact use-case?

There are no shortage of examples of your proposed approach working very nicely (e.g. writing a VCS commit message), so there's certainly nothing wrong with that -- it's tried and true. In addition, if it really is a large (or simply not small) block of text, then I suspect that providing a normal buffer to edit in may provide the nicest experience for the user.

If you're talking about collecting multiple input fields including a multi-line field, then the widget-based approach (as suggested by wvxvw) would enable you to put everything in a single buffer, which might also be desirable.

Or you could use the mail-like approach of using a single buffer for multiple fields, and then parse the results afterwards.

phils
  • 71,335
  • 11
  • 153
  • 198
2

First of all Emacs 23.4 is very old. You should upgrade.

The work-flow you describe is what org-mode uses to edit source blocks. Org-mode is included in Emacs 24.

See the source of org-edit-special for how it works.

It does a bit more than you need.

Basically, you want to set up a minor-mode for the created buffer that has a binding to gather the text and restore window configuration.

I've written ges to edit arbitrary blocks in a new buffer using org-mode machinery, but it's more complex than what you need.

event_jr
  • 17,467
  • 4
  • 47
  • 62