Прежде всего, отказ от ответственности. Я исследовал это много раз, и я почти уверен, что я уже нашел ответ, так или иначе, но я просто не понимаю его.
Моя проблема заключается в следующем:
- У меня процесс проходит через коминт
- Я хочу отправить строку ввода, захватить вывод и посмотреть, когда он закончится (когда последняя строка вывода соответствует регулярному выражению для приглашения)
- только когда процесс завершит отправку вывода, я хочу отправить еще одну строку ввода (например).
Для некоторой предыстории подумайте о основном режиме, реализующем взаимодействие с программой, которое может возвращать произвольное количество выходных данных в произвольно долгое время. Это не должно быть необычной ситуацией, верно? Хорошо, возможно, часть, где мне нужно ждать между входами, необычна, но у нее есть несколько преимуществ по сравнению с отправкой ввода в целом:
- выходной буфер красиво отформатирован: ввод-вывод-вывод-вывод ...
- что еще более важно, при отправке большого количества текста процессу, текст разрезается на части, а части вставляются процессом обратно; точки обрезки являются произвольными, и это иногда приводит к неправильному вводу (мой процесс, например, не будет правильно вставлять вырезку ввода в середине идентификатора)
Во всяком случае, необычный или нет, то получается, что это будет сложно. Прямо сейчас я использую что-то вроде
(defun mymode--wait-for-output ()
(let ((buffer (mymode-get-buffer)))
(with-current-buffer buffer
(goto-char (point-max))
(forward-line 0)
(while (not (mymode-looking-at-prompt))
(accept-process-output nil 0.001)
(redisplay)
(goto-char (point-max))
(forward-line 0))
(end-of-line))))
и я вызываю это каждый раз после отправки строки ввода и перед отправкой следующей. Ну ... это работает, это уже что-то.
Но это также приводит к зависанию emacs в ожидании вывода. Причина очевидна, и я подумал, что если бы я включил sleep-for
в цикл какой-то асинхронный (например, 1 с), он бы задержал вывод на 1 с, но подавил зависание. За исключением того, что кажется, что такого рода асинхронный
sleep-for
не существует .
Или это? В более общем смысле, есть ли идиоматический способ добиться этого с помощью emacs? Другими словами:
Как отправить входные данные процессу, дождаться вывода, а затем отправить дополнительные входные данные асинхронно?
При поиске (см. Связанные вопросы) я в основном видел упоминания о дозорных (но я не думаю, что это применимо в моем случае, так как процесс не завершается) и о некоторых ловушках для коминтов (но что тогда? сделайте ловушку буферной локальной, превратите мою «оценку оставшихся строк» в функцию, добавьте эту функцию в ловушку и затем очистите ловушку - это звучит очень грязно, не так ли?).
Мне жаль, если я не проясняю себя, или если где-то есть действительно очевидный ответ, меня действительно смущают все сложности взаимодействия процессов.
При необходимости я могу сделать все это рабочим примером, но я боюсь, что это просто сделает еще один «конкретный вопрос процесса с конкретным ответом процесса», как все те, что я нашел ранее и не помог мне.
Некоторые связанные вопросы по SO:
источник
Ответы:
Прежде всего, вы не должны использовать,
accept-process-output
если вы хотите асинхронную обработку. Emacs будет принимать вывод каждый раз, когда ожидает ввода пользователя.Правильный путь - использовать функции фильтра для перехвата вывода. Вам не нужно создавать или удалять фильтры в зависимости от того, есть ли еще строки для отправки. Скорее, вы будете обычно объявлять один фильтр на время жизни процесса и использовать локальные переменные буфера для отслеживания состояния и выполнения различных действий по мере необходимости.
Низкоуровневый интерфейс
Функции фильтра - это то, что вы ищете. Функции фильтра предназначены для вывода того, что часовые являются для завершения.
Посмотрите на руководство или на многих примерах , которые приходят с Emacs (
grep
дляprocess-filter
в.el
файлах).Зарегистрируйте свою функцию фильтра с
Интерфейс Comint
Comint определяет функцию фильтра, которая делает несколько вещей:
comint-preoutput-filter-functions
, передав им новый текст в качестве аргумента.comint-prompt-regexp
.comint-output-filter-functions
, передав им новый текст в качестве аргумента.Учитывая, что ваш режим основан на comint, вы должны зарегистрировать свой фильтр в
comint-output-filter-functions
. Вы должны установить,comint-prompt-regexp
чтобы соответствовать вашей подсказке. Я не думаю, что в Comint есть встроенное средство для обнаружения полного выходного блока (то есть между двумя запросами), но это может помочь. Маркерcomint-last-input-end
устанавливается в конце последнего входного блока. У вас есть новый выходной блок, когда конец последнего приглашения послеcomint-last-input-end
. Как найти конец последнего приглашения зависит от версии Emacs:comint-last-prompt-overlay
охватывает последнюю подсказку.comint-last-prompt
содержит маркеры для начала и конца последнего приглашения.Вы можете добавить защиту в случае, если процесс выдает выходные данные в последовательности, отличной от {получать входные данные, выдавать выходные данные, отображать приглашение}.
источник