การควบคุม Zig Fmt
(matklad.github.io)zig fmtสามารถใช้เป็น formatter ที่ควบคุมได้ ซึ่งจัดวางโค้ดเดียวกันได้หลายรูปแบบโดยอิงจากรูปแบบไวยากรณ์ที่มีอยู่แล้วในไฟล์- ในการเรียกฟังก์ชัน การมีหรือไม่มี trailing comma จะทำให้ผลลัพธ์ต่างกัน: ถ้าไม่มี comma จะถูกรวมเป็นบรรทัดเดียว แต่ถ้ามี comma จะจัดอาร์กิวเมนต์แยกตามบรรทัด
- ขั้นตอนการใช้งานจริงคือกำหนดเลย์เอาต์โค้ดที่ต้องการก่อน แล้วเพิ่ม comma บางจุด จากนั้นกดคีย์ลัดสำหรับฟอร์แมตเพื่อให้
zig fmtจัดการส่วนที่เหลือ - ใน array ไม่ได้สะท้อนแค่ trailing comma เท่านั้น แต่ยังสะท้อน ตำแหน่งขึ้นบรรทัดใหม่ครั้งแรก ด้วย โดยถ้าขึ้นบรรทัดใหม่ครั้งแรกหลังสมาชิกตัวที่สาม ก็จะจัดแนวให้ได้ 3 รายการต่อบรรทัด
- หากใช้การเชื่อม array ด้วย
++อย่างระมัดระวัง ก็สามารถจัดจำนวนสมาชิกต่อบรรทัดให้ต่างกันได้ และเมื่อส่งคู่--keyกับvalueไปยัง subprocess ก็สามารถเชื่อม array ของอาร์กิวเมนต์คงที่กับ array ของคู่ตัวเลือกเพื่อจัดแนวได้
วิธีควบคุม zig fmt
zig fmtสามารถดูรูปแบบไวยากรณ์ที่มีอยู่แล้วในไฟล์ปัจจุบัน และใช้เป็น formatter ที่ ควบคุมได้ ซึ่งจัดวางไวยากรณ์เดียวกันได้หลายแบบ- ในการเรียกฟังก์ชัน การมีหรือไม่มี trailing comma จะเปลี่ยนเลย์เอาต์
f(1, 2, 3); // -> zig fmt -> f(1, 2, 3);f(1, 2, 3,); // -> zig fmt -> f( 1, 2, 3, ); - เวิร์กโฟลว์ในการใช้งานจริงคือกำหนดเลย์เอาต์โค้ดที่ต้องการก่อน เพิ่ม
,บางตัว แล้วกดคีย์ลัดฟอร์แมตเพื่อให้zig fmtจัดการส่วนที่เหลือ - แทนที่จะให้ formatter เดาเลย์เอาต์เอง การปล่อยให้ผู้ใช้คงการตัดสินใจหลักไว้ด้วยตัวเองอาจเหมาะกว่า
- ข้อสรุปคือ 90% ของการฟอร์แมตที่ดีขึ้นอยู่กับบรรทัดว่างระหว่าง logical block และการเลือกตัวแปรกลางที่เหมาะสม ดังนั้นแทนที่จะตัดตัวเลือกเหล่านี้ทิ้ง ก็ควรใช้ประโยชน์จากมัน
เลย์เอาต์การจัดแนวเป็นคอลัมน์ของ array
- ใน array ไม่ได้มีแค่ trailing comma ที่ทำให้จัดวางเป็นหนึ่งรายการต่อบรรทัด แต่
zig fmtยังสะท้อน ตำแหน่งขึ้นบรรทัดใหม่ครั้งแรก ด้วย.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; - ถ้าขึ้นบรรทัดใหม่ครั้งแรกหลังสมาชิกตัวที่สาม ผลลัพธ์ก็จะจัดแนวให้ได้ 3 รายการต่อบรรทัด
.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; - หากใช้การเชื่อม array ด้วย
++อย่างระมัดระวัง ก็สามารถจัดจำนวนสมาชิกต่อบรรทัดให้ต่างกันได้ - เมื่อส่งคู่
--keyกับvalueไปยัง subprocess สามารถเชื่อม array ของอาร์กิวเมนต์คงที่กับ array ของคู่ตัวเลือกเพื่อจัดแนวได้ดังนี้try run(&(.{ "aws", "s3", "sync", path, url } ++ .{ "--include", "*.html", "--include", "*.xml", "--metadata-directive", "REPLACE", "--cache-control", "max-age=0", }));
1 ความคิดเห็น
ความเห็นจาก Lobste.rs
จำได้ว่า
gofmtก็เคยมี พฤติกรรมชี้นำการจัดรูปแบบ คล้ายกัน และฉันชอบ formatter แบบนั้นมากกว่าrustfmtถึงอย่างนั้นก็ยังคิดว่ามีระบบจัดรูปแบบอัตโนมัติแบบไหนสักอย่างย่อมดีกว่าไม่มี formatter เลย
auto-formatter บังคับความธรรมดา ยกระดับคนที่ใช้การจัดรูปแบบไม่เก่ง แต่ก็ฉุดคนที่ใช้มันได้ดีลงมาด้วย
ถ้าทำงานร่วมกับคนอื่นแล้วพวกเขาต้องการ หรือเชื่อถือกฎการจัดรูปแบบส่วนตัวของแต่ละคนได้ยาก ฉันก็จะใช้ แต่ถ้าทำงานคนเดียวจะไม่ใช้เด็ดขาด
ฉันมีรสนิยมของฉัน และ formatter ก็มีรสนิยมของมัน ซึ่งทั้งสองอย่างต่างกันแบบไม่มีทางประนีประนอม
แต่สิ่งที่บทความนี้แสดงให้เห็นนั้นน่าประทับใจมาก
ประโยคนี้ทำให้นึกถึงบทพูดของแม่สื่อ Yente ใน Fiddler on the Roof ที่ว่า “ถึงจะเป็นสามีที่แย่—พระเจ้าช่วยห้ามไว้เถิด—ก็ยังดีกว่าไม่มีสามี!”
clang-formatแล้วมันแย่มากเสถียรภาพ ระหว่างเวอร์ชันต่ำเกินไป จนการอัปเกรด
clang-formatกลายเป็นคอมมิตจัดรูปแบบที่แตะทุกบรรทัดในโค้ดไม่แน่ใจจริง ๆ ว่ามันดีกว่าไม่มี formatter หรือเปล่า
auto-formatter ส่วนใหญ่แก้ปัญหาคน โดยกำจัด การเถียงเรื่องจุกจิก ใน pull request
แต่ตอนนี้เรากำลังย้ายไปสู่ การพัฒนาแบบเอเจนต์ ปัญหานั้นก็ยิ่งสำคัญน้อยลงเรื่อย ๆ
ตอนนี้หลายโปรเจกต์ให้เครื่องทำงานส่วนใหญ่แล้ว และในบริบทนั้นฉันเริ่มรู้สึกว่าการไม่รัน formatter น่าจะดีกว่า
เวลาเขียนอาร์กิวเมนต์บรรทัดคำสั่งใน Python ฉันชอบวิธี splat tuple ลงใน list ดังนั้นตัวอย่างสุดท้ายในบทความน่าจะเขียนแบบนี้
ตอนที่เช็กล่าสุด
zig fmtไม่มีวิธีตั้งให้ใช้ ข้อจำกัด 80 คอลัมน์ แทน 100 คอลัมน์ ยังเป็นแบบนั้นอยู่ไหม?ถ้าต้องทำงานวันละหลายชั่วโมง ฉันจะเพิ่มขนาดฟอนต์ในเทอร์มินัลเพราะล้าตาน้อยกว่า และความต่างระหว่าง 80 กับ 100 คอลัมน์เป็นตัวตัดสินเลยว่าจะวาง
vimแบบ split สองช่องกับnerd treeข้าง ๆ กันได้หรือไม่zig fmtไม่มี ข้อจำกัดจำนวนคอลัมน์ในฐานะคนที่เคยนำ rigid formatter เข้าไปใช้กับทีมที่ไม่เคยมี formatter มาก่อน บางครั้งก็คิดถึงความสามารถในการปรับผลลัพธ์การจัดรูปแบบด้วยมืออยู่เหมือนกัน
ในแง่นั้นความยืดหยุ่นของ Zig เจ๋งมากจริง ๆ
เยี่ยมเลย!
มี formatter สำหรับ TS/JS แบบนี้ไหม?
ฉันมีโปรเจกต์ที่ใช้
maplibre-glและ expression ของ style spec บางทีก็ถูกจัดรูปแบบหนักเกินไปจนอ่านอะไรไม่ออกตอนนี้เลยหยุดใช้ formatter ไปแล้ว แต่พอทั้งดีบัก ทั้งคัดลอก ทั้งคอมเมนต์โค้ด โค้ดก็เริ่มรกขึ้นเรื่อย ๆ
บางทีอาจทำให้ Zig formatter จัดรูปแบบภาษาอื่นได้ด้วยก็ได้ :)
เช่น ไม่มีทางสั่ง formatter ให้จัด 4 องค์ประกอบต่อบรรทัดได้
และถ้า object literal ยาวเกินไป ไม่ว่าข้อความต้นฉบับจะเป็นอย่างไร Prettier ก็จะเปลี่ยนมันเป็นแบบ “แต่ละองค์ประกอบอยู่คนละบรรทัด” อยู่ดี
ฉันเคยผิดหวังกับ formatter แบบเบา ๆ ที่จริง ๆ แล้วแทบจะเป็น formatter ที่ทำแค่ตัดบรรทัด ดังนั้นแนวคิดเรื่องความยืดหยุ่นแบบนี้ภายใต้ตัวอย่างที่เข้มงวดกว่าจึงทั้งน่าอิจฉาและน่าชอบ :p
ไม่นานมานี้ตอนตอบคำถามบน fedi เรื่องการเขียนและจัดรูปแบบ Lisp ด้วยฟอนต์แบบสัดส่วน ฉันได้พูดถึง s-expression สายพันธุ์ที่ใช้ช่องว่างอย่างมีความหมาย ได้แก่ wisp, Readable/Sweet expressions, SRFI 119 และ 110
และยังสังเกตด้วยว่าตระกูลไวยากรณ์นี้ใช้งานส่วนขยาย infix แบบเลือกใช้ เพื่อคืนอำนาจควบคุมเรื่องการขึ้นบรรทัดใหม่กลับมาได้บางส่วน
เป็นการออกแบบที่น่าสนใจ แต่ยังไม่แน่ใจว่าชอบไหม
ใน formatter ของฉัน ฉันทำอีกแบบ: formatter จะ ไม่สนใจ comma ท้ายรายการ ตอนตัดสินใจจัดรูปแบบ จากนั้นถ้ากลายเป็นหลายบรรทัดก็จะเติม comma ท้ายรายการเสมอ และถ้าอยู่บรรทัดเดียวก็จะลบทิ้งเสมอ
แบบนี้จึงทำ “การชี้นำ” ไม่ได้ แต่
f(1, 2, 3)จะถูกจัดรูปแบบอย่างสม่ำเสมอ ไม่ว่าจะมี comma ท้ายรายการหรือไม่ หรือมีช่องว่างระหว่าง token แบบไหนและมากน้อยเพียงใดการชี้นำระดับหนึ่งยังจำเป็นอยู่
ตัวอย่างเช่น ถ้ามี list literal ยาว ๆ
[<expr1>, <expr2>, ..., <expr100>]formatter ส่วนใหญ่คงจะวางแต่ละ expression คนละบรรทัด แต่บางครั้งคุณอาจอยากยัดให้ได้มากที่สุดในหนึ่งบรรทัดการใช้ comma ท้ายรายการมาตัดสินสองแบบนี้ดูแปลกสำหรับฉัน และโดยทั่วไปตัวเลือกอาจมีได้ไม่ใช่แค่ 2 แต่เป็น N แบบ
ฉันคิดว่าแอตทริบิวต์เหมาะกับจุดประสงค์นี้มากกว่า
เช่น อาจมีอยู่แล้วก็ได้ แต่แนวคิดอย่างใส่
#[rustfmt::list_layout(flow)]หน้า statement เพื่อให้มีผลกับการจัดรูปแบบ list literal ภายใน statement นั้นถ้ามีการชี้นำมากเกินไป มันจะบ่อนทำลาย จุดประสงค์ของ formatter ที่ต้องการทำให้รูปแบบโค้ดสม่ำเสมอทั้ง ecosystem และช่วยให้ code review ง่ายขึ้น ดังนั้นควรจำกัดไว้เฉพาะบางกรณี
สำหรับฉัน list literal ยาว ๆ เป็นตัวอย่างที่จำเป็นจริง ๆ
ในโปรเจกต์ของฉันก็มีกรณีที่การจัดรูปแบบช่วยในการรีวิวค่าคาดหวังของเทสต์อยู่เหมือนกัน ซึ่งก็คือที่นี่
และยังนึกถึง “การชี้นำ” แบบอื่นของ Dart formatter ได้อีกอย่างหนึ่ง: ใน list literal ยาว ๆ คุณสามารถเพิ่มบรรทัดคอมเมนต์เพื่อจัดกลุ่มบรรทัดได้
เช่น ถ้าเป็น
[1, 2, 3, ..., 1000]ปกติจะวางหนึ่งองค์ประกอบต่อหนึ่งบรรทัด แต่คุณสามารถจัดกลุ่มด้วยมือแบบนี้ได้ไม่แน่ใจว่านี่เป็นฟีเจอร์ที่ใส่มาโดยตั้งใจ หรือเป็นผลพลอยได้จากวิธีจัดการคอมเมนต์
rustfmtแบบเป๊ะ ๆ และนั่นแหละที่ทำให้ฉันแทบบ้าบางครั้งถึงจะ เกินข้อจำกัดความยาวบรรทัด ไปบ้าง แต่การไม่แยก function call ออกเป็นหลายบรรทัดกลับอ่านง่ายกว่า และฉันอยากสะท้อนการตัดสินใจนั้นของตัวเองได้
ตัวอย่างหนึ่งที่นึกออกคือ OpenGL
ปกติคุณมักแก้ไขหรือใช้งานรีซอร์สเดียวต่อเนื่องกัน เช่นตอนตั้งค่า texture เริ่มต้นซึ่งมีการเรียก
gl.*หลายครั้งติดกัน แต่rustfmtจะดันจัดแบบไร้ความรู้สึกโดยมีเป้าหมายเหมือนหุ่นยนต์แค่ว่า “บรรทัดยาวเกินไป ต้องหั่นบรรทัด”ตัวอย่างนี้จงใจทำขึ้นเพื่อแสดงพฤติกรรม และไม่ได้ตรงกับพฤติกรรมจริงของ
rustfmtทุกประการบรรทัดก็ไม่ได้ยาวมากขนาดนั้นด้วย
ตอนนี้ฉันพิมพ์จากมือถือเลยไม่มีเครื่องมือทำตัวอย่างที่แม่น 100% การแยก
gl.tex_parameteriต่อ ๆ กันออกเป็นหลายบรรทัดแบบนี้ จริง ๆ แล้วแสดงแต่ละคำสั่งเต็ม ๆ ในบรรทัดเดียวกลับดีกว่าเพราะถ้าจัดคอลัมน์ตรงกัน จะมองหาความต่างของสองบรรทัดได้ง่ายกว่ามาก
เวอร์ชันที่ถูกแยกบรรทัดทำให้ความใกล้ชิดทางสายตาลดลงและอ่านยากกว่า
มันทำให้เปรียบเทียบสองบรรทัดด้วยตาได้ไม่สะดวกอีกต่อไป
และยังมีกรณีน่าขำที่ formatter ล้มเหลวไปเลยเมื่อไม่สามารถจัดรูปแบบให้เข้าในข้อจำกัดจำนวนอักขระได้
เรื่องนี้เกิดบ่อยเวลาเขียนโค้ดคอมไพเลอร์และสร้างข้อความวินิจฉัยเป็น string literal เพราะข้อความอาจยาวพอสมควร
rustfmtไม่รู้จะแยกอย่างไร จึงยอมแพ้และไม่จัดรูปแบบ statement ทั้งก้อนนั้นเลยมักจะออกมาประมาณนี้ ตรงนี้เพียงเพราะการเรียก
emit_diagnosticเป็น expression มันจึงยอมแพ้กับการจัดรูปแบบ match statement ทั้งอัน ซึ่งงี่เง่ามากทั้งหมดนี้หลีกเลี่ยงได้ ถ้ามันไม่พยายามยัดโค้ดของฉันให้ไม่เกิน 100 คอลัมน์ตลอดเวลา
เผื่อใครที่ต้องไปค้นเหมือนฉันจากคอมเมนต์ท้าย ๆ:
++คือ ตัวดำเนินการเชื่อมอาร์เรย์ดังนั้นถ้าแยกอาร์เรย์เป็นสองส่วน ก็สามารถให้มันถูกจัดรูปแบบต่างกันได้