1 คะแนน โดย GN⁺ 4 시간 전 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
  • Ghostel เป็นอีมูเลเตอร์เทอร์มินัลสำหรับ Emacs ที่ใช้เอนจิน libghostty-vt เดียวกับ Ghostty โดยโมดูลเนทีฟที่เขียนด้วย Zig รับหน้าที่สถานะเทอร์มินัล การเรนเดอร์ และ I/O ของ local PTY ส่วน Elisp จัดการคีย์แมป บัฟเฟอร์ คำสั่ง และโปรเซสระยะไกล
  • รองรับโปรโตคอลคีย์บอร์ดและกราฟิกของ Kitty, ไฮเปอร์ลิงก์ OSC 8, การสอบถามสี OSC 4/10/11, เอาต์พุตแบบซิงโครไนซ์ DEC 2026 และ โหมดอินพุต 5 แบบ พร้อมเปิดใช้การผสานเชลล์ให้อัตโนมัติสำหรับ local bash, zsh, fish และ nushell
  • รองรับ Emacs 28.1 ขึ้นไป รวมถึง macOS, Linux, FreeBSD และ Windows แบบเนทีฟ และเมื่อใช้งานครั้งแรกสามารถดาวน์โหลด โมดูลที่คอมไพล์ไว้ล่วงหน้า ตามแพลตฟอร์มได้อัตโนมัติ ทำให้การติดตั้งทั่วไปไม่จำเป็นต้องมี toolchain ของ Zig
  • เบนช์มาร์กบน Apple M4 Max และ Emacs 32.0.50 สำหรับการประมวลผลเอาต์พุตขนาด 1MB วัดได้ Ghostel 75MB/s สำหรับ ASCII ทั่วไป, vterm 18MB/s และ eat 6.2MB/s ส่วน native PTY สามารถรองรับงาน cat ขนาด 10MB ได้ในเวลาประมาณ 110ms
  • มีทั้งเทอร์มินัลระยะไกลผ่าน TRAMP, การป้องกันการป้อนรหัสผ่าน, ภาพอินไลน์ของ Kitty และการเชื่อมต่อกับ compile, Eshell, Comint, Evil และ input method แต่ปลายทางระยะไกลและการประมวลผลภาพยังมี ข้อจำกัดตามแพลตฟอร์มและโปรโตคอล

โครงสร้างและการติดตั้ง

  • Ghostel ได้แรงบันดาลใจจากสถาปัตยกรรม 2 ชั้นของ emacs-libvterm แต่ใช้ เอนจิน VT สมัยใหม่ของ Ghostty แทน libvterm
    • โมดูล Zig จัดการสถานะเทอร์มินัล การเรนเดอร์ การเข้ารหัสคีย์ local PTY และการอ่านแบบเบื้องหลัง
    • Elisp รับผิดชอบคีย์แมป บัฟเฟอร์ คำสั่ง การเชื่อมต่อกับโปรเจกต์ และโปรเซสระยะไกลผ่าน TRAMP
  • ข้อกำหนดคือ Emacs 28.1 ขึ้นไป ที่รองรับ dynamic module และใช้งานบน macOS, Linux, FreeBSD หรือ Windows แบบเนทีฟ
    • ไบนารีที่คอมไพล์ไว้ล่วงหน้ามีให้สำหรับ aarch64-macos, x86_64-macos, x86_64-linux, aarch64-linux, x86_64-freebsd, x86_64-windows, aarch64-windows
    • แพลตฟอร์มอื่นหรือการบิลด์จากซอร์สต้องใช้ Zig 0.15.2
  • ติดตั้งได้ผ่าน MELPA, use-package :vc ของ Emacs 30 ขึ้นไป, :load-path หรือ require แบบแมนนวล และเปิดเทอร์มินัลด้วย M-x ghostel
  • หากไม่มี native module ระบบจะเสนอให้ดาวน์โหลดหรือคอมไพล์จากซอร์สตามค่าตั้ง ghostel-module-auto-install
    • M-x ghostel-download-module จะรับไบนารีขั้นต่ำที่รองรับ และหากใส่ prefix argument จะสามารถเลือก release tag ได้
    • M-x ghostel-module-compile ใช้ zig build เพื่อบิลด์โมดูล
    • หากไดเรกทอรีแพ็กเกจอาจถูกติดตั้งใหม่ สามารถกำหนด ghostel-module-directory ไปยังตำแหน่งอื่นได้
  • การบิลด์จากซอร์สมี vendor/emacs-module.h ที่สร้างไว้รวมมาให้แล้ว จึงโดยทั่วไปไม่ต้องใช้เฮดเดอร์ Emacs ในเครื่อง และสามารถเปลี่ยนตำแหน่งเฮดเดอร์ได้ด้วย EMACS_INCLUDE_DIR หรือ EMACS_BIN_DIR
    • terminfo ของ xterm-ghostty ที่คอมไพล์แล้วถูกรวมไว้ใน etc/terminfo/ และอยู่ในรูปแบบที่พกพาได้ทั้งบน BSD และ ncurses

การรองรับ Windows

  • บน Emacs สำหรับ Windows แบบเนทีฟ จะรัน local terminal ผ่าน ConPTY และสถาปัตยกรรมของ DLL โมดูลต้องตรงกับสถาปัตยกรรมของ Emacs ไม่ใช่ของ Windows
  • จะดาวน์โหลดไฟล์สนับสนุน conpty.dll และ OpenConsole.exe มาพร้อมกับโมดูลที่คอมไพล์ไว้ล่วงหน้า และหากไม่มีไฟล์เหล่านี้จะถอยไปใช้ Windows ConPTY API ที่มีในระบบ
  • การบิลด์ Emacs แบบกำหนดเองพิเศษ เช่น static CRT รองรับแบบพยายามอย่างดีที่สุดเท่านั้น
  • เทอร์มินัล TRAMP บน Windows รองรับเฉพาะปลายทางระยะไกลแบบ POSIX โดย PTY จะอยู่บนโฮสต์ระยะไกล และขณะนี้ยังไม่รองรับ การปรับขนาดหน้าต่างแบบไดนามิก

