ポリデント

ソフトウェアに潜むポリデントの話。

インデント

インデントとは、木構造のある行の列をテキストで表現するとき行頭に詰める空白文字、あるいはその行為のことである。

int main(void) {
    return 0;
//↑この空白がインデント
}
* 項目
    + リストアイテム1
    + リストアイテム2<!--
↑この空白がインデント-->

インデントの流儀

インデントには、「何をいくつ詰めるか」で複数の流儀がある。 (これらはしばしば俗に言う「宗教戦争」のテーマとなっているが、今回それには触れない。)

いくつか例を挙げよう。

  • 1レベルごとに空白 (0x20, ' ') を2文字
  • 1レベルごとに空白を3文字
  • 1レベルごとに空白を4文字
  • 1レベルごとに水平タブ (0x09, '\t') を1文字
  • 1レベルごとに空白を2文字、ただし4レベル分 (空白8文字分) ずつまとめて水平タブに置き換える
  • 1レベルごとに空白を4文字、ただし2レベル分 (空白8文字分) ずつまとめて水平タブに置き換える

水平タブと空白の混合

空白が増えてきたらまとめて水平タブに置き換える流儀では、エディタのタブ幅によって表示が壊れやすい。 標準的なターミナル等では水平タブは8文字幅だが、テキストエディタの設定によっては4文字幅で表示する場合などがあるからだ。 プロジェクト側でタブ幅の設定ファイルを持ちテキストエディタにそれを優先させるという手段で回避できるが、とにかく一手間必要になる。

void foo()
  {
// ↑空白2つ
    if(bar)
// ↑空白4つ
      {
// ↑空白6つ
        baz(
// ↑タブ文字1つ
          qux0,
// ↑タブ文字1つ + 空白2つ
          qux1,
          qux2,
          qux3,
        );
      }
  }

こうしたブロックレベルを表現する場合では、水平タブと空白の混合は (たしかに実在はするものの) 特定プロジェクト等が採用していたりするのみで、頻繁にそこら中でみられるような流儀ではない。 しかし、これが局所的に発生しやすい文脈もある。

    if (!dry_run) {
//↑タブ1つ
        prepare();
//↑タブ2つ
        letsgo(param0,
               param1,
//↑タブ2つ + 空白7つ (!?)
               param2,
               param3);
    }

関数定義や関数呼出において引数が多かったり長かったりするとき複数行に跨がることがあるが、このとき最初の引数を別の行にしない流儀では関数名と開き括弧の分 (あるいは、加えて括弧の前の空白分) の空白で引数の先頭が揃うようインデントを追加する派閥がある。 この場合、引数用のインデント幅は関数名の長さに依存し固定幅の水平タブでは表現できないため、どうしても空白文字を使うことになる。 ブロックインデントを水平タブで行っていた場合、めでたくタブ+空白の混合インデントの完成だ。

ポリデント

水平タブ+空白の混合インデントのように多数 (poly) の種類の空白を用いるインデント (indent) のことを、個人的にポリデントと呼ぶことにしている。

この記事はこれを紹介するためだけに書かれた。

おまけ