2

background

I had elisp code (in this very small git repo) like

(defun bulk-replace-file (filepath)
  (interactive "FPath to file to bulk-replace: ")
  (bulk-replace-file-non-interactive filepath)
)

That worked. Then I decided to parameterize the prompt, producing code like

(defconst BULK-REPLACE-FILEPATH-PROMPT "Path to file to bulk-replace: ")

(defun bulk-replace-file (filepath)
;  (interactive "FPath to file to bulk-replace: ") ; should always work
  (interactive (concat "F" BULK-REPLACE-FILEPATH-PROMPT))
  (bulk-replace-file-non-interactive filepath)
)

However that fails with (in *Messages*)

command-execute: Wrong type argument: listp, "FPath to file to bulk-replace: "

Which frankly stuns me, esp since I'm positive this code formerly worked, but that's why one does regression testing :-(.

questions

  1. How is (interactive) getting a list from "FPath to file to bulk-replace: ", given that the latter surely looks like a string? Am I missing something?
  2. Why is (interactive) not getting a list from (concat "F" BULK-REPLACE-FILEPATH-PROMPT), given that
    • (concat "F" BULK-REPLACE-FILEPATH-PROMPT) returns a string (I have checked with (type-of))
    • (concat "F" BULK-REPLACE-FILEPATH-PROMPT) == "FPath to file to bulk-replace: " (per the error message above)
    • (interactive "FPath to file to bulk-replace: ") works (see first code snippet)
  3. What's "the right way" to parameterize a prompt used for (interactive)?

über-answer

Given that, as Drew points out, (interactive) is quite demanding, an easier (and working) way to achieve the desired functionality avoids prompting via (interactive):

(defconst BULK-REPLACE-FILEPATH-PROMPT "Path to file to bulk-replace: ")

(defun bulk-replace-file ()
  (interactive) ; Purely to enable `M-x`: let `read-file-name` get the filepath
  (bulk-replace-file-non-interactive
    (read-file-name BULK-REPLACE-FILEPATH-PROMPT)))
)
1

interactive is a special form, not a function. As such, it need not handle its argument as would an ordinary function.

interactive expects a literal string as argument, or else a sexp that is evaluated to produce the list of arguments for the function.

If the argument is a literal string then the various interactive codes apply. In other words, the string has special formatting, with certain codes (first char and first char after each newline char (\n) in the string that are interpreted to produce the list of arguments.

If the argument is a sexp then it must do everything to come up with the correct list of arguments.

In your case, you could use a sexp, but it would need to do two things:

  1. Construct the prompt -- you know how to do that, using concat.

  2. Use the prompt to read and return a file name. For that, you need to use a function such as read-file-name (which expects a prompt-string argument).

  3. Put the file name in a list. (interactive must return a list of all arguments.)

So, for example:

(interactive (list (read-file-name BULK-REPLACE-FILEPATH-PROMPT)))

See the Elisp manual, node Using Interactive.

It is almost always best to ask Emacs first. In this case, in the Elisp manual, use i interactive RET to get information about interactive, including the answer to this very question.

  • (interactive (list (read-file-name (concat "F" BULK-REPLACE-FILEPATH-PROMPT)))): did you test that? I know (empirically) that I don't want to concat the "F" onto the read-file-name prompt: (read-file-name) displays the prompt normally, while (interactive) does not display the "F". So how should one, umm, give (interactive) the "F"? (pun intended :-) – TomRoche Dec 26 '15 at 3:17
  • ask Emacs first: I would, except that, for reasons unknown (but probably due to my misconfiguration), I don't see manual=Emacs when I do C-h i. Although I do, oddly, see manual=Emacs FAQ. – TomRoche Dec 26 '15 at 3:21
  • Right. If that was the F as the interactive code, then obviously you don't need to prefix the prompt with it. I don't know what your BULK...PROMPT string is. If it is the whole prompt string then that's all you need. (Answer updated accordingly.) – Drew Dec 26 '15 at 3:54
  • 1
    Too bad, if you don't have the Emacs manual. I was talking about the Elisp manual, but maybe you don't have that either, in which case, doubly too bad. Fortunately, these manuals are at least available on the web: Emacs, Elisp. – Drew Dec 26 '15 at 3:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.