How to prompt the user for a block of text in elisp? - emacs

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:
create a temporary buffer (via with-temporary-buffer?)
seed the buffer with some default text
display the buffer
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)
wait for the user to hit the key sequence
collect the buffer text and put it in a variable (via buffer-string)
destroy the temporary buffer and restore the window layout as it was before
do stuff with the text

(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): ")

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.

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.

Related

How to insert date only in org-mode

I can insert an inactive timestamp interactively with org-time-stamp-inactive (C-c C-!), then pressing Enter when presented with the calendar. This inserts a value like [2021-12-10 Fri].
I would like to know if this can be achieved programmatically with org-time-stamp-inactive. I tried org-time-stamp-inactive '(16) but this also inserts a time which I do not want. I can do insert (format-time-string "[%Y-%m-%d %a]") but this uses a different approach, and I would like to know if what I want can be achieved with org-time-stamp-inactive.
EDIT (in response to comment): No, it is not possible, but you got to look at the code to make sure. org-time-stamp-inactive is a thin wrapper around org-time-stamp. That in turn calls org-insert-time-stamp, which takes a time and a with-hm argument (among others - see the doc string of the function with C-h f org-insert-time-stamp for the details). If with-hm is nil, then no time is printed. But org-time-stamp (and therefore org-time-stamp-inactive) always calls org-insert-time-stamp with a non-nil with-hm, so the time is always inserted in that case. The best you can do is call the underlying function org-insert-time-stamp like so: (org-insert-time-stamp (current-time) nil 'inactive).
I still think your simple suggested solution (elaborated a bit in the original answer below) is the best way to go about the problem.
[ORIGINAL ANSWER]
Org mode is plain text: things may appear differently, but all that is smoke and mirrors: you can cat an Org mode file in the terminal and see precisely what it contains. In particular, inactive time data are just strings of the form [2021-12-11]. So there is no penalty if you do it manually, instead of going through some Org mode interfaces: the end result is the same, so your manually inserted date is just as good as the one inserted by org-time-stamp-inactive.
So assuming that the simple solution you suggested is acceptable, here's filling in the details:
(defun my/org-insert-current-time-as-inactive-time-stamp ()
(interactive)
(insert (format-time-string "[%Y-%m-%d]")))
(define-key org-mode-map (kbd "C-c _") #'my/org-insert-current-time-as-inactive-time-stamp)
C-c _ was undefined in the Org mode keymap in my case, but YMMV: choose something that is not used.

emacs > org mode > agenda - always use current buffer

I am using many different org mode files for various projects, and I rev them by adding the date to the filename eg filename-2020-09-17.org. I realize I could use version control but in this case that is not possible, due to needing to share the file with others who are not using VC.
I would like the Agenda to always show just the items for the current file/buffer.
When I save eg the file with filename-2020-09-16.org to filename-2020-09-17.org, then the agenda still shows the old file name unless I remove it from the agenda file list and add the new file.
I realize that I can use C-c a < a but I am lazy and would rather not have to type S-, each time to get the <.
I looked at
Agenda view of the current buffer
And the OP says the solution was simple but he/she/they did not provide the solution - at least I don't see it and I tried the posted code but it no works.
I also found https://www.reddit.com/r/orgmode/comments/bxwovd/agenda_for_current_buffer/ but that did not seem to meet my need.
Specifically I would like to put something in .emacs so that this would apply to all files all the time.
I also looked into a keystroke macro programs but this does not seem ideal.
Any help is appreciated.
Thanks ahead of time.
Here's a simple function to do what you want, but there is no error checking to make sure e.g. that you are invoking it from a buffer that is visiting an Org mode file. The idea is that you set the org-agenda-files list to contain just the file which the buffer is visiting and then you call the regular org-agenda function. Binding the modified function to the C-c a key may or may not be what you want to do, but you can try and decide for yourself:
(defun org-agenda-current-buffer ()
(interactive)
(let ((org-agenda-files (list (buffer-file-name (current-buffer)))))
(org-agenda)))
(define-key global-map (kbd "C-c a") #'org-agenda-current-buffer)

Emacs: How to select word under cursor and append to file

I want the following behavior:
Append the word under cursor into a file(~/vocabulary.txt, for example)
Better still to bind a key for it.
Could anyone show me how to do it?
Should I put those code into .emacs ?
Try the following function:
(defun my-write-to-file ()
"Save word at point to file"
(interactive)
(write-region (concat (thing-at-point 'word) "\n") nil "~/vocabulary.txt" 'append))
When called, this function will save the word at point (the word the cursor is on or the word right before the cursor) to ~/vocabulary.txt.
You can bind it to a key (C-c w in this case, but you can change it to whatever you like) like this:
(global-set-key (kbd "C-c w") 'my-write-to-file)
To use, simply put the function and the keybinding assignment in your .emacs.
#Elethan wrote you a command that does just what you ask for, and bound it to a key.
It might also help to mention some general commands that you can use for this kind of thing. M-x append-to-file appends the region contents to a file, and M-x write-region prepends.
The manual is your friend for things like this. See nodes Misc File Ops and Accumulating Text.
Be aware too that for the two commands just mentioned, as the manual says about append-to-file (it should say it about both):
You should use append-to-file only with files that are not being
visited in Emacs. Using it on a file that you are editing in Emacs
would change the file behind Emacs’s back, which can lead to losing some
of your editing.
Accumulating Text also tells you about commands for adding text to a buffer, including the case of adding to a buffer for a file that you are visiting (as opposed to what the above quote warns you about for append-to-file). These include commands append-to-buffer and prepend-to-buffer.

How do you list and manage hidden buffers?

When calling switch-to-buffer, in the minibuffer, when you press SPACE, you can see hidden buffers that you normally don't see, like *Minibuf-0* for example.
How could you list those hidden buffers into the list of buffers shown by list-buffers ? If it's not possible using list-buffers, how do you manage them ?
You can tweak the function to show all buffers, like so:
(defun list-all-buffers (&optional files-only)
"Display a list of names of existing buffers.
The list is displayed in a buffer named `*Buffer List*'.
Non-null optional arg FILES-ONLY means mention only file buffers.
For more information, see the function `buffer-menu'."
(interactive "P")
(display-buffer (list-buffers-noselect files-only (buffer-list))))
(define-key ctl-x-map "\C-b" 'list-all-buffers)
ElectricBufferList does it for me:
(global-set-key "\C-x\C-b" 'electric-buffer-list)
Shows all buffers.
I prefer bs-show to list-buffers and electric-buffer-list.
bs-show can be configured to display all buffers by changing the value of the variable bs--intern-show-never
I think though that because they begin with a space, they're not supposed to be easily visible to you. They're more like internal variables, and manipulating them may make things start misbehaving pretty quickly. Best to just ignore them for the most part.
In any buffer do...
(buffer-list) C-x C-e
The list will now be in *messages*
now leave those hidden buffers alone ;-)

Simple Emacs keybindings

I have two operations that I do all the time in Emacs:
Create a new buffer and paste the clipboard. C-S-n
Close the current buffer. C-S-w
Switch to the last viewed buffer. C-TAB
I feel like a keyboard acrobat when doing the first two operations. I think it would be worth trying some custom key bindings and macros.
A few questions about this customization:
How would I make a macro for #1?
Are these good key bindings? (I know this is a bit subjective, but they might be used by something popular that I don't use.)
Has anyone found a C-TAB macro that will act like Alt+Tab in Linux/Windows? Specifically, I want to have a stack of buffers according to the last viewed timestamp (most recent on top). I want to continue cycling through the stack until I let go of the Ctrl key. When the Ctrl key is released, I want the current buffer to get an updated position on the stack.
Have you tried using vertically or horizontally split windows for this (via C-x 3 or C-x 2)? It seems like it would give you fewer steps - even if you implement something like you're talking about.
I find split windows really speed up copy and pasting operations. I use the arrow keys on my num pad to switch among windows (windmove-left/-right/-up/-down), so it's only one key to press and you go to the window you want.
I guess this is a little different from what you're asking for, but it sounds like it might help speed things along a bit.
C-x left and C-x right cycle through buffers, but you have to hit it multiple times, you can't just keep the key pressed down.
For creating a macro for #1, you just start a macro, hit the keys you usually do to create a new buffer, and stop the macro.
So it would be something like:
C-x ( C-x b NEW RET C-x )
You can then save NEW to a file once you're done pasting, so you can use the macro again to create a new buffer. C-x e to try out the macro. If it works you can save it into your init.el file. This is done with:
M-x name-last-kbd-macro
Then you'll get a prompt to enter the name of your choice. This is only good for the current session. Then you save the named macro to your initialization file. First you open your .emacs or init.el file. Then you place point where you want the macro definition to go, then you type:
M-x insert-kbd-macro
Now you can run your macro using its name via M-x <macroname> . You can bind your macro to keys too (in your .emacs or init.el file):
(global-set-key (kbd "C-c a") '<macroname>)
For example this is how your init.el would look after creating a macro that opens a new buffer called NEW that is not associated with a file and binding this macro to C-c n:
;; Creates a new unassociated buffer called NEW
(fset 'new-buffer "\C-xbNEW\C-m");
;; Shortcut for new-buffer
(global-set-key (kbd "C-c n") 'new-buffer)
You can also throw in the paste, buffer close, and buffer switching operations. I guess you'd have to save the buffer to a file manually.
Some resources
Information about macros on EmacsWiki
Possibly useful: Swap text between buffers
start by invoking start-kbd-macro, finish by with end-kbd-macro. Afterwards you may immediately test the new macro with call-last-kbd-macro. If you're happy with the result you might want to save the macro.
Emacs generally doesn't use C-S keybindings and they are easy to use, so I'd call them good. They might cause problems if you're using the terminal version of Emacs, but I assume that's not the case with you.
I use this simple snippet:
(global-set-key (kbd "<C-tab>") 'bury-buffer)
bury-buffer basically makes the current buffer the last in the buffer-list so you'll be able to cycle buffers in a predictable order.
I wouldn't make a macro for that but write a function like someone else posted on this page. Instead of (cua-paste nil) you could also use (yank). I'm not sure which one's better and why.
I don't like them that much. For things that I use often I'd like to do as little finger acrobatics as possible, so that would mean modifier+key instead of modifier1+modifier2+key.. or use a function key if you don't feel tied to the homerow.
no comment