コードレビューをしているときに、未だに出会うことが多いNo newline at end of fileですが、Prettierやエディタ設定のおかげで、そもそも認識すらしてない人も多いのでは?とも思います。
「ファイルの末尾には改行を入れてください」と言うのは簡単ですが、「なぜ入れないといけないのか」をきちんと説明できたほうが、レビューに説得力が増すので、改めて調べてみました。
理由その1: 【歴史・規格】POSIXにおける「行(line)」の定義
技術的な大前提として、UNIXやLinuxなどの標準規格であるPOSIXにおいて、「行」の定義が以下のように決まっています。
行(line)とは
0個以上の改行以外の文字と、末尾の改行文字(LF) の連なりのこと。
つまり、「改行で終わっていない文字列は、正式な『行』とは認められない(不完全な行)」というのが、コンピュータの世界の古くからのルールです。
コンパイラの小話
古いC言語の処理系では、ファイル末尾に改行がないことでプリプロセッサ処理が正しく行われず、予期せぬエラーを引き起こすことがありました。
そのため、C言語の規格でも「空でないソースファイルは改行文字で終わらなければならない」と定義されています。
理由その2: 【実務】Gitの「差分(diff)」とgit blameが汚れる
これが一番実務で影響が出る部分だと思います。
GitHubだと行の末尾に⛔️(赤い進入禁止みたいなマーク)が出ますよね。
例えば、末尾に改行がないファイル(A.txt)があるとします。
hello
world[ここに改行がない]
後から別の開発者が、3行目を追加しようとすると、ツールが自動で2行目の末尾に改行を補完した上で3行目を追加します。
hello
world
new line
このとき、Gitの差分(diff)をとると、以下のように見えてしまいます。
hello
-world
+world
+new line
本当はnew lineを追加しただけなのに、「触っていないはずのworldの行まで変更された」とGitにみなされてしまいます。
これにより、コードレビュー時に余計なノイズが生じるだけでなく、末尾行のgit blameが実際の変更内容とは無関係なコミットで更新されてしまうという実害も発生します。
理由その3: 【CLI】伝統的なUNIXコマンドが意図しない挙動をする
ターミナルでファイルを操作するとき、末尾改行がないとツールが正しく処理できないことがあります。
wc -l(行数を数えるコマンド)のカウント漏れ
wc -lは「改行の数」をカウントするコマンドです。そのため、POSIXの定義通り「改行がない文字列は行とみなされない」結果、末尾に改行がないと実際の行数より1行少なくカウントされてしまうという現象が起きます。
# 改行がないファイル
$ echo -n "hello" > no_newline.txt
$ wc -l no_newline.txt
0 no_newline.txt # 1行あるのに0とカウントされてしまう
catコマンドで結合したときの崩れ
例えばファイルをcat file1.txt file2.txtで結合したとき、file1.txtの末尾に改行がないと、file2.txtの1行目が同じ行にくっついて出力されてしまいます。
# 改行がないファイル
$ echo -n "hello" > no_newline.txt
$ cat no_newline.txt no_newline.txt
hellohello # 改行がないため、2つのファイルが繋がってしまう
対策
手動で毎回改行を入れるのは不毛ですし、忘れることもあります。こういう類のものはツールの設定で自動化してしまいましょう。
EditorConfigの設定(推奨)
チーム開発であれば、個人のエディタ設定に依存するのではなく、リポジトリに.editorconfigを配置して統一するのがおすすめです。
root = true
[*]
insert_final_newline = true
insert_final_newline = trueを指定すると、対応しているエディタで保存時に末尾改行が自動挿入されるようになります。
EditorConfigはVS CodeやWebStormをはじめ、多くのエディタやIDEでサポートされています。
チーム全体で末尾改行の有無を統一したい場合は、エディタ個別の設定よりもEditorConfigによる管理のほうが効果的です。
VS Codeの設定
settings.json に以下の1行を追加するだけで、すべてのファイルで保存時に自動で末尾改行が挿入されるようになります。
"files.insertFinalNewline": true
Prettierの設定
Prettierを導入している場合、標準で末尾改行が挿入される仕様になっています。
もし動いていない場合は、フォーマット対象にそのファイルの拡張子が含まれているか確認してみてください。
参考文献・公式仕様
この記事で紹介した内容の一次情報(仕様書・ドキュメント)です。さらに詳しく知りたい方はこちらを参照してください。
-
POSIXによる「行」の定義(英語)
- The Open Group Base Specifications Issue 7 / IEEE Std 1003.1™-2017 - 3.206 Line
- UNIX/Linuxの標準規格における「行(Line)」の定義です。「末尾に改行(newline)を含む文字列の連なり」と厳密に定義されています
-
C言語の標準規格(ISO/IEC 9899)におけるソースファイルの定義(英語)
- ISO/IEC 9899:1999 (C99 Specification) - 5.1.1.2 Translation phases ※PDFリンク
- セクション5.1.1.2のPhase 2に
A source file that is not empty shall end in a new-line character...
(空でないソースファイルは改行文字で終わらなければならない)と明記されています
-
GNU Diffutils(diffコマンド)の仕様(英語)
- GNU Diffutils Manual - Incomplete Lines
- なぜ
\ No newline at end of fileという文字列が出力されるのか、その挙動についてのGNU公式ドキュメントです
まとめ
現代の開発環境では大きな問題になりにくくなったものの、
- POSIXの行定義に沿う
- Gitの差分ノイズを減らす
- CLIツールとの相性を良くする
といった理由から、末尾には改行を入れてNo newline at end of fileを回避するのがベストプラクティスです。
結論
改行しろ
