สารานุกรมการจัดการหน้าต่างใน Emacs
(karthinks.com)- การจัดการหน้าต่าง ของ Emacs อยู่บนโครงสร้างที่ยืดหยุ่นซึ่งแยกบัฟเฟอร์กับหน้าต่างออกจากกัน แต่มีการใช้งานพื้นฐานที่ค่อนข้างเทอะทะครอบอยู่ ผู้ใช้จึงต้องผสมผสานเครื่องมือกับความเคยชินของตนเองเพื่อสร้างเวิร์กโฟลว์
- จุดเน้นของบทความนี้ไม่ได้อยู่ที่กฎ
display-bufferหรือ workspace แบบแท็บ แต่เน้นไปที่การใช้งานด้วยมือภายใน Emacs frame เดียว เช่น การย้ายโฟกัส, การจัดวางบัฟเฟอร์, การแบ่ง/ลบหน้าต่าง, และงานชั่วคราว other-window,windmove,winum,ace-window, เมาส์,transpose-frame,window-prefix-map,other-window-prefix,winner-modeต่างแบ่งหน้าที่กันในด้านการย้ายแบบวนรอบ, การย้ายตามทิศทาง, การเลือกตามหมายเลข, การเลือกด้วย hint, การแปลง layout, และการกู้คืน- หากใช้
ace-windowร่วมกับ Avy คุณสามารถเลือกหน้าต่างที่ต้องการแล้วค่อยสั่งคำสั่ง หรือกระโดดไปยังตำแหน่งข้อความได้ทันทีเพื่อลดความจำเป็นในการ สลับหน้าต่างโดยตรง - การจัดการหน้าต่างของ Emacs ไม่ได้เป็นระบบตายตัวที่ซับซ้อน แต่ใกล้เคียงกับ ปัญหาที่ยังเปิดอยู่ มากกว่า โดย
display-buffer-alist, Popper·Popwin, การจัดการ window tree, และการผสานกับ tiling WM นั้นทรงพลัง แต่ก็มาพร้อมต้นทุนด้านการตั้งค่าและช่องโหว่ของมันเอง
สิ่งที่ Emacs เรียกว่า window management
- window ของ Emacs คือ viewport หรือ panel ภายใน frame ส่วน buffer คือก้อนข้อความต่อเนื่องที่อาจเป็นเนื้อหาไฟล์หรือไม่ใช่ก็ได้
- IDE และเอดิเตอร์จำนวนมากแทบจะรวมแนวคิดเรื่อง window กับ buffer เข้าด้วยกันเพื่อลดภาระในการทำความเข้าใจ แต่ Emacs แยกสองสิ่งนี้ออกจากกันเพื่อให้จัดวางได้ยืดหยุ่นกว่า
- การเปิดดูไฟล์เดียวกันสองครั้งทำได้ง่าย
- สามารถจัดการบัฟเฟอร์ที่ไม่ได้ตรงกับไฟล์โดยตรงได้อย่างเป็นธรรมชาติ
- indirect buffers ของ Emacs ใกล้เคียงกับสำเนาแบบ “live” ของทั้งบัฟเฟอร์
- โครงสร้างแบบแยกนี้มีต้นทุนสำหรับผู้ใช้ใหม่
- ต้องวางหน้าต่างให้อยู่ในตำแหน่งที่ต้องการภายใน frame
- ต้องใส่บัฟเฟอร์ที่ต้องการลงในหน้าต่างที่ต้องการ
- ในช่วงแรก ภาระในการควบคุมมักเด่นชัดกว่าข้อดีของโครงสร้างแบบแยกนี้
สิ่งที่บทความนี้ไม่ได้ครอบคลุม
- กฎการแสดงบัฟเฟอร์อัตโนมัติ ที่ควบคุม
display-bufferอย่างสมบูรณ์ไม่ใช่หัวข้อหลัก- API ของ
display-bufferต้องอาศัยความเข้าใจรายละเอียดอย่างwindow-parameters, slot, หน้าต่างเฉพาะทาง เป็นต้น - แหล่งอ้างอิงที่เกี่ยวข้องได้แก่ บทความของ Mickey Peterson, วิดีโอของ Protesilaos Stavrou, และ Emacs Lisp manual
- API ของ
- แท็บ, workspace, การแยกบัฟเฟอร์, และการคงอยู่ของเซสชัน ก็อยู่นอกขอบเขตหลักเช่นกัน
- เครื่องมือที่เกี่ยวข้องมี tab-bar, tabspaces, eyebrowse, tab-bookmark, desktop.el, persp-mode.el, perspective, project-tab-groups, beframe, activities.el
- ทางเลือกแบบสุดโต่งที่ทำงานเสมือนเป็น window manager แยกต่างหากภายใน Emacs ก็ไม่ถูกรวมไว้ในเนื้อหาหลัก
- จุดสนใจที่แท้จริงคือ การใช้งานด้วยมือในชีวิตประจำวัน
- การสลับโฟกัสหน้าต่าง
- การย้ายบัฟเฟอร์ระหว่างหน้าต่าง
- การแบ่งและลบหน้าต่าง
- การควบคุมหน้าต่างปลายทางที่ใช้แสดงผลระหว่างรันคำสั่ง
การย้ายพื้นฐาน: other-window และ “หน้าต่างถัดไป”
other-windowคือคำสั่งพื้นฐานสำหรับสลับหน้าต่างที่เรียนกันในบทสอน Emacs โดยคีย์เริ่มต้นคือC-x o- ลำดับการเลือกโดยทั่วไปจะวนตามเข็มนาฬิกาภายใน frame
- เมื่อมีหน้าต่างน้อย ก็เรียบง่ายและใช้งานได้ดีพอ
- เมื่อหน้าต่างมากขึ้น ก็ต้องเรียกหลายครั้งกว่าจะไปถึงตำแหน่งที่ต้องการ
- ยังมีวิธีปรับให้ใช้งานได้จริงมากขึ้น
- สามารถ bind ไปที่คีย์ที่กดง่ายกว่า เช่น
M-o - ใช้ numeric argument เพื่อข้ามหลายช่องหรือย้ายย้อนทิศได้
- ถ้าเปิด
repeat-modeจะกดซ้ำได้แบบC-x o o o...หรือM-o o o...
- สามารถ bind ไปที่คีย์ที่กดง่ายกว่า เช่น
- หากตั้งค่า window parameter
no-other-windowไว้other-windowจะข้ามบางหน้าต่าง- นี่คือเหตุผลที่หน้าต่างด้านข้างแบบ file manager อย่าง
dired-sidebarหรือdirvish-sideไม่ถูกรวมอยู่ในเป้าหมายของother-window
- นี่คือเหตุผลที่หน้าต่างด้านข้างแบบ file manager อย่าง
- แนวคิดเรื่อง “หน้าต่างถัดไป” ยังเป็นเป้าหมายเริ่มต้นของคำสั่งที่ทำงานกับอีกหน้าต่างหนึ่ง เช่น
scroll-other-window
การย้ายตามทิศทาง: windmove
windmoveเป็น ไลบรารีที่มีมาในตัว สำหรับย้ายโฟกัสหน้าต่างตามทิศทาง- ใกล้เคียงกับการย้ายซ้าย/ขวา/ขึ้น/ลงที่ผู้ใช้ Vim คาดหวัง
- ผู้ใช้
evil-modeก็ใช้ Windmove อยู่ภายในเช่นกัน
- สามารถผูก
windmove-left,windmove-right,windmove-up,windmove-downเข้ากับWASD,HJKL, ปุ่มลูกศร ฯลฯ ได้ - คำสั่ง
windmove-swap-states-left/right/up/downใช้ สลับบัฟเฟอร์ของหน้าต่าง ตามทิศทาง- ในกรณีนี้ โฟกัสจะย้ายไปพร้อมกับบัฟเฟอร์
- ตระกูลคำสั่ง
windmove-delete-*ก็ใช้ลบหน้าต่างในทิศที่กำหนดได้ แต่ในบทความจะพูดถึงวิธีอื่นเพิ่มเติมด้านล่าง - ในสภาพแวดล้อม tiling WM สามารถผสานให้ใช้คีย์เดียวกันในการย้ายระหว่างหน้าต่างภายใน Emacs กับหน้าต่างของ OS ได้
- ตัวอย่างการผสาน Emacs+i3wm ดูได้ที่ i3 integration
- ตัวอย่างการตั้งค่าสำหรับ qtile ดูได้ใน emacs-wm.el
ปล่อยให้ OS จัดการ Emacs window แทน
- frames-only-mode จะเปิดทุกบัฟเฟอร์เป็น frame ใหม่แทน Emacs window เพื่อให้ OS window manager เป็นผู้จัดการ
- วิธีนี้ทำให้บัฟเฟอร์ของ Emacs ถูกจัดการในระดับเดียวกับหน้าต่างของ OS จึงสามารถใช้คีย์เดียวกันควบคุมทั้งสองฝั่งได้
- เครื่องมืออย่าง Avy,
winum,ace-window,scroll-other-windowสามารถใช้ได้ไม่เฉพาะกับหน้าต่าง แต่กับทั้ง frame ด้วย - คำสั่งบางอย่างของ Emacs สมมุติว่ามันสามารถแบ่ง frame ได้ตามต้องการ จึงอาจเกิดกรณียกเว้นขึ้น
- โดยเฉพาะคำสั่งใน org-mode มักมีแนวโน้มเช่นนั้น
- ยังไม่มีประสบการณ์ใช้งาน
frames-only-modeบน Wayland compositor
เลือกได้ทันทีด้วยหมายเลขและคำใบ้
- winum แสดงหมายเลขหน้าต่างบน mode line และให้เลือกหน้าต่างด้วยหมายเลขได้
- ถ้า
other-windowใกล้เคียงกับ O(n) และwindmoveเป็นวิธีที่อาศัยการจัดวางเชิงพื้นที่winumจะใกล้เคียงกับการเข้าถึงแบบ O(1) เมื่อรู้หมายเลข - ถ้าเรียกคำสั่งเลือกพร้อม prefix argument ติดลบ จะลบหน้าต่างนั้น
- เมื่อ minibuffer ทำงานอยู่ จะได้รับหมายเลข 0 เสมอ
- ทำงานข้าม Emacs frame ได้
- ถ้า
- คีย์เริ่มต้น
C-x w <n>อาจยาว จึงมีตัวอย่างการตั้งค่าให้ใช้M-0ถึงM-9สำหรับเลือกหน้าต่าง - ace-window ถูกยกให้เป็นเครื่องมือทรงพลังสำหรับควบคุมหน้าต่าง Emacs ด้วยคีย์บอร์ด
- แสดงคำใบ้บนแต่ละหน้าต่าง แล้วกดคีย์นั้นเพื่อเลือก
- ถ้าเปิด
ace-window-display-modeก็จะแสดงคำใบ้บน mode line ตลอดได้เหมือน winum - กด
?เพื่อเปิดเมนู dispatch
- จุดแข็งของ
ace-windowคือไม่ได้มีแค่การเลือก แต่เป็นการ เลือกหน้าต่างแล้วค่อยรันแอ็กชัน- ลบหน้าต่าง
- ย้ายและสลับหน้าต่าง
- แบ่งหน้าต่าง
- แสดงบัฟเฟอร์ที่ต้องการ
- ใช้งานกับหน้าต่างอื่นโดยไม่ต้องออกจากหน้าต่างที่เลือกอยู่
เมาส์และการแปลงเลย์เอาต์
- ใน Emacs เมาส์เข้ากับการเลือกหน้าต่าง ปรับขนาด เมนูตามบริบท และ drag and drop ได้อย่างเป็นธรรมชาติ
context-menu-modeและแถบเมนูช่วยให้ค้นพบฟีเจอร์ได้ง่ายขึ้น
- ถ้ามือออกจากคีย์บอร์ดไปแล้ว การควบคุม Emacs ด้วยเมาส์อาจเป็นเส้นทางที่ฝืนน้อยที่สุด
- ยังมีงานที่ทำได้ด้วย mouse gesture
- แบ่ง frame แนวตั้ง·แนวนอน
- ลบหน้าต่าง
- วนบัฟเฟอร์ภายในหน้าต่าง
- สลับหน้าต่างไปทางขวา·ซ้าย
- สลับสองบัฟเฟอร์ล่าสุดในหน้าต่างเดียว
- ตั้งค่า
mouse-autoselect-windowเพื่อเปิดพฤติกรรมแบบ focus-follows-mouse ได้ - transpose-frame มีคำสั่งสำหรับหมุนหรือพลิกเลย์เอาต์หน้าต่างของ frame
rotate-frame,flip-frame,flop-frameถูกยกให้เป็นคำสั่งที่น่าใช้บ่อย- ส่วน
transpose-frameเองเป็นการ transpose ตามแนวทแยงหลัก จึงถูกมองว่าใช้งานจริงได้น้อย
window-prefix-map และต้นไม้ของหน้าต่าง
window-prefix-mapคือชุดคำสั่งจัดการหน้าต่างที่มากับ Emacs และผูกไว้กับC-x wsplit-root-window-rightและsplit-root-window-belowใช้แบ่ง root window ของ frame- ผูกไว้กับ
C-x w 3,C-x w 2ตามลำดับ - ต่างจากคำสั่งแบ่งปกติที่ซอยหน้าต่างปัจจุบันลงไปอีก จึงมีประโยชน์เมื่ออยากสร้าง workspace แยก
- ผูกไว้กับ
- หน้าต่างใน Emacs ถูกจัดวางเป็นโครงสร้างแบบต้นไม้
- หน้าต่างจริงคือโหนดใบ
- การแบ่งจะเปลี่ยน leaf ให้กลายเป็นโหนดพาเรนต์ของสองหน้าต่าง
- คล้ายการจัดวางของตัวจัดการหน้าต่างแบบ manual tiling อย่าง i3 หรือ bspwm
tab-window-detachและtear-off-windowย้ายหน้าต่างปัจจุบันไปเป็นแท็บใหม่หรือ frame ใหม่- คีย์เริ่มต้นคือ
C-x w ^ t,C-x w ^ fซึ่งถูกมองว่ายาว - ใช้แอ็กชัน dispatch ของ
ace-windowหรือการ bind กับเมาส์แทนได้
- คีย์เริ่มต้นคือ
other-window-prefix: เปลี่ยนตำแหน่งแสดงผลของคำสั่งถัดไป
other-window-prefixเป็นคำสั่งในตัวที่ผูกกับC-x 4 4และทำให้บัฟเฟอร์ที่คำสั่งถัดไปแสดง ไปปรากฏใน หน้าต่างถัดไป- คำสั่งนี้แยกคำสั่งที่ใช้แสดงบัฟเฟอร์ เช่น เปิดไฟล์ สลับบัฟเฟอร์ หรือกระโดดไป bookmark ออกจากการเลือกหน้าต่างปลายทาง
- ทำให้ไม่ต้องคอยจำชุดคำสั่งอย่าง
find-file-other-window,find-file-other-tab,find-file-other-frameทุกครั้ง - สามารถใส่ prefix ไว้หน้าคำสั่งพื้นฐานอย่าง
find-file,find-file-read-only,switch-to-bufferเพื่อเปลี่ยนเฉพาะตำแหน่งแสดงผลได้
- ทำให้ไม่ต้องคอยจำชุดคำสั่งอย่าง
- ในตัวอย่างของ Forge ปัญหาที่กด
RETจากรายการ issue แล้วหน้าต่างปัจจุบันถูกเขียนทับ สามารถหลีกเลี่ยงได้ด้วยother-window-prefix - ยังให้พฤติกรรมที่สม่ำเสมอได้เมื่อแต่ละแพ็กเกจ เช่น Magit, Org mode, Notmuch, Elfeed, EWW มีวิธี “เปิดในหน้าต่างอื่น” ไม่เหมือนกัน
- ยังมี prefix ที่เกี่ยวข้องด้วย
same-window-prefixบังคับให้บัฟเฟอร์ของคำสั่งถัดไปเปิดในหน้าต่างปัจจุบันother-frame-prefixบังคับให้เปิดใน frame ใหม่other-tab-prefixบังคับให้เปิดในแท็บใหม่
บันทึกและย้อนคืนการจัดหน้าต่าง
window-configuration-to-registerเป็นคำสั่งพื้นฐานสำหรับบันทึกการจัดหน้าต่างปัจจุบันลง register โดยคีย์เริ่มต้นคือC-x r wjump-to-registerใช้กู้คืนการจัดที่บันทึกไว้ โดยคีย์เริ่มต้นคือC-x r j- ในระดับ Elisp สามารถเก็บผลลัพธ์ของ
current-window-configurationไว้ในตัวแปร แล้วนำไปใช้ด้วยset-window-configurationได้- ถ้าบันทึกลงดิสก์ด้วยวิธีอย่าง
prin1,persist,multisessionก็จะเป็นจุดเริ่มต้นของความสามารถในการกู้คืนข้ามเซสชัน - แต่การที่ตำแหน่งเคอร์เซอร์ของแต่ละหน้าต่างถูกกู้คืนด้วย อาจเป็นพฤติกรรมที่ปกติแล้วไม่ต้องการ
- ถ้าบันทึกลงดิสก์ด้วยวิธีอย่าง
- ยังมีตัวเลือกแบบ “oops” ที่คอยสะสมการจัดก่อนหน้าให้อัตโนมัติ
winner-mode: ถ้าไม่ได้ใช้แท็บ สามารถใช้winner-undo,winner-redoเพื่อย้อนหรือทำซ้ำการเปลี่ยนแปลงการจัดหน้าต่างtab-bar-history-mode: มี history stack แยกตามแต่ละแท็บundelete-frame-modeและtab-undo: กู้คืน frame หรือแท็บที่ปิดไปโดยไม่ตั้งใจ
- การใช้
winner-undoเป็นวิธีคอยแก้สถานการณ์ทุกครั้งหลัง Emacs เปิดบัฟเฟอร์ไปผิดที่ เป็นเพียงทางออกชั่วคราวมากกว่า- ปัญหาที่แท้จริงคือกติกาการแสดงบัฟเฟอร์ยังไม่ได้ตั้งให้ตรงตามที่ต้องการ
ปรับให้เหมาะกับสองหน้าต่างที่สลับไปมาบ่อย
- ในการทำงานจริง แม้บนหน้าจอจะมีหลายหน้าต่าง แต่ส่วนใหญ่จะสลับบ่อยอยู่ ระหว่างสองหน้าต่าง
- Code & REPL
- Code & Grep
- Prose & Notes
- Listing & Item
- หน้าต่างอื่นอาจเป็นพื้นที่อ้างอิงที่ดูบ่อยแต่แทบไม่สลับไปมา เช่น เอกสาร ข้อมูลดีบัก ล็อก ผลลัพธ์คำสั่ง สารบัญ ตัวสำรวจไฟล์ หรือพรีวิว
- มีตัวอย่าง
other-window-mruที่ใช้get-mru-windowเพื่อสลับไปยังหน้าต่างที่ใช้งานล่าสุด- ไม่ว่าจะเลือกหน้าต่างที่สองด้วยเมาส์,
ace-window,winumหรือวิธีใด หลังจากนั้นก็สามารถสลับไปกลับได้อย่างรวดเร็ว
- ไม่ว่าจะเลือกหน้าต่างที่สองด้วยเมาส์,
- ยังมีแนวทางปรับปรุง
other-windowเองด้วย- ถ้ามีหน้าต่างเดียวก็ให้แบ่งอัตโนมัติ
- วนตามลำดับการใช้งานล่าสุดแบบ switchy-window
- หรือแบบ
other-window-alternatingที่กลับทิศทางทุกครั้งเมื่อไม่ได้เรียกต่อเนื่อง เพื่อให้การสลับไปมาระหว่างสองหน้าต่างเป็นธรรมชาติ
ขยาย ace-window ให้เป็นฐานสำหรับการรันคำสั่ง
ace-windowสามารถใช้เฉพาะขั้นตอนเลือกหน้าต่างอย่างเดียวได้ผ่านaw-select- ถ้านำหน้าต่างที่
aw-selectส่งคืนมาไปใช้รันงานที่ต้องการ ก็จะสร้างแพตเทิร์น “เลือกหน้าต่าง → รันงาน → คงหน้าต่างเดิมไว้” ได้ - คำสั่งตัวอย่างมีดังนี้
ace-tear-off-window: แยกหน้าต่างที่เลือกด้วยace-windowออกไปเป็น frame ใหม่ace-tab-window-detach: ย้ายหน้าต่างที่เลือกไปเป็นแท็บใหม่
ace-window-one-commandทำให้รันคำสั่งเดี่ยวใดก็ได้ในหน้าต่างที่เลือก- ก่อนอื่นเลือกหน้าต่างด้วย
ace-window - จากนั้นอ่าน key sequence แล้วรันคำสั่งนั้น
- จึงสามารถสั่งงานหน้าต่างอื่นได้โดยไม่เปลี่ยนหน้าต่างที่เลือกเดิม
- ก่อนอื่นเลือกหน้าต่างด้วย
ace-window-prefixทำงานคล้ายเวอร์ชันที่ตรงไปตรงมาของother-window-prefix- เลือกหน้าต่างปลายทางที่บัฟเฟอร์ของคำสั่งถัดไปจะไปแสดง ด้วย
ace-windowโดยตรง - ถ้าจำเป็นก็สร้างหน้าต่างใหม่ขึ้นมาทันทีด้วยแอ็กชันของ
ace-windowแล้วแสดงที่นั่นได้ - คีย์ลัดที่เสนอคือ
C-x 4 o
- เลือกหน้าต่างปลายทางที่บัฟเฟอร์ของคำสั่งถัดไปจะไปแสดง ด้วย
จำเป็นต้องสลับหน้าต่างเสมอไปหรือไม่
- เป้าหมายของการสลับหน้าต่างแบ่งได้ใหญ่ ๆ เป็นสองแบบ
- Switch and stay: แก้ไขหรือทำงานต่อในหน้าต่างปลายทาง
- Switch and return: ทำงานชั่วคราวอย่างการเลื่อน ค้นหา คัดลอก หรือลบ แล้วกลับไปยังตำแหน่งเดิม
- ในกรณีแรก Avy สามารถรวมการสลับหน้าต่างกับการย้ายเคอร์เซอร์ให้เป็นอย่างเดียวได้
avy-goto-char-timerมอง Emacs window และ frame หลายอันที่มองเห็นบนหน้าจอเป็นกลุ่มผู้สมัครสำหรับการกระโดดชุดเดียว- กระโดดไปยังสตริงที่กำหนดหรือตำแหน่งของอักขระ พร้อมสลับหน้าต่างให้อัตโนมัติ
- ถ้า Avy ไม่ข้ามระหว่าง window และ frame ควรตรวจสอบการตั้งค่า
avy-all-windows
pop-global-markเป็นวิธีสำหรับย้อนกลับไปยังตำแหน่งก่อนหน้า และหากจำเป็นก็อาจมีการสลับหน้าต่างร่วมด้วย- โดยปกติจะสลับบัฟเฟอร์ในหน้าต่างปัจจุบัน จึงมี advice ที่ทำให้ใช้
pop-to-buffer
- โดยปกติจะสลับบัฟเฟอร์ในหน้าต่างปัจจุบัน จึงมี advice ที่ทำให้ใช้
- ในกรณีที่สอง สามารถห่อขั้นตอน “สลับ → ลงมือทำ → กลับคืน” ไว้ในคำสั่งเพื่อทำงานอัตโนมัติได้
ace-window-one-commandเป็นวิธีทั่วไปแบบหนึ่ง- สำหรับงานเฉพาะ สามารถทำเป็นคำสั่งหรือมาโครเฉพาะได้
การเลื่อน ค้นหา และสลับบัฟเฟอร์ในหน้าต่างอื่น
scroll-other-windowและscroll-other-window-downจะเลื่อนหน้าต่างอื่นโดยไม่ออกจากหน้าต่างปัจจุบัน- เป้าหมายเริ่มต้นคือ “หน้าต่างถัดไป” ตามเข็มนาฬิกาจากหน้าต่างปัจจุบัน
- หากมีหน้าต่างตั้งแต่สามบานขึ้นไป อาจเลื่อนไปยังหน้าต่างอื่นที่ไม่ใช่หน้าต่างอ้างอิงที่คาดไว้
- สามารถตั้งค่า
other-window-scroll-defaultเพื่อเปลี่ยนกฎการเลือกหน้าต่างที่จะเลื่อนได้- ถ้าใช้
get-lru-windowจะเลื่อนหน้าต่างที่ถูกใช้งานล่าสุดน้อยที่สุด - ถ้าใช้
get-mru-windowก็สามารถกำหนดให้เลื่อนหน้าต่างที่ใช้งานล่าสุดได้
- ถ้าใช้
- หากตั้งค่า
other-window-scroll-bufferก็สามารถระบุให้หน้าต่างที่แสดงบัฟเฟอร์เฉพาะเป็นเป้าหมายการเลื่อนได้- โดยมากถือเป็นตัวเลือกที่มีประโยชน์กับผู้เขียนแพ็กเกจ
- ตัวอย่าง
isearch-other-windowคือการเริ่มค้นหาในหน้าต่างเป้าหมายสำหรับการเลื่อน แล้วกลับมายังหน้าต่างเดิมเมื่อค้นหาเสร็จ- มีตัวอย่างการใช้งานร่วมกันของบัฟเฟอร์ shell และ Man
next-buffer,previous-buffer,switch-to-bufferก็สามารถขยายให้ทำงานในหน้าต่างอื่นได้ด้วยการใช้ prefix argument ร่วมกับother-window-for-scrolling- หากสร้าง keymap สำหรับ
repeat-modeก็จะสามารถใช้n,p,bเพื่อวนหรือเลือกบัฟเฟอร์ของหน้าต่างอื่นต่อเนื่องได้
- หากสร้าง keymap สำหรับ
master-mode, scroll-all-mode, with-other-window
master-modeกำหนดบัฟเฟอร์ปัจจุบันเป็น “master” และบัฟเฟอร์อื่นเป็น “slave” เพื่อส่ง action ไปยังบัฟเฟอร์ slave ได้โดยไม่ต้องออกจากตำแหน่งปัจจุบัน- โดยทั่วไปถูกมองว่าเป็นทางเลือกที่โปร่งใสน้อยกว่าวิธี
other-window-scroll-defaultข้างต้น - ผ่าน
master-saysสามารถสร้างคีย์สำหรับสั่ง action ใดก็ได้ เช่นrecenterในบัฟเฟอร์ slave
- โดยทั่วไปถูกมองว่าเป็นทางเลือกที่โปร่งใสน้อยกว่าวิธี
scroll-all-modeจะผูกพฤติกรรมการเลื่อนของทุกหน้าต่างภายใน frame เข้าด้วยกัน- มีประโยชน์เมื่ออยากดูหลายมุมมองของหน้าต่างแบบซิงก์กัน
- มาโคร
with-other-windowเป็นตัวช่วย Elisp ที่รันโค้ดเนื้อหาในหน้าต่างที่other-window-for-scrollingเลือกไว้- ช่วยสร้างคำสั่งอย่าง
isearch-other-window,isearch-other-window-backwardsได้อย่างง่ายดาย - ใช้ได้เหมือนเป็นคู่ Elisp ของ
ace-window-one-commandแบบโต้ตอบ
- ช่วยสร้างคำสั่งอย่าง
กลยุทธ์ลดจำนวนหน้าต่าง
- UI ของเอดิเตอร์สมัยใหม่โดยมากมักลงตัวที่หน้าต่างหลักหนึ่งบาน แถบแท็บด้านบน แถบด้านข้างซ้ายสำหรับไดเรกทอรีหรือสารบัญ แผงด้านขวาแบบเลือกได้ และเทอร์มินัลด้านล่าง
- ใน Emacs ก็สร้างเลย์เอาต์แบบนั้นได้ แต่ก็สามารถใช้วิธีโฟกัสหน้าจอกับบัฟเฟอร์เดียว แล้วแทนการสลับหน้าต่างด้วยการสลับบัฟเฟอร์ได้เช่นกัน
- หากผ่อนให้มีได้แค่สองหน้าต่าง ก็ยังคงความเรียบง่ายไว้ได้เกือบทั้งหมด พร้อมใช้หน้าต่างที่สองเป็นข้อมูลอ้างอิงแบบสด
- การตั้งค่าเริ่มต้นของ Emacs รองรับแพตเทิร์นนี้ได้ค่อนข้างดีผ่านคำสั่งอย่าง
scroll-other-window
- การตั้งค่าเริ่มต้นของ Emacs รองรับแพตเทิร์นนี้ได้ค่อนข้างดีผ่านคำสั่งอย่าง
- ยังมีแนวทางที่มองหน้าต่างเป็นเพียงคอนเทนเนอร์ของข้อความแล้วไม่สนใจมัน
- ใช้ Avy กระโดดตรงไปยังตำแหน่งข้อความบนหน้าจอ
- ใช้
mark-ring,global-mark-ringเพื่อตามรอยตำแหน่งก่อนหน้า - dogears อาจให้ UI สำหรับการย้อนรอยที่ละเอียดกว่านี้
- สำหรับการตรึงตำแหน่งด้วยมือ สามารถใช้
point-to-registerและjump-to-registerได้ และสำหรับการบันทึกที่ถาวรกว่านั้นสามารถใช้bookmark-setและbookmark-jump
กฎการแสดงผลอัตโนมัติและการจัดการป๊อปอัป
display-buffer-alistเป็นตัวแปรที่จับคู่บัฟเฟอร์กับกฎ เพื่อกำหนดวิธีแสดงผลเมื่อโค้ด Elisp พยายามแสดงบัฟเฟอร์- ในทางทฤษฎี หากสร้างกฎเรื่องขนาด ตำแหน่ง บทบาท และโฟกัส สำหรับประเภทบัฟเฟอร์ที่ใช้ทุกวันแต่ละแบบ ก็จะแก้ปัญหาการจัดการหน้าต่างไปได้มาก
- ปัญหาคือ ต้นทุนในการตั้งค่า
- predicate ของบัฟเฟอร์และโหมด
- ประเภทหน้าต่างและสล็อต
- ฟังก์ชัน action ของ display-buffer
- พารามิเตอร์ของหน้าต่าง
- เจตนาง่าย ๆ อย่าง “อย่ามายุ่งกับการจัดวางหน้าต่างของฉัน” กลับแสดงออกได้ยาก
- ยังมีเครื่องมือเสริมอยู่
- Shackle ช่วยซ่อนความซับซ้อนของ
display-buffer-alistและให้อินเทอร์เฟซ Elisp ที่เรียบง่าย - Doom Emacs มีคำสั่งอำนวยความสะดวกอย่าง
set-popup-rule!
- Shackle ช่วยซ่อนความซับซ้อนของ
- Popwin และ Popper เริ่มต้นจากข้อสังเกตที่ว่าบัฟเฟอร์ไม่ได้เท่ากันทั้งหมด
- แยกระหว่างบัฟเฟอร์งานหลักกับบัฟเฟอร์ป๊อปอัปชั่วคราว
- แสดงเอกสาร shell สถานะงาน ผลคอมไพล์ ผลการค้นหา ข้อความ ฯลฯ เป็นหน้าต่างเสริมขนาดเล็กที่ปิดหรือวนดูได้ง่าย
- Popper เน้นการยกขึ้นและซ่อนลงของบัฟเฟอร์ป๊อปอัปที่กำหนดไว้ด้วยคีย์เดียว
- Popwin เป็น implementation ที่เก่ากว่าและครอบคลุมกว่า แต่รวมการตั้งค่า
display-bufferของตัวเองมาด้วย - หากเพียงต้องการเปิดปิด shell อย่างรวดเร็ว shell-pop หรือ vterm-toggle ก็อาจเพียงพอ
ชิ้นส่วนที่ยังขาด: การจัดการ window tree
- Emacs แทนหน้าต่างภายใน frame เป็น tree แต่คำสั่งสำหรับผู้ใช้ส่วนใหญ่กลับทำงานโดยดูจากตำแหน่งเชิงพื้นที่ ไม่ใช่โครงสร้าง tree
- ความไม่สอดคล้องนี้ทำให้การ split และ delete บางครั้งให้ผลต่างจากที่คาด หรือเกิดข้อจำกัดกับโครงสร้างการแบ่งที่สร้างได้
- หากมีแพ็กเกจสมมติอย่าง
window-treeก็อาจทำให้จัดการในระดับโหนดภายในได้- split, transpose, mirror เฉพาะบางส่วนของ frame
- เลือกหลายหน้าต่างเป็นองค์ประกอบย่อยบางส่วน
- ส่งองค์ประกอบที่เลือกไปยังแท็บหรือ frame
- โคลนหรือบันทึกองค์ประกอบย่อย
- ปกป้อง branch ของ tree บางส่วนจากคำสั่งตระกูล
display-buffer
- มีองค์ประกอบ Elisp ที่มีอยู่แล้วเช่นกัน
window-treeจะคืนค่า tree เองframe-root-windowจะคืนค่า root- มี
window-parent,window-child,window-*-sibling - สามารถวนสำรวจได้ด้วย
walk-window-tree,walk-windows
- จุดที่ยังขาดก็ชัดเจน
- นอกจาก split/delete ทั่วไปแล้ว ไม่มีฟังก์ชันพื้นฐานสำหรับแปลง tree
- ไม่มีแนวคิดเรื่องการเลือกหน้าต่างภายใน จึงต้องจำลองผ่าน UI
ชิ้นส่วนที่ขาดหายไป: การผสานรวมกับ tiling WM
- โมเดลต้นไม้ของหน้าต่างใน Emacs คล้ายกับตัวจัดการหน้าต่างแบบ tiling ที่ควบคุมด้วยตนเองอย่าง i3 หรือ bspwm มาก
- หากใช้ Emacs ภายใน i3, bspwm หรือ tmux ก็ย่อมอยากสลับไปมาระหว่างหน้าต่างภายในและภายนอก Emacs ด้วยคีย์ไบน์ดชุดเดียวกันอย่างเป็นธรรมชาติ
- ตัวอย่างที่มีอยู่แล้วคือ i3-integration ของ Pavel Korytov และ emacs-wm.el สำหรับ qtile
- อินเทอร์เฟซการผสานรวมที่สะอาดกว่านี้ควรมีองค์ประกอบต่อไปนี้
- ตัวจัดการหน้าต่างต้องสามารถระบุคลาสของหน้าต่างที่กำลังทำงานอยู่ และต้องมีการย้าย/จัดการหน้าต่างแบบโปรแกรมได้
- วิธีสื่อสารอาจเป็นคำสั่ง shell, socket, IPC แบบอิงเซิร์ฟเวอร์ หรือ D-Bus ของ Linux
- ฝั่ง Emacs ต้องมีอินเทอร์เฟซที่ไม่ขึ้นกับวิธีสื่อสาร ซึ่งเลียนแบบการจัดการแบบตัวจัดการหน้าต่าง
- เมื่อต้องสลับหน้าต่างของ OS ให้ตรวจสอบก่อนว่าหน้าต่างที่กำลังใช้งานคือ Emacs หรือไม่ และหากจำเป็น Emacs จะจัดการการควบคุมภายในเฟรมของตัวเอง
บทสรุป
- การจัดการหน้าต่างของ Emacs มีตัวเลือกหลายชั้น ตั้งแต่
other-windowไปจนถึงace-window, Avy, Popper และdisplay-buffer-alist - ไม่จำเป็นต้องใช้ทุกวิธี เพราะมีทางเลือกมากมายสำหรับแก้ปัญหาเดียวกัน จึงเลือกใช้เพียงอย่างหนึ่งและมองข้ามที่เหลือได้
- เครื่องมือพื้นฐานที่มากับระบบมีแนวโน้มจะได้รับการดูแลต่อเนื่องยาวนาน แต่แพ็กเกจจากภายนอกอาจมีสถานะแตกต่างกันไปตามการพัฒนา การถูกปล่อยทิ้ง หรือการกลายเป็นโครงการกำพร้า
- การจัดการหน้าต่างของ Emacs ไม่ได้ใกล้เคียงกับระบบตายตัวที่ซับซ้อน แต่คล้ายโครงสร้างแบบเปิดที่มอบวัตถุดิบและสูตรอาหารมากกว่า
- ต่อให้ตั้งค่าเพียงเล็กน้อยก็ใช้งานพื้นฐานได้ และด้วยการผสมผสานเพิ่มอีกนิดพร้อมการปรับ Elisp ก็สามารถสร้างเวิร์กโฟลว์ที่ปรับให้เหมาะกับตนเองได้มากขึ้น
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
บทความดี และคำอธิบายกับแนวทางก็ยอดเยี่ยม บางครั้งหน้าต่างที่แบ่งเท่า ๆ กันอย่างเดียวก็ไม่พอ เลยใส่ฟังก์ชันด้านล่างไว้ใน
init.elแล้ว bind กับC-x 7เพื่อปรับหน้าต่างปัจจุบันกับหน้าต่างคู่กันให้มีขนาดแนวตั้ง 70%/30%แนวนอนก็จัดการแบบเดียวกันด้วยการ bind กับ
C-x 8(defun partial-size-window ()"Set the two split windows to 70% and 30% vertically."(interactive)(let ((size (- (truncate (* .70 (frame-height))) (window-height))))(if (> size 0)(enlarge-window size))))(defun partial-size-window-h ()"Set the two split windows to 70% and 30% horizontally."(interactive)(let ((size (- (truncate (* .70 (frame-width))) (window-width))))(if (> size 0)(enlarge-window-horizontally size))))เป็นการสำรวจที่ดี ตอนนี้อยู่บนมือถือเลยยังลองทันทีไม่ได้ แต่ก็อยากลองใช้ ace-window,
ace-window-display-mode,frames-only-modeเร็ว ๆยอมรับตามตรงว่าใช้ Emacs มาราว 40 ปีแล้ว แต่ส่วนใหญ่ก็ใช้แค่
next-window, สร้างหน้าต่างใหม่ด้านล่างหรือด้านข้าง และพึ่งtmuxเวลาทำงานบนเซิร์ฟเวอร์ระยะไกลเท่านั้นC-x 1/2/3,C-x oแต่จริง ๆ แล้วมันก็ทรงพลังพอสมควรและพอสำหรับการใช้งานของผม เลยรู้สึกว่า tiling window manager หลายตัวก็น่าจะพอแค่นั้นเหมือนกัน
C-x oค่อนข้างยุ่งยาก เลยตั้งค่าด้านล่างไว้ แต่ก็ไม่ได้รู้สึกว่าแพ็กเกจเสริมพวกนี้จำเป็นขนาดนั้น(global-set-key (kbd "M-o") #'other-window)ถึงอย่างนั้นการมีบทความอ้างอิงที่ลงลึกแบบนี้ก็ดี และงานเขียนของ Karthik ก็มักให้ความรู้และน่าสนใจเสมอ
ขอเสริมจากบันทึกของ Karthink เรื่อง
windmove-modeว่าสามารถตั้งค่าwindmove-default-keybindingsเพื่อย้ายระหว่างหน้าต่างด้วยCtrl-<ปุ่มลูกศร>ได้(windmove-default-keybindings 'control)ผมใส่ไว้ใน config ของตัวเองด้วย และรวมไว้ใน starter kit แบบมินิมัลด้วย ซึ่งเป็น game changer จริง ๆ
https://codeberg.org/ashton314/emacs-bedrock
.emacsของผมแล้วพบว่าใช้เหมือนกันเป๊ะ ชอบแบบนี้มากกว่าC-x Oเพราะกดน้อยลงหนึ่งคีย์และยืดหยุ่นกว่าตั้งค่าไว้ไม่ให้ Emacs แบ่ง frame ออกเป็นหลายหน้าต่าง คล้ายกับที่เว็บเบราว์เซอร์ไม่ได้แบ่งหน้าต่างเบราว์เซอร์ออกเป็น viewport หลายอัน
Emacs ของผมไม่สร้าง frame ที่สองด้วย วิธีทำเปลี่ยนไปทุกปี เพราะวิธีเก่าเลิกทำงานไปเรื่อย ๆ
ด้วยวิธีปัจจุบัน ในบางสถานการณ์ frame จะถูกแบ่ง เช่นตอนที่ Emacs แสดงบัฟเฟอร์
*Completions*แต่เท่าที่จำได้ ผมไม่เคยต้องเรียกdelete-windowเองเพื่อปิดหน้าต่างที่สอง และมันจะหายไปเองเสมอผมใช้ frame แบบหลายหน้าต่างค่อนข้างเยอะ บน macOS รุ่นใหม่กับ Emacs รุ่นใหม่ บางครั้ง frame ที่มีหลายหน้าต่างดูเหมือนค้างและไม่ตอบสนองต่อคีย์บอร์ดหรือแทร็กแพด แม้กด
Ctrl-Gหลายครั้งก็ยังไม่หายแต่การสร้าง frame ใหม่ด้วย
C-x 5 2ยังทำงานได้ และเมื่อกลับไปยัง frame ที่ค้าง ก็สามารถปิด frame นั้นด้วยC-x 5 0ได้บทความลงลึกมาก และชอบจริง ๆ ที่ผู้เขียนใช้เวลาในการวาด แผนภาพ ดี ๆ
เป็นบทความที่ดี และอยากแชร์แพ็กเกจยอดเยี่ยมที่ใช้คู่กันได้ สำหรับผม zygospore เป็นเครื่องมือที่ขาดไม่ได้
“zygospore ทำให้สามารถกด
C-x 1(delete-other-window) แล้วกดC-x 1อีกครั้งเพื่อย้อนกลับได้”https://github.com/LouisKottmann/zygospore.el
https://www.emacswiki.org/emacs/WinnerMode
undoไม่ใช่หรือถึงจะแปลกหน่อย แต่ถ้าเป็น
C-u C-x 1ก็ดูสอดคล้องดีถ้าผู้เขียนเห็นสิ่งนี้ อยากบอกว่าดีใจที่มีการกล่าวถึง Acme editor นิดหน่อย
เวลาเรียก
hledger-balance-sheetอยากให้มันเปิดทางขวาด้วย ความกว้าง 30% โดยไม่แทนที่.hledger.journalเดิมและอยากให้
.hledger.journalยังคงเปิดอยู่ทางซ้ายและยังเป็นหน้าต่างที่ถูกเลือกอยู่