Ghostel.el - อีมูเลเตอร์เทอร์มินัลสำหรับ Emacs ที่ใช้ libghostty
(dakra.github.io)- 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-installM-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
- terminfo ของ
การรองรับ 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ด้วยตนเอง
- การผสานรวมเชลล์ของ nushell เองมี OSC 7·133·2 อยู่แล้ว และ
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ได้
- ในเครื่องจะตรวจสอบ canonical mode และการปิด echo ด้วย
- หากเปิดใช้ OSC 52 อย่างชัดเจน โปรแกรมฝั่งรีโมตจะสามารถตั้งค่า kill ring ของ Emacs และคลิปบอร์ดของระบบได้ และการวางจะถูกส่งแบบ bracketed paste
- การแจ้งเตือน OSC 9 และ OSC 777 จะถูกส่งต่อไปยัง
ghostel-notification-function- ตัวจัดการเริ่มต้นจะใช้ alert หากติดตั้งไว้ และถ้าไม่มีจะใช้
messageใน echo area แทน
- ตัวจัดการเริ่มต้นจะใช้ alert หากติดตั้งไว้ และถ้าไม่มีจะใช้
- ความคืบหน้า 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-shellslogin-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-ghosttyterminfo การเปิดเทอร์มินัลอาจล้มเหลว ดังนั้น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ไปยัง Ghostelgrep-modeจะคงการทำงานเดิมไว้เป็นค่าเริ่มต้น
ghostel-eshell-visual-command-modeจะรัน visual command ของ Eshell อย่าง vim·htop·less ในบัฟเฟอร์ Ghostel โดยเฉพาะแทนterm-modeghostel-comint-modeประมวลผลสตรีมเอาต์พุตของ comint ด้วยlibghostty-vtเพื่อให้ได้ true color, ขีดเส้นใต้หลายแบบ, ลิงก์ OSC 8 และการอัปเดตไดเรกทอรี OSC 7- เนื่องจากไม่ใช่เทอร์มินัลเต็มรูปแบบ จึงละทิ้งการวางเคอร์เซอร์, alternate screen และการวาดหน้าจอใหม่ทั้งจอ ทำให้ไม่เหมาะกับ
htopหรือless
- เนื่องจากไม่ใช่เทอร์มินัลเต็มรูปแบบ จึงละทิ้งการวางเคอร์เซอร์, alternate screen และการวาดหน้าจอใหม่ทั้งจอ ทำให้ไม่เหมาะกับ
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 ทีละไบต์
- หากต้องใช้ OSC callback หรือการวาดซ้ำ จะส่ง Lisp event ผ่าน Emacs pipe เพื่อให้
- สำหรับ 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 ความคิดเห็น
ดูเหมือนว่าจะพิมพ์ภาษาเกาหลีไม่ได้ครับ
ถึงจะกด
Ctrl-\แล้วตัวป้อนข้อมูลเหมือนจะสลับเป็นภาษาเกาหลี แต่พอลองพิมพ์จริงกลับพิมพ์ได้แค่อังกฤษเท่านั้นครับเสียดายนิดหน่อยครับ
ความคิดเห็นจาก Lobste.rs
ghostel-projectถ้ามีอินสแตนซ์สำหรับโปรเจ็กต์อยู่แล้ว ก็จะสลับไปยังอินสแตนซ์นั้นได้ทันที เลยสลับไปมาระหว่างเทอร์มินัลได้เร็วมาก
vtermมีจุดที่น่าเสียดายอยู่บ้าง แต่การที่ยัง ใช้คำสั่งของเอดิเตอร์ได้เหมือนเดิมแม้อยู่ในเทอร์มินัลนั้นดีมากจริง ๆเวลาใน Emacs มีคนพูดถึงเทอร์มินัลอีมูเลเตอร์ ก็มักจะมีคำตอบกลับมาว่าแทนที่จะใช้
cd/mv/ls/cp/rm/mkdr/...ก็ใช้diredแทน ใช้compileแทนคำสั่งบิลด์ได้ แล้วจะต้องมีไปทำไม แต่ถึงจะไม่ได้ทำงานซับซ้อน เทอร์มินัลก็ดูคุ้นมือและยืดหยุ่นกว่าสำหรับฉันโดยเฉพาะถ้ามีไกด์ที่สอนวิธีลดการพึ่งพาเทอร์มินัลใน Doom Emacs ฉันก็อยากลองทำตาม การเห็นคนใช้การตั้งค่าขั้นสูงเพื่อไล่ลัดไปมาระหว่างโปรเจ็กต์ได้อย่างรวดเร็วนั้นน่าประทับใจเสมอ
vtermและเพราะปัญหาการคำนวณขนาดบรรทัด หน้าจอของjjจึงเพี้ยนไปหมดในบางครั้ง ซึ่งน่าหงุดหงิดมากสุดท้ายก็ยังต้องเปิด Ghostty ไว้ข้าง ๆ แต่ก็หวังว่าสักวันหนึ่งทุกเครื่องมือจะทำงานได้อย่างถูกต้องภายใน Emacs
eshellเยอะมากถ้าใช้
eat-eshellโปรแกรมที่ต้องพึ่งการจำลองเทอร์มินัล เช่นเครื่องมือบิลด์, Podman CLI และแอปพลิเคชัน TUI ก็จะทำงานได้ตรงจากบัฟเฟอร์eshellเลย สำหรับคนที่ใช้eshellบ่อย ฟีเจอร์นี้ถือว่าเปลี่ยนเกมeshellแยกต่างหาก ฉันไม่ได้ใช้eshellมากนัก เลยรู้สึกบ่อย ๆ ว่าตัวเองอาจกำลังพลาดประโยชน์ของมันไปความคิดเห็นจาก 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 ได้เลย
f1ไว้กับการ สลับระหว่างโหมดอักขระกึ่งหนึ่งกับโหมดคัดลอก และก่อนรันคำสั่งจะคิดก่อนว่า “ถ้า Emacs ตายหรือถูกบังคับปิดจะโอเคไหม?” ถ้าไม่โอเคก็รันในเทอร์มินัลปกติGhostel ยังไม่ได้ผสานรวมในระดับเดียวกัน และส่วนขยายอย่าง evil-ghostel-mode ก็มีข้อจำกัด อยากรู้ว่ามีแผนจะปรับปรุงตรงนี้หรือไม่ หรือเป็นข้อจำกัดเชิงโครงสร้างที่ต้องยอมรับ และถ้ามี ฟีเจอร์ที่เทียบเท่า eat-eshell-mode ก็น่าจะยอดเยี่ยมมาก
ควรมี Emacs อยู่ในชื่อเรื่องด้วย เทอร์มินัลอีมูเลเตอร์ทั่วไปกับเทอร์มินัลอีมูเลเตอร์สำหรับ Emacs นั้นต่างกัน
.elคือ นามสกุลไฟล์ของ Emacs Lispเพิ่งย้ายจาก vterm มาเป็น Ghostel เมื่อไม่นานนี้ โดยรวมดีกว่ามาก เร็วขึ้นอย่างเห็นได้ชัด จนแอป TUI หรู ๆ ที่อัปเดตทั้งเทอร์มินัลทุกเฟรมก็ทำงานได้ถูกต้อง การจัดการอินพุตเสถียรกว่า และ ELisp API ก็ดี
แต่ก็เคยเจอกรณีที่มันล้างเทอร์มินัลไม่ถูก ทำให้มีเศษค้างอยู่เหนือ prompt ปัจจุบัน หรือค้างสนิทจนต้องปิดบัฟเฟอร์แล้วเริ่มใหม่ สำหรับการใช้งานประจำวันถือว่าเพียงพอและอนาคตดูสดใส แต่กว่าจะเป็นผลิตภัณฑ์ที่สุกงอม ยังต้องมี การแก้บั๊กและงานเก็บรายละเอียด อีก
อาการมีเศษค้างด้านบนของหน้าจออาจเป็น https://github.com/dakra/ghostel/issues/495 ซึ่งแก้แล้วในเวอร์ชันล่าสุด แต่ก็อาจเป็นบั๊กอื่นได้ ส่วนที่ยากคือการ replicate ข้อมูลภายในของ libghostty-vt ไปยังบัฟเฟอร์ Emacs แล้วแทนที่เฉพาะส่วนที่จำเป็น เรามี property-based tests ที่ตรวจสอบเรื่องนี้ค่อนข้างกว้าง แต่บางครั้งปัญหาก็ยังหลุดรอดไป
ใน release ล่าสุดตอนนี้มีการปรับปรุงการจัดการวงจรชีวิต จึงอาจแก้ปัญหาบางอย่างไปพร้อมกันแล้ว ตอนนี้ยังอยู่ช่วงเริ่มต้น จึงตั้งใจจะค่อย ๆ ขัดเกลาไป
อยากเห็น ตัวอย่างจริงของการใช้โหมดอินพุตต่าง ๆ อย่างมีประสิทธิภาพ เทอร์มินัลต้องการรับทุกคีย์ที่พิมพ์ ส่วน editor ก็อยากใช้คีย์กับคำสั่งของตัวเอง ดังนั้นเทอร์มินัลที่ฝังอยู่ใน editor จำเป็นต้องมีโหมดสำหรับสลับความเป็นเจ้าของคีย์บอร์ด
:termของ Neovim มีแค่สองโหมด แต่ถ้าไม่เข้าใจว่าทำไม Ghostel ถึงมีถึงห้าโหมด ก็อาจเข้าใจผิดว่าออกแบบเกินความจำเป็นได้ง่าย จริง ๆ แล้วมันซับซ้อนเพราะแก้ปัญหาได้มากกว่า และโหมดเพิ่มเติมก็เป็นเครื่องมือเสริมสำหรับงานที่ nvim ไม่ได้จัดการ เพียงแต่ยังไม่ชัดพอว่าจะใช้ข้อดีนี้ในการทำงานจริงอย่างมีประสิทธิภาพได้อย่างไรระหว่างที่มีปัญหากับการใช้ vterm ลองใช้ 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 กลายเป็น ศูนย์กลางหลัก ของงานไปแล้ว
ถ้าเปิดเทอร์มินัลประมาณ 10 ตัวในหลายหน้าต่าง Ghostty แครชแทบทุกคืน ทำให้ผมใช้ Ghostty จริงจังไม่ได้ และก็ไม่อยากฝังมันไว้ในเครื่องมือที่ใช้ทุกวันด้วย
ยังลองรัน
ls -lRใน shell ให้มากที่สุดเท่าที่ทำได้บน filesystem ที่มีไฟล์หลายล้านไฟล์ แต่ก็ไม่พบปัญหาหรือการแครช ใช้งานหนักตลอดทั้งวันเป็นเวลาหลายเดือนก็ไม่เคยเห็นการแครชหรือพฤติกรรมน่าสงสัยดูมีความเป็นไปได้สูงว่าเป็นบั๊กของ Ghostty บางเวอร์ชัน หรือเป็น interaction แปลก ๆ กับซอฟต์แวร์อื่นอย่างไดรเวอร์ GPU ที่นี่ใช้ NVIDIA GPU
เห็นบอกว่า “native module เป็นไบนารีที่ build ไว้ล่วงหน้า และจะดาวน์โหลดอัตโนมัติเมื่อใช้งานครั้งแรก” เลยสงสัยว่าทำไม ไม่รวมไว้ในแพ็กเกจที่แจกจ่าย
แพ็กเกจยอดนิยมที่ใช้ Emacs native module อย่าง jinx ก็มีวิธีคอมไพล์ตอนใช้งานครั้งแรกเหมือน vterm ดังนั้นถ้าจะรองรับการติดตั้งที่เป็นมิตรต่อผู้ใช้ในแพ็กเกจ Emacs ในทางปฏิบัติก็มีแค่ ดาวน์โหลดหรือคอมไพล์ตอนรันครั้งแรก เท่านั้น