Programming w/ C# ~ DocFx の利用方法

はじめに

Javajavadoc のように C# においても /// がドキュメントコメントになることは知っており VS Code にもそれ用の拡張機能を加えていた。しかし、これまできちんと運用したことがなかったため、ここらで導入してみることにした。(DoxFx は 2.61.0 時点でまだまだバグがたくさんありそうだ。)
DocFx で Web ページのセットを構築するには、けっこう注意点があるため、ここに記録する。

運用方法

以下のように運用すると非常に便利。

  1. ソースコードとは独立した場所に仕様文書専用フォルダを切る。
  2. すべてのプロジェクトはそのサブフォルダに文書化する。
  3. 各プロジェクトの仕様文書専用フォルダに docfx.json を配置し、そのフォルダを working dir として docfx を起動する doc タスク定義をソースコードの側の .vscode/tasks.json に記述する。
  4. 仕様文書専用フォルダを Docker の Apache で常時 Web 展開する。

VS Code タスク設定

ビルド中間生成物や DocFx のキャッシュなどが悪さをすることがあるため、clean コマンドも整備しておく。

    {
      "label": "doc",
      "type": "shell",
      "command": "docfx",
      "args": [
        "...(仕様文書専用フォルダ)...\\...(プロジェクトサブフォルダ)...\\docfx.json",
        "--cleanupCacheHistory",
      ],
      "group": {
        "kind": "build",
        "isDefault": true,
      },
      "presentation": {
        "reveal": "always",
        "clear": true,
      },
    },
    {
      "label": "clean",
      "type": "shell",
      "command": ".vscode\\_clean.bat",
      "args": [
      ],
      "group": {
        "kind": "build",
        "isDefault": true,
      },
      "presentation": {
        "reveal": "always",
        "clear": true,
      },
    },

クリーンアップバッチは各自の環境におけるビルド出力先 obj フォルダ, bin フォルダに応じて設定する。

@echo off
dotnet clean --nologo
rd /s /q bin
for /f "usebackq tokens=1 delims==" %%d in (`dir /d /b /s ^| findstr /e \\obj ^| findstr /v \\obj\\`) do rd /s /q %%d

名前空間ページの作成

名前空間のページを作成するには namespaces フォルダを新設し、下記のようにアクセスを許可しておく。

    "overwrite": [
      {
        "files": [
          "namespaces/**.md"
        ]
      }
    ]

そして、各名前空間のページを Markdown で記述する。uid には名前空間の識別子を、summary には *content を指定する。

---
uid: UserLib.Common
summary: *content
---
UserLib.Common 名前空間は, 標準の各種オブジェクト型に対する拡張メソッドまたは機能拡張した派生クラスを扱う.

表示抑制の設定

継承メンバーの表示抑制

継承メンバーの表示が煩わしい場面があるため、以下のようにしてノイズ的な情報を表示抑制する。
filterConfig.yml に正規表現を記述して表示抑制する際は docfx --cleanupCacheHistory で文書ビルドしないと正しく反映されないことが多い

継承メンバーの項目全消去 (不採用)

継承メンバーの項目全体を消去するには下記を追記する。docfx の出力文書ではなく css で表示を抑制するようだ。ただ一律消去は弊害が大きいため、不採用とする。

inheritedMembers { display: none; }

Obsolete メンバーの消去

Obsolete メンバーを消去するには、スタイル設定ではなく filterConfig.yml が必要だ。filterConfig.yml は Python のようにインデントにセンシティブであることに注意を要する。

  "build": {
    "filter": "filterConfig.yml",
  }

apiRules:
- exclude:
    hasAttribute:
        uid: System.ObsoleteAttribute

下記のように GitHub の Q&A に従うとメンバーではなく [Obsolete] 属性自体しか消えないため注意。これはこれで属性を表示抑制するときに使えるが。
また uidRegex正規表現であるため、名前空間や引数の揺らぎを吸収するように記述する必要がある。

attributeRules:
- exclude:
    uidRegex: ^System\.Obsolete$

基本クラスのメンバーの消去

