Tout d'abord, un avertissement. J'ai fait des recherches à ce sujet plusieurs fois, et je suis presque sûr d'avoir déjà trouvé la réponse d'une manière ou d'une autre, mais je ne la comprends tout simplement pas.
Mon problème est le suivant:
- J'ai un processus en cours d'exécution via comint
- Je veux envoyer une ligne d'entrée, capturer la sortie et voir quand elle est terminée (lorsque la dernière ligne de la sortie correspond à l'expression rationnelle pour une invite)
- seulement lorsque le processus a fini d'envoyer la sortie, je veux envoyer une autre ligne d'entrée (par exemple).
Pour un peu de contexte, pensez à un mode majeur implémentant l'interaction avec un programme, qui peut retourner une quantité arbitraire de sortie, dans un temps arbitrairement long. Cela ne devrait pas être une situation inhabituelle, non? D'accord, la partie où je dois attendre entre les entrées est peut-être inhabituelle, mais elle présente quelques avantages par rapport à l'envoi de l'entrée dans son ensemble:
- le tampon de sortie est bien formaté: entrée sortie entrée sortie ...
- plus important encore, lorsque vous envoyez beaucoup de texte à un processus, le texte est coupé en morceaux et les morceaux sont collés par le processus; les points de coupure sont arbitraires et cela rend parfois une entrée invalide (mon processus ne recollera pas correctement une entrée coupée au milieu d'un identifiant, par exemple)
Quoi qu'il en soit, inhabituelle ou non, il se trouve qu'il est compliqué. En ce moment, j'utilise quelque chose comme
(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))))
et j'appelle cela à chaque fois après avoir envoyé une ligne d'entrée, et avant d'envoyer la suivante. Eh bien ... ça marche, c'est déjà quelque chose.
Mais cela fait aussi bloquer emacs en attendant la sortie. La raison est évidente, et j'ai pensé que si j'incluais une sorte d'asynchrone sleep-for
(par exemple 1s) dans la boucle, cela retarderait la sortie de 1s, mais supprimerait le blocage. Sauf qu'il semble que ce type d'asynchrone
sleep-for
n'existe pas .
Ou alors? Plus généralement, existe-t-il un moyen idiomatique d'y parvenir avec emacs? En d'autres termes:
Comment envoyer une entrée à un processus, attendre la sortie, puis envoyer plus d'entrée, de manière asynchrone?
En cherchant autour (voir les questions connexes), j'ai principalement vu des mentions de sentinelles (mais je ne pense pas que cela s'applique dans mon cas, car le processus ne se termine pas) et de quelques crochets de comint (mais alors quoi? Devrais-je rendre le crochet tampon local, transformer mon "évaluer les lignes restantes" en une fonction, ajouter cette fonction au crochet et nettoyer le crochet par la suite? cela semble vraiment sale, n'est-ce pas?).
Je suis désolé si je ne suis pas clair, ou s'il y a vraiment une réponse évidente disponible quelque part, je suis vraiment confus par toutes les subtilités de l'interaction des processus.
Si nécessaire, je peux faire de tout cela un exemple de travail, mais je crains que cela ne fasse qu'une "question de processus spécifique avec une réponse de processus spécifique" comme toutes celles que j'ai trouvées plus tôt et ne m'a pas aidé.
Quelques questions connexes sur SO:
la source
Réponses:
Tout d'abord, vous ne devriez pas utiliser
accept-process-output
si vous souhaitez un traitement asynchrone. Emacs acceptera la sortie à chaque fois qu'il attend une entrée utilisateur.La bonne façon de procéder consiste à utiliser les fonctions de filtrage pour intercepter la sortie. Vous n'avez pas besoin de créer ou de supprimer les filtres selon que vous avez encore des lignes à envoyer. Au lieu de cela, vous déclarerez généralement un seul filtre pour la durée de vie du processus et utiliserez des variables locales de tampon pour suivre l'état et faire différentes choses selon les besoins.
L'interface de bas niveau
Les fonctions de filtrage sont ce que vous recherchez. Les fonctions de filtrage servent à afficher ce que sont les sentinelles jusqu'à la fin.
Regardez le manuel ou les nombreux exemples fournis avec Emacs (
grep
pourprocess-filter
dans les.el
fichiers).Enregistrez votre fonction de filtrage avec
L'interface comint
Comint définit une fonction de filtre qui fait plusieurs choses:
comint-preoutput-filter-functions
en leur passant le nouveau texte comme argument.comint-prompt-regexp
.comint-output-filter-functions
en leur passant le nouveau texte comme argument.Étant donné que votre mode est basé sur comint, vous devez enregistrer votre filtre dans
comint-output-filter-functions
. Vous devez définircomint-prompt-regexp
pour correspondre à votre invite. Je ne pense pas que Comint dispose d'une fonction intégrée pour détecter un morceau de sortie complet (c'est-à-dire entre deux invites), mais cela peut aider. Le marqueurcomint-last-input-end
est placé à la fin du dernier bloc d'entrée. Vous avez un nouveau bloc de sortie lorsque la fin de la dernière invite est aprèscomint-last-input-end
. Comment trouver la fin de la dernière invite dépend de la version d'Emacs:comint-last-prompt-overlay
s'étend sur la dernière invite.comint-last-prompt
contient des marqueurs au début et à la fin de la dernière invite.Vous souhaiterez peut-être ajouter des protections au cas où le processus émet une sortie dans une séquence autre que {recevoir une entrée, émettre une sortie, afficher une invite}.
la source