Zunächst ein Haftungsausschluss. Ich habe dies oft recherchiert und bin mir ziemlich sicher, dass ich die Antwort auf die eine oder andere Weise schon gefunden habe, aber ich verstehe es einfach nicht.
Mein Problem ist folgendes:
- Ich habe einen Prozess, der durch comint läuft
- Ich möchte eine Eingabezeile senden, die Ausgabe erfassen und sehen, wann sie beendet ist (wenn die letzte Zeile der Ausgabe mit dem regulären Ausdruck für eine Eingabeaufforderung übereinstimmt).
- Erst wenn der Vorgang das Senden der Ausgabe beendet hat, möchte ich eine weitere Eingabezeile senden (zum Beispiel).
Stellen Sie sich für ein wenig Hintergrund einen Hauptmodus vor, der die Interaktion mit einem Programm implementiert, das in beliebig langer Zeit eine beliebige Menge an Ausgabe zurückgeben kann. Dies sollte keine ungewöhnliche Situation sein, oder? Okay, vielleicht ist der Teil, in dem ich zwischen den Eingaben warten muss, ungewöhnlich, aber es hat ein paar Vorteile gegenüber dem Senden der Eingabe als Ganzes:
- Der Ausgabepuffer ist schön formatiert: Eingabe Ausgabe Eingabe Ausgabe ...
- Noch wichtiger ist, dass beim Senden einer großen Menge Text an einen Prozess der Text in Teile geschnitten und die Teile durch den Prozess wieder eingefügt werden. Die Schnittpunkte sind willkürlich und dies führt manchmal zu ungültigen Eingaben (mein Prozess fügt beispielsweise einen Eingabeschnitt in der Mitte eines Bezeichners nicht korrekt ein).
Wie auch immer, ungewöhnliche oder nicht, es stellt sich heraus , dass es ist kompliziert. Im Moment benutze ich etwas in der Art von
(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))))
und ich rufe dies jedes Mal nach dem Senden einer Eingabezeile und vor dem Senden der nächsten Zeile auf. Nun ... es funktioniert, das ist schon etwas.
Aber es lässt auch Emacs hängen, während sie auf die Ausgabe warten. Der Grund liegt auf der Hand, und ich stellte mir vor, dass, wenn ich eine Art asynchroner sleep-for
Schleife (z. B. 1s) einbinde, die Ausgabe um 1s verzögert wird, das Hängen jedoch unterdrückt wird. Abgesehen davon, dass es diese Art von Asynchronität anscheinend
sleep-for
nicht gibt .
Oder doch? Gibt es einen idiomatischen Weg, dies mit Emacs zu erreichen? Mit anderen Worten:
Wie kann ich Eingaben an einen Prozess senden, auf die Ausgabe warten und dann asynchron weitere Eingaben senden?
Beim Durchsuchen (siehe die zugehörigen Fragen) habe ich hauptsächlich Erwähnungen von Sentinels (aber ich glaube nicht, dass dies in meinem Fall zutrifft, da der Prozess nicht abgeschlossen ist) und von einigen Comint-Hooks (aber was soll ich dann tun?) Gesehen mache den Hook buffer-local, verwandle meine "restlichen Zeilen auswerten" in eine Funktion, füge diese Funktion dem Hook hinzu und reinige den Hook danach - das klingt wirklich schmutzig, oder?).
Es tut mir leid, wenn ich mich nicht klar mache oder wenn irgendwo wirklich eine offensichtliche Antwort verfügbar ist, bin ich wirklich verwirrt über all die Komplikationen der Prozessinteraktion.
Bei Bedarf kann ich das alles zu einem funktionierenden Beispiel machen, aber ich befürchte, dass es nur eine weitere "spezifische Prozessfrage mit spezifischer Prozessantwort" geben wird, wie all jene, die ich zuvor gefunden habe und die mir nicht geholfen haben.
Einige verwandte Fragen zu SO:
quelle
Antworten:
Zunächst sollten Sie nicht verwenden,
accept-process-output
wenn Sie eine asynchrone Verarbeitung wünschen. Emacs akzeptiert die Ausgabe jedes Mal, wenn es auf Benutzereingaben wartet.Der richtige Weg besteht darin, Filterfunktionen zum Abfangen der Ausgabe zu verwenden. Sie müssen die Filter nicht erstellen oder löschen, je nachdem, ob Sie noch zu sendende Zeilen haben. Stattdessen deklarieren Sie normalerweise einen einzelnen Filter für die Lebensdauer des Prozesses und verwenden pufferlokale Variablen, um den Status zu verfolgen und je nach Bedarf verschiedene Aktionen auszuführen.
Die Low-Level-Schnittstelle
Filterfunktionen sind genau das, wonach Sie suchen. Filterfunktionen sollen ausgeben, was Sentinels zur Terminierung sind.
Schauen Sie sich das Handbuch oder an vielen Beispielen , die mit Emacs kommen (
grep
fürprocess-filter
in den.el
Dateien).Registrieren Sie Ihre Filterfunktion mit
Die Comint-Schnittstelle
Comint definiert eine Filterfunktion, die einige Dinge tut:
comint-preoutput-filter-functions
und übergeben Sie ihnen den neuen Text als Argument.comint-prompt-regexp
.comint-output-filter-functions
und übergeben Sie ihnen den neuen Text als Argument.Da Ihr Modus auf comint basiert, sollten Sie Ihren Filter in registrieren
comint-output-filter-functions
. Sie sollten festlegencomint-prompt-regexp
, dass sie Ihrer Eingabeaufforderung entsprechen. Ich glaube nicht, dass Comint über eine integrierte Funktion verfügt, mit der ein vollständiger Ausgabeabschnitt (dh zwischen zwei Eingabeaufforderungen) erkannt werden kann, aber dies kann Abhilfe schaffen. Der Markercomint-last-input-end
wird am Ende des letzten Eingabeabschnitts gesetzt. Sie haben einen neuen Ausgabeabschnitt, wenn das Ende der letzten Eingabeaufforderung nach liegtcomint-last-input-end
. Wie Sie das Ende der letzten Eingabeaufforderung finden, hängt von der Emacs-Version ab:comint-last-prompt-overlay
über die letzte Eingabeaufforderung.comint-last-prompt
enthält die Variable Markierungen am Anfang und Ende der letzten Eingabeaufforderung.Sie können Schutzmechanismen hinzufügen, wenn der Prozess die Ausgabe in einer anderen Reihenfolge als {Eingaben empfangen, Ausgaben ausgeben, Eingabeaufforderung anzeigen} ausgibt.
quelle