- JEP 413: Code Snippets in Java API Documentation
- インラインスニペット
- ハイライト
- リンク
- テキストの置き換え
- 外部スニペット(External snippets)
- source-path オプションの設定
- 外部スニペットのスニペットパス指定
- ハイブリッドスニペット
- snippet タグの属性
- マークアップタグ
- まとめ
JEP 413: Code Snippets in Java API Documentation
Java API ドキュメントにコード断片を書く場合、以前は以下のように <pre>{@code ... }</pre>
として記載することが一般的でした。
例えば Stream
の JavaDoc は以下のようになっています。
/** * A sequence of elements supporting sequential and parallel aggregate * operations. The following example illustrates an aggregate operation using * {@link Stream} and {@link IntStream}: * * <pre>{@code * int sum = widgets.stream() * .filter(w -> w.getColor() == RED) * .mapToInt(w -> w.getWeight()) * .sum(); * }</pre> * ... */
Java 18 では、専用の {@snippet ...}
タグにてコード断片を記載できるようになりました。
/** * {@snippet : * int sum = widgets.stream() * .filter(w -> w.getColor() == RED) * .mapToInt(w -> w.getWeight()) * .sum(); * * } */
出力は以下のようになります。
インラインスニペット
{@snippet :
から }
の中にコード断片を記載します。
/** * The following code shows how to use {@code Optional.isPresent}: * {@snippet : * if (v.isPresent()) { * System.out.println("v: " + v.get()); * } * } */
{@snippet ...}
タグでは、<
や >
などの文字をエスケープする必要はなく、&HTMLエンティティを使用する必要はありません。
ただし、/* ... */
のコメントは使用できません。
インデントは {@snippet ...}
タグの閉じタグ }
の位置が起点となります。
package-info.java
の JavaDoc にコードスニペットを書くこともできます。
Java コードだけでなく、例えば、プロパティ値を以下のように記載することもできます。
/** * {@snippet lang="properties" : * house.number=42 * house.street=Main St. * house.town=AnyTown, USA * } */
ハイライト
@snippet
タグの属性として @highlight
を指定することで、コードのハイライトを指定できます。
/** * A simple program. * {@snippet : * class HelloWorld { * public static void main(String... args) { * System.out.println("Hello World!"); // @highlight substring="println" * } * } * } */
正規表現のバウンダリ・マッチャー \b
を使ってい以下のようにハイライトを指定することもできます。
/** * {@snippet : * public static void main(String... args) { * for (var arg : args) { // @highlight region regex = "\barg\b" * if (!arg.isBlank()) { * System.out.println(arg); * } * } // @end * } * } */
リンク
@link
にてコードスニペットにリンクを作成できます。
/** * {@snippet : * public static void main(String... args) { * System.out.println("Hello, World!"); // @link substring="System.out" target="System#out" * } * } */
以下のように System.out の API ドキュメントにリンクを貼ることができます。
テキストの置き換え
@replace
で API ドキュメントに出力する内容を置き換えることができます。
/** * A simple program. * {@snippet : * class HelloWorld { * public static void main(String... args) { * System.out.println("Hello World!"); // @replace regex='".*"' replacement="..." * } * } * } */
外部スニペット(External snippets)
コードスニペットを外部ファイルに配備して参照することができます。
外部スニペットとすることで、/* ... */
のコメントも利用することができます。
外部スニペットは、snippet-files
サブディレクトリに配備します。
例えば、Main.java から参照する外部スニペットファイル ShowOptional.java
は以下のように配備します。
src └── foo ├── Main.java ├── doc-files │ └── icon.png └── snippet-files └── ShowOptional.java
ShowOptional.java
は以下のように記載し、
package foo; import java.util.Optional; public class ShowOptional { void show(Optional<String> v) { // @start region="example" if (v.isPresent()) { System.out.println("v: " + v.get()); } // @end } }
Main.java
からは以下のように参照できます。
/** * The following code shows how to use {@code Optional.isPresent}: * {@snippet class="ShowOptional" region="example"} */ public class Main { }
上記のような外部スニペットには、ドキュメント対象のファイルを検索できるよう --source-path
の指定が必要です(後述します)。
region
を指定しない場合は、外部ファイルの内容が全てそのままスニペットとしてAPIドキュメントに含まれます。
class 属性で指定できる他、file属性で file="ShowOptional.java"
のように指定することもできます。
また、Java コードだけではなく、プロパティファイルなども扱えます。
/** * Here are the configuration properties: * {@snippet file="config.properties" region="house"} */
... # @start region=house house.number=42 house.street=Main St. house.town=AnyTown, USA # @end region=house ...
source-path オプションの設定
前述の外部スニペットを使用するには、javadoc コマンドに--source-path
( または -sourcepath
) を指定する必要があります。
Gradle 7.4 では Java18 がサポートされていないため、 -sourcepath
を以下のように指定する必要があります。
build.gradle.kts
tasks.withType<Javadoc> { options { this as StandardJavadocDocletOptions addStringOption("sourcepath", sourceSets.main.get().java.srcDirs.joinToString(File.pathSeparator)) } }
build.gradle の場合は以下のようになるでしょう。
tasks.withType<Javadoc> {
options.addStringOption('sourcepath', sourceSets.main.allJava.getSourceDirectories().getAsPath())
}
外部スニペットのスニペットパス指定
javadoc コマンドに --snippet-path
で特定のディレクトリを指定することで、特定のディレクトリに外部スニペットファイルを配備することができます。
テストコードをAPI ドキュメントにスニペットとして記載するのが良いプラクティスになるでしょう。
以下のようなディレクトリ構成の Gradle プロジェクトの場合、
src ├─ main │ └── java │ └── foo │ └── Main.java └── test └── java └── foo └── MainTest.java
build.gradle.kts に以下のように -sourcepath
と --snippet-path
を指定します。
tasks.withType<Javadoc> { //configureEach is not needed options { this as StandardJavadocDocletOptions // unsafe cast addStringOption("sourcepath", sourceSets.main.get().java.srcDirs.joinToString(File.pathSeparator)) addStringOption("-snippet-path", sourceSets.test.get().java.srcDirs.joinToString(File.pathSeparator)) } }
Main.java
では以下のように外部スニペットを指定します。
/** * The following code shows how to use {@code Optional.isPresent}: * {@snippet file="test/MainTest.java" region="example"} */ public class Main { }
MainTest.java
に以下のようにスニペットを用意した場合、
class MainTest { void show(Optional<String> v) { // @start region="example" if (v.isPresent()) { System.out.println("v: " + v.get()); } // @end } }
以下のような API ドキュメントが得られます。
ハイブリッドスニペット
インラインスニペットと外部スニペットを合わせて以下のように記載することができます。
/** * {@snippet class=ExternalSnippets region=join2 : * // join a series of strings * var delimiter = ... ; * var result = String.join(delimiter, args); * } */
ハイブリッドスニペットをインライン・スニペットとして処理した結果と、外部スニペットとして処理した結果が一致しない場合は、エラーとなります。
クラスのソースコードを読む人の便宜のために、タグ自体の中にスニペットの内容を含み、またスニペットの内容を含む別のファイルを参照したものです。
スニペットの内容が開発を続けていく中で修正漏れとなることを防ぐ用途で利用することができます。
snippet タグの属性
属性 | 説明 |
---|---|
class | スニペットのコンテンツを含むクラスを指定 |
file | スニペットのコンテンツを含むファイルを指定 |
lang | スニペットの言語または形式を指定 |
region | スニペットのリージョンを指定 |
id | スニペットを識別するためのスニペットの識別子 |
マークアップタグ
- @start : リージョンの始まり
- region:リージョン名
@end:リージョンの終わり
- region:リージョン名(省略可能)
@highlight:テキストの強調表示
- substring:強調表示するテキスト
- regex:強調表示するテキストの正規表現
- region:強調表示するテキストを検索する領域
- type:強調表示のタイプ(bold, italic, highlighted)
@replace:テキスト置換
- substring:置換するテキスト
- regex:置換するテキストの正規表現
- region:置換するテキストを検索する領域
- replacement—置換テキスト
@link:テキストのリンクを作成
- substring:リンクに置き換えられるテキスト
- regex:リンクに置換するテキストの正規表現
- region:リンクに置換するテキストを検索する領域
- target:リンクのターゲット
- type:リンクのタイプ(link がデフォルト)または linkplain
まとめ
Java18で追加された Code Snippets in Java API Documentation について見てみました。
現在は周辺ツールの対応がありませんが、タグをサポートするコンパイラツリーAPIを拡張も導入されているため、将来は外部ツールによるコードスニペットの検証などが行えるようになるものと思われます。