How can i achieve my goal? Should i take another approach?
IIUC the problem arises from a misuse of nconc
. The value of unread-command-events
is often nil
, in which case nconc
will not alter it in-place, as there is no cdr
to modify. Instead, you should probably be using push
or setq
to record the value change of unread-command-events
.
invocation of command does nothing
If unread-test
is rewritten as
(defun unread-test ()
"Repeat input after toggling input method."
(interactive)
(setq unread-command-events
(nconc (listify-key-sequence "Hello, World!") unread-command-events)))
then invoking it will cause the characters corresponding to Hello, World!
to be inserted into the current (writable) buffer.
From OP's comment:
I would like my command to allow me to fix strings that were typed with wrong input method. See http://paste.lisp.org/+7OXT for example
Here is a listing of the linked example, for posterity:
(defun krv/punto (p m)
"Repeat input after toggling input method"
(interactive (if (use-region-p)
(list (region-beginning) (region-end))
nil))
(if (use-region-p)
(let* ((region-string (buffer-substring p m))
(old-input-method current-input-method)
(input-keys
(if (not old-input-method)
;; if there is no current input method, just take character
(listify-key-sequence region-string)
;; we have imput method, try to translate characters back
(cl-mapcan
(lambda (ch)
(let ((quail-list (quail-find-key ch)))
(if (equal quail-list t)
(list ch)
(cl-mapcan (lambda (str) (listify-key-sequence str))
quail-list))))
region-string))))
(delete-region p m)
(toggle-input-method)
(setq unread-command-events (nconc unread-command-events input-keys)))
(call-interactively 'toggle-input-method)))
This example behaved as intended for me after only a little bit of tweaking. Specifically, having mandatory arguments means the interactive
spec cannot just return nil
; either it must return '(nil nil)
or the arguments must be optional. The latter option makes more sense to me in this case, as the function changes its behaviour subject to whether the region is active.
Below is my attempt at correcting this hiccup and simplifying the code overall:
(defun krv/punto (&optional beg end)
"Toggle input method and repeat any input in region."
(interactive (and (use-region-p)
(list (region-beginning) (region-end))))
(let ((events (and beg end
(mapcan (lambda (char)
(let ((keys (quail-find-key char)))
(if (consp keys)
(mapcan #'listify-key-sequence keys)
(list char))))
(delete-and-extract-region beg end)))))
(toggle-input-method)
(setq unread-command-events (nconc events unread-command-events))))
Note:
- This follows the suggestion in
(elisp) Event Input Misc
of prepending, instead of appending, to unread-command-events
.
- If you are not yet running Emacs 25 you can use
cl-mapcan
in place of mapcan
as you did before.
You can invert the innermost if
form as follows, if you prefer:
(if (booleanp keys)
(list char)
(mapcan #'listify-key-sequence keys))
What's interesting is that Emacs includes the package robin.el
, which is a newer and simpified version of quail.el
supporting precisely your intended behaviour - translating buffer regions back and forth between input methods. The package implementation can afford to be very simple because it only strives to achieve a subset of its predecessor's functionality. In particular, it forgoes CJK character support. I think it's a (understandable) shame that barely any input methods use it.
unread-test
(elisp uses lisp-case) do? How / where is it called? The value ofunread-command-events
is oftennil
, so passing it tonconc
will not modify it. For programmatically entering the input of an interactive command,unread-command-events
is usually set right before calling said interactive command.unread-command-events
.