โหมดอินพุต 5 แบบ

  • โหมด semi-char ซึ่งเป็นค่าเริ่มต้น จะส่งคีย์ส่วนใหญ่ไปยังเทอร์มินัล แต่คงข้อยกเว้นอย่าง C-c, C-x, C-u, C-h, M-x, M-:, C-\ ไว้ให้ Emacs
    • มีคีย์สำหรับส่ง interrupt, suspend, EOF, bracketed paste, ลบ scrollback และย้ายไปยังพรอมป์ต์หรือไฮเปอร์ลิงก์
  • โหมด char จะส่งทุกคีย์ไปยังเทอร์มินัล จึงเหมาะกับ TUI ที่ใช้ C-x, M-x, C-h ด้วยตัวเอง โดยเหลือเพียง M-RET เป็นคีย์สำหรับออก
  • โหมด Emacs จะยังรับเอาต์พุตจากเทอร์มินัลต่อไป แต่เปลี่ยนบัฟเฟอร์เป็นอ่านอย่างเดียว เพื่อให้ใช้การนำทางมาตรฐานของ Emacs เช่น isearch, occur, การมาร์ก และการคัดลอกได้
    • อินพุตทั่วไปจะไม่ถูกส่งไปยังเชลล์ และมีเพียงการวางด้วย C-y แบบตั้งใจเท่านั้นที่ถูกส่งเป็น bracketed paste
  • โหมด copy จะหยุดการอัปเดตจากเทอร์มินัล เพื่อให้เลือก นำทาง และคัดลอกได้อย่างเสถียร
    • ข้อความที่คัดลอกจะลบตัวขึ้นบรรทัดใหม่ที่เกิดจาก soft wrap ออก
    • ในค่าเริ่มต้น ghostel-readonly-fast-exit=t ระบบจะกลับสู่เซสชันเทอร์มินัลปกติโดยอัตโนมัติหลังป้อนอักขระหรือคัดลอกเสร็จ
  • โหมด line จะไม่ส่งคีย์ไปยังเชลล์ทันที แต่ให้แก้ไขหนึ่งบรรทัดในบัฟเฟอร์ Emacs ก่อนแล้วค่อยส่งทั้งหมดเมื่อกด RET
    • แม้จะมีเอาต์พุตแบบอะซิงโครนัสหรือพรอมป์ต์ใหม่เข้ามา ก็จะเก็บ snapshot ของอินพุตที่กำลังพิมพ์ไว้และแทรกกลับคืน
    • หากมี OSC 133 จะใช้ขอบเขตพรอมป์ต์ และหากไม่มีจะค้นหาขอบเขตด้วย ghostel-prompt-regexp
    • เมื่อ TUI แบบเต็มหน้าจอเริ่มทำงาน จะสลับไปโหมด semi-char แล้วกลับสู่โหมด line หลังออกจาก TUI
    • TAB ใช้การเติมคำแบบ comint และ programmable completion ของ bash แบบเลือกเปิดได้
  • โดยค่าเริ่มต้น เมื่อมีการลากเมาส์ คลิกหลายครั้ง เปิดใช้งานมาร์ก หรือออกจากตำแหน่งอินพุตสดด้วย isearch หรือ consult-line ระบบจะสลับไปโหมด copy เพื่อปกป้องการเลือกและตำแหน่ง
    • หาก TUI เปิดใช้ DEC mouse tracking จะส่งอีเวนต์เมาส์ไปยังโปรแกรมนั้นขณะอยู่ในโหมดอินพุต
    • สามารถเปลี่ยนพฤติกรรมการสลับได้ด้วย ghostel-mouse-drag-input-mode, ghostel-mark-activation-input-mode, ghostel-point-leave-input-mode

ความสามารถของเทอร์มินัลและการเรนเดอร์

  • ผ่าน libghostty-vt รองรับ VT emulation แบบเต็มรูปแบบ, สี 256 สีและ 24 บิต, alternate screen, รูปแบบและสีของขีดเส้นใต้หลายแบบ, ตัวหนา ตัวเอียง ตัวจาง ขีดฆ่า กลับสี รวมถึงรูปทรงเคอร์เซอร์ 4 แบบและการกะพริบ
  • ค่าเริ่มต้น TERM=xterm-ghostty และ terminfo ที่แถมมาจะบอกแอปพลิเคชันว่ารองรับความสามารถอย่าง DEC 2026 synchronized output, คีย์บอร์ด Kitty, true color, ขีดเส้นใต้แบบมีสี และการรายงานโฟกัส
    • การสอบถาม OSC 4/10/11 ทำให้ TUI สามารถดึงพาเลตต์และสี foreground/background จากสีของ face ใน Emacs ได้
    • รองรับคลิปบอร์ด OSC 52 แต่ปิดไว้เป็นค่าเริ่มต้น ดังนั้น terminfo ที่แถมมาจึงไม่ประกาศความสามารถ Ms
  • scrollback เริ่มต้นคือ 5MB หรือประมาณ 5,000 บรรทัด และถูกทำให้เป็นรูปธรรมในบัฟเฟอร์ Emacs จึงค้นหาได้ด้วย isearch และ consult-line
  • นอกจากไฮเปอร์ลิงก์ OSC 8 แล้ว ยังตรวจจับ URL แบบ HTTP·HTTPS ปกติ และพาธไฟล์ในรูปแบบ /path/file.el:42 เพื่อทำให้เป็นลิงก์ที่คลิกได้
  • รองรับตัวเข้ารหัสคีย์ของ Ghostty และโปรโตคอลคีย์บอร์ด Kitty, อีเวนต์เมาส์ SGR, อีเวนต์โฟกัส DEC 1004 รวมถึงการลากและวางพาธไฟล์และข้อความ
  • ตัวเรนเดอร์จะข้ามแถวที่ไม่เปลี่ยนแปลง จัดกลุ่มการอัปเดตด้วยตัวจับเวลา และปรับอัตราเฟรมแบบยืดหยุ่น
    • PTY echo ที่มาถึงทันทีหลังการกดคีย์จะข้ามตัวจับเวลาเริ่มต้น 33ms เพื่อเรนเดอร์ทันที
    • ghostel-sync-theme จะซิงก์พาเลตต์ใหม่ให้ตรงกับธีมของ Emacs
  • สี ANSI ทั้ง 16 สีเป็น face ของ Emacs ที่สืบทอดจาก term-color-* และสามารถตั้งค่าฟอนต์ ขนาด สี foreground และ background สำหรับเทอร์มินัลโดยเฉพาะได้ด้วย ghostel-default

