Sandcastle を使わずにクラスリファレンスを出力するライブラリを作成しました。

# このドキュメントの内容

Sandcastle を使わずにクラスリファレンスを出力するライブラリ `mxProject.Tools.ClassDoc` を作成しました。
特徴を簡単に説明します。GitHub の Readme とほぼ同じ内容です。

## 公開先

* GitHub
  * https://github.com/mxProject/ClassDoc
* Nuget
  * https://www.nuget.org/packages/mxProject.Tools.ClassDoc/
  * https://www.nuget.org/packages/mxProject.Tools.ClassDoc.Razor/
  * https://www.nuget.org/packages/mxProject.Tools.ClassDoc.Razor.NetCore/

## 機能

### XMLコメントファイルの読み込み

VisualStudio から出力される XML コメントファイルを読み込み、リフレクションで取得された型やメンバの情報に関連付けます。次のタグをサポートしています。

* summary
* param
* returns
* paramref
* typeparamref
* exception
* remarks

### inheritdoc タグ

&lt;inheritdoc&gt; タグによるコメントの継承をサポートしています。
次の例では、SubClass.OutputDodument のメソッドから BaseClass.OutputDodument メソッドに記述されたコメントの内容を参照できます。

```c#
public class BaseClass
{
    /// <summary>
    /// Output to document.
    /// </summary>
    public virtual void OutputDocument() {}
}

public class SubClass : BaseClass
{
    /// <inheritdoc/>
    public override void OutputDocument() {}
}
```

### 参照タグの置き換え

&lt;see&gt;, &lt;seealso&gt;, &lt;paramref&gt;, &lt;typeparamref&gt; タグをリンク文字列などに置き換えることができます。

次のような see タグがソースコードの XML コメント内に記述されているとします。

```xml
<see cref="SampleClass"/>
<see cref="SampleClass.SampleMethod"/>
```

XML コメントファイルには次のように出力されます。cref には型やメンバを表す識別子が出力されます。

```xml
<see cref="T:SampleNamespace.SampleClass"/>
<see cref="M:SampleNamespace.SampleClass.SampleMethod"/>
```

`IClassDocumentFormatter` インターフェースの実装により、次のような文字列に置き換えて出力することができます。

```Markdown
[SampleClass](./SampleNamespace/SampleClass.md)
[SampleClass.SampleMethod](./SampleNamespace/SampleClass.md#SampleMethod)
```

※ 異なるページ内のアンカーへ直接遷移することは実現できていません。上の例では "#SampleMethod" アンカーには遷移せず、"SampleClass.md" ページの先頭へ遷移します。

### ドキュメントへの出力

ドキュメント出力は主に [IClassDocumentWriter](https://github.com/mxProject/ClassDoc/blob/master/Documents/mxProject.Tools.ClassDoc/IClassDocumentWriter.md) インターフェースと [IClassDocumentFormatter](https://github.com/mxProject/ClassDoc/blob/master/Documents/mxProject.Tools.ClassDoc/IClassDocumentFormatter.md) インターフェースの機能を利用します。

ライブラリには次の実装を含んでいます。上記のリンク先のページもこれらのクラスを用いて出力したものです。

* RazorEngine を使用してファイルに出力する RazorDocumentWriter クラス
* Markdown 形式のリンクやアンカーへの文字列フォーマットを行う MarkdownFormatter クラス

## 使用方法

次の例では、LoadAssemblies フォルダに格納されたアセンブリとXMLコメントファイルを読み込み、クラスリファレンスを出力しています。使用しているテンプレートと出力結果は公開先を参照してください。

* テンプレート
  * https://github.com/mxProject/ClassDoc/tree/master/RazorTemplates

* 出力ファイル
  * https://github.com/mxProject/ClassDoc/tree/master/Documents
  * https://github.com/mxProject/ClassDoc/tree/master/DocumentSamples/SampleLibrary

```c#

static void OutputDocument()
{
    // Razor テンプレート
    string typeTemplate = @".\RazorTemplates\TypeTemplate.txt";
    string namespaceTemplate = @".\RazorTemplates\NamespaceTemplate.txt";

    // 読み込むアセンブリ
    string[] dlls = new[]
    {
        @".\LoadAssemblies\mxProject.Tools.ClassDoc.dll",
        @".\LoadAssemblies\mxProject.Tools.ClassDoc.Razor.dll",
        @".\LoadAssemblies\SampleLibrary1.dll",
        @".\LoadAssemblies\SampleLibrary2.dll"
    };


    // ライターを設定（.NET Framework バージョンの場合）
    RazorDocumentWriter writer = new RazorDocumentWriter(Encoding.UTF8)
    {
        RootDirectory = @".\Documents\",
        TypeDocumentTemplate = File.ReadAllText(typeTemplate, Encoding.UTF8),
        NamespaceDocumentTemplate = File.ReadAllText(namespaceTemplate, Encoding.UTF8),
    };

    // ライターを設定（.NET Core バージョンの場合）
    string typeDocTemplate = File.ReadAllText(typeTemplate, Encoding.UTF8);
    string namespaceDocTemplate = File.ReadAllText(namespaceTemplate, Encoding.UTF8);
    using RazorDocumentWriter writer = RazorDocumentWriter.Create
    (
        Encoding.UTF8,
        namespaceDocTemplate,
        typeDocTemplate
    );
    weiter.RootDirectory = @".\Documents\";


    // コンテキストとフォーマッターを設定
    ClassDocContext context = new ClassDocContext()
    {
        // 出力対象を決定するフィルタメソッド
        // ここで設定している値は ClassDocContext の初期値として設定されているものと同じです
        ConstructorFilter = ConstructorInfoExtensions.IsRecommendOutputToDocument,
        TypeFilter = TypeExtenstions.IsRecommendOutputToDocument,
        PropertyFilter = PropertyInfoExtensions.IsRecommendOutputToDocument,
        FieldFilter = FieldInfoExtensions.IsRecommendOutputToDocument,
        MethodFilter = MethodInfoExtensions.IsRecommendOutputToDocument,
        EventFilter = EventInfoExtensions.IsRecommendOutputToDocument,
    };

    MarkdownFormatter formatter = new MarkdownFormatter(context)
    {
        // アンカーのフォーマット
        // ここで設定している値は MarkdownFormatter の初期値として設定されているものと同じです
        ConstructorAnchorDefaultFormat = "{0} Constructor",
        PropertyAnchorDefaultFormat = "{0} Property",
        FieldAnchorDefaultFormat = "{0} Field",
        MethodAnchorDefaultFormat = "{0} Method",
        EventAnchorDefaultFormat = "{0} Event",
        ParameterNameDefaultFormat = "`{0}`",
    };

    // 型情報を読み込みます
    IReadOnlyList<TypeWithComment> types = TypeLoader.LoadTypes(dlls, context, null);

    // 名前空間ごとにグループ化します
    foreach (var group in types.GroupBy(type => type.Namespace).OrderBy(group => group.Key))
    {
        NamespaceInfo nameSpace = new NamespaceInfo(group.First()?.Namespace, group);

        // 名前空間の情報をドキュメントに出力します
        writer.WriteNamespaceDocument(nameSpace, formatter);

        // 型の情報をドキュメントに出力します
        foreach (var type in group.OrderBy(type => type.Name))
        {
            writer.WriteTypeDocument(type, formatter);
        }
    }
}
```

## 最後に

こういったライブラリの拡充が XML コメントを記述するモチベーションにつながれば幸いです。