EditorConfig の細かな仕様っぽいこと


こんにちは、 EditorConfig Emacs Plugin をつくっています

この文章はなに?

EditorConfig を書くとき・使うときに知っておくとべんりかもしれない細かな話を備忘録的につらつらと書いておくものです。
順番は適当ですが、下に行くほどどうでもいい情報かもしれません。

ここに書かれていることは、実のところほとんどドキュメント化されているわけではありません。
ただし、 .editorconfig ファイルが正しく解釈されるかをテストする editorconfig-core-test の中ですべて明示されていることなので、よほどのことがない限り変わらないんじゃないかなあと思います。

この文章では、 EditorConfig の基本的な目的・使い方については説明しません。
公式ドキュメント や、 EditorConfig タグ などをいい感じに参照してください。

editorconfig コマンドの使い方

独自なプロパティ定義

editorconfig コマンドはサポートされている設定のみしか扱えないというわけではないので、 .editorconfig ファイルには好きな設定名と値のペアを追加することができます。

例えば、 /path/to/repo/.editorconfig

/path/to/repo/.editorconfig
[*.js]
indent_style = space
key0 = value0

と書いたうえで、 editorconfig /path/to/repo/a.js を実行すると、この二つの設定値を得ることができます。

~ $ editorconfig /path/to/repo/a.js
indent_style=space
key0=value0

これを利用して、独自に設定を定義し利用するようなコマンドやエディタ拡張を書くことができると思います。

  • 例えば、 dotnet/roslyn では、 dotnet_ csharp_ から始まる独自のプロパティを設定し利用しています
  • 同様に、 dfmt というツールは、独自に dfmt_ から始まる設定を定義しコマンドから利用しています
  • また、例えば Emacs Plugin では、 editorconfig-custom-hooks に関数を追加することで独自に定義した設定値を利用することができます。これを利用した拡張の例のようなものとして、 editorconfig-custom-majormode を以前作りました。

逆に、設定名を typo しても気づかない(エラーを吐いたりしない)ので気をつけてください。

出力時の値の書き換え

editorconfig コマンドは、基本的には .editorconfig に記述された設定値をそのまま素通しして出力しますが、いくつかの微妙な加工を行う場合があります。

まず、値の名前はすべて downcase されます。

/path/to/repo/.editorconfig
[*.js]
Indent_Size = 4
FOO = BAR
~ $ editorconfig /path/to/repo/a.js
indent_size=4
foo=BAR

また、公式でドキュメントされている設定のうち、真偽値 (true, false) を取るものについては、コマンド実行時にその値がすべて小文字に downcase されます。

/path/to/repo/.editorconfig
[*.js]
trim_trailing_whitespace = TRUE
insert_final_newline = trUe
~ $ editorconfig /path/to/repo/a.js
trim_trailing_whitespace=true
insert_final_newline=true

インデント幅に関する設定には、少し複雑なルールが存在します。

  • indent_size = tab であり、 tab_width に値が設定されている場合、 indent_sizetab_width と同じ値に設定します
  • indent_size に数字が設定されていて tab_width が設定されていない場合、 tab_widthindent_size と同じ値に設定します
  • indent_size が設定されておらず、 tab_width に値が設定されていて、 indent_style = tab である場合、 indent_sizetab_width と同じ値に設定します

ただし、実用上はユーザはこの仕様をあまり気にしなくても大丈夫だと思います。

  • indent_style = space のときは、 indent_size を適切に設定しましょう
  • indent_style = tab のときは、 tab_width を適切に設定しましょう

.editorconfig ファイルの書き方

値の優先順位・値の削除

.editorconfig の値は、常に、上から下へ、親ディレクトリから子ディレクトリへと読み込まれ、後に定義された値が優先されます。

CSS のように詳細に指定されたセクションのほうが優先されるみたいなルールはないので、 * でマッチするルールは普通にファイルの先頭においておくことをオススメします。

root = true

[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

場合によっては、上で定義されていた値を削除し、未定義に戻したい場合があると思います。

このような場合、基本的には「その設定の値として正しくない値」を入れれば良いです(例えば、 indent_size = none など)。

ただ、このやり方だと、その値が何を意味するのか初見でわからない場合がもしかしたらあるかもしれない・その値があとから有効な値になるかもしれないというような感じの懸念があったため、値を未定義にするための値が(わりと最近?)定められました。

unset がこの目的のために reserve されています。以下のように使うことができます。

root = true

[*]
indent_style = space
indent_size = 4

[*.js]
indent_size = unset

ファイル名のパターンマッチ

ファイル名のマッチには少しの独自拡張がされた Glob パターンマッチを用います。

使用可能なパターンのリスト自体に関しては 公式ページのドキュメント で尽くされているので参照してください。
.gitignore で使われるのと似てる、と考えて良いです。

一つ注意するといいこととして、マッチ時の動きとして、(これも .gitignore と同様に)パターンに / が入っているかどうかでマッチの方法が変化する、というものがあります。
この方式は Basename Matching と呼ばれることがあるみたいです。

まず、パターンの中に / がない場合、マッチはファイルの Basename のみを対象として行われます。

例えば、以下のようなパターンは、 a.js, lib/b.js の双方にマッチします。

[*.js]
indent_size = 2

一方、パターン中に / が一つでも含まれる場合、 .editorconfig ファイルからの相対パス全体に対するマッチが行われます。

例えば、以下のように書くと、これは .editorconfig ファイルが存在するのと同階層の a.js などにしかマッチしません。

[/*.js]
indent_size = 2

コメント

基本的に、 # または ; があるとそこから行末までがコメントとして扱われます。

ただし、正確にはコメントとして解釈されるには加えて以下のどちらかが必要です。

  • この文字が行頭にある
  • 直前が半角スペースである

つまり、以下はコメントにはならず、値が value# Not comment であると解釈されます。

[*.js]
key = value# Not comment

値のセパレータ

値を定義するとき、通常は key = value と書くと思います。
公式ページでも = が使われています。
しかし、実はこの = の代わりに、 : を使うことができたりします。
しかも、 =: が混在しても大丈夫です。

[*.py]
indent_style = space
indent_size : 2

実際には普通に = だけを使ったほうが良いと思います。