11

I occasionally find myself using interactive functions inside of a function that I'm writing for my own use. If a function asks for some information (e.g. "Output file: ~/") is there a general elisp way to add text to the minibuffer and then press enter so that the user doesn't have to do so?

As an example, suppose that I want to run org-latex-export-to-pdf inside of a function but I don't want the user to have to specify a file name. Running (org-latex-export-to-pdf) will move the point to the minibuffer, but putting something like (insert "filename.tex") on the next line doesn't seem to work.

6
  • 3
    Typically, an interactive function should prompt for such information in its interactive clause. When called from elisp, your should be able to pass the information as a function argument. Of course, this does not help you, in case the function you are trying to call doesn't follow this design. Mar 30, 2015 at 5:05
  • Yeah, I've found that to be the case as well usually (unfortunately not at the moment) which might be why the answer to this has been so hard to find. Do you know if there's a design reason for really wanting the user to type out answers to prompts from the minibuffer? Mar 30, 2015 at 5:09
  • Your comment is unclear, to me. Do you want the user to be queried and respond or not? If not then programmatically come up with the argument values (output file name) you want/need and pass them to the function. I suggest that you show some code or in some other way make specific and clear what your problem & question are, or the question risks being closed as unclear.
    – Drew
    Mar 30, 2015 at 5:57
  • I do not want the user (me) queried. I can clarify the question. After reading part of an email thread involving you from a few years ago about yanking to the minibuffer, I think I know how to do this. If I get it working I'll write it up. Mar 30, 2015 at 6:18
  • 1
    Please post a specific example of an interactive function that you would like to use when writing your own function, so that another forum participant can demonstrate how to pass an argument to the function and bypass the minibuffer entirely.
    – lawlist
    Mar 30, 2015 at 6:53

3 Answers 3

3

Interesting problem. Looks like the editor runs the post-command-hook every time it enters a new command-loop, i.e. a recursive-edit. But we can start with minibuffer-setup-hook, which executes a function after entering the minibuffer. While this allows the inserting of input, it is too soon to exit the minibuffer, because the catch hasn't been setup yet.

(defmacro with-minibuffer-input (form &rest inputs)
  (declare (indent 1))
  `(minibuffer-with-setup-hook
       (lambda ()
         (minibuffer-input-provider ',inputs))
     ,form))

That's were we need to wrap the 'arguments' in our own 'command loop', which get's executed every time we enter a recursive-edit, at which point it pops one argument and throws one level up, via exit-minibuffer.

;; -*- lexical-binding: t -*-
(defun minibuffer-input-provider (inputs)
  (let ((hook (make-symbol "hook")))
    (fset hook (lambda ()
                 (remove-hook 'post-command-hook hook)
                 (when inputs
                   (when (= 0 (minibuffer-depth))
                     (error "Too many inputs"))
                   (when (cdr inputs)
                     (add-hook 'post-command-hook hook))
                   (insert (pop inputs))
                   (exit-minibuffer))))
    (add-hook 'post-command-hook hook)))


(with-minibuffer-input (call-interactively 'find-file)
  "/")

(with-minibuffer-input (call-interactively 'occur)
  "\\(foo\\)\\(bar\\)" "\\1");;C-u C-x C-e

;;foobar

(with-minibuffer-input (call-interactively 'replace-string)
  "foo" "bar")

;; foo
4

I wrote a macro for this called with-simulated-input, which you can get here. It allows you to provide arbitrary input as well as execute arbitrary lisp forms in order to simulate user interaction.

For example:

(with-simulated-input '("hello SPC" (insert "world") "RET")
  (read-string "Enter greeting: "))

would return "hello world", with the "hello " inserted by the first string, the "world" inserted via lisp code, and finally "RET" to finish the input.

It comes with a test suite that you can look at for more usage examples.

0

It seems that using run-with-timer with insert will get the job done.

(run-with-timer .2 nil 'insert "filename.tex")
(run-with-timer .3 nil 'execute-kbd-macro (kbd "RET"))
(org-latex-export-to-pdf)

The command insert when placed afterwards is coming up too quickly. It tries to insert the string before there is a place to insert it.

2
  • I would recommend revising your question to seek assistance programmatically passing a file name to org-export-output-file-name when using org-latex-export-to-pdf so that the user is not prompted for the file name. You can put your efforts in the question -- e.g., run-with-timer, etc. -- however, it is not a good solution (in my opinion). The better solution is to properly pass a file name programmatically so that the minibuffer never opens in the first place. I would recommend deleting this answer so that you get a better solution by someone with more elisp experience.
    – lawlist
    Mar 30, 2015 at 18:40
  • @lawlist the question of how to pass a file name to org-latex-export-to-pdf is not the one I am interested in. It is an example since you seemed intent on me adding one. The question I asked is the one I meant: is there a way to reliably answer a minibuffer prompt through elisp. A case by case solution is very much not what I am looking for. From your comment, I can infer that it is not recommended. Mar 30, 2015 at 18:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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