helper to create (interactive ...) functions
...so that fn.el can be used as a substitute for (lambda () (interactive) (do-something 'very 'special)), e.g. for use with define-key.
I've been considering using a helper like this myself, but I'm a bit reluctant to add one to fn.el, since it's supposed to be really minimal. The helper definition would be trivial and probably only useful in init files or interactively, so I'm not sure how much benefit there would be in adding it to the (extremely small) API.
An alternative might be to give a macro definition in the README for people who want to use such a helper in their init files. A helper for defining nullary commands only requires one line:
(defmacro fn! (&rest body) `(lambda () (interactive) ,@body))
(Silly) example:
(global-set-key (kbd "<C-f12>") (fn! (search-forward-regexp "fu?n")))
Not sure what the best name would be. fn!? fn-command? fn-com?
thanks for getting back to me. i was just thinking out loud.
it would generally only be useful in init files. sometimes i tend to bind things like "enable minor mode x and then call a command from minor mode x", which is typically (turn-on-x) (do-something). having a lambda with an (interactive) spec introduces quite some noise, which i do not like.
I've been using the above fn! macro for a few years now almost exclusively for bound interactive functions -- I've realized that naming the function can be pretty helpful (otherwise in EG whichkey you see an awful lot of bound to "closure") if you have forgotten what you bound.
These names are bad, but this fills the gap for me:
(defmacro fni (name &rest body)
"Create an interactive function prefixed with ia/ and no arguments"
(let ((fnname (intern (format "ia/%s" (prin1-to-string name)))))
`(defun ,fnname ()
(interactive)
,@body)))
(defmacro fnn (&rest body)
"Create an interactive function prefixed with ia/ and no arguments (infer name)"
`(fni ,(first (-list (first body)))
,@body))
The prefix ia/ is for (i)nter(a)ctive.
Here's an example usage:
(general-define-key
:states '(normal visual)
:prefix "SPC"
;; create 'ia/messages and bind it
"nm" (fni messages (counsel-switch-to-buffer-or-window "*Messages*"))
;; behavior change:
;; create 'ia/split-window-horizontally and bind it
"ws" (fnn (split-window-horizontally)
(evil-window-right 1)))
And if you'd like to hide the "ia/" in whichkey:
(add-to-list 'which-key-replacement-alist '((nil . "\\`ia/") . (nil . "")))
edit: also I've not checked out https://github.com/doomemacs/doomemacs/blob/8f6b045dfdb6d00e5a324c3f9044dce773791502/lisp/doom-lib.el#L429 at all yet
edit2: I settled on this for now:
(defmacro fn! (&rest body)
"Create an interactive function prefixed with ia/ and no arguments (optionally, infer name from first sexp)"
(let* ((has-name? (symbolp (first body)))
(fnname (intern (format "ia/%s" (prin1-to-string
(if has-name?
(first body)
(first (first body))))))))
`(defun ,fnname ()
(interactive)
,@(if has-name?
(-drop 1 body)
body))))
which will accept a name OR infer the function name from the first sexp