แบบอะซิงโครนัสรอผลลัพธ์จากกระบวนการ comint


12

ก่อนอื่นข้อจำกัดความรับผิดชอบ ฉันค้นคว้ามาหลายครั้งแล้วและฉันค่อนข้างมั่นใจว่าฉันได้พบคำตอบแล้วไม่ทางใดก็ทางหนึ่ง แต่ฉันก็ไม่เข้าใจ

ปัญหาของฉันคือต่อไปนี้:

  • ฉันมีกระบวนการทำงานผ่าน comint
  • ฉันต้องการส่งบรรทัดอินพุตจับเอาต์พุตและดูเมื่อมันจบ (เมื่อบรรทัดสุดท้ายของเอาต์พุตตรงกับ regexp สำหรับพรอมต์)
  • เมื่อกระบวนการส่งเอาต์พุตเสร็จสิ้นแล้วเท่านั้นฉันต้องการส่งอินพุตอีกบรรทัดหนึ่ง (ตัวอย่าง)

สำหรับพื้นหลังเล็กน้อยให้คิดว่าเป็นโหมดหลักที่ใช้การโต้ตอบกับโปรแกรมซึ่งอาจส่งคืนเอาต์พุตจำนวนเท่าใดก็ได้ในเวลานานโดยพลการ นี่ไม่ควรเป็นสถานการณ์ที่ผิดปกติใช่มั้ย โอเคบางทีส่วนที่ฉันต้องรอระหว่าง อินพุตนั้นผิดปกติ แต่ก็มีข้อดีเล็กน้อยในการส่งอินพุตโดยรวม:

  • บัฟเฟอร์เอาต์พุตถูกจัดรูปแบบเป็นอย่างดี: อินพุตเอาต์พุตอินพุตเอาต์พุต ...
  • ที่สำคัญกว่านั้นเมื่อส่งข้อความจำนวนมากไปยังกระบวนการข้อความจะถูกตัดเป็นชิ้น ๆ และชิ้นส่วนจะถูกวางกลับโดยกระบวนการ จุดตัดเป็นสิ่งที่ไม่แน่นอนและบางครั้งสิ่งนี้ทำให้อินพุตไม่ถูกต้อง (กระบวนการของฉันจะไม่วางกลับอย่างถูกต้องเช่นการป้อนข้อมูลที่อยู่ตรงกลางของตัวระบุ)

อย่างไรก็ตามผิดปกติหรือไม่ปรากฏว่ามีความซับซ้อน ตอนนี้ฉันกำลังใช้บางสิ่งบางอย่างตามแนวของ

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

และฉันเรียกสิ่งนี้ทุกครั้งหลังจากส่งสายอินพุตและก่อนที่จะส่งสายถัดไป ดี ... มันใช้งานได้แล้ว

แต่มันก็ทำให้ emacs ค้างในขณะที่รอเอาต์พุต เหตุผลชัดเจนและฉันคิดว่าถ้าฉันรวม asynchronous sleep-for(ตัวอย่างเช่น 1s) ในลูปมันจะหน่วงเวลาเอาต์พุตโดย 1s แต่ระงับการแขวน ยกเว้นว่ามันดูเหมือนว่าชนิดของการไม่ตรงกันนี้ ไม่ได้อยู่sleep-for

หรือไม่ โดยทั่วไปแล้วจะมีวิธีการสำนึกในเรื่องนี้ด้วย emacs หรือไม่? ในคำอื่น ๆ :

วิธีส่งอินพุตไปยังกระบวนการรอเอาต์พุตจากนั้นส่งอินพุตเพิ่มเติมแบบอะซิงโครนัส?

เมื่อค้นหาไปรอบ ๆ (ดูคำถามที่เกี่ยวข้อง) ฉันได้เห็นการกล่าวถึง sentinels เป็นส่วนใหญ่ (แต่ฉันไม่คิดว่ามันใช้ในกรณีของฉันเนื่องจากกระบวนการไม่เสร็จสิ้น) และมี comint hooks (แต่ถ้าเป็นเช่นนั้นฉันควรทำอย่างไร ทำให้ hook buffer-local เปลี่ยน "ประเมินบรรทัดที่เหลือ" ของฉันเป็นฟังก์ชั่นเพิ่มฟังก์ชั่นนี้ลงใน hook และทำความสะอาด hook หลังจากนั้น - ซึ่งฟังดูสกปรกจริงๆใช่ไหม?)

ฉันขอโทษถ้าฉันไม่ทำให้ตัวเองชัดเจนหรือถ้ามีคำตอบที่ชัดเจนอยู่ที่ไหนสักแห่งฉันก็สับสนด้วยความสลับซับซ้อนทั้งหมดของการมีส่วนร่วมในกระบวนการ

หากจำเป็นฉันสามารถทำให้เป็นตัวอย่างที่ใช้งานได้ทั้งหมด แต่ฉันกลัวว่ามันจะทำให้ "คำถามกระบวนการเฉพาะเจาะจงอีกหนึ่งคำตอบกับกระบวนการเฉพาะ" อีกครั้งเหมือนกับที่ฉันพบก่อนหน้านี้และไม่ได้ช่วยฉันเลย

บางคำถามที่เกี่ยวข้องกับ SO:


@nicael มีอะไรผิดปกติเกี่ยวกับลิงค์ที่เกี่ยวข้อง?
T. Verron

แต่ทำไมคุณต้องรวมพวกเขา
nicael