การผสานรวมเชลล์และการเรียกใช้ Emacs

  • สำหรับ bash·zsh·fish·nushell จะตั้งค่า การติดตามไดเรกทอรี OSC 7, การย้ายพรอมป์ต์ OSC 133, การติดตามชื่อเรื่อง OSC 2 และ ghostel_cmd ให้อัตโนมัติ
    • การผสานรวมเชลล์ของ nushell เองมี OSC 7·133·2 อยู่แล้ว และ ghostel.nu จะเพิ่ม ghostel_cmd กับ terminfo wrapper สำหรับ outbound SSH
    • bash 3.2 ที่มาพร้อม macOS จะไม่สนใจ ENV ที่ใช้สำหรับการแทรกอัตโนมัติ จึงต้องใช้ bash รุ่นใหม่กว่าหรือโหลด ghostel.bash ด้วยตนเอง
  • ghostel_cmd ที่ใช้เรียก Elisp จากเชลล์ ใช้ส่วนขยาย OSC 52;e โดยเฉพาะ และจะรันได้เฉพาะฟังก์ชันที่อยู่ใน allowlist ของ ghostel-eval-cmds
    • ฟังก์ชันที่อนุญาตโดยค่าเริ่มต้นคือ find-file, find-file-other-window, dired, dired-other-window, message
  • บุ๊กมาร์กของ Emacs จะบันทึกไดเรกทอรีทำงานของเทอร์มินัลและชื่อบัฟเฟอร์
    • หากมีบัฟเฟอร์เดิมอยู่แล้วจะนำกลับมาใช้ซ้ำ และถ้าไม่มีก็จะเริ่มเชลล์ใหม่จากไดเรกทอรีที่บันทึกไว้
    • จะไม่บันทึก scrollback และเนื้อหาเซสชัน

รหัสผ่าน·คลิปบอร์ด·การแจ้งเตือน

  • เมื่อตรวจพบพรอมป์ต์รหัสผ่านจาก sudo, ssh, gpg, passwd เป็นต้น จะเปิด read-passwd และส่งคำตอบตรงไปยัง PTY ทำให้ข้อมูลที่พิมพ์ไม่ถูกเก็บใน view-lossage, kill ring ล่าสุด หรือคีย์บอร์ดแมโคร
    • ในเครื่องจะตรวจสอบ canonical mode และการปิด echo ด้วย tcgetattr
    • จะใช้การตรวจจับแบบแทนที่ด้วย regex เฉพาะใน remote shell เท่านั้น เพื่อลด false positive กับ local raw-mode TUI
    • หลังส่งแล้ว สตริงรหัสผ่านจะถูกจัดการด้วย clear-string ทันที
    • สามารถเพิ่มแหล่งข้อมูลอย่าง auth-source, KeePass, pass เป็นต้น ลงใน ghostel-password-prompt-functions ได้
  • หากเปิดใช้ OSC 52 อย่างชัดเจน โปรแกรมฝั่งรีโมตจะสามารถตั้งค่า kill ring ของ Emacs และคลิปบอร์ดของระบบได้ และการวางจะถูกส่งแบบ bracketed paste
  • การแจ้งเตือน OSC 9 และ OSC 777 จะถูกส่งต่อไปยัง ghostel-notification-function
    • ตัวจัดการเริ่มต้นจะใช้ alert หากติดตั้งไว้ และถ้าไม่มีจะใช้ message ใน echo area แทน
  • ความคืบหน้า OSC 9;4 จะส่งสถานะ remove, set, error, indeterminate, pause และค่าตั้งแต่ 0~100
    • มีทั้งตัวแสดงผลข้อความเริ่มต้นและตัวแสดงภาพเคลื่อนไหวที่อิง spinner.el

ภาพอินไลน์ของ Kitty

  • รองรับทั้งการวางแบบ direct placement และแบบ Unicode-placeholder U+10EEEE ของ Kitty graphics protocol จึงเรนเดอร์ภาพจาก timg, kitty +kitten icat, yazi เป็นต้น ได้
  • PNG จะถูกจัดการด้วยตัวถอดรหัส stb ที่รวมมาให้ และข้อมูล RGB·RGBA·Gray·GrayAlpha จะถูกแปลงเป็น PPM ใน native module ดังนั้น ไม่จำเป็นต้องใช้ ImageMagick
  • ตอบกลับการสอบถามขนาด CSI 14·16·18 t และสามารถตั้งอัตราส่วนพิกเซลจริงได้ด้วย ghostel-cell-pixel-scale
  • การประมวลผลภาพมีข้อจำกัดดังนี้
    • alpha channel จะไม่ถูก compositing แต่จะถูกลบออก จึงไม่เหมาะกับขอบแบบกึ่งโปร่งใส
    • ไม่รองรับการครอปบางส่วนแบบ Kitty source-rect และจะส่งข้อผิดพลาดกลับอย่างชัดเจน
    • virtual placement หลายรายการในบัฟเฟอร์เดียวกันจะแชร์การเรนเดอร์เดียวกัน และข้อมูลที่ส่งล่าสุดจะมีลำดับความสำคัญ
    • ค่าเริ่มต้นจะอนุญาตเฉพาะ inline base64 และต้องเปิดใช้งานอย่างชัดเจนหากต้องการใช้ไฟล์·ไฟล์ชั่วคราว·สื่อ shared memory เพื่อความปลอดภัย
    • ขีดจำกัดการเก็บกราฟิกเริ่มต้นต่อเทอร์มินัลคือ 320MiB

