Primeiro de tudo, um aviso. Pesquisei isso muitas vezes e tenho certeza de que já encontrei a resposta de uma maneira ou de outra, mas simplesmente não a entendo.
Meu problema é o seguinte:
- Eu tenho um processo executando comint
- Quero enviar uma linha de entrada, capturar saída e ver quando ela termina (quando a última linha da saída corresponde ao regexp para um prompt)
- somente quando o processo terminar de enviar a saída, desejo enviar outra linha de entrada (por exemplo).
Para um pouco de segundo plano, pense em um modo principal implementando a interação com um programa, que pode retornar uma quantidade arbitrária de saída, em um tempo arbitrariamente longo. Esta não deve ser uma situação incomum, certo? Ok, talvez a parte em que eu preciso esperar entre as entradas seja incomum, mas ela tenha algumas vantagens sobre o envio da entrada como um todo:
- o buffer de saída está bem formatado: output input input output ...
- mais importante, ao enviar muito texto para um processo, o texto é cortado em pedaços e os pedaços são colados de volta pelo processo; os pontos de corte são arbitrários e isso às vezes gera uma entrada inválida (meu processo não colará corretamente uma entrada no meio de um identificador, por exemplo)
De qualquer forma, incomum ou não, verifica-se que é complicado. No momento, estou usando algo ao longo das linhas de
(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))))
e eu ligo para isso toda vez depois de enviar uma linha de entrada e antes de enviar a próxima. Bem ... funciona, isso já é alguma coisa.
Mas também faz o emacs travar enquanto aguarda a saída. O motivo é óbvio e achei que, se eu incluísse algum tipo de assíncrono sleep-for
(por exemplo, 1s) no loop, isso atrasaria a saída em 1s, mas suprimiria a interrupção. Exceto que parece que esse tipo de assíncrono
sleep-for
não existe .
Ou faz? De maneira mais geral, existe uma maneira idiomática de conseguir isso com o emacs? Em outras palavras:
Como enviar entrada para um processo, aguardar a saída e enviar mais entradas de forma assíncrona?
Ao pesquisar ao redor (consulte as perguntas relacionadas), vi principalmente menções de sentinelas (mas não acho que isso se aplique no meu caso, já que o processo não termina) e de alguns ganchos comuns (mas então? torne o local do buffer do gancho, transforme em "avaliar as linhas restantes" em uma função, adicione essa função ao gancho e limpe o gancho depois - isso soa muito sujo, não é?).
Sinto muito se não estou me esclarecendo, ou se há realmente uma resposta óbvia disponível em algum lugar, estou realmente confuso com todas as complexidades da interação do processo.
Se necessário, posso fazer disso tudo um exemplo de trabalho, mas tenho medo de que isso faça apenas mais uma "pergunta de processo específica com resposta de processo específica", como todas as que encontrei anteriormente e não me ajudaram.
Algumas perguntas relacionadas ao SO:
fonte
Respostas:
Primeiro de tudo, você não deve usá-
accept-process-output
lo se desejar processamento assíncrono. O Emacs aceita saída sempre que estiver aguardando a entrada do usuário.O caminho certo a seguir é usar as funções de filtro para interceptar a saída. Você não precisa criar ou excluir o (s) filtro (s), dependendo se você ainda tem linhas para enviar. Em vez disso, você geralmente declara um único filtro para a vida útil do processo e usa variáveis locais de buffer para rastrear o estado e fazer coisas diferentes conforme necessário.
A interface de baixo nível
As funções de filtro são o que você está procurando. As funções de filtro são para mostrar o que são os sentinelas para finalização.
Olhe para o manual ou em muitos exemplos que vêm com o Emacs (
grep
paraprocess-filter
nos.el
arquivos).Registre sua função de filtro com
A interface do comint
Comint define uma função de filtro que faz algumas coisas:
comint-preoutput-filter-functions
, passando o novo texto como argumento.comint-prompt-regexp
.comint-output-filter-functions
, passando o novo texto como argumento.Como seu modo é baseado em comint, você deve registrar seu filtro
comint-output-filter-functions
. Você deve definircomint-prompt-regexp
para corresponder ao seu prompt. Não acho que o Comint tenha um recurso interno para detectar um pedaço de saída completo (ou seja, entre dois prompts), mas pode ajudar. O marcadorcomint-last-input-end
é definido no final do último pedaço de entrada. Você tem um novo bloco de saída quando o final do último prompt é posteriorcomint-last-input-end
. Como encontrar o final do último prompt depende da versão do Emacs:comint-last-prompt-overlay
abrange o último prompt.comint-last-prompt
contém marcadores para o início e o fim do último prompt.Você pode adicionar proteções caso o processo emita uma saída em uma sequência diferente de {receber entrada, emitir saída, exibir prompt}.
fonte