Search code examples
emacselisp

Use previous interactive argument in initial value for the next parameter


I need to access the previously entered interactive argument so that I can use it as the initial value of the next argument. To clarify what I mean, consider an example function which renames buffers:

(defun my/rename-buffer (old-name new-name)
  (interactive (list (read-buffer "Buffer to rename: " (current-buffer) t)
                     (read-string "Rename buffer to: ")))
  ;; Implementation here.
)

How can I access the value which was entered for old-name so that I can use it as the initial value for the new-name prompt?

Let me explain in more detail. When we do M-x my/example, the user is prompted like so (assuming *scratch* is the current buffer):

Buffer to rename (default *scratch*):
Rename buffer to:

If the user accepts the default for the first prompt (*scratch*), I would like the second prompt to use *scratch as the initial value displayed for the user:

Rename buffer to: *scratch*

If the user has instead typed the name of a different buffer for the first prompt, say another-buffer, the second prompt should have another-buffer as the initial value:

Rename buffer to: another-buffer

So the second prompt should always use the value of the first parameter as the initial value for the second prompt.

Some notes for clarity:

  • I am not interested in the specific example of my/rename-buffer since the standard rename-buffer works fine. It really is just an example.

  • I am looking for a solution which does not involve using a string as the arg-descriptor. I need to compose the solution with existing arg-descriptors which are lists of expressions. I don't believe they can be re-implemented as strings, but even if they could be, I not interested in such a solution.

  • I do not wish to show the last value as part of the read-only prompt string, but instead I wish to use it as the modifiable initial value which is shown after the prompt string.

  • The user pressing to access the previous argument is not a solution because I do not want to require user interaction to get the last value. I may also (in the future or for different functions) wish to show a slightly modified version of the last value rather than displaying it verbatim. Such a solution would also not work if the prompts are of different types, since they would not have the same history.


Solution

  • interactive, when given a lisp form, simply evaluates that form to obtain a list. You can do anything at all in that form, provided that the eventual return value is an appropriate list of values for the function arguments.

    (defun my/rename-buffer (old-name new-name)
      (interactive
       (let* ((old (read-buffer "Buffer to rename: " (current-buffer) t))
              (new (read-string "Rename buffer to: " old)))
         (list old new)))
      ;; ...
      )