2
ฉันพบว่าพวกเขาเป็นคำถามที่เกี่ยวข้องแม้ว่าคำตอบจะไม่ช่วยฉันก็ตาม หากมีคนต้องการช่วยฉันคงสันนิษฐานว่าพวกเขาจะมีความรู้ในเรื่องที่ลึกซึ้งกว่าที่ฉันมี แต่บางทีพวกเขาอาจยังต้องทำการวิจัยล่วงหน้า ในกรณีนี้คำถามให้จุดเริ่มต้น นอกจากนี้ถ้าสักวันหนึ่งมีคนเข้าสู่หน้านี้ แต่มีปัญหามากกว่าคนที่ฉันเชื่อมโยงพวกเขาจะมีทางลัดไปยังคำถามที่เหมาะสม
T. Verron

@nicael (ลืมที่จะ ping ในโพสต์แรก, เสียใจด้วย) เป็นปัญหาที่ลิงก์ไม่ได้มาจาก mx.sx หรือไม่?
T. Verron

ตกลง. คุณสามารถย้อนกลับไปยังการแก้ไขของคุณโพสต์ของคุณ
nicael

คำตอบ:


19

ก่อนอื่นคุณไม่ควรใช้accept-process-outputหากคุณต้องการการประมวลผลแบบอะซิงโครนัส Emacs จะรับเอาท์พุททุกครั้งเมื่อมันกำลังรอการป้อนข้อมูลของผู้ใช้

วิธีที่เหมาะสมในการเดินทางคือการใช้ฟังก์ชั่นตัวกรองเพื่อดักจับผลลัพธ์ คุณไม่จำเป็นต้องสร้างหรือลบตัวกรองทั้งนี้ขึ้นอยู่กับว่าคุณยังมีสายที่จะส่ง แต่คุณมักจะประกาศตัวกรองเดียวสำหรับอายุการใช้งานของกระบวนการและใช้ตัวแปรบัฟเฟอร์ในเครื่องเพื่อติดตามสถานะและทำสิ่งต่าง ๆ ตามที่ต้องการ

อินเทอร์เฟซระดับต่ำ

ฟังก์ชันตัวกรองคือสิ่งที่คุณกำลังมองหา ฟังก์ชั่นตัวกรองคือการส่งออกสิ่งที่รักษาการณ์จะสิ้นสุด

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

ดูคู่มือการใช้งานหรือตัวอย่างมากมายที่มาพร้อมกับ Emacs ( grepสำหรับprocess-filterใน.elไฟล์)

ลงทะเบียนฟังก์ชั่นตัวกรองของคุณด้วย

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

อินเตอร์เฟส comint

Comint กำหนดฟังก์ชันตัวกรองซึ่งทำบางสิ่ง:

  • สลับไปยังบัฟเฟอร์ที่ควรมีผลลัพธ์ของกระบวนการ
  • เรียกใช้ฟังก์ชั่นในรายการcomint-preoutput-filter-functionsผ่านข้อความใหม่เป็นอาร์กิวเมนต์
  • comint-prompt-regexpดำเนินการเฉพาะกิจที่ซ้ำกันกำจัดพรอมต์บางอย่างอยู่บนพื้นฐานของ
  • ใส่ผลลัพธ์ของกระบวนการที่ส่วนท้ายของบัฟเฟอร์
  • เรียกใช้ฟังก์ชั่นในรายการcomint-output-filter-functionsผ่านข้อความใหม่เป็นอาร์กิวเมนต์

ระบุว่าโหมดของคุณจะขึ้นอยู่กับ COMINT comint-output-filter-functionsคุณควรลงทะเบียนตัวกรองของคุณใน คุณควรตั้งค่าcomint-prompt-regexpให้ตรงกับพรอมต์ของคุณ ฉันไม่คิดว่า Comint มีสิ่งอำนวยความสะดวกในตัวเพื่อตรวจสอบก้อนข้อมูลที่สมบูรณ์ (เช่นระหว่างสองข้อความแจ้ง) แต่มันสามารถช่วยได้ เครื่องหมายcomint-last-input-endถูกตั้งค่าไว้ที่ส่วนท้ายของอินพุตก้อนสุดท้าย comint-last-input-endคุณมีก้อนส่งออกใหม่เมื่อตอนท้ายของพรอมต์ที่ผ่านมาคือหลังจากที่ วิธีค้นหาจุดสิ้นสุดของพรอมต์สุดท้ายขึ้นอยู่กับเวอร์ชั่นของ Emacs:

  • สูงสุด 24.3 โฆษณาซ้อนทับจะcomint-last-prompt-overlayขยายพรอมต์สุดท้าย
  • ตั้งแต่ 24.4 ตัวแปรจะcomint-last-promptมีเครื่องหมายที่จุดเริ่มต้นและจุดสิ้นสุดของพรอมต์สุดท้าย
(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)))
      ...)))

คุณอาจต้องการเพิ่มการป้องกันในกรณีที่กระบวนการปล่อยเอาต์พุตในลำดับอื่นนอกเหนือจาก {รับอินพุต, ปล่อยเอาต์พุต, แสดงพรอมต์}


ดูเหมือนว่าตัวแปร comint-last-prompt-overlay ไม่ได้ถูกกำหนดใน Emacs 25 ใน comint.el มาจากที่อื่นไหม
John Kitchin

@JohnKitchin ส่วนหนึ่งของ comint เปลี่ยนไปใน 24.4 ฉันจะเขียนคำตอบสำหรับ 24.3 ฉันได้เพิ่มวิธีการโพสต์ -24.4
Gilles 'ดังนั้น - หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.