Không đồng bộ chờ đầu ra từ một quy trình comint


12

Trước hết, từ chối trách nhiệm. Tôi đã nghiên cứu điều này nhiều lần và tôi khá chắc chắn rằng tôi đã tìm thấy câu trả lời bằng cách này hay cách khác, nhưng tôi không hiểu nó.

Vấn đề của tôi là như sau:

  • Tôi có một quá trình chạy qua comint
  • Tôi muốn gửi một dòng đầu vào, bắt đầu ra và xem khi nào nó kết thúc (khi dòng cuối cùng của đầu ra khớp với biểu thức chính quy cho một dấu nhắc)
  • chỉ khi quá trình gửi xong đầu ra, tôi mới muốn gửi một dòng đầu vào khác (ví dụ).

Đối với một chút nền tảng, hãy nghĩ đến một chế độ chính thực hiện tương tác với một chương trình, có thể trả về một lượng đầu ra tùy ý, trong thời gian dài tùy ý. Đây không phải là một tình huống bất thường, phải không? Được rồi, có thể phần tôi cần đợi giữa các đầu vào là không bình thường, nhưng nó có một vài lợi thế so với việc gửi toàn bộ đầu vào:

  • bộ đệm đầu ra được định dạng độc đáo: đầu vào đầu vào đầu ra ...
  • quan trọng hơn, khi gửi nhiều văn bản đến một quy trình, văn bản được cắt thành từng mảnh và các phần được dán lại theo quy trình; các điểm cắt là tùy ý và điều này đôi khi làm cho đầu vào không hợp lệ (ví dụ quy trình của tôi sẽ không dán lại chính xác một lần cắt đầu vào ở giữa một định danh, chẳng hạn)

Dù sao, bất thường hay không, nó chỉ ra rằng nó phức tạp. Ngay bây giờ, tôi đang sử dụng một cái gì đó dọc theo dòng

(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))))

và tôi gọi nó mỗi lần sau khi gửi một dòng đầu vào, và trước khi gửi dòng tiếp theo. Chà ... nó hoạt động, đó đã là một cái gì đó.

Nhưng nó cũng làm cho emacs bị treo trong khi chờ đầu ra. Lý do rất rõ ràng và tôi đoán rằng nếu tôi đưa một số loại không đồng bộ sleep-for(ví dụ 1s) vào vòng lặp thì nó sẽ trì hoãn đầu ra thêm 1 giây, nhưng triệt tiêu việc treo. Ngoại trừ việc có vẻ như loại không đồng bộ sleep-for này không tồn tại .

Hay không? Tổng quát hơn, có một cách thành ngữ để đạt được điều này với emacs? Nói cách khác:

Làm thế nào để gửi đầu vào cho một quá trình, chờ đầu ra, sau đó gửi thêm đầu vào, không đồng bộ?

Khi tìm kiếm xung quanh (xem các câu hỏi liên quan), tôi chủ yếu thấy đề cập đến các câu thần chú (nhưng tôi không nghĩ rằng nó áp dụng trong trường hợp của tôi, vì quá trình không kết thúc) và một số móc nối (nhưng sau đó thì sao? làm cho hook hook-local, biến "đánh giá các dòng còn lại" của tôi thành một hàm, thêm chức năng này vào hook và làm sạch hook sau đó? nghe có vẻ rất bẩn phải không?).

Tôi xin lỗi nếu tôi không làm cho mình rõ ràng, hoặc nếu thực sự có một câu trả lời rõ ràng có sẵn ở đâu đó, tôi thực sự bối rối bởi tất cả những rắc rối của quá trình tương tác.

Nếu cần, tôi có thể biến tất cả thành một ví dụ hoạt động, nhưng tôi sợ rằng nó sẽ chỉ tạo thêm một "câu hỏi quy trình cụ thể với câu trả lời quy trình cụ thể" như tất cả những gì tôi tìm thấy trước đó và không giúp tôi.

Một số câu hỏi liên quan về SO:


@nicael Có gì sai về các liên kết liên quan?
T. Verron

Nhưng tại sao bạn cần bao gồm chúng?
nicael

1
Chà, tôi đã thấy chúng là những câu hỏi liên quan, ngay cả khi câu trả lời không giúp tôi. Nếu ai đó muốn giúp tôi, có lẽ họ sẽ có kiến ​​thức sâu hơn về vấn đề mà tôi có, nhưng có lẽ họ vẫn sẽ cần phải thực hiện một nghiên cứu trước đó. Trong trường hợp này, các câu hỏi cung cấp cho họ một số điểm bắt đầu. Ngoài ra, nếu một ngày nào đó có người nào đó truy cập trang này nhưng với một vấn đề giống với những vấn đề tôi liên kết, họ sẽ có một lối tắt cho câu hỏi thích hợp.
T. Verron

