Нажатие C-g во время выполнения функции на Lisp заставляет Emacs выйти из того, что он делает. Это означает, что управление возвращается в самый внутренний активный цикл команд.
Нажатие C-g, пока цикл команд ожидает ввод с клавиатуры,
не вызывает выход; оно действует как обычный символ ввода. В
простейшем случае вы не можете отличить, потому что C-g
обычно выполняет команду keyboard-quit, эффект которой — выход.
Однако когда C-g следует за префиксным ключом, они объединяются в
неопределённый ключ. Эффект состоит в отмене как префиксного ключа, так и любого
префиксного аргумента.
В минибуфере C-g имеет другое определение: он прерывает работу минибуфера. Это означает, по сути, что он выходит из минибуфера и затем выполняет выход. (Простой выход вернул бы управление в цикл команд внутри минибуфера.) Причина, по которой C-g не выполняет выход напрямую, когда считыватель команд читает ввод, заключается в том, чтобы его значение можно было переопределить в минибуфере таким образом. C-g после префиксного ключа не переопределяется в минибуфере и оказывает своё обычное действие — отмену префиксного ключа и префиксного аргумента. Это также было бы невозможно, если бы C-g всегда выполнял выход напрямую.
Когда C-g действительно выполняет прямой выход, он делает это, устанавливая переменную
quit-flag в t. Emacs проверяет эту переменную в подходящие
моменты и выполняет выход, если она не равна nil. Установка quit-flag
в любое значение, отличное от nil, таким образом вызывает выход.
На уровне кода C выход не может произойти где угодно; только в тех
особых местах, которые проверяют quit-flag. Причина в том,
что выход в других местах мог бы оставить несогласованность во внутреннем
состоянии Emacs. Поскольку выход откладывается до безопасного места, выход
не может привести к краху Emacs.
Некоторые функции, такие как read-key-sequence или
read-quoted-char, полностью предотвращают выход, даже если они ожидают
ввод. Вместо выхода C-g служит запрашиваемым
вводом. В случае read-key-sequence это служит для обеспечения
особого поведения C-g в цикле команд. В случае
read-quoted-char это сделано для того, чтобы C-q можно было использовать
для экранирования C-g.
Вы можете предотвратить выход на части функции Lisp, привязав
переменную inhibit-quit к значению, отличному от nil. Тогда,
хотя C-g по-прежнему устанавливает quit-flag в t как обычно,
обычный результат этого — выход — предотвращается. В конечном счёте,
inhibit-quit снова станет nil, например, когда его
привязка будет развёрнута в конце формы let. В этот момент, если
quit-flag всё ещё не равен nil, запрошенный выход происходит
немедленно. Такое поведение идеально, когда вы хотите гарантировать, что
выход не произойдёт внутри критического участка программы.
В некоторых функциях (таких как read-quoted-char) C-g
обрабатывается особым образом, не связанным с выходом. Это делается
путём чтения ввода с inhibit-quit, привязанным к t, и
установкой quit-flag в nil до того, как inhibit-quit
снова станет nil. Этот фрагмент из определения
read-quoted-char показывает, как это делается; он также показывает,
что обычный выход разрешён после первого символа ввода.
(defun read-quoted-char (&optional prompt)
"…документация…"
(let ((message-log-max nil) done (first t) (code 0) char)
(while (not done)
(let ((inhibit-quit first)
…)
(and prompt (message "%s-" prompt))
(setq char (read-event))
(if inhibit-quit (setq quit-flag nil)))
…установить переменную code…)
code))
Если эта переменная не равна nil, то Emacs немедленно выполняет выход, если
только inhibit-quit не равен nil. Нажатие C-g обычно устанавливает
quit-flag в значение, отличное от nil, независимо от inhibit-quit.
Эта переменная определяет, должен ли Emacs выполнять выход, когда quit-flag
установлен в значение, отличное от nil. Если inhibit-quit
не равен nil, то quit-flag не имеет особого эффекта.
Этот макрос выполняет формы body последовательно, но позволяет выход, по
крайней мере локально, внутри body, даже если inhibit-quit был
не равен nil вне этой конструкции. Он возвращает значение
последней формы в body, если только не произошёл выход, в котором случае
он возвращает nil.
Если inhibit-quit равен nil на входе в with-local-quit,
он просто выполняет body, и установка quit-flag вызывает
обычный выход. Однако если inhibit-quit не равен nil, так
что обычный выход задерживается, ненулевое значение quit-flag
вызывает особый вид локального выхода. Это завершает выполнение
body и выходит из тела with-local-quit с
quit-flag, всё ещё не равным nil, так что другой (обычный) выход
произойдёт, как только это будет разрешено. Если quit-flag
уже не равен nil в начале body, локальный выход
происходит немедленно, и тело не выполняется вовсе.
Этот макрос в основном полезен в функциях, которые могут вызываться из
таймеров, фильтров процессов, сторожей процессов, pre-command-hook,
post-command-hook и других мест, где inhibit-quit
обычно привязан к t.
Эта функция генерирует условие quit с помощью (signal 'quit
nil). Это то же самое, что делает выход. (См. signal
в Ошибки.)
Чтобы выйти без прерывания определения или выполнения макроса клавиатуры,
вы можете сгенерировать условие minibuffer-quit. Оно имеет почти
тот же эффект, что и условие quit, за исключением того, что обработка
ошибок в цикле команд обрабатывает его без выхода из определения
или выполнения макроса клавиатуры.
Вы можете указать символ, отличный от C-g, для использования при выходе.
См. функцию set-input-mode в Режимы ввода.