TRAMP และ terminfo ระยะไกล

  • หาก default-directory เป็นพาธ TRAMP จะรันเชลล์บนโฮสต์ POSIX นั้น และสามารถกำหนดวิธี TRAMP ตระกูล SSH กับเชลล์อย่าง docker ได้ผ่าน ghostel-tramp-shells
    • login-shell จะตรวจจับ login shell ของผู้ใช้ฝั่งรีโมตด้วย getent passwd
    • การติดตามไดเรกทอรี OSC 7 จะคงวิธี ผู้ใช้ และคำนำหน้า multi-hop เดิมของ TRAMP ไว้
    • ปลายทางรีโมตแบบ Windows ไม่รองรับในเส้นทาง POSIX เชลล์·tty นี้
  • โดยค่าเริ่มต้นจะไม่ฉีด remote shell integration และมีให้เลือก 2 วิธี
    • ใช้ ghostel-tramp-shell-integration=t เพื่อส่งสคริปต์ชั่วคราวและลบออกเมื่อออกจากระบบ
    • สำหรับโฮสต์ถาวร สามารถติดตั้งสคริปต์ใน etc/shell/ และติดตั้ง terminfo เพิ่มเองได้ตามต้องการ
  • หากฝั่งรีโมตไม่มี xterm-ghostty terminfo การเปิดเทอร์มินัลอาจล้มเหลว ดังนั้น ghostel-ssh-install-terminfo จะจัดการการติดตั้งให้
    • การทำงานผ่าน TRAMP จะส่ง terminfo ไปยังไดเรกทอรีชั่วคราวผ่านการเชื่อมต่อเดิมและลบออกเมื่อจบการทำงาน
    • ssh ที่รันจาก Ghostel ในเครื่องจะทำ normalization ของปลายทางด้วย ssh -G ตรวจสอบแคช แล้วหากจำเป็นจะเปิดการเชื่อมต่อเพิ่มอีกครั้งเดียวเพื่อรัน tic -x -
    • แฮช terminfo ในเครื่องจะถูกรวมอยู่ใน cache key จึง invalidation อัตโนมัติเมื่อ libghostty อัปเดต แต่จะตรวจจับ terminfo ที่ถูกแก้แยกต่างหากบนฝั่งรีโมตไม่ได้
    • จะข้ามการติดตั้งในกรณี ssh HOST cmd, การเรียกใช้ออปชันที่ไม่มีโฮสต์, หรือเมื่อในเครื่องไม่มี infocmp
  • หากไม่ต้องการแก้ไขโฮสต์รีโมต สามารถปิดการติดตั้งอัตโนมัติและติดตั้ง terminfo เองได้ หรือเปลี่ยน ghostel-term เป็น xterm-256color เพื่อไม่ต้องตั้งค่าส่วนนี้ แต่ก็จะสูญเสีย เส้นทางเร่งความเร็ว synchronous output เฉพาะของ Ghostty ไปด้วย

ความสามารถเสริมของ Emacs

  • evil-ghostel จะซิงก์สถานะ insert·normal ของ Evil กับเคอร์เซอร์ของเทอร์มินัล
    • รองรับการย้าย การแทรก การลบ การเปลี่ยน การแทนที่ bracketed paste, readline undo และรูปทรงเคอร์เซอร์ตามแต่ละสถานะ
    • จะไม่กระทบกับ vim·less·htop ใน alternate screen
  • ghostel-compile รันคำสั่งบน PTY จริง พร้อมให้ส่วนหัว·ส่วนท้าย·การไฮไลต์ข้อผิดพลาด·การนำทาง next-error ในรูปแบบ compilation-mode
    • ค่าเริ่มต้นจะเป็นการนำทางแบบอ่านอย่างเดียว และหากใส่ prefix argument จะส่งต่ออินพุตแบบโต้ตอบได้
    • ระหว่างรันอยู่ก็สลับระหว่างอินพุตแบบโต้ตอบกับการนำทาง compilation ได้ด้วย C-c C-j และ C-c C-e หรือ C-c C-t
    • ghostel-compile-global-mode จะส่งการเรียก compile, recompile, project-compile และ compilation-start ไปยัง Ghostel
    • grep-mode จะคงการทำงานเดิมไว้เป็นค่าเริ่มต้น
  • ghostel-eshell-visual-command-mode จะรัน visual command ของ Eshell อย่าง vim·htop·less ในบัฟเฟอร์ Ghostel โดยเฉพาะแทน term-mode
  • ghostel-comint-mode ประมวลผลสตรีมเอาต์พุตของ comint ด้วย libghostty-vt เพื่อให้ได้ true color, ขีดเส้นใต้หลายแบบ, ลิงก์ OSC 8 และการอัปเดตไดเรกทอรี OSC 7
    • เนื่องจากไม่ใช่เทอร์มินัลเต็มรูปแบบ จึงละทิ้งการวางเคอร์เซอร์, alternate screen และการวาดหน้าจอใหม่ทั้งจอ ทำให้ไม่เหมาะกับ htop หรือ less
  • ghostel-ime-mode จะลบผลลัพธ์จาก Elisp input method ที่แทรกอักขระลงในบัฟเฟอร์โดยตรง เช่น Hangul แล้วส่งต่อเป็น UTF-8 ไปยัง PTY
    • ระหว่างการประกอบอักขระของ Quail จะหน่วงการเรนเดอร์ไว้เพื่อไม่ให้บัฟเฟอร์เขียนทับสตริงที่กำลังประกอบ

