Steering Zig Fmt
(matklad.github.io)zig fmtは、ファイル内にすでにある構文の形を反映して、同じコードでも複数のレイアウトに配置できる操縦可能なフォーマッタとして使える- 関数呼び出しでは trailing comma の有無が結果を変え、カンマがなければ1行にまとまり、カンマがあれば引数が各行に配置される
- 実際の流れは、望むコード配置を先に決めてカンマをいくつか追加し、その後フォーマット用ショートカットを押して
zig fmtに残りを処理させる方式 - 配列では trailing comma だけでなく、最初の改行位置 も反映され、最初の改行が3番目の項目の後にあると、項目が3つずつそろうように整列される
++配列連結を慎重に使えば、行ごとに項目数を変えて配置でき、subprocess に--keyとvalueの組を渡すときは固定引数配列とオプション組配列を連結して整列できる
zig fmt を操縦する方法
zig fmtは、現在のファイルにすでにある構文の形を見て、同じ構文でも複数の方法で配置できるため、操縦可能なフォーマッタとして使える- 関数呼び出しでは trailing comma の有無がレイアウトを変える
f(1, 2, 3); // -> zig fmt -> f(1, 2, 3);f(1, 2, 3,); // -> zig fmt -> f( 1, 2, 3, ); - 実際の使い方の流れは、望むコード配置を先に決めて
,をいくつか追加し、その後フォーマット用ショートカットを押してzig fmtに残りを処理させる方式 - フォーマッタにレイアウトを推測させるより、ユーザーが重要な選択を直接残す方法のほうが合っているかもしれない
- 良いフォーマットの90%は、論理ブロック間の空行と適切な中間変数の選択にかかっているため、こうした選択をなくすより活用するほうがよいという結論
配列の列揃えレイアウト
- 配列では trailing comma だけで1行に1つずつ配置されるのではなく、最初の改行位置 も
zig fmtが反映する.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; - 最初の改行が3番目の項目の後にあると、結果も項目3つずつそろうように整列される
.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; ++配列連結を慎重に使えば、行ごとに項目数を変えて配置できる- subprocess に
--keyとvalueの組を渡す場合は、固定引数配列とオプション組配列を連結して次のように整列できるtry run(&(.{ "aws", "s3", "sync", path, url } ++ .{ "--include", "*.html", "--include", "*.xml", "--metadata-directive", "REPLACE", "--cache-control", "max-age=0", }));
1件のコメント
Lobste.rsの意見
gofmtにも似たような書式誘導の動作があった気がするし、その手のフォーマッタのほうがrustfmtより好きそれでも、フォーマッタがまったくないよりは、どんな形でも書式の自動化があるほうがいいと思う
自動フォーマッタは平凡さを強制し、書式をうまく扱えない人を底上げする一方で、うまく扱える人も引き下げる
協業で他の人たちが望んでいたり、彼らの個人的な書式ルールを信頼しにくかったりするなら使うが、一人で作業するときは絶対に使わない
自分の好みもあればフォーマッタの好みもあって、その二つは和解不能なくらい違う
ただ、この記事で示されている内容自体は印象的だ
その一文を読むと、Fiddler on the Roofで仲人のイェンテが「悪い夫でも――神よお守りください――夫がいないよりはまし!」と言う台詞を思い出す
clang-formatを使っているけどひどいバージョン間の安定性が低すぎて、
clang-formatのアップグレードがコードの全行に触れる書式コミットにつながるフォーマッタがないよりマシなのか本当に自信がない
自動フォーマッタは主にプルリクエストでのバイクシェディングという人間の問題を解決する
でも今はエージェント型開発に移りつつあり、その問題はだんだん重要ではなくなってきている
いま複数のプロジェクトで機械が作業の大半をやっているので、そうなるとフォーマッタを回さないほうがいい気がしてくる
Pythonでコマンドライン引数を組み立てるときは、タプルをリストにスプラットする書き方が好きなので、記事の最後の例はこう書くと思う
前に見たとき、
zig fmtには80桁制限を100桁制限の代わりに使う設定方法がなかったが、まだそうなのだろうか?1日に何時間も作業するので、目が疲れにくいようにターミナルのフォントサイズを大きくしているのだが、80桁と100桁の違いは
vimの分割2枚とnerd treeを横に並べられるかどうかを左右するzig fmtには桁数制限はないフォーマッタがまったくなかったチームにrigid formatterを導入した者として、たまには手動で書式に影響を与えられる能力が恋しくなる
その点で、Zigが柔軟なのは本当にすばらしい
すばらしい!
こういうTS/JSフォーマッタはあるだろうか?
maplibre-glを使うプロジェクトがあるのだが、スタイル仕様の式がときどき過剰に整形されて、何も見えなくなる今はフォーマッタの使用をやめているが、デバッグやコピーやコメントアウトをしているうちにコードが散らかってきている
もしかするとZigフォーマッタを他の言語もフォーマットできるようにできるかもしれない :)
たとえば1行に4要素ずつ置けとフォーマッタに指示する方法はない
また、オブジェクトリテラルが長くなりすぎると、入力テキストがどうであれ、Prettierは結局「各要素を別々の行」の形式にしてしまう
軽量なタイプのフォーマッタ、実質的に改行しかしないフォーマッタにはがっかりしたことがあるので、もっと厳格な例の中でこうした柔軟性があるという発想はうらやましいし気に入っている :p
最近fediで、プロポーショナルフォントでLispを書いて整形することについての質問に答えたとき、意味のある空白を使うs-expressionの変種、つまりwisp、Readable/Sweet expressions、SRFI 119と110に触れた
この文法ファミリーは、任意の中置記法拡張を活用することで改行に対する制御をある程度取り戻している、という観察も添えた
興味深い設計だが、気に入るかどうかはよくわからない
自分のフォーマッタでは違うやり方をしている。フォーマッタが末尾カンマを無視して書式を決め、その後で複数行になったら必ず末尾カンマを追加し、1行なら必ず末尾カンマを削除する
だから「誘導」はできないが、
f(1, 2, 3)は末尾カンマの有無やトークン間の空白の量や種類に関係なく一貫してフォーマットされるある程度の誘導は必要だ
たとえば長いリストリテラル
[<expr1>, <expr2>, ..., <expr100>]があると、たいていのフォーマッタは各式を1行に1つずつ置くだろうが、できるだけ多くを1行に詰め込みたいこともあるこれを末尾カンマで決めるのは変だと思うし、一般には選択肢は2つではなくN個ありうる
こういう目的には属性のほうが向いていると思う
たとえば、すでにあるかもしれないが、文の前に
#[rustfmt::list_layout(flow)]のようなものを付けて、その文の中のリストリテラルの書式に影響を与える、といったことができる誘導が多すぎると、エコシステム全体でコードの書式を一貫させ、コードレビューをしやすくするというフォーマッタの目的を損なうので、限定的なケースにとどめるべきだ
長いリストリテラルは本当に必要な例だと思う
自分のプロジェクトにも、テスト期待値のレビューで書式が役に立つ例があり、ここがそれだ
Dartフォーマッタにも別の「誘導」動作を1つ思い出した。長いリストリテラルでコメント行を追加して、行をグループ化できる
たとえば
[1, 2, 3, ..., 1000]なら各要素を1行に1つずつ置くが、手動でこうグループ化できるこれが意図的に入れられた機能なのか、コメント処理のやり方から生じた副産物なのかはわからない
rustfmtの挙動そのもので、それが腹立たしいときには行長制限を超えてでも関数呼び出しを分割しないほうが読みやすい場合があり、そこに自分の判断を反映できるといいのにと思う
ひとつ思い浮かぶ例はOpenGLだ
ふつうは1つのリソースを変更したり使ったりしながら、たとえばテクスチャ初期化のように
gl.*呼び出しを連続してたくさん書くことになるのだが、rustfmtは「行が長すぎる。分割しなければならない」というロボット的な目的以外の感覚なしに押し進めてしまうこの例は動作を示すための作為的なもので、実際の
rustfmtの挙動と完全に同じではない行もそこまで長くはない
今はスマホで書いているので、100%正確な例を作る手段がない このように連続する
gl.tex_parameteri呼び出しを複数行に分割するのだが、実際には各呼び出しを1行に完全に展開しておくほうがよい桁がそろっていれば、2行の違いをずっと簡単に見比べられるからだ
分割した版は視覚的な近接性が低く、読むのが難しい
2行を目で簡単に比較できなくなる
また、文字数制限内に収めて整形できないと完全に失敗するという滑稽なことも起きる
コンパイラのコードで文字列リテラルの診断メッセージを書くときによく起きることで、メッセージがかなり長くなることがある
rustfmtはそれをどう分割すればよいかわからず、あきらめてその文全体のフォーマットをしないよくあるのはこんな形だ ここで
emit_diagnostic呼び出しが単なる式でしかないという理由で、match文全体のフォーマットをあきらめるのは、ただただ愚かだ自分のコードを最大100桁に押し込もうとしなければ、全部避けられたはずだ
最後のほうのコメントを見て、自分のように調べる必要があった人のために書いておくと、
++は配列連結演算子だなので配列を2つに分ければ、別々にフォーマットできる