Delphi のソースコードフォーマッタ

# はじめに

他人が書いた古いプロジェクトをマイグレーションする場合には、ソースコードをあらかじめ**ソースコードフォーマッタ**で整形しておくといいと思います。

Delphi (Pascal) に不慣れな方、あるいはメインで Delphi を使っていない方が C++ とかの流儀で書いたコードは **「気持ちは解るけどとても読みにくいんだ...」** と思ってしまいます。逆もしかりなのでお互い様なんでしょうけどね...。

# フォーマッタ

IDE ビルトインのソースコードフォーマッタは **Delphi 2010** から実装されています。コマンドラインツール版 (formatter.exe) は **Delphi XE** から追加されています。

## フォーマッタの使い方

DocWiki に詳細な記事があります。ざっくり言うと...


| やりたい事 | やり方 |
|:--|:---|
| プロジェクトに含まれるすべてのソースコードファイルを整形する | プロジェクトマネージャからプロジェクトを右クリックして [プロジェクトソースを整形...]  |
| ソースコードファイル全体を整形する | コードエディタ上で右クリックして [ソースの整形]  |
| ソースコードファイルの一部を整形する | コードエディタで整形したい部分をマウスで選択し、右クリックして [ソースの整形]  |

**See also:**

 - [ソース コードを整形する (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%BD%E3%83%BC%E3%82%B9_%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%95%B4%E5%BD%A2%E3%81%99%E3%82%8B)

## コマンドライン版フォーマッタ (formatter.exe) の使い方

コマンドライン版のフォーマッタもあります。DocWiki に詳細な記事があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/d713ccb4-0366-a22a-7f64-040a0de2f075.png)


**See also:**

 - [コマンドライン フォーマッタ： Formatter.EXE (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3_%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%82%BF%EF%BC%9A_Formatter.EXE)


## フォーマッタの設定

フォーマッタの設定は [ツール | オプション] でオプションダイアログを出し、[言語 > フォーマッタ > Delphi] で設定できます。

[プロファイルと状態] では、設定をプロファイルとして保存する事もでき、標準で 3 つのプロファイルが用意されています。初期状態に戻すには `Formatter_Default.config` を指定して [適用] ボタンを押します。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/80db3864-e192-6039-c280-bdc2df20fc34.png)
標準で持っている 3 つのプロファイルの差異を調べてみました。

|  | プロファイルファイル名 |
|:---|:---|
| デフォルト | Formatter_Default.config |
| コンパクト | Formatter_Compact.config |
| ワイド | Formatter_Wide.config |

フォーマッタのプロファイルファイルは `%AppData%\Embarcadero\BDS\nn.n` にあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/5cdb11f7-16a4-34ba-a387-40803a9f780d.png)
各設定項目の意味と凡例は下部に表示されているのですが、デフォルト状態では見切れているので、ガバッと広げましょう。

### インデント

| 設定| Default | Compact | Wide |
|:---|:---|:---|:---|
| case ラベルのインデント | はい | <font color="Crimson">いいえ</font> | はい |
| case 文の else のインデント | いいえ | いいえ | <font color="Crimson">はい</font> |
| case 文の内容のインデント | はい | はい | はい |
| ラベルのインデント | 一段減らす | 一段減らす | 一段減らす |
| begin および end キーワードのインデント | いいえ | いいえ | いいえ |
| begin と end で囲まれたブロックのインデント | はい | はい | はい |
| interface セクション、implementation セクション、およびその他セクションのインデント| いいえ | いいえ | いいえ |
| アセンブラ セクションのインデント | はい | はい | はい |
| インデント位置の上限 | 40 | 40 | 40 |
| クラス定義本体のインデント | いいえ | いいえ | <font color="Crimson">はい</font> |
| コメントのインデント | はい | はい | はい |
| コンパイラ指令のインデント | いいえ | いいえ | いいえ |
| ネストした角かっこと丸かっこのインデント | いいえ | いいえ | いいえ |
| 関数本体のインデント | いいえ | いいえ | <font color="Crimson">はい</font> |
| 継続行のインデント | 2 | 2 | 2 |
| 内部関数のインデント | はい | はい | はい |