System.Object.ToString() などすべてのクラスが継承しているメンバーを Inherited Members に表示されると煩わしいため、消去する。object は SystemObjectobject の間で揺らぎがあることに注意。ここでは Member としているが、Field, Event, Method, Property ごとの指定も可能。(.NET API Docs | docfx 参照)

- exclude:
    uidRegex: ^(System\.)?[Oo]bject
    type: Member

ただし、.Equals(),.HashCode(),.SequenceEquals() などを利用して演算子オーバーロードしている演算系ユーティリティなどでは Inherited Members を消去しない方が望ましいと思われる。object のみならず、独自 Exception を定義している場合は System.Exception のメソッドを消去してもよいかもしれない。

- exclude:
    uidRegex: ^(System\.)?Exception
    type: Method

隠蔽した基底クラス・メソッドの消去

基礎ライブラリのメソッドが充実しすぎているためにオーバーライドして隠蔽したケースなどでは基底クラスのメソッド表示を消去する。

- exclude:
    uidRegex: ^DbExtensions.Database
    type: Member

データコンテナのプロパティの消去

コード付帯のオブジェクトというよりは POCO データコンテナとして用いる record class などは各プロパティの個別説明表示ではなく一覧表として remarks タブ内にリスト表示した方が見やすいため、プロパティ表示を消去する。

- exclude:
    uidRegex: ^DataModel\.(.*\.)?ContainerFormat.+
    type: Property

Markdown 記事の追加

Markdown 記事の追加方法

Markdown で追加記事を記述できる。まずは docfx.json ファイル内 buid > content の第2要素 (メニューバー内容相当) に追加することでリソースアクセスを許可する。

  "build": {
    "content": [
      {
        "files": [
          "reference/**.md",
          "reference/**/toc.yml",
        ]
      }
    ]
  }

次にメニューバーにリンクを設ける。

- name: Reference
  href: reference/

そしてフォルダを切って記事を書く。

### API Reference
- [Ms Docs](https://learn.microsoft.com/ja-jp/dotnet/api/)
- [DbExtensions](https://maxtoroq.github.io/DbExtensions/)

toc.yml は左サイドメニューペインのコンテンツになる。ここで Markdown のページ内参照 (# 以降の部分) の記法は、英文字を小文字に、スペースをハイフンに、アンダースコア以外の記号文字を省略にする必要がある。

- name: API Reference
  href: index.md#api-reference

画像等の追加方法

画像等のファイルは Markdown 記事のフォルダに置いても参照できないようだ。working dir 直下に images フォルダを切ってその中に格納する。

    "resource": [
      {
        "files": [
          "images/**"
        ]
      }
    ]

外部リンク動作の設定

外部リンクは別タブで開く設定を Web ページに施す。そのためにまず javascript の格納場所 styles を作る。注意すべきは docfx.json での記述は格納場所のアクセス権の設定でしかないということだ。ここにスクリプトを置いてもそれだけで Web ページに反映されるわけではない。

 "build": {
    "resource": [
      {
        "files": [
          "styles/*.js",
        ]
      }
    ]
  }

javascript コードを記述する。target="_blank" を使う時は rel="noopener noreferrer" の設定も忘れないこと。

$(document).ready( function () {
  $("a[href^='http']").attr('target', '_blank');
  $("a[href^='http']").attr('rel', 'noopener noreferrer');
} );

最後に、適用する Web ページ (Markdown ファイル) の冒頭に下記の 2 行を追記する。各 Web ページに javascript コントロールのインポートを記述しなければ適用されない。また、google api スクリプトのインポートを 2 つ目のスクリプトに内包して動的呼び出しにしようとすると HTML 評価タイミングの関係で動作しなくなるようで script タグは 2 つ記述する必要がある。

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="../styles/target_blank.js"></script>

スタイル設定

事前準備としてスタイル用の javascript, css をアクセス権の専用フォルダに配置する。その上でスタイル記述をしたファイルへの参照をスタイル適用するページに追記する必要がある。

  "build": {
    "resource": [
      {
        "files": [
          "styles/*.js",
          "styles/*.css"
        ]
      }
  }