How does Embark call the actions?
Embark actions are normal Emacs commands, that is, functions with an interactive specification. In order to execute an action, Embark calls the command with ‘call-interactively’, so the command reads user input exactly as if run directly by the user. For example the command may open a minibuffer and read a string (‘read-from-minibuffer’) or open a completion interface (‘completing-read’). If this happens, Embark takes the target string and inserts it automatically into the minibuffer, simulating user input this way. After inserting the string, Embark exits the minibuffer, submitting the input. (The immediate minibuffer exit can be disabled for specific actions in order to allow editing the input; this is done by adding the ‘embark--allow-edit’ function to the appropriate entry of ‘embark-target-injection-hooks’). Embark inserts the target string at the first minibuffer opened by the action command, and if the command happens to prompt the user for input more than once, the user still interacts with the second and further prompts in the normal fashion. Note that if a command does not prompt the user for input in the minibuffer, Embark still allows you to use it as an action, but of course, never inserts the target anywhere. (There are plenty of examples in the default configuration of commands that do not prompt the user bound to keys in the action maps, most of the region actions, for instance.)
This is how Embark manages to reuse normal commands as actions. The mechanism allows you to use as Embark actions commands that were not written with Embark in mind (and indeed almost all actions that are bound by default in Embark’s action keymaps are standard Emacs commands). It also allows you to write new custom actions in such a way that they are useful even without Embark.
Emacs has a variable ‘y-or-n-p-use-read-key’, which when set to ‘t’ causes ‘y-or-n-p’ to use ‘read-key’ instead of ‘read-from-minibuffer’. Setting ‘y-or-n-p-use-read-key’ to ‘t’ is recommended for Embark users because it keeps Embark from attempting to insert the target at a ‘y-or-n-p’ prompt, which would almost never be sensible. Also consider this as a warning to structure your own action commands so that if they use ‘y-or-n-p’, they do so only after the prompting for the target.
Here is a simple example illustrating the various ways of reading input from the user mentioned above. Bind the following commands to the ‘embark-symbol-map’ to be used as actions, then put the point on some symbol and run them with ‘embark-act’:
(defun example-action-command1 ()
(interactive)
(message "The input was `%s'." (read-from-minibuffer "Input: ")))
(defun example-action-command2 (arg input1 input2)
(interactive "P\nsInput 1: \nsInput 2: ")
(message "The first input %swas `%s', and the second was `%s'."
(if arg "truly " "")
input1
input2))
(defun example-action-command3 ()
(interactive)
(message "Your selection was `%s'."
(completing-read "Select: " '("E" "M" "B" "A" "R" "K"))))
(defun example-action-command4 ()
(interactive)
(message "I don't prompt you for input and thus ignore the target!"))
(keymap-set embark-symbol-map "X 1" #'example-action-command1)
(keymap-set embark-symbol-map "X 2" #'example-action-command2)
(keymap-set embark-symbol-map "X 3" #'example-action-command3)
(keymap-set embark-symbol-map "X 4" #'example-action-command4)Also note that if you are using the key bindings to call actions, you can pass prefix arguments to actions in the normal way. For example, you can use ‘C-u X2’ with the above demonstration actions to make the message printed by ‘example-action-command2’ more emphatic. This ability to pass prefix arguments to actions is useful for some actions in the default configuration, such as ‘embark-shell-command-on-buffer’.