Как я могу симулировать событие произвольного ключа от Elisp?

26

Можно ли смоделировать произвольное ключевое событие из elisp? Мне известны способы, с помощью которых я могу найти привязку для данного ключа и затем вызвать эту команду в интерактивном режиме, но что, если это событие ключа не связано с командой?

В качестве одного примера , что если бы я хотел связать C-`себя так же, как ESCключ во всех контекстах ?

nispio
источник
Кажется, что key-bindingsэто неправильный тег, если вы не пытаетесь связать псевдоним ключа. Также, возможно, вам следует изменить свой пример на что-то другое, чтобы оно не запуталось.
b4hand
@ b4hand Я открыт для предложений по улучшению тегов. Там нет key-eventsтега. Должен ли я сделать один?
Ниспио
звучит разумно для меня, но события могут быть лучше, так как это также может быть применимо к событиям мыши.
b4hand
2
Я все еще не понимаю, хотите ли вы смоделировать ключевое событие в elisp, или вы конкретно хотите, чтобы возможность заставить ключ действовать так, как если бы он был другим ключом? Подобное key-translation-mapоблегчает последнее, поэтому, если это все, что вы хотите, я бы предложил использовать его, а не делать что-то более ручное.
Филс
... и если вам действительно нужен ключевой перевод, я думаю, это другой вопрос, и вы должны задать его отдельно; и затем перефразируйте ваш пример для этого вопроса, чтобы он был более подходящим для более общей проблемы "как мне симулировать ключевое событие в elisp?"
Филс

Ответы:

24

Вы можете передавать произвольные события (нажатия клавиш, щелчки мыши и т. Д.) В командный цикл, помещая их в unread-command-events. Например, следующее приведёт к тому, что командный цикл выполнит разрыв при следующем запуске:

(setq unread-command-events (listify-key-sequence "\C-g"))

Обратите внимание, что это только передает события в командный цикл, поэтому ничего интересного не получится, если вы зацикливаетесь в своем собственном коде.

Другой подход, о котором вы, похоже, знаете, состоит в том, чтобы найти функцию, с которой связан данный ключ, и выполнить ее самостоятельно:

(funcall (global-key-binding "\C-g"))

Это выполнит команду немедленно. Однако помните, что некоторые команды имеют различное поведение в зависимости от того, вызываются ли они интерактивно, например, по умолчанию используются аргументы. Вы хотите компенсировать это с помощью call-interactively:

(call-interactively (global-key-binding "\C-g"))
JCH
источник
Я читал о, unread-command-eventsно я не смог понять, как его использовать. Установка этого не имела никакого эффекта для меня. Есть ли хороший пример того, как он используется?
Ниспио
Я видел это, когда просил пользователя нажать пробел, чтобы продолжить - если пользователь нажимает что-то еще, он переходит unread-command-events.
JCH
@nispio: unread-command-eventsэто то, что говорит его название. Вы можете просмотреть событие и затем, в зависимости от того, что это такое, условно отодвинуть его обратно, u-c-eчтобы затем оно было обработано в обычном режиме. Есть много примеров его использования в исходном коде Emacs - grepэто ваш друг.
Дрю
1
Я смог добраться unread-command-eventsдо работы. Часть, которую я пропустил прежде, была listify-key-sequenceфункцией. Я только что использовал необработанный ключевой вектор.
Ниспио
1
Спасибо за этот ответ. Я хотел реализовать неинтерактивные тесты моей системы завершения, поэтому я использовал эту идею для реализации with-simulated-inputмакроса, который оценивает любое выражение с unread-command-eventsпривязкой let к указанной последовательности клавиш: github.com/DarwinAwardWinner/ido-ubiquitous/blob/…
Райан К. Томпсон
8

Самый простой способ, который я знаю, это просто использовать execute-kbd-macro:

(defun foo () (interactive) (execute-kbd-macro (kbd "<escape>")))
(global-set-key (kbd "C-`") 'foo)
shosti
источник
Оценка выше и последующее нажатие C-` дает мне ошибку apply: Wrong number of arguments: #[(ad--addoit-function ....
Ниспио
1
@nispio Не для меня. Эта ошибка выглядит как совет.
Малабарба
@ Malabarba Я думаю, ты прав. После запуска заново с emacs -Qэтой ошибкой нет. Я все еще получаю эту ошибку, хотя:After 0 kbd macro iterations: foo: Lisp nesting exceeds `max-lisp-eval-depth'
Nispio
Это на самом деле то, что я искал. По какой-то странной причине (возможно, с некоторыми деталями взаимодействия evil) прямой вызов нужной функции в моем случае имел неожиданный эффект ( evilmi-jump-items), и мне пришлось использовать(execute-kbd-macro (kbd "%"))
xji
4

Исходя из этого ответа , вы можете использовать глобальный набор ключей, как это

(global-set-key (kbd "C-`") (kbd "<escape>"))

Который будет относиться C-`какescape

Это, кажется, имеет некоторые проблемы, хотя, если вторая комбинация не выполняет функцию. Так что, если escapeиспользуется как Meta, то это не работает правильно. Но, похоже, работает для команд, связанных с функциями.

resueman
источник
@nispio: На самом деле, это работает, поскольку второй аргумент неявно преобразуется в макрос клавиатуры.
Шости
1
@shosti Оценка выше и нажав C-` дает мне ошибку: After 0 kbd macro iterations: command-execute: Lisp nesting exceeds `max-lisp-eval-depth'.
Ниспио
@nispio: Возможно, вы уже C-связаны ESCкаким-либо другим методом, так что это идет в бесконечный цикл.
Шости
@shosti Вы были правы. Слишком много всего eval-sexpпроисходит за один сеанс. :-) Но попытка снова с emacs -Qпричинами C-` просто ничего не делать.
Ниспио
В зависимости от вашей системы, (kbd "<escape>")и (kbd "ESC")может означать разные вещи - вы пробовали оба?
Шости
2

Прочитав предложение от jch об использовании unread-command-events, я смог собрать решение, которое выполнит некоторые из тех вещей, которые я ищу.

(defun my-simulate-key-event (event &optional N)
  "Simulate an arbitrary keypress event.

This function sets the `unread-command-events' variable in order to simulate a
series of key events given by EVENT. Can also For negative N, simulate the
specified key EVENT directly.  For positive N, removes the last N elements from
the list of key events in `this-command-keys' and then appends EVENT.  For N nil,
treat as N=1."
  (let ((prefix (listify-key-sequence (this-command-keys)))
         (key (listify-key-sequence event))
         (n (prefix-numeric-value N)))
     (if (< n 0)
         (setq prefix key)
       (nbutlast prefix n)
       (nconc prefix key))
       (setq unread-command-events prefix)))

Есть еще много перегибов, чтобы решить. А именно, я не получаю правильный результат, если вызываю эту функцию дважды подряд в течение одного defun.


Примечание:

После проверки Phils' предложение , чтобы использовать key-translation-mapя был в состоянии найти local-function-key-mapчто также является очень полезным в достижении некоторых из моих более широких целей.

nispio
источник