ประสิทธิภาพ

  • ผลลัพธ์จากการส่งข้อมูล 1MB ผ่าน process pipe จริงและคง scrollback ไว้ราว 1,000 บรรทัด บน Apple M4 Max และ Emacs 32.0.50 มีดังนี้
    • ASCII ทั่วไป: Ghostel 75MB/s, Ghostel แบบปิดการตรวจจับลิงก์ 76MB/s, vterm 18MB/s, eat 6.2MB/s, term ที่มากับระบบ 7.2MB/s
    • เอาต์พุตที่มี URL จำนวนมาก: Ghostel 36MB/s, Ghostel แบบปิดการตรวจจับลิงก์ 78MB/s, vterm 15MB/s, eat 4.5MB/s, term 5.9MB/s
  • การตรวจจับ URL·พาธไฟล์ทำงานผ่าน unified timer ที่อยู่นอกเส้นทางการวาดซ้ำ จึงแทบไม่มีต้นทุนกับเอาต์พุตทั่วไป แต่เมื่อเอาต์พุตมีลิงก์หนาแน่น throughput จะลดลงราวครึ่งหนึ่ง
  • เพื่อให้เปรียบเทียบได้อย่างยุติธรรม ตัวเลขข้างต้นใช้เส้นทาง process ของ Emacs ส่วน native PTY ภายในเครื่องตามค่าปริยายจะให้ Zig background thread อ่านเอาต์พุต จึงเร็วขึ้นราว 2 เท่าในการ dump ข้อมูลขนาดใหญ่ต่อเนื่อง
  • ค่ามัธยฐานจากการ cat ไฟล์ขนาด 10MB จำนวน 5 ครั้งคือ Ghostel แบบ native ราว 110ms, Ghostel ผ่าน Emacs PTY ราว 220ms, vterm ราว 550ms และ eat ราว 1.8 วินาที
    • vterm และ eat จะยึด Emacs main thread แบบ synchronous ระหว่างการถ่ายโอน แต่ Ghostel native PTY จะ parse อยู่เบื้องหลัง ทำให้ UI ยังตอบสนองได้ต่อเนื่อง
  • M-x ghostel-debug-typing-latency ใช้วัดค่าต่ำสุด, ค่ามัธยฐาน, p99 และค่าสูงสุดของความหน่วง PTY·การเรนเดอร์·โดยรวมในระดับต่อปุ่ม

ความแตกต่างจาก vterm·eat

  • vterm ใช้ native module ของ libvterm ที่พัฒนาด้วย C ส่วน eat เขียนด้วย Elisp ล้วน จึงพกพาได้โดยไม่ต้อง build แต่มี throughput ต่ำกว่า
  • มีเพียง Ghostel เท่านั้นที่รองรับ Kitty keyboard·graphics, ขีดเส้นใต้ 5 แบบ, OSC 8, OSC 4/10/11, การแจ้งเตือน OSC 9·777, รวมถึง OSC 9;4 progress และ DEC 2026 ครบทั้งหมด
    • eat รองรับภาพ Sixel แต่ไม่รองรับภาพ Kitty ส่วน vterm ไม่รองรับทั้งสอง image protocol
  • Ghostel และ eat จะส่งต่ออีเวนต์เมาส์แบบ SGR ไปยัง TUI แต่ vterm ใช้การคลิกเพื่อย้าย point ของ Emacs
  • Ghostel และ eat มี line mode แต่ vterm ไม่มี
    • Ghostel มีทั้ง copy mode ที่เอาต์พุตหยุดนิ่ง และ Emacs mode ที่เอาต์พุตยังไหลต่อ
    • vterm มี copy mode แบบหยุดนิ่ง ส่วน Emacs mode แบบอ่านอย่างเดียวของ eat จะอัปเดตต่อไปเรื่อย ๆ
  • Ghostel ฉีด shell integration ให้อัตโนมัติ แต่ vterm และ eat ต้องโหลดสคริปต์เองแบบ manual
  • Ghostel ตรวจจับ password prompt ได้อัตโนมัติ, eat มีเพียงคำสั่งส่งแบบ manual และ vterm ไม่ได้ดักอินพุตแยกต่างหาก

สถาปัตยกรรมภายใน

  • สำหรับ local buffer โดยปริยาย Zig จะเปิด PTY และสร้าง child process จากนั้นส่งเอาต์พุตไปยัง libghostty-vt บน background thread
    • หากต้องใช้ OSC callback หรือการวาดซ้ำ จะส่ง Lisp event ผ่าน Emacs pipe เพื่อให้ ghostel--events-filter จัดการ
    • เอาต์พุตปริมาณมากจะไม่ต้องผ่าน Emacs process filter ทีละไบต์
  • สำหรับ TRAMP buffer นั้น Emacs process จะรับเอาต์พุตและส่งต่อไปยัง terminal model เดียวกันแบบ synchronous เพื่อคงการสร้างฝั่ง remote และ file handler ไว้
  • คีย์อินพุตจะผ่าน libghostty encoder แล้วเขียนลง PTY ทันที ส่วนเอาต์พุตจะผ่าน VT parser และขั้นตอน invalidation จากนั้น Renderer.zig จะอัปเดตเฉพาะแถวที่เปลี่ยน โดยทำทันทีถ้าเป็น interactive echo ไม่เช่นนั้นจะทำใน unified timer
  • การเรนเดอร์เทอร์มินัลจะแก้ไข Emacs buffer ผ่านการแทนที่แถว, การวาดใหม่ทั้งหน้าจอ, การเปลี่ยนขนาดและ reflow, รวมถึงการเพิ่ม·ลบ scrollback ดังนั้น renderer จึงต้องรับผิดชอบการคง point·mark·ตำแหน่งหน้าต่าง
    • Elisp ทำหน้าที่ตัดสินใจเรื่องนโยบายและเจตนาของผู้ใช้ เช่น จะย้ายไปยัง live viewport หลังอินพุตหรือไม่ และจะคงสถานะการนำทางในแต่ละโหมดอย่างไร
    • ไม่ควรใช้ heuristic ก่อน·หลัง redraw ของ Elisp มาชดเชยการเปลี่ยนแปลงจาก renderer เพราะจะทำให้แยกสาเหตุระหว่างการเลื่อนของผู้ใช้, การชดเชย redisplay, การเปลี่ยนขนาด และการเขียนเนื้อหาใหม่ได้ยาก