### スペース

| 設定| Default | Compact | Wide |
|:---|:---|:---|:---|
| // コメントのスペース設定 | 前後 | 前後 | 前後 |
| { および (* コメントのスペース設定 | 内側と外側 | 内側と外側 | 内側と外側 |
| スペース競合の解消方法 | スペース | スペース | スペース |
| 代入演算子前後のスペース設定 | 前後 | 前後 | 前後 |
| 単項前置演算子前後のスペース設定 | なし | なし | <font color="Crimson">前後</font> |
| 二項演算子前後のスペース設定 | 前後 | 前後 | 前後 |
| ジェネリックスにおける山かっこの内側のスペース設定 | いいえ | いいえ | <font color="Crimson">はい</font> |
| 角かっこ前後のスペース設定 | いいえ | いいえ | <font color="Crimson">はい</font> |
| 丸かっこ内のスペース設定 | いいえ | いいえ | <font color="Crimson">はい</font> |
| コロン前後のスペース設定 | 後のみ | 後のみ | <font color="Crimson">前後</font> |
| コンマ前後のスペース設定 | 後のみ | 後のみ | 後のみ |
| セミコロン前後のスペース設定 | 後のみ | 後のみ | 後のみ |
| 関数での丸かっこ前のスペース | いいえ | いいえ | いいえ |
| 書式内のコロン前後のスペース設定 | なし | なし | なし |

### 改行

| 設定 | Default | Compact | Wide |
|:---|:---|:---|:---|
| begin キーワードの後で改行 | はい | はい | はい |
| try-except ブロック内の単一命令文の前で改行 | はい | <font color="Crimson">いいえ</font> | はい |
| メソッド定義の begin キーワードの後で改行 | はい | はい | はい |
| 制御文の begin キーワードの後で改行 | はい | はい | はい |
| 制御文の begin キーワードの前で改行 | はい | <font color="Crimson">いいえ</font> | はい |
| 制御文内の単一命令文の前で改行 | はい | <font color="Crimson">いいえ</font> | はい |
| 'else' と 'if' の間で改行 | いいえ | いいえ | いいえ |
| 'end else begin' 内での改行の削除 | いいえ | <font color="Crimson">はい</font> | いいえ |
| 'end else if' 内での改行の削除 | いいえ | <font color="Crimson">はい</font> | いいえ |
| label 句、exports 句、requires 句、contains 句での改行 | 現状どおり | <font color="Crimson">いいえ</font> | <font color="Crimson">はい</font> |
| then キーワードの前で改行 | いいえ | いいえ | <font color="Crimson">はい</font> |
| uses キーワードの後で改行 | 現状どおり | <font color="Crimson">いいえ</font> | <font color="Crimson">はい</font> |
| uses 句の中で改行 | 現状どおり | <font color="Crimson">いいえ</font> | <font color="Crimson">はい</font> |
| var セクションと const セクションの中で改行 | はい | <font color="Crimson">いいえ</font> | はい |
| セミコロンの後で改行 | はい | はい | はい |
| プロパティ宣言での改行 | いいえ | いいえ | <font color="Crimson">はい</font> |
| ラベルの後で改行 | はい | <font color="Crimson">いいえ</font> | はい |
| 継承リストでの改行 | いいえ | いいえ | いいえ |
| 配列の初期化での改行 | はい | はい | はい |
| 無名関数使用時の改行 | はい | <font color="Crimson">いいえ</font> | はい |
| 無名関数の代入時の改行 | いいえ | いいえ | <font color="Crimson">はい</font> |
| 関数の戻り値型の改行 | いいえ | いいえ | いいえ |
| 関数呼び出しでパラメータごとに改行 | いいえ | いいえ | いいえ |
| 関数定義でパラメータごとに改行 | いいえ | いいえ | <font color="Crimson">はい</font> |
| implementation セクションで区切り記号として使用される空行の数 | 1 | 1 | 1 |
| interface セクションで区切り記号として使用される空行の数 | 1 | 1 | 1 |
| type キーワード前の空行の数 | 1 | <font color="Crimson">0</font> | 1 |
| コンパイラ指令前後の空行の数 | 0 | 0 | 0 |
| サブセクション前の空行の数 | 1 | 1 | 1 |
| セクション キーワード前後の空行の数 | 1 | 1 | 1 |
| 可視性修飾子前の空行の数 | 0 | 0 | 0 |
| 隣接する空行の最大数 | 1 | 1 | 1 |
| ソースのトリミング | はい | はい | はい |
| ユーザー入力の改行を保持 | いいえ | いいえ | いいえ |
| 右マージン | 80 | 80 | 80 |
| 改行文字 | システムのデフォルト値 | システムのデフォルト値 | システムのデフォルト値 |

### 大文字表記

| 設定 | Default | Compact | Wide |
|:---|:---|:---|:---|
| コンパイラ指令の大文字表記 | 大文字で表記 | 大文字で表記 | 大文字で表記 |
| その他の単語の大文字表記 | 最初の出現通り | 最初の出現通り | 最初の出現通り |
| 数値の大文字表記 | 大文字で表記 | 大文字で表記 | 大文字で表記 |
| 予約語と指令の大文字表記 | 現状どおり | 現状どおり | 現状どおり |

### 整列

| 設定 | Default | Compact | Wide |
|:---|:---|:---|:---|
| パラメータ型の整列 | いいえ | いいえ | <font color="Crimson">はい</font> |
| プロパティでのフィールドの整列 | いいえ | いいえ | いいえ |
| 型宣言での '=' の整列 | いいえ | いいえ | いいえ |
| 型名の整列 | いいえ | いいえ | いいえ |
| 行末コメントの整列 | いいえ | いいえ | いいえ |
| 初期化文での '=' の位置の整列 | いいえ | いいえ | いいえ |
| 代入演算子の整列 | いいえ | いいえ | いいえ |
| 定数宣言での '=' の整列 | いいえ | いいえ | いいえ |
| 型名の前での ':' の整列 | いいえ | いいえ | いいえ |
| 整列位置の上限 | 60 | 60 | 60 |
| 非整列行の最大数 | 0 | 0 | 0 |

## 個人的なオススメ設定

個人的なオススメ設定をデフォルトとの差異で書いてみたいと思います。理由も付記しておきます。
### インデント

| 設定 | オススメ |
|:---|:---|
| begin および end キーワードのインデント | いいえ |
| コメントのインデント [^1]| <font color="Crimson">いいえ</font> |

 - 個人的には `begin および end キーワードのインデント` を `はい` にして **J&W スタイル** [^2] にしたい所ですが、一般的ではないので。
 - コメントのインデントはそのままにしておかないと悲惨なことになります。

### スペース

| 設定 | オススメ |
|:---|:---|
| // コメントのスペース設定 [^1]| <font color="Crimson">スペースを保持</font>  | 
| { および (* コメントのスペース設定 [^1]| <font color="Crimson">スペースを保持</font>  |
| 単項前置演算子前後のスペース設定 | <font color="Crimson">前後</font> |

 - コメントのスペースはそのままにしておかないと悲惨なことになります。
 - **10.1 Berlin** 以前のフォーマッタは**疑似プロパティ (Pseudo-Property)** をブロックコメントとして認識してしまうバグがあるため、整形するとデータモジュールが壊れてしまいます。
 - 単項前置演算子というのは `+`, `-`, `not` です。個人的には **not** だけ前後にスペースが欲しいトコロですが、一緒になっているので仕方なく。前置の `+`, `-` ってそんなにないのでこれでいいかと。

**See also:**

 - [Delphiのフォーマッタ(コードの整形)でDataMoudleが壊れる (山本隆の開発日誌)](https://www.gesource.jp/weblog/?p=7605)
 - [[RSP-11981] Formatter breaks CLASSGROUP entry (Quality portal)](https://quality.embarcadero.com/browse/RSP-11981)

### 改行

| 設定 | オススメ |
|:---|:---|


### 大文字表記

| 設定 | オススメ |
|:---|:---|
| 予約語と指令の大文字表記 | <font color="Crimson">小文字で表記</font> |

 - 予約語と指令は小文字に統一したい所。

### 整列

| 設定 | オススメ |
|:---|:---|
| 行末コメントの整列 | <font color="Crimson">はい</font> |

 - 狙った通りにならないかもしれませんが、整列しておくと後の編集が楽です。

## 個人的な**イラッと**ポイント

### ■ 部分範囲型の `..` の前後に強制的にスペースが入る

配列とかですね。

**解決方法:**
`[SP]..[SP]` を `..` で全置換してしまえばいいと思います。全置換しても、問題が発生する事はそんなにないでしょう。

### ■ `end.` 行の前に必ず空行が入る

ユニットの時はいいですけど、プロジェクトソース / コンソールアプリケーション (*.dpr) だと違和感があります。

**解決方法:**
手動で削除しましょう。

### ■ フォーマッタはインライン変数宣言を知らない

10.3 Rio で実装されたインライン変数宣言を知りません。

**解決方法:**
なし。`var セクションと const セクションの中で改行` を `いいえ` や `現状どおり` に設定してもダメなときはダメみたいです。古いコードのマイグレーションでは問題にならないのでしょうけれど...。

**See also:**

 - [[RSP-22089] Code Formatter Fails on Inline Vars (Quality Portal)](https://quality.embarcadero.com/browse/RSP-22089)

### ■ フォーマッタはプログラマ定義の型 (New Type) を知らない

これは割と問題です。例えば次のようなレコード型は大丈夫なのですが、

```pascal
type
  TRec = packed record
    ID: Integer;
    Password: string;
  end;
```

次のようなレコード型の配列は破綻します。

```pascal
type
  TRecArr = array [1..10] of packed record
    ID: Integer;
    Password: string;
  end;
```

上記コードを実際に整形するとこうなります。

```pascal
type
  TRecArr = array [1 .. 10] of packed record ID: Integer; // セミコロンで型定義の終わりだと思っている
Password:                                                 // ラベルだと思っている
string;                                                   // なにこれ？関数？
end;                                                      // 余剰の end; ができるので、以降の整形が破綻する
```

**標準 Pascal** でも可能な文法なのですが... [^3]。

**解決方法:**
なし。あえていうなら事前にブロックコメントで保護するくらいでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/a9da56f2-1f1f-0054-9685-c3566db20b65.png)


**See also:**

 - [[RSP-18273] Formatter formats variables of type record ... end incorrectly (Quality Portal)](https://quality.embarcadero.com/browse/RSP-18273)


### ■ フォーマッタは複数行文字列を知らない

12 Athens で実装された複数行文字列を知りません。

**解決方法:**
なし。

# おわり
正直、コードフォーマッタにはバグがありますし、思った通りの結果は得られないかもしれません。それでも、大雑把に整形してあれば、その後の作業がかなり楽になると思うのでオススメです。

**Delphi 2007?** **DelForExp** を使えばいいんじゃないかな？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/06a0bc73-3a2f-4f7a-7511-d5fa12aa09d8.png)

 - [DelFor Expert (Torry.com)](https://torry.net/authorsmore.php?id=894)
 - [DelFor Expert (for Delphi 2007) (ht-deko.com)](https://ht-deko.com/tech053.html#DELFOREX)

[^1]: これらをまとめて設定しておく事により、整形したくない場所をブロックコメントで保護できます。逆に言うと、コードをブロックコメントでコメントアウトしている場合、これらの設定をしないとブロックコメント内コードのインデントが破壊されます。
[^2]: 詳しくは[こちら](https://qiita.com/ht_deko/items/50bac0de384d66208944#832--pascal-%E3%81%AE%E5%AD%97%E4%B8%8B%E3%81%92-%E3%82%A4%E3%83%B3%E3%83%87%E3%83%B3%E3%83%88-%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB)を。
[^3]: こういうのがあるから、**Modula-2** のレコード型ではフィールドを `|` で区切るようになっているのでしょうね。