Przede wszystkim wyłączenie odpowiedzialności. Badałem to wiele razy i jestem całkiem pewien, że już znalazłem odpowiedź w ten czy inny sposób, ale po prostu jej nie rozumiem.
Mój problem jest następujący:
- Mam proces przebiegający przez komendę
- Chcę wysłać wiersz danych wejściowych, przechwycić dane wyjściowe i zobaczyć, kiedy się skończy (kiedy ostatni wiersz danych wyjściowych pasuje do wyrażenia regularnego w odpowiedzi na monit)
- dopiero gdy proces zakończy wysyłanie danych wyjściowych, chcę wysłać kolejną linię danych wejściowych (na przykład).
Dla odrobiny tła, pomyśl o głównym trybie implementującym interakcję z programem, który może zwrócić dowolną ilość danych wyjściowych w dowolnie długim czasie. To nie powinna być niezwykła sytuacja, prawda? Okej, może ta część, w której muszę czekać między danymi wejściowymi, jest niezwykła, ale ma kilka zalet w porównaniu z wysyłaniem danych wejściowych jako całości:
- bufor wyjściowy jest ładnie sformatowany: dane wejściowe dane wyjściowe dane wyjściowe ...
- co ważniejsze, podczas wysyłania dużej ilości tekstu do procesu, tekst jest cięty na kawałki i kawałki są wklejane z powrotem przez proces; punkty cięcia są dowolne, co czasami powoduje nieprawidłowe wprowadzanie danych (na przykład mój proces nie wkleja poprawnie wycięcia wejściowego w środku identyfikatora)
W każdym razie, niezwykłe czy nie, okazuje się, że jest skomplikowane. W tej chwili używam czegoś w stylu
(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))))
i wywołuję to za każdym razem po wysłaniu linii wejściowej, a przed wysłaniem następnej. Cóż ... to działa, to już coś.
Ale powoduje również zawieszenie się emacsa podczas oczekiwania na wynik. Powód jest oczywisty i doszedłem do wniosku, że jeśli włączyłem jakiś rodzaj asynchroniczny sleep-for
(na przykład 1s) do pętli, opóźniłoby to wyjście o 1s, ale tłumiłoby zawieszanie. Tyle że wydaje się, że tego rodzaju asynchroniczny
sleep-for
nie istnieje .
A może to? Mówiąc bardziej ogólnie, czy istnieje idiomatyczny sposób na osiągnięcie tego za pomocą emacsa? Innymi słowy:
Jak wysłać dane wejściowe do procesu, poczekać na dane wyjściowe, a następnie wysłać asynchronicznie więcej danych wejściowych?
Podczas przeszukiwania (patrz powiązane pytania) widziałem głównie wzmianki o wartownikach (ale nie sądzę, żeby miało to zastosowanie w moim przypadku, ponieważ proces się nie kończy) i niektórych haczyków (ale co z tego? ustaw bufor bufora na lokalny, zamień moją „ocenę pozostałych linii” na funkcję, dodaj tę funkcję do haka i wyczyść hak później? to brzmi naprawdę brudno, prawda?).
Przykro mi, jeśli nie wyrażam się jasno lub jeśli gdzieś istnieje naprawdę oczywista odpowiedź, jestem naprawdę zdezorientowany wszystkimi zawiłościami interakcji procesu.
W razie potrzeby mogę uczynić z tego cały działający przykład, ale obawiam się, że stworzy to jeszcze jedno „konkretne pytanie procesowe z konkretną odpowiedzią procesową”, jak wszystkie te, które znalazłem wcześniej i nie pomogło mi.
Niektóre powiązane pytania dotyczące SO:
źródło
Odpowiedzi:
Przede wszystkim nie powinieneś używać,
accept-process-output
jeśli chcesz przetwarzać asynchronicznie. Emacs zaakceptuje dane wyjściowe za każdym razem, gdy czeka na dane wejściowe użytkownika.Właściwym sposobem jest użycie funkcji filtru do przechwycenia wyniku. Nie musisz tworzyć ani usuwać filtrów w zależności od tego, czy nadal masz linie do wysłania. Zamiast tego zwykle deklarujesz pojedynczy filtr na cały czas trwania procesu i używasz zmiennych lokalnych bufora do śledzenia stanu i robienia różnych rzeczy w razie potrzeby.
Interfejs niskiego poziomu
Funkcje filtrów są tym, czego szukasz. Funkcje filtrowania mają wyprowadzać sygnały, które mają zostać zakończone.
Spójrz na ręczne lub na wielu przykładach, które pochodzą z Emacs (
grep
dlaprocess-filter
w.el
plikach).Zarejestruj swoją funkcję filtrowania za pomocą
Interfejs komend
Comint definiuje funkcję filtrującą, która wykonuje kilka czynności:
comint-preoutput-filter-functions
, przekazując im nowy tekst jako argument.comint-prompt-regexp
.comint-output-filter-functions
, przekazując im nowy tekst jako argument.Biorąc pod uwagę, że twój tryb oparty jest na komendzie, powinieneś zarejestrować swój filtr w
comint-output-filter-functions
. Powinieneś ustawić,comint-prompt-regexp
aby dopasować monit. Nie sądzę, aby Comint miał wbudowane narzędzie do wykrywania pełnego fragmentu wyjściowego (tj. Między dwoma monitami), ale może to pomóc. Znacznikcomint-last-input-end
jest ustawiony na końcu ostatniej porcji wejściowej. Nowa porcja danych wyjściowych jest dostępna po zakończeniu ostatniego monitucomint-last-input-end
. Jak znaleźć koniec ostatniego monitu, zależy od wersji Emacsa:comint-last-prompt-overlay
obejmuje ostatni monit.comint-last-prompt
zawiera znaczniki na początku i na końcu ostatniego monitu.Możesz chcieć dodać zabezpieczenia na wypadek, gdyby proces emitował dane wyjściowe w sekwencji innej niż {odbierać dane wejściowe, emitować dane wyjściowe, wyświetlać monit}.
źródło