การทดสอบและสัญญาอนุญาต

  • การทดสอบใช้ ERT เป็นฐาน โดยการทดสอบ Elisp สามารถรันได้โดยไม่ต้องมี Zig module และการทดสอบที่ติดแท็ก native ต้องใช้โมดูลที่ build แล้ว
    • มีเป้าหมาย Makefile สำหรับ Zig unit test, full test, การทดสอบส่วนขยาย Evil และ benchmark แบบรวดเร็ว
  • รับ bug, feature request และ pull request ผ่าน GitHub repository และสำหรับปัญหาการเรนเดอร์จำเป็นต้องมีคำสั่งที่ใช้ทำซ้ำ, escape sequence, เวอร์ชัน Emacs และข้อมูลแพลตฟอร์ม
  • สามารถเก็บรวบรวม diagnostic event ได้ด้วย M-x ghostel-debug-info
  • Ghostel เป็น free software ที่เผยแพร่ภายใต้ GNU GPL v3 หรือใหม่กว่า

3 ความคิดเห็น

 
lum7671 1 시간 전

ดูเหมือนว่าจะพิมพ์ภาษาเกาหลีไม่ได้ครับ
ถึงจะกด Ctrl-\ แล้วตัวป้อนข้อมูลเหมือนจะสลับเป็นภาษาเกาหลี แต่พอลองพิมพ์จริงกลับพิมพ์ได้แค่อังกฤษเท่านั้นครับ
เสียดายนิดหน่อยครับ

 
GN⁺ 2 시간 전
ความคิดเห็นจาก Lobste.rs
  • มองว่า Ghostel คือคำตอบสุดท้ายของการจำลองเทอร์มินัลบน Emacs สำหรับหน้าต่างเทอร์มินัลเล็ก ๆ ที่เปิดใช้ครั้งเดียวก็ใช้ Ghostty ตัวจริงไป ส่วนงานตามโปรเจ็กต์ก็ใช้ Ghostel ที่ผูกกับโปรเจ็กต์นั้นผ่านคำสั่ง ghostel-project
    ถ้ามีอินสแตนซ์สำหรับโปรเจ็กต์อยู่แล้ว ก็จะสลับไปยังอินสแตนซ์นั้นได้ทันที เลยสลับไปมาระหว่างเทอร์มินัลได้เร็วมาก
  • ทำให้อยากลอง Doom Emacs อีกครั้งและดูว่าการผสานทำได้ดีแค่ไหน หนึ่งในเหตุผลที่เคยเลิกใช้ครั้งก่อนคือเทอร์มินัลของ Emacs ไม่เสถียร
    • ฉันก็อยากลองเหมือนกัน vterm มีจุดที่น่าเสียดายอยู่บ้าง แต่การที่ยัง ใช้คำสั่งของเอดิเตอร์ได้เหมือนเดิมแม้อยู่ในเทอร์มินัลนั้นดีมากจริง ๆ
      เวลาใน Emacs มีคนพูดถึงเทอร์มินัลอีมูเลเตอร์ ก็มักจะมีคำตอบกลับมาว่าแทนที่จะใช้ cd/mv/ls/cp/rm/mkdr/... ก็ใช้ dired แทน ใช้ compile แทนคำสั่งบิลด์ได้ แล้วจะต้องมีไปทำไม แต่ถึงจะไม่ได้ทำงานซับซ้อน เทอร์มินัลก็ดูคุ้นมือและยืดหยุ่นกว่าสำหรับฉัน
      โดยเฉพาะถ้ามีไกด์ที่สอนวิธีลดการพึ่งพาเทอร์มินัลใน Doom Emacs ฉันก็อยากลองทำตาม การเห็นคนใช้การตั้งค่าขั้นสูงเพื่อไล่ลัดไปมาระหว่างโปรเจ็กต์ได้อย่างรวดเร็วนั้นน่าประทับใจเสมอ
    • การจำลองเทอร์มินัลบน Emacs มาถึงระดับที่แทบใช้งานได้สมบูรณ์แล้ว แต่เครื่องมือเทอร์มินัลบางยุคบางประเภทก็ยังพังในสภาพแวดล้อมอย่าง vterm และเพราะปัญหาการคำนวณขนาดบรรทัด หน้าจอของ jj จึงเพี้ยนไปหมดในบางครั้ง ซึ่งน่าหงุดหงิดมาก
      สุดท้ายก็ยังต้องเปิด Ghostty ไว้ข้าง ๆ แต่ก็หวังว่าสักวันหนึ่งทุกเครื่องมือจะทำงานได้อย่างถูกต้องภายใน Emacs
  • ดูเหมือนจะไม่มี ฟังก์ชันที่เทียบได้กับ eat-eshell เลยทำให้เป็นตัวเลือกที่ยากสำหรับฉัน ฉันสนใจเทอร์มินัลอีมูเลชันที่เร็วขึ้นก็จริง แต่ก็ใช้ eshell เยอะมาก
    ถ้าใช้ eat-eshell โปรแกรมที่ต้องพึ่งการจำลองเทอร์มินัล เช่นเครื่องมือบิลด์, Podman CLI และแอปพลิเคชัน TUI ก็จะทำงานได้ตรงจากบัฟเฟอร์ eshell เลย สำหรับคนที่ใช้ eshell บ่อย ฟีเจอร์นี้ถือว่าเปลี่ยนเกม
    • ทำในเทอร์มินัลก็ได้อยู่แล้ว เลยสงสัยว่ามีจุดประสงค์อะไรที่ต้องใช้ eshell แยกต่างหาก ฉันไม่ได้ใช้ eshell มากนัก เลยรู้สึกบ่อย ๆ ว่าตัวเองอาจกำลังพลาดประโยชน์ของมันไป
 