@nicael (Quên ping trong bài đăng đầu tiên, xin lỗi) Có phải vấn đề là các liên kết không phải từ mx.sx?
T. Verron

Đồng ý. Bạn có thể quay lại bản sửa đổi, bài đăng của bạn.
nicael

Câu trả lời:


19

Trước hết, bạn không nên sử dụng accept-process-outputnếu bạn muốn xử lý không đồng bộ. Emacs sẽ chấp nhận đầu ra mỗi khi nó chờ người dùng nhập.

Cách thích hợp để sử dụng là sử dụng các chức năng lọc để chặn đầu ra. Bạn không cần tạo hoặc xóa (các) bộ lọc tùy thuộc vào việc bạn vẫn có dòng để gửi hay không. Thay vào đó, bạn thường sẽ khai báo một bộ lọc duy nhất trong suốt vòng đời của quá trình và sử dụng các biến cục bộ đệm để theo dõi trạng thái và thực hiện các thao tác khác nhau khi cần.

Giao diện cấp thấp

Chức năng lọc là những gì bạn đang tìm kiếm. Các chức năng của bộ lọc là để xuất ra những gì được gửi đến chấm dứt.

(defun mymode--output-filter (process string)
  (let ((buffer (process-buffer process)))
    (when (buffer-live-p buffer)
      (with-current-buffer buffer
        (goto-char (point-max))
        (forward-line 0)
        (when (mymode-looking-at-prompt)
          (do-something)
          (goto-char (point-max)))))))

Nhìn vào hướng dẫn hoặc ít nhiều ví dụ đi kèm với Emacs ( grepcho process-filtertrong .eltập tin).

Đăng ký chức năng lọc của bạn với

(set-process-filter 'mymode--output-filter)

Giao diện comint

Comint định nghĩa một chức năng lọc thực hiện một số điều:

  • Chuyển sang bộ đệm nên chứa đầu ra quá trình.
  • Chạy các hàm trong danh sách comint-preoutput-filter-functions, chuyển cho chúng văn bản mới làm đối số.
  • Thực hiện một số loại bỏ nhắc nhở trùng lặp ad hoc, dựa trên comint-prompt-regexp.
  • Chèn đầu ra của quá trình vào cuối bộ đệm
  • Chạy các hàm trong danh sách comint-output-filter-functions, chuyển cho chúng văn bản mới làm đối số.

Cho rằng chế độ của bạn dựa trên comint, bạn nên đăng ký bộ lọc của mình comint-output-filter-functions. Bạn nên thiết lập comint-prompt-regexpđể phù hợp với lời nhắc của bạn. Tôi không nghĩ Comint có một cơ sở tích hợp để phát hiện một đoạn đầu ra hoàn chỉnh (nghĩa là giữa hai lời nhắc), nhưng nó có thể giúp ích. Điểm đánh dấu comint-last-input-endđược đặt ở cuối đoạn đầu vào cuối cùng. Bạn có một đoạn đầu ra mới khi kết thúc dấu nhắc cuối cùng là sau comint-last-input-end. Cách tìm phần cuối của dấu nhắc cuối tùy thuộc vào phiên bản của Emacs:

  • Lên đến 24.3, lớp phủ comint-last-prompt-overlaykéo dài lời nhắc cuối cùng.
  • Kể từ ngày 24.4, biến comint-last-promptchứa các điểm đánh dấu đến điểm bắt đầu và kết thúc của dấu nhắc cuối cùng.
(defun mymode--comint-output-filter (string)
  (let ((start (marker-position comint-last-input-end))
        (end (if (boundp 'comint-last-prompt-overlay)
                 (and comint-last-prompt-overlay (overlay-start comint-last-prompt-overlay))
               (and comint-last-prompt (cdr comint-last-prompt))))
  (when (and start end (< start end))
    (let ((new-output-chunk (buffer-substring-no-properties start end)))
      ...)))

Bạn có thể muốn thêm các biện pháp bảo vệ trong trường hợp quá trình phát ra đầu ra theo một chuỗi khác với {nhận đầu vào, đầu ra phát ra, nhắc nhở hiển thị}.


Lớp phủ comint-last-prompt-overlay dường như không được xác định trong Emacs 25 trong comint.el. Có phải từ một nơi khác?
John Kitchin

@JohnKitchin Một phần của comint đã thay đổi vào ngày 24.4, tôi đã viết câu trả lời của mình cho ngày 24.3. Tôi đã thêm một phương pháp sau 24.4.
Gilles 'SO- ngừng trở nên xấu xa'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.