Steering Zig Fmt
(matklad.github.io)zig fmtको फ़ाइल में पहले से मौजूद syntax shape को देखकर उसी कोड को कई layouts में व्यवस्थित करने वाले एक steerable formatter की तरह इस्तेमाल किया जा सकता है- function calls में trailing comma की मौजूदगी या अनुपस्थिति नतीजा बदल देती है; comma न हो तो यह एक ही लाइन में समेट देता है, और comma हो तो arguments को हर लाइन में रखता है
- वास्तविक workflow यह है कि पहले मनचाहा code layout तय किया जाए, फिर कुछ commas जोड़े जाएँ, और उसके बाद format shortcut दबाया जाए ताकि
zig fmtबाकी काम कर दे - arrays में trailing comma के साथ-साथ पहले line break की स्थिति भी प्रतिबिंबित होती है; अगर पहला line break तीसरे item के बाद है, तो items 3-3 के समूह में align होते हैं
++array concatenation का सावधानी से उपयोग करने पर हर लाइन में items की संख्या अलग रखी जा सकती है, और subprocess को--keyऔरvaluepairs भेजते समय fixed argument array और option-pair array को जोड़कर उन्हें align किया जा सकता है
zig fmt को steer करने का तरीका
zig fmtमौजूदा फ़ाइल में पहले से मौजूद syntax shape को देखकर उसी syntax को कई तरीकों से व्यवस्थित कर सकता है, इसलिए इसे एक steerable formatter की तरह इस्तेमाल किया जा सकता है- function call में trailing comma की मौजूदगी layout बदल देती है
f(1, 2, 3); // -> zig fmt -> f(1, 2, 3);f(1, 2, 3,); // -> zig fmt -> f( 1, 2, 3, ); - व्यावहारिक उपयोग में पहले मनचाहा code layout तय किया जाता है, फिर कुछ
,जोड़े जाते हैं, और उसके बाद format shortcut दबाया जाता है ताकिzig fmtबाकी काम संभाल ले - formatter से layout का अनुमान लगवाने के बजाय, यह तरीका बेहतर बैठ सकता है कि उपयोगकर्ता खुद महत्वपूर्ण choices छोड़कर जाए
- अच्छी formatting का 90% तार्किक blocks के बीच खाली लाइनों और उपयुक्त intermediate variables चुनने पर निर्भर करता है, इसलिए निष्कर्ष यह है कि ऐसी choices को हटाने के बजाय उनका उपयोग करना बेहतर है
arrays के column-aligned layouts
- arrays में केवल trailing comma के कारण हर item अलग लाइन में नहीं जाता, बल्कि
zig fmtपहले line break की स्थिति को भी ध्यान में रखता है.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; - अगर पहला line break तीसरे item के बाद हो, तो परिणाम भी 3-3 items के हिसाब से align होता है
.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; ++array concatenation का सावधानी से उपयोग करने पर हर लाइन में items की संख्या अलग रखी जा सकती है- subprocess को
--keyऔरvaluepairs भेजते समय fixed argument array और option-pair array को जोड़कर उन्हें नीचे की तरह align किया जा सकता हैtry run(&(.{ "aws", "s3", "sync", path, url } ++ .{ "--include", "*.html", "--include", "*.xml", "--metadata-directive", "REPLACE", "--cache-control", "max-age=0", }));
1 टिप्पणियां
Lobste.rs की राय
मुझे याद है कि
gofmtमें भी इसी तरह का formatting guidance behavior था, और ऐसे formatter मुझेrustfmtसे ज़्यादा पसंद हैंफिर भी, मेरा मानना है कि formatter बिल्कुल न होने से किसी भी तरह का formatting automation होना बेहतर है
auto-formatter साधारणपन को लागू करते हैं, formatting में कमज़ोर लोगों को ऊपर उठाते हैं, लेकिन जो लोग इसे अच्छे से करते हैं उन्हें भी नीचे खींच लेते हैं
collaboration में, अगर दूसरे लोग ऐसा चाहते हों या उनकी निजी formatting discipline पर भरोसा करना मुश्किल हो, तो मैं इसका इस्तेमाल करूँगा, लेकिन अकेले काम करते समय कभी नहीं
मेरी अपनी पसंद है और formatter की भी अपनी पसंद है, और दोनों में समझौता नामुमकिन है
फिर भी, इस लेख में जो दिखाया गया है वह अपने-आप में प्रभावशाली है
उस वाक्य ने Fiddler on the Roof की matchmaker Yente की यह पंक्ति याद दिला दी: “बुरा पति भी—भगवान बचाए—पति न होने से बेहतर है!”
clang-formatइस्तेमाल करते हैं, और वह भयानक हैversions के बीच इसकी stability इतनी कम है कि
clang-formatupgrade करने पर ऐसा formatting commit बन जाता है जो code की लगभग हर line को छू देता हैसच कहूँ तो यक़ीन नहीं कि यह formatter न होने से बेहतर है
auto-formatter मुख्य रूप से pull request में होने वाली bike-shedding जैसी मानवीय समस्या को हल करते हैं
लेकिन अब जब हम agentic development की ओर बढ़ रहे हैं, वह समस्या धीरे-धीरे कम महत्वपूर्ण होती जा रही है
अभी कई projects में मशीनें ज़्यादातर काम कर रही हैं, और ऐसे में लगता है कि formatter न चलाना ही बेहतर हो सकता है
Python में command-line arguments बनाते समय मुझे tuple को list में splat करने का तरीका पसंद है, इसलिए लेख के आख़िरी example को मैं शायद ऐसे लिखूँगा
पिछली बार जब मैंने देखा था,
zig fmtमें 80-column limit को 100-column limit की जगह इस्तेमाल करने का कोई तरीका नहीं था — क्या अभी भी ऐसा ही है?अगर रोज़ कई घंटे काम करना हो, तो आँखों पर कम ज़ोर पड़े इसलिए मैं terminal font size बड़ा रखता हूँ, और 80 columns बनाम 100 columns का फ़र्क यह तय करता है कि
vimके दो splits औरnerd treeको साथ-साथ रखा जा सकता है या नहींzig fmtमें कोई column limit नहीं हैएक ऐसे व्यक्ति के रूप में जिसने ऐसी टीम में rigid formatter लागू किया था जहाँ पहले formatter था ही नहीं, मुझे कभी-कभी manual formatting को प्रभावित कर पाने की क्षमता की कमी महसूस होती है
उस नज़रिए से Zig का flexible होना वाकई बहुत बढ़िया है
शानदार!
क्या इस तरह का कोई TS/JS formatter है?
मेरा एक project है जो
maplibre-glइस्तेमाल करता है, और style spec expressions कभी-कभी इतने ज़्यादा format हो जाते हैं कि कुछ दिखाई ही नहीं देताफ़िलहाल मैंने formatter का इस्तेमाल बंद कर दिया है, लेकिन debug करने, copy-paste करने और comments लगाने के दौरान code गंदा होता जा रहा है
शायद Zig formatter को दूसरी भाषाओं के लिए भी formatting करने लायक बनाया जा सके :)
उदाहरण के लिए, formatter को यह बताने का कोई तरीका नहीं है कि हर line में चार elements रखे
और अगर object literal बहुत लंबा हो जाए, तो input text जैसा भी हो, Prettier आख़िरकार उसे “हर element अलग line में” वाले रूप में बदल देता है
हल्के किस्म के formatter, यानी लगभग सिर्फ line breaks करने वाले formatter, ने मुझे निराश किया है, इसलिए ज़्यादा सख़्त उदाहरणों के भीतर ऐसी flexibility का विचार ईर्ष्याजनक भी लगता है और अच्छा भी :p
हाल ही में fedi पर proportional font में Lisp लिखने और format करने से जुड़े सवाल का जवाब देते हुए, मैंने meaningful whitespace इस्तेमाल करने वाले s-expression variants — wisp, Readable/Sweet expressions, और SRFI 119 व 110 — का ज़िक्र किया था
मैंने यह भी जोड़ा कि यह syntax family optional infix notation extensions का उपयोग करके line breaks पर कुछ नियंत्रण वापस देती है
design दिलचस्प है, लेकिन मुझे यक़ीन नहीं कि यह मुझे पसंद है
अपने formatter में मैं इसे अलग तरह से संभालता हूँ: formatter trailing comma को नज़रअंदाज़ करके formatting तय करता है, फिर अगर चीज़ कई lines में टूटती है तो हमेशा trailing comma जोड़ देता है, और अगर एक line में रहती है तो trailing comma हमेशा हटा देता है
इसलिए “guidance” तो नहीं हो पाता, लेकिन
f(1, 2, 3)trailing comma हो या न हो, या tokens के बीच spaces की मात्रा और प्रकार कुछ भी हो, हमेशा एक जैसा format होता हैकुछ हद तक guidance की ज़रूरत होती है
उदाहरण के लिए, अगर कोई लंबा list literal
[<expr1>, <expr2>, ..., <expr100>]हो, तो ज़्यादातर formatter हर expression को अलग line में रखेंगे, लेकिन हो सकता है आप चाहें कि एक line में जितना हो सके उतना भरा जाएइसे trailing comma से तय करना मुझे अजीब लगता है, और आम तौर पर विकल्प 2 नहीं बल्कि N हो सकते हैं
ऐसे उद्देश्य के लिए attributes ज़्यादा उपयुक्त लगते हैं
उदाहरण के लिए, शायद यह पहले से हो भी, लेकिन किसी statement के आगे
#[rustfmt::list_layout(flow)]जैसा कुछ लगाकर उस statement के भीतर list literals की formatting को प्रभावित किया जा सकता हैबहुत ज़्यादा guidance देने से formatter के उद्देश्य को नुकसान पहुँचता है, क्योंकि उसका मक़सद पूरे ecosystem में code formatting को एकसमान बनाना और code review को आसान करना है, इसलिए यह सिर्फ़ सीमित मामलों में होना चाहिए
लंबे list literals मुझे सच में ऐसा ही एक ज़रूरी मामला लगते हैं
मेरे project में भी एक उदाहरण है जहाँ formatting test expectations की review में मदद करती है, यहाँ वैसा ही मामला है
Dart formatter में एक और “guidance” behavior याद आता है: लंबे list literals में comment lines जोड़कर lines को group किया जा सकता है
उदाहरण के लिए, अगर
[1, 2, 3, ..., 1000]हो तो वह हर element को अलग line में रखेगा, लेकिन आप हाथ से इसे इस तरह group कर सकते हैंपता नहीं यह सुविधा जानबूझकर डाली गई थी या comment handling का एक side effect है
rustfmtका behavior है, और यही बात मुझे पागल कर देती हैकभी-कभी line length limit पार होने देने पर भी function call को न तोड़ना ज़्यादा readable होता है, और अच्छा होता अगर मैं उस पर अपना निर्णय लागू कर पाता
एक उदाहरण जो तुरंत दिमाग में आता है, वह OpenGL है
आम तौर पर आप एक ही resource को modify या use कर रहे होते हैं, और जैसे texture initialization में
gl.*calls की लंबी श्रृंखला बन जाती है, लेकिनrustfmtकिसी संवेदनशील समझ के बिना सिर्फ़ “line बहुत लंबी है, तोड़नी पड़ेगी” जैसी रोबोटिक प्राथमिकता से उसे तोड़ देता हैयह example behavior दिखाने के लिए कृत्रिम है, और असली
rustfmtbehavior से बिल्कुल एक जैसा नहीं हैlines भी इतनी लंबी नहीं हैं
मैं अभी फ़ोन पर लिख रहा हूँ, इसलिए 100% सटीक example बनाने के साधन नहीं हैं इस तरह लगातार आने वाली
gl.tex_parametericalls को कई lines में तोड़ दिया जाता है, जबकि वास्तव में हर call को एक ही line में पूरी तरह फैलाकर रखना बेहतर हैक्योंकि जब columns align होते हैं, तो दोनों lines के बीच का अंतर काफ़ी आसानी से दिख जाता है
टूटा हुआ version visual proximity खो देता है और पढ़ने में मुश्किल हो जाता है
आप दो lines की तुलना आँखों से आसानी से नहीं कर पाते
एक और हास्यास्पद बात यह है कि जब formatter किसी चीज़ को character limit के अंदर fit नहीं कर पाता, तो वह पूरी तरह fail हो जाता है
compiler code लिखते समय string literals में diagnostic messages बनाते हुए यह अक्सर होता है, क्योंकि message काफ़ी लंबे हो सकते हैं
rustfmtसमझ नहीं पाता कि इसे कैसे तोड़े, इसलिए हार मानकर पूरे statement को format ही नहीं करताआम तौर पर मामला कुछ ऐसा होता है यहाँ सिर्फ़ इसलिए कि
emit_diagnosticcall एक expression है, वह पूरेmatchstatement की formatting छोड़ देता है — यह बस बेवकूफ़ी हैअगर वह मेरे code को ज़बरदस्ती 100 columns के अंदर ठूँसने की कोशिश न करता, तो यह सब टल सकता था
आख़िर में, उस comment को देखकर मेरे जैसे जिन लोगों को यह खोजकर देखना पड़ा, उनके लिए:
++array concatenation operator हैइसलिए arrays को दो हिस्सों में बाँटकर उन्हें अलग-अलग तरह से format किया जा सकता है