1 คะแนน โดย GN⁺ 2 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • 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 ความคิดเห็น

 
GN⁺ 2 시간 전
ความเห็นจาก Lobste.rs
  • จำได้ว่า gofmt ก็เคยมี พฤติกรรมชี้นำการจัดรูปแบบ คล้ายกัน และฉันชอบ formatter แบบนั้นมากกว่า rustfmt
    ถึงอย่างนั้นก็ยังคิดว่ามีระบบจัดรูปแบบอัตโนมัติแบบไหนสักอย่างย่อมดีกว่าไม่มี formatter เลย

    • คำพูดที่ว่า “มีอะไรก็ยังดีกว่าไม่มี formatter” นี่ปล่อยผ่านยากจริง ๆ
      auto-formatter บังคับความธรรมดา ยกระดับคนที่ใช้การจัดรูปแบบไม่เก่ง แต่ก็ฉุดคนที่ใช้มันได้ดีลงมาด้วย
      ถ้าทำงานร่วมกับคนอื่นแล้วพวกเขาต้องการ หรือเชื่อถือกฎการจัดรูปแบบส่วนตัวของแต่ละคนได้ยาก ฉันก็จะใช้ แต่ถ้าทำงานคนเดียวจะไม่ใช้เด็ดขาด
      ฉันมีรสนิยมของฉัน และ formatter ก็มีรสนิยมของมัน ซึ่งทั้งสองอย่างต่างกันแบบไม่มีทางประนีประนอม
      แต่สิ่งที่บทความนี้แสดงให้เห็นนั้นน่าประทับใจมาก
      ประโยคนี้ทำให้นึกถึงบทพูดของแม่สื่อ Yente ใน Fiddler on the Roof ที่ว่า “ถึงจะเป็นสามีที่แย่—พระเจ้าช่วยห้ามไว้เถิด—ก็ยังดีกว่าไม่มีสามี!”
    • ถ้าจำไม่ผิด Elm formatter ก็ทำงานคล้ายกัน และให้ความรู้สึกดีกว่า formatter ที่ไม่สนใจรูปแบบเดิมพอสมควร
    • บางโปรเจกต์ C++ ใช้ clang-format แล้วมันแย่มาก
      เสถียรภาพ ระหว่างเวอร์ชันต่ำเกินไป จนการอัปเกรด clang-format กลายเป็นคอมมิตจัดรูปแบบที่แตะทุกบรรทัดในโค้ด
      ไม่แน่ใจจริง ๆ ว่ามันดีกว่าไม่มี formatter หรือเปล่า
    • เมื่อก่อนฉันคิดมานานว่า “formatter แบบไหนก็ยังดีกว่าไม่มี” แต่ช่วงหลังเปลี่ยนความคิดไปแบบสิ้นเชิง
      auto-formatter ส่วนใหญ่แก้ปัญหาคน โดยกำจัด การเถียงเรื่องจุกจิก ใน pull request
      แต่ตอนนี้เรากำลังย้ายไปสู่ การพัฒนาแบบเอเจนต์ ปัญหานั้นก็ยิ่งสำคัญน้อยลงเรื่อย ๆ
      ตอนนี้หลายโปรเจกต์ให้เครื่องทำงานส่วนใหญ่แล้ว และในบริบทนั้นฉันเริ่มรู้สึกว่าการไม่รัน formatter น่าจะดีกว่า
  • เวลาเขียนอาร์กิวเมนต์บรรทัดคำสั่งใน Python ฉันชอบวิธี splat tuple ลงใน list ดังนั้นตัวอย่างสุดท้ายในบทความน่าจะเขียนแบบนี้

    [  
      "aws",  
      "s3",  
      "sync",  
      path,  
      url,  
            *("--include", "*.html"),  
      *("--include", "*.xml"),  
      *("--metadata-directive", "REPLACE"),  
      *("--cache-control", "max-age=0"),  
    ]  
    
  • ตอนที่เช็กล่าสุด 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 จัดรูปแบบภาษาอื่นได้ด้วยก็ได้ :)

    • Prettier ก็มีฟีเจอร์คล้ายกัน แต่เจาะจงเฉพาะ object literal และเลือกได้แค่ระหว่าง “ทั้งหมดในบรรทัดเดียว” กับ “แต่ละองค์ประกอบอยู่คนละบรรทัด”
      เช่น ไม่มีทางสั่ง 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] ปกติจะวางหนึ่งองค์ประกอบต่อหนึ่งบรรทัด แต่คุณสามารถจัดกลุ่มด้วยมือแบบนี้ได้

    [1, 2, 3, 4, 5,  //  
     6, 7, 8, 9, 10, //  
     ...]  
    

    ไม่แน่ใจว่านี่เป็นฟีเจอร์ที่ใส่มาโดยตั้งใจ หรือเป็นผลพลอยได้จากวิธีจัดการคอมเมนต์

    • วิธีนั้นคือพฤติกรรมของ rustfmt แบบเป๊ะ ๆ และนั่นแหละที่ทำให้ฉันแทบบ้า
      บางครั้งถึงจะ เกินข้อจำกัดความยาวบรรทัด ไปบ้าง แต่การไม่แยก function call ออกเป็นหลายบรรทัดกลับอ่านง่ายกว่า และฉันอยากสะท้อนการตัดสินใจนั้นของตัวเองได้
      ตัวอย่างหนึ่งที่นึกออกคือ OpenGL
      ปกติคุณมักแก้ไขหรือใช้งานรีซอร์สเดียวต่อเนื่องกัน เช่นตอนตั้งค่า texture เริ่มต้นซึ่งมีการเรียก gl.* หลายครั้งติดกัน แต่ rustfmt จะดันจัดแบบไร้ความรู้สึกโดยมีเป้าหมายเหมือนหุ่นยนต์แค่ว่า “บรรทัดยาวเกินไป ต้องหั่นบรรทัด”
      ตัวอย่างนี้จงใจทำขึ้นเพื่อแสดงพฤติกรรม และไม่ได้ตรงกับพฤติกรรมจริงของ rustfmt ทุกประการ
      บรรทัดก็ไม่ได้ยาวมากขนาดนั้นด้วย
      ตอนนี้ฉันพิมพ์จากมือถือเลยไม่มีเครื่องมือทำตัวอย่างที่แม่น 100%
      gl.bind_texture(gl::TEXTURE_2D, tex);  
      gl.tex_parameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST);  
      gl.tex_parameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST);
      
      // -->
      
      gl.bind_texture(gl::TEXTURE_2D, tex);  
      gl.tex_parameteri(  
          gl::TEXTURE_2D,  
          gl::TEXTURE_MIN_FILTER,  
          gl::NEAREST,  
      );  
      gl.tex_parameteri(  
          gl::TEXTURE_2D,  
          gl::TEXTURE_MAG_FILTER,  
          gl::NEAREST,  
      );  
      
      การแยก gl.tex_parameteri ต่อ ๆ กันออกเป็นหลายบรรทัดแบบนี้ จริง ๆ แล้วแสดงแต่ละคำสั่งเต็ม ๆ ในบรรทัดเดียวกลับดีกว่า
      เพราะถ้าจัดคอลัมน์ตรงกัน จะมองหาความต่างของสองบรรทัดได้ง่ายกว่ามาก
      เวอร์ชันที่ถูกแยกบรรทัดทำให้ความใกล้ชิดทางสายตาลดลงและอ่านยากกว่า
      มันทำให้เปรียบเทียบสองบรรทัดด้วยตาได้ไม่สะดวกอีกต่อไป
      และยังมีกรณีน่าขำที่ formatter ล้มเหลวไปเลยเมื่อไม่สามารถจัดรูปแบบให้เข้าในข้อจำกัดจำนวนอักขระได้
      เรื่องนี้เกิดบ่อยเวลาเขียนโค้ดคอมไพเลอร์และสร้างข้อความวินิจฉัยเป็น string literal เพราะข้อความอาจยาวพอสมควร
      rustfmt ไม่รู้จะแยกอย่างไร จึงยอมแพ้และไม่จัดรูปแบบ statement ทั้งก้อนนั้นเลย
      มักจะออกมาประมาณนี้
      match something {  
          // ... match arms above this one ...  
          _ => emit_diagnostic(&mut state, "This is a very long message to try and illustrate the problem. Help: please consult a doctor.")  
      }  
      
      ตรงนี้เพียงเพราะการเรียก emit_diagnostic เป็น expression มันจึงยอมแพ้กับการจัดรูปแบบ match statement ทั้งอัน ซึ่งงี่เง่ามาก
      ทั้งหมดนี้หลีกเลี่ยงได้ ถ้ามันไม่พยายามยัดโค้ดของฉันให้ไม่เกิน 100 คอลัมน์ตลอดเวลา
  • เผื่อใครที่ต้องไปค้นเหมือนฉันจากคอมเมนต์ท้าย ๆ: ++ คือ ตัวดำเนินการเชื่อมอาร์เรย์
    ดังนั้นถ้าแยกอาร์เรย์เป็นสองส่วน ก็สามารถให้มันถูกจัดรูปแบบต่างกันได้