Điều khiển `zig fmt`
(matklad.github.io)zig fmtcó thể được dùng như một trình định dạng có thể điều khiển được: nó phản ánh hình thức cú pháp đã có sẵn trong tệp và có thể sắp cùng một đoạn mã theo nhiều bố cục khác nhau- Trong lời gọi hàm, sự có mặt hay không của trailing comma sẽ làm thay đổi kết quả: không có dấu phẩy thì mã được gộp về một dòng, có dấu phẩy thì các đối số được đặt theo từng dòng
- Quy trình thực tế là trước tiên quyết định cách bố trí mã mong muốn, thêm vài dấu phẩy rồi nhấn phím tắt định dạng để
zig fmtxử lý phần còn lại - Với mảng, không chỉ trailing comma mà cả vị trí xuống dòng đầu tiên cũng được phản ánh, nên nếu lần xuống dòng đầu tiên nằm sau phần tử thứ ba thì các phần tử sẽ được căn theo nhóm 3
- Nếu dùng nối mảng
++một cách cẩn thận, có thể bố trí số phần tử khác nhau trên mỗi dòng; khi truyền cặp--keyvàvaluevào subprocess, có thể nối mảng đối số cố định với mảng cặp tùy chọn để căn chỉnh
Cách điều khiển zig fmt
zig fmtcó thể được dùng như một trình định dạng có thể điều khiển vì nó nhìn vào hình thức cú pháp đã có trong tệp hiện tại và có thể sắp cùng một cú pháp theo nhiều cách khác nhau- Trong lời gọi hàm, sự có mặt hay không của trailing comma sẽ thay đổi bố cục
f(1, 2, 3); // -> zig fmt -> f(1, 2, 3);f(1, 2, 3,); // -> zig fmt -> f( 1, 2, 3, ); - Luồng sử dụng thực tế là trước tiên quyết định cách bố trí mã mong muốn, thêm vài dấu
,, rồi nhấn phím tắt định dạng đểzig fmtxử lý phần còn lại - Thay vì để trình định dạng tự đoán bố cục, có thể phù hợp hơn nếu người dùng tự để lại những lựa chọn cốt lõi
- Kết luận là 90% của việc định dạng tốt phụ thuộc vào dòng trống giữa các khối logic và việc chọn biến trung gian phù hợp, nên tốt hơn là tận dụng những lựa chọn này thay vì loại bỏ chúng
Bố cục căn cột của mảng
- Với mảng, không phải chỉ trailing comma mới khiến mỗi phần tử được đặt trên một dòng; vị trí xuống dòng đầu tiên cũng được
zig fmtphản ánh.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; - Nếu lần xuống dòng đầu tiên nằm sau phần tử thứ ba, kết quả cũng sẽ được căn theo nhóm 3 phần tử
.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; - Nếu dùng nối mảng
++một cách cẩn thận, có thể bố trí số phần tử khác nhau trên mỗi dòng - Khi truyền cặp
--keyvàvaluevào subprocess, có thể nối mảng đối số cố định với mảng cặp tùy chọn để căn chỉnh như sautry run(&(.{ "aws", "s3", "sync", path, url } ++ .{ "--include", "*.html", "--include", "*.xml", "--metadata-directive", "REPLACE", "--cache-control", "max-age=0", }));
1 bình luận
Ý kiến trên Lobste.rs
Tôi nhớ là
gofmtcũng từng có cách định hướng việc định dạng tương tự, và tôi thích kiểu formatter như vậy hơnrustfmtDù vậy, tôi vẫn nghĩ có bất kỳ hình thức tự động hóa định dạng nào còn hơn là hoàn toàn không có formatter
Trình định dạng tự động ép mọi thứ trở nên tầm thường, kéo những người không biết định dạng lên nhưng cũng kéo cả những người làm tốt xuống
Khi cộng tác, nếu những người khác muốn dùng nó hoặc khó tin vào quy tắc định dạng cá nhân của họ thì tôi sẽ dùng, nhưng khi làm một mình thì tôi tuyệt đối không dùng
Tôi có gu của tôi, formatter có gu của formatter, và hai bên khác nhau đến mức không thể hòa giải
Dù vậy, bản thân những gì bài viết này cho thấy vẫn rất ấn tượng
Câu đó khiến tôi nhớ đến lời của bà mối Yente trong Fiddler on the Roof: “Một người chồng tệ—cầu Chúa ngăn lại—cũng còn hơn là không có chồng!”
clang-format, và nó thật kinh khủngTính ổn định giữa các phiên bản quá thấp, nên việc nâng cấp
clang-formatdẫn đến một commit định dạng đụng vào mọi dòng mãTôi thực sự không chắc nó có tốt hơn việc không có formatter hay không
Trình định dạng tự động chủ yếu giải quyết vấn đề con người bằng cách loại bỏ tranh cãi vặt vãnh trong pull request
Nhưng giờ khi đang chuyển sang phát triển kiểu agent, vấn đề đó ngày càng bớt quan trọng
Hiện tại trong nhiều dự án, máy móc làm phần lớn công việc, và trong bối cảnh đó tôi lại thấy có lẽ tốt hơn là đừng chạy formatter
Khi tạo đối số dòng lệnh trong Python, tôi thích cách splat tuple vào list, nên có lẽ tôi sẽ viết ví dụ cuối bài như thế này
Lần cuối tôi xem thì
zig fmtkhông có cách cấu hình để dùng giới hạn 80 cột thay vì 100 cột, giờ vẫn thế à?Khi làm việc nhiều giờ mỗi ngày, tôi tăng cỡ chữ terminal để mắt đỡ mỏi hơn, và khác biệt giữa 80 cột với 100 cột quyết định việc có thể đặt cạnh nhau hai cửa sổ tách của
vimcùngnerd treehay khôngzig fmtkhông có giới hạn số cộtLà người từng đưa rigid formatter vào một đội trước đó hoàn toàn không có formatter, đôi khi tôi nhớ khả năng có thể tác động đến định dạng bằng tay
Về mặt đó, việc Zig linh hoạt thật sự rất tuyệt
Tuyệt vời!
Có formatter TS/JS nào kiểu này không?
Tôi có một dự án dùng
maplibre-gl, mà các biểu thức trong đặc tả style đôi khi bị định dạng quá đà đến mức chẳng còn nhìn ra gìHiện giờ tôi đã ngừng dùng formatter, nhưng vì phải debug, sao chép và comment code nên mã đang ngày càng bừa bộn
Biết đâu có thể làm cho formatter của Zig định dạng cả ngôn ngữ khác nữa :)
Ví dụ, không có cách nào bảo formatter hãy đặt bốn phần tử trên mỗi dòng
Ngoài ra, nếu object literal quá dài thì dù văn bản đầu vào thế nào, Prettier cuối cùng vẫn sẽ đổi sang kiểu “mỗi phần tử một dòng”
Tôi từng thất vọng với các formatter kiểu nhẹ nhàng, về cơ bản chỉ là formatter xuống dòng, nên tôi thấy vừa ghen tị vừa thích ý tưởng có được sự linh hoạt này bên trong một khuôn mẫu chặt chẽ hơn :p
Gần đây khi trả lời một câu hỏi trên fedi về việc viết và format Lisp bằng font tỷ lệ, tôi đã nhắc đến các biến thể s-expression dùng khoảng trắng có ý nghĩa như wisp, Readable/Sweet expressions, SRFI 119 và 110
Tôi cũng bổ sung nhận xét rằng họ cú pháp này phần nào trao lại quyền kiểm soát việc xuống dòng bằng cách tận dụng phần mở rộng ký pháp trung tố tùy chọn
Thiết kế này thú vị, nhưng tôi không chắc mình có thích nó hay không
Trong formatter của tôi, tôi làm khác: formatter sẽ bỏ qua dấu phẩy cuối khi quyết định định dạng, rồi nếu đã tách thành nhiều dòng thì luôn thêm dấu phẩy cuối, còn nếu chỉ một dòng thì luôn bỏ dấu phẩy cuối
Vì vậy nó không thể “định hướng”, nhưng
f(1, 2, 3)sẽ luôn được định dạng nhất quán bất kể có dấu phẩy cuối hay không, hay khoảng trắng giữa các token nhiều ít và kiểu gìVẫn cần một mức độ định hướng nào đó
Chẳng hạn, nếu có một list literal dài
[<expr1>, <expr2>, ..., <expr100>]thì hầu hết formatter sẽ để mỗi biểu thức trên một dòng, nhưng đôi khi bạn lại muốn nhét được càng nhiều càng tốt trên một dòngTôi thấy việc dùng dấu phẩy cuối để quyết định giữa hai kiểu đó là kỳ quặc, và nói chung số lựa chọn có thể là N chứ không chỉ 2
Tôi nghĩ attribute sẽ phù hợp hơn cho mục đích này
Ví dụ, có lẽ đã tồn tại rồi, nhưng có thể gắn thứ gì đó như
#[rustfmt::list_layout(flow)]trước câu lệnh để ảnh hưởng đến cách định dạng list literal bên trong câu lệnh đóNếu có quá nhiều kiểu định hướng thì sẽ làm tổn hại mục tiêu của formatter là tạo định dạng mã nhất quán trong toàn bộ hệ sinh thái và giúp review code dễ hơn, nên chỉ nên có trong những trường hợp giới hạn
Tôi cho rằng list literal dài là một ví dụ thật sự cần thiết
Trong dự án của tôi cũng có ví dụ mà định dạng giúp việc review giá trị kỳ vọng trong test dễ hơn, như ở đây
Tôi cũng nghĩ ra một hành vi “định hướng” khác trong formatter của Dart: trong list literal dài, có thể thêm các dòng comment để nhóm các dòng lại với nhau
Ví dụ, nếu có
[1, 2, 3, ..., 1000]thì mỗi phần tử sẽ nằm trên một dòng, nhưng có thể nhóm thủ công như sauTôi không rõ đây có phải tính năng được đưa vào có chủ đích không, hay chỉ là hệ quả phụ từ cách nó xử lý comment
rustfmthoạt động, và nó làm tôi phát điênĐôi khi giữ nguyên một lời gọi hàm không bị tách ra còn dễ đọc hơn, ngay cả khi nó vượt giới hạn độ dài dòng, và sẽ thật tốt nếu tôi có thể phản ánh phán đoán đó của mình
Một ví dụ tôi nghĩ đến là OpenGL
Thông thường khi sửa đổi hoặc dùng một tài nguyên, chẳng hạn lúc khởi tạo texture, bạn sẽ có nhiều lời gọi
gl.*liên tiếp, nhưngrustfmtcứ đẩy chúng đi mà không có chút cảm quan nào ngoài mục tiêu máy móc là “dòng quá dài, phải bẻ ra”Ví dụ này là ví dụ nhân tạo để minh họa hành vi, và không hoàn toàn giống hệt hành vi thực tế của
rustfmtDòng cũng không dài đến thế
Tôi đang gõ trên điện thoại nên không có công cụ để làm ví dụ chính xác 100% Việc bẻ các lời gọi
gl.tex_parameteriliên tiếp như thế thành nhiều dòng thực ra lại tệ hơn so với để nguyên mỗi lời gọi trên một dòngVì khi các cột được căn hàng, việc tìm ra khác biệt giữa hai dòng sẽ dễ hơn nhiều
Bản bị bẻ dòng làm mất sự gần gũi về mặt thị giác và khó đọc hơn
Bạn không còn dễ dàng so sánh hai dòng bằng mắt nữa
Cũng có những trường hợp buồn cười khi nó thất bại hoàn toàn vì không thể format để khớp với giới hạn số ký tự trên dòng
Điều này hay xảy ra khi viết mã compiler và tạo thông báo chẩn đoán bằng string literal, vì thông báo có thể khá dài
rustfmtkhông biết phải tách nó thế nào nên bỏ cuộc và không định dạng toàn bộ câu lệnh đóThường là kiểu như sau Chỉ vì lời gọi
emit_diagnosticlà một biểu thức mà nó từ bỏ việc định dạng toàn bộ câu lệnhmatch, điều đó thật ngu ngốcMọi chuyện đã có thể tránh được nếu nó không cố ép mã của tôi vào tối đa 100 cột
Viết thêm cho những ai như tôi đã phải đi tra ở phần cuối:
++là toán tử nối mảngVì vậy có thể tách một mảng thành hai phần để định dạng chúng khác nhau