En primer lugar, un descargo de responsabilidad. He investigado esto muchas veces, y estoy bastante seguro de que ya he encontrado la respuesta de una forma u otra, pero simplemente no la entiendo.
Mi problema es el siguiente:
- Tengo un proceso que se ejecuta a través de comint
- Quiero enviar una línea de entrada, capturar salida y ver cuándo termina (cuando la última línea de la salida coincide con la expresión regular para una solicitud)
- solo cuando el proceso haya terminado de enviar la salida, quiero enviar otra línea de entrada (por ejemplo).
Para un poco de antecedentes, piense en un modo principal que implementa la interacción con un programa, que puede devolver una cantidad arbitraria de salida, en un tiempo arbitrariamente largo. Esto no debería ser una situación inusual, ¿verdad? De acuerdo, tal vez la parte donde necesito esperar entre entradas es inusual, pero tiene algunas ventajas sobre el envío de la entrada en su conjunto:
- el búfer de salida está bien formateado: entrada salida entrada salida ...
- lo que es más importante, cuando se envía mucho texto a un proceso, el texto se corta en pedazos y el proceso los vuelve a pegar; los puntos de corte son arbitrarios y esto a veces hace una entrada no válida (mi proceso no pegará correctamente un corte de entrada en el medio de un identificador, por ejemplo)
De todos modos, inusual o no, resulta que es complicado. En este momento, estoy usando algo en la línea 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))))
y lo llamo cada vez que envío una línea de entrada y antes de enviar la siguiente. Bueno ... funciona, eso ya es algo.
Pero también hace que emacs se cuelgue mientras espera la salida. La razón es obvia, y pensé que si incluía algún tipo de asíncrono sleep-for
(por ejemplo, 1s) en el bucle, retrasaría la salida en 1s, pero suprimiría el bloqueo. Excepto que parece que este tipo de asíncrono
sleep-for
no existe .
O lo hace? En términos más generales, ¿hay una forma idiomática de lograr esto con emacs? En otras palabras:
¿Cómo enviar entradas a un proceso, esperar la salida y luego enviar más entradas de forma asincrónica?
Al buscar alrededor (ver las preguntas relacionadas), principalmente he visto menciones de centinelas (pero no creo que se aplique en mi caso, ya que el proceso no termina) y de algunos ganchos de comint (pero ¿qué? haga que el gancho sea localmente búfer, convierta mi "evaluar las líneas restantes" en una función, agregue esta función al gancho y limpie el gancho después (eso suena muy sucio, ¿no?).
Lo siento si no me estoy aclarando, o si realmente hay una respuesta obvia disponible en alguna parte, estoy realmente confundido por todas las complejidades de la interacción del proceso.
Si es necesario, puedo hacer de todo esto un ejemplo de trabajo, pero me temo que solo haría una "pregunta de proceso específica con una respuesta de proceso específica" como todas las que encontré anteriormente y que no me ayudaron.
Algunas preguntas relacionadas sobre SO:
fuente
Respuestas:
En primer lugar, no debe usarlo
accept-process-output
si desea un procesamiento asincrónico. Emacs aceptará la salida cada vez que esté esperando la entrada del usuario.La forma correcta de hacerlo es usar funciones de filtro para interceptar la salida. No necesita crear o eliminar los filtros dependiendo de si todavía tiene líneas para enviar. Por el contrario, generalmente declarará un solo filtro durante la vida útil del proceso y utilizará variables locales de búfer para rastrear el estado y hacer diferentes cosas según sea necesario.
La interfaz de bajo nivel.
Las funciones de filtro son lo que estás buscando. Las funciones de filtro son la salida de lo que los centinelas son a la terminación.
Mira el manual o en muchos ejemplos que vienen con Emacs (
grep
deprocess-filter
los.el
archivos).Registre su función de filtro con
La interfaz de comint
Comint define una función de filtro que hace algunas cosas:
comint-preoutput-filter-functions
, pasándoles el nuevo texto como argumento.comint-prompt-regexp
.comint-output-filter-functions
, pasándoles el nuevo texto como argumento.Dado que su modo se basa en comint, debe registrar su filtro
comint-output-filter-functions
. Debe configurarcomint-prompt-regexp
para que coincida con su solicitud. No creo que Comint tenga una función incorporada para detectar un fragmento de salida completo (es decir, entre dos avisos), pero puede ayudar. El marcadorcomint-last-input-end
se establece al final del último fragmento de entrada. Tiene un nuevo fragmento de salida cuando finaliza la última solicitudcomint-last-input-end
. Cómo encontrar el final de la última solicitud depende de la versión de Emacs:comint-last-prompt-overlay
abarca la última solicitud.comint-last-prompt
contiene marcadores al inicio y al final de la última solicitud.Es posible que desee agregar protecciones en caso de que el proceso emita resultados en una secuencia que no sea {recibir entrada, emitir salida, mostrar mensaje}.
fuente