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


26

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

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


Кажется, что 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"))

Я читал о, 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)

Оценка выше и последующее нажатие 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, то это не работает правильно. Но, похоже, работает для команд, связанных с функциями.


@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что также является очень полезным в достижении некоторых из моих более широких целей.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.