GN⁺ 4 시간 전
ความคิดเห็นจาก Hacker News
  • ผมเป็นผู้ดูแลหลักของ Ghostel เดิมทีตั้งใจจะโพสต์ Show HN กับ baokaola ในสัปดาห์หน้า แต่มีคนส่งลิงก์ก่อนแล้ว
    ถ้าอยากดูแบบเร็ว ๆ GitHub repository จะสะดวกกว่า: https://github.com/dakra/ghostel
    Ghostel คือ เทอร์มินัลอีมูเลเตอร์สำหรับ Emacs ที่อิงกับ libghostty-vt และดูการเปรียบเทียบฟีเจอร์กับ vterm และ eat ได้ที่ https://dakra.github.io/ghostel/#ghostel-vs-vterm นอกจากนี้ยังมีข้อมูลเปรียบเทียบประสิทธิภาพและความถูกต้องเป็นรูปภาพด้วย: https://gist.github.com/dakra/4a0b76ebcf5d52338e134864378465...
    ส่วนตัวแล้วผมใช้มันแทนไม่ใช่แค่ vterm/eat แต่รวมถึงเทอร์มินัลภายนอกอย่าง kitty และ Ghostty ด้วย การจัดการข้อความในเทอร์มินัลเหมือนเป็นบัฟเฟอร์ Emacs ทั่วไปเปิดโอกาสให้ขยายความสามารถได้มากมายแบบที่เทอร์มินัลอื่นไม่มี แม้แต่งานง่าย ๆ อย่างค้นหา scrollback แล้วเลื่อนด้วยคีย์บอร์ดอย่างเดียว เลือกย่อหน้าแล้วคัดลอก ก็เป็นธรรมชาติและรวดเร็วกว่าสำหรับผู้ใช้ Emacs
    ถ้าลองใช้ Ghostel แล้วคุณยังคงใช้เทอร์มินัลภายนอกอยู่ ผมอยากรู้ว่าเป็นเพราะฟีเจอร์ที่ยังขาด หรือเพราะต้องการรันบางโปรเซสนอก Emacs ผมก็ตอบสนองบน GitHub อย่างแข็งขัน ถ้ามีปัญหาก็เปิด issue ได้เลย

    • จนถึงตอนนี้ Ghostel ทำงานได้ดีมาก ผมผูก f1 ไว้กับการ สลับระหว่างโหมดอักขระกึ่งหนึ่งกับโหมดคัดลอก และก่อนรันคำสั่งจะคิดก่อนว่า “ถ้า Emacs ตายหรือถูกบังคับปิดจะโอเคไหม?” ถ้าไม่โอเคก็รันในเทอร์มินัลปกติ
    • เพราะเวลาที่ Magit และเครื่องมืออื่น ๆ parse diff ขนาดใหญ่ Emacs ทั้งตัวอาจค้างได้ สุดท้ายเลยยังต้องมีเทอร์มินัลแยกไว้
    • ใน eshell สามารถจัดการข้อความได้เหมือนบัฟเฟอร์ Emacs อื่น ๆ ทุกประการ ถ้าผูกฟังก์ชันสำหรับครอบคำด้วยเครื่องหมายคำพูดเข้ากับคีย์ มันก็ทำงานใน eshell ได้เหมือนกัน และยังใช้ evil-mode, xah-fly-keys, ispell ได้ทั้งหมด จุดนี้คือ ข้อได้เปรียบชี้ขาด
      Ghostel ยังไม่ได้ผสานรวมในระดับเดียวกัน และส่วนขยายอย่าง evil-ghostel-mode ก็มีข้อจำกัด อยากรู้ว่ามีแผนจะปรับปรุงตรงนี้หรือไม่ หรือเป็นข้อจำกัดเชิงโครงสร้างที่ต้องยอมรับ และถ้ามี ฟีเจอร์ที่เทียบเท่า eat-eshell-mode ก็น่าจะยอดเยี่ยมมาก
    • อยากให้มี โหมด Quick-select แบบอิงแพตเทิร์น ของ WezTerm หลังจากรู้จักฟีเจอร์นี้แล้ว ก็ถึงขั้นไม่อยากใช้เทอร์มินัลอื่นเลย
    • ใช้กับ Doom Emacs มาสักพักแล้วและใช้งานได้ดี อยากรู้ว่าทำไมโปรแกรมที่เลื่อนหน้าจออย่าง Lazygit หรือ Reasonix ถึงล้มเหลวในอีมูเลเตอร์อื่น แต่ทำงานได้ใน Ghostel มีการจัดการพิเศษใน implementation หรือ library หรือไม่
  • ควรมี Emacs อยู่ในชื่อเรื่องด้วย เทอร์มินัลอีมูเลเตอร์ทั่วไปกับเทอร์มินัลอีมูเลเตอร์สำหรับ Emacs นั้นต่างกัน

    • ถ้ายังไม่รู้จักโปรเจกต์ การลิงก์ไปที่ https://github.com/dakra/ghostel จะมีประโยชน์กว่าเอกสาร
    • .el คือ นามสกุลไฟล์ของ Emacs Lisp
  • เพิ่งย้ายจาก vterm มาเป็น Ghostel เมื่อไม่นานนี้ โดยรวมดีกว่ามาก เร็วขึ้นอย่างเห็นได้ชัด จนแอป TUI หรู ๆ ที่อัปเดตทั้งเทอร์มินัลทุกเฟรมก็ทำงานได้ถูกต้อง การจัดการอินพุตเสถียรกว่า และ ELisp API ก็ดี
    แต่ก็เคยเจอกรณีที่มันล้างเทอร์มินัลไม่ถูก ทำให้มีเศษค้างอยู่เหนือ prompt ปัจจุบัน หรือค้างสนิทจนต้องปิดบัฟเฟอร์แล้วเริ่มใหม่ สำหรับการใช้งานประจำวันถือว่าเพียงพอและอนาคตดูสดใส แต่กว่าจะเป็นผลิตภัณฑ์ที่สุกงอม ยังต้องมี การแก้บั๊กและงานเก็บรายละเอียด อีก

    • ผมเป็นผู้ดูแลหลักร่วมของ Ghostel ถ้ามีข้อมูลที่นำไปใช้ได้ แม้จะไม่มีวิธี reproduce ก็ขอให้เปิด issue หรือส่งมาทางอื่น
      อาการมีเศษค้างด้านบนของหน้าจออาจเป็น https://github.com/dakra/ghostel/issues/495 ซึ่งแก้แล้วในเวอร์ชันล่าสุด แต่ก็อาจเป็นบั๊กอื่นได้ ส่วนที่ยากคือการ replicate ข้อมูลภายในของ libghostty-vt ไปยังบัฟเฟอร์ Emacs แล้วแทนที่เฉพาะส่วนที่จำเป็น เรามี property-based tests ที่ตรวจสอบเรื่องนี้ค่อนข้างกว้าง แต่บางครั้งปัญหาก็ยังหลุดรอดไป
      ใน release ล่าสุดตอนนี้มีการปรับปรุงการจัดการวงจรชีวิต จึงอาจแก้ปัญหาบางอย่างไปพร้อมกันแล้ว ตอนนี้ยังอยู่ช่วงเริ่มต้น จึงตั้งใจจะค่อย ๆ ขัดเกลาไป
  • อยากเห็น ตัวอย่างจริงของการใช้โหมดอินพุตต่าง ๆ อย่างมีประสิทธิภาพ เทอร์มินัลต้องการรับทุกคีย์ที่พิมพ์ ส่วน editor ก็อยากใช้คีย์กับคำสั่งของตัวเอง ดังนั้นเทอร์มินัลที่ฝังอยู่ใน editor จำเป็นต้องมีโหมดสำหรับสลับความเป็นเจ้าของคีย์บอร์ด
    :term ของ Neovim มีแค่สองโหมด แต่ถ้าไม่เข้าใจว่าทำไม Ghostel ถึงมีถึงห้าโหมด ก็อาจเข้าใจผิดว่าออกแบบเกินความจำเป็นได้ง่าย จริง ๆ แล้วมันซับซ้อนเพราะแก้ปัญหาได้มากกว่า และโหมดเพิ่มเติมก็เป็นเครื่องมือเสริมสำหรับงานที่ nvim ไม่ได้จัดการ เพียงแต่ยังไม่ชัดพอว่าจะใช้ข้อดีนี้ในการทำงานจริงอย่างมีประสิทธิภาพได้อย่างไร

  • ระหว่างที่มีปัญหากับการใช้ vterm ลองใช้ Ghostel แล้วพบว่ายอดเยี่ยมมาก จนอาจกลายเป็น เทอร์มินัลหลัก ได้เลย

    • ผมย้ายจาก vterm ผ่าน eat มายัง Ghostel และอย่างน้อยจนถึงตอนนี้มองว่า Ghostel ดีที่สุด
  • ใช้มานานกว่าหนึ่งเดือนแล้ว ฟีเจอร์ที่อยู่ในสรุปของ Codex คือ การคลิก code reference แล้วเปิดในบัฟเฟอร์ Emacs ได้ทันที ชอบเป็นพิเศษ

    • ไม่ใช่แค่เมาส์เท่านั้น ยังสามารถใช้ ghostel-previous-hyperlink หรือ ghostel-next-hyperlink เพื่อเลื่อนไปมาระหว่างไฮเปอร์ลิงก์ที่แสดงล่าสุดได้อย่างรวดเร็ว
      มีการติดตั้ง repeat-mode map ด้วย ดังนั้นถ้าใน output มี URL หรือลิงก์ไฟล์สามรายการ ก็สามารถเปิดลิงก์แรกได้ด้วย C-c C-p p p RET เท่านั้น ผมใช้บ่อยมาก
  • การผสานรวม Ghostel กับ Claude Code ทำให้ผมใช้ Emacs มากขึ้นอย่างมาก และตอนนี้ Emacs กลายเป็น ศูนย์กลางหลัก ของงานไปแล้ว

    • อยากรู้ว่าการผสานรวมในที่นี้หมายถึงโมเดล ควบคุม Emacs โดยตรงผ่านเซิร์ฟเวอร์ หรือเปล่า
  • ถ้าเปิดเทอร์มินัลประมาณ 10 ตัวในหลายหน้าต่าง Ghostty แครชแทบทุกคืน ทำให้ผมใช้ Ghostty จริงจังไม่ได้ และก็ไม่อยากฝังมันไว้ในเครื่องมือที่ใช้ทุกวันด้วย

    • ผมใช้ Ghostty 1.1.3 บน Gentoo Linux และตอนนี้เปิดแท็บราว 20 แท็บต่อหน้าต่างประมาณ 12 หน้าต่าง รัน shell instance มากกว่า 100 ตัว
      ยังลองรัน ls -lR ใน shell ให้มากที่สุดเท่าที่ทำได้บน filesystem ที่มีไฟล์หลายล้านไฟล์ แต่ก็ไม่พบปัญหาหรือการแครช ใช้งานหนักตลอดทั้งวันเป็นเวลาหลายเดือนก็ไม่เคยเห็นการแครชหรือพฤติกรรมน่าสงสัย
      ดูมีความเป็นไปได้สูงว่าเป็นบั๊กของ Ghostty บางเวอร์ชัน หรือเป็น interaction แปลก ๆ กับซอฟต์แวร์อื่นอย่างไดรเวอร์ GPU ที่นี่ใช้ NVIDIA GPU
  • เห็นบอกว่า “native module เป็นไบนารีที่ build ไว้ล่วงหน้า และจะดาวน์โหลดอัตโนมัติเมื่อใช้งานครั้งแรก” เลยสงสัยว่าทำไม ไม่รวมไว้ในแพ็กเกจที่แจกจ่าย

    • MELPA และ ELPA ไม่มีวิธีแนบไฟล์แยกตามแพลตฟอร์ม และแจกจ่ายทั้งหมดผ่าน Git checkout ถ้าจะรวมไว้ใน distribution ก็ต้องใส่ไบนารีโมดูลของทุกแพลตฟอร์มลงใน repository ซึ่งรวมกันแล้วเกิน 10MB
      แพ็กเกจยอดนิยมที่ใช้ Emacs native module อย่าง jinx ก็มีวิธีคอมไพล์ตอนใช้งานครั้งแรกเหมือน vterm ดังนั้นถ้าจะรองรับการติดตั้งที่เป็นมิตรต่อผู้ใช้ในแพ็กเกจ Emacs ในทางปฏิบัติก็มีแค่ ดาวน์โหลดหรือคอมไพล์ตอนรันครั้งแรก เท่านั้น