
The command-line tool fsdocs
can be used to generate documentation
for F# libraries with XML comments. The documentation is normally built using fsdocs build
and developed using fsdocs watch
. For
the former the output will be placed in output\reference
by default.
fsdocs
automatically selects the projects and "cracks" the project files for information
- Projects with
GenerateDocumentationFile
and without IsTestProject
are selected.
- Projects must not use
TargetFrameworks
(only TargetFramework
, singular).
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
|
The HTML is built by instantiating a template. The template used is the first of:
Usually the same template can be used as for other content.
XML Doc Comments may use the normal F# and C# XML doc standards.
The tags that form the core of the XML doc specification are:
<c> <para> <see>* <value>
<code> <param>* <seealso>*
<example> <paramref> <summary>
<exception>* <permission>* <typeparam>*
<include>* <remarks> <typeparamref>
<list> <inheritdoc> <returns>
In addition, you may also use the Recommended XML doc extensions for F# documentation tooling.
<a href = "...">
links
Arbitrary paragraph-level HTML such as <b>
for bold in XML doc text
<namespacedoc>
giving documentation for the enclosing namespace
<exclude>
to exclude from XML docs
-
<category>
to give a category for an entity or member. An optional index
attribute can be specified
to help sort the list of categories.
-
\(...\)
for inline math and $$...$$
and \[...\]
for math environments, see http://docs.mathjax.org.
Some escaping of characters (e.g. <
, >
) may be needed to form valid XML
An example of an XML documentation comment, assuming the code is in namespace TheNamespace
:
/// <summary>
/// A module
/// </summary>
///
/// <namespacedoc>
/// <summary>A namespace to remember</summary>
///
/// <remarks>More on that</remarks>
/// </namespacedoc>
///
module SomeModule =
/// <summary>
/// Some actual comment
/// <para>Another paragraph, see <see cref="T:TheNamespace.SomeType"/>. </para>
/// </summary>
///
/// <param name="x">The input</param>
///
/// <returns>The output</returns>
///
/// <example>
/// Try using
/// <code>
/// open TheNamespace
/// SomeModule.a
/// </code>
/// </example>
///
/// <category>Foo</category>
let someFunction x = 42 + x
/// <summary>
/// A type, see <see cref="T:TheNamespace.SomeModule"/> and
/// <see cref="M:TheNamespace.SomeModule.someFunction"/>.
/// </summary>
///
type SomeType() =
member x.P = 1
Like types, members are referred to by xml doc sig. These must currently be precise as the F#
compiler doesn't elaborate these references from simpler names:
type Class2() =
member this.Property = "more"
member this.Method0() = "more"
member this.Method1(c: string) = "more"
member this.Method2(c: string, o: obj) = "more"
/// <see cref="P:TheNamespace.Class2.Property" />
/// and <see cref="M:TheNamespace.Class2.OtherMethod0" />
/// and <see cref="M:TheNamespace.Class2.Method1(System.String)" />
/// and <see cref="M:TheNamespace.Class2.Method2(System.String,System.Object)" />
let referringFunction1 () = "result"
Generic types are referred to by .NET compiled name, e.g.
type GenericClass2<'T>() =
member this.Property = "more"
member this.NonGenericMethod(_c: 'T) = "more"
member this.GenericMethod(_c: 'T, _o: 'U) = "more"
/// See <see cref="T:TheNamespace.GenericClass2`1" />
/// and <see cref="P:TheNamespace.GenericClass2`1.Property" />
/// and <see cref="M:TheNamespace.GenericClass2`1.NonGenericMethod(`0)" />
/// and <see cref="M:TheNamespace.GenericClass2`1.GenericMethod``1(`0,``0)" />
let referringFunction2 () = "result"
(*
## Go to Source links
'fsdocs' normally automatically adds GitHub links to each functions, values and class members for further reference.
This is normally done automatically based on the following settings:
<RepositoryUrl>https://github.com/...</RepositoryUrl>
<RepositoryBranch>...</RepositoryBranch>
<RepositoryType>git</RepositoryType>
If your source is not built from the same project where you are building documentation then
you may need these settings:
<FsDocsSourceRepository>...</FsDocsSourceRepository> -- the URL for the root of the source
<FsDocsSourceFolder>...</FsDocsSourceFolder> -- the root soure folder at time of build
It is assumed that `sourceRepo` and `sourceFolder` have synchronized contents.
## Markdown Comments
You can use Markdown instead of XML in `///` comments. If you do, you should set `<UsesMarkdownComments>` in
your F# project file.
> Note: Markdown Comments are not supported in all F# IDE tooling.
### Adding cross-type links to modules and types in the same assembly
You can automatically add cross-type links to the documentation pages of other modules and types in the same assembly.
You can do this in two different ways:
* Add a [markdown inline link](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#links) were the link
title is the name of the type you want to link.
/// this will generate a link to [Foo.Bar] documentation
* Add a [Markdown inline code](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code) (using
back-ticks) where the code is the name of the type you want to link.
/// This will also generate a link to `Foo.Bar` documentation
You can use either the full name (including namespace and module) or the simple name of a type.
If more than one type is found with the same name the link will not be generated.
If a type with the given name is not found in the same assembly the link will not be generated.
*)
/// Contains two types [Bar] and [Foo.Baz]
module Foo =
/// Bar is just an `int` and belongs to module [Foo]
type Bar = int
/// Baz contains a `Foo.Bar` as its `id`
type Baz = { id: Bar }
/// This function operates on `Baz` types.
let f (b: Baz) = b.id * 42
/// Referencing [Foo3] will not generate a link as there is no type with the name `Foo3`
module Foo3 =
/// This is not the same type as `Foo.Bar`
type Bar = double
/// Using the simple name for [Bar] will fail to create a link because the name is duplicated in
/// [Foo.Bar] and in [Foo3.Bar]. In this case, using the full name works.
let f2 b = b * 50
If you want to exclude modules or functions from the API docs you can use the [omit]
tag.
It needs to be set on a separate tripple-slashed line, but it could be either the first or the last:
/// Some actual comment
module Bar =
let a = 42
You can build library documentation programatically using the functionality
in the ApiDocs type. To do this, load the assembly and open necessary namespaces:
#r "FSharp.Formatting.ApiDocs.dll"
open FSharp.Formatting.ApiDocs
open System.IO
For example the ApiDocs.GenerateHtml method:
let file = Path.Combine(root, "bin/YourLibrary.dll")
let input = ApiDocInput.FromFile(file)
ApiDocs.GenerateHtml(
[ input ],
output = Path.Combine(root, "output"),
collectionName = "YourLibrary",
template = Path.Combine(root, "templates", "template.html"),
substitutions = []
)
val root: string
type 'T list = List<'T>
<summary>The type of immutable singly-linked lists. </summary>
<remarks>See the <see cref="T:Microsoft.FSharp.Collections.ListModule" /> module for further operations related to lists.
Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or
the notation <c>[1; 2; 3]</c>. Use the values in the <c>List</c> module to manipulate
values of this type, or pattern match against the values directly.
See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/lists">F# Language Guide - Lists</a>.
</remarks>
val someFunction: x: int -> int
<summary>
Some actual comment
<para>Another paragraph, see <see cref="T:TheNamespace.SomeType"/>. </para>
</summary>
<param name="x">The input</param>
<returns>The output</returns>
<example>
Try using
<code>
open TheNamespace
SomeModule.a
</code>
</example>
<category>Foo</category>
val x: int
Multiple items
type SomeType =
new: unit -> SomeType
member P: int
<summary>
A type, see <see cref="T:TheNamespace.SomeModule"/> and
<see cref="M:TheNamespace.SomeModule.someFunction"/>.
</summary>
--------------------
new: unit -> SomeType
val x: SomeType
member SomeType.P: int
Multiple items
type Class2 =
new: unit -> Class2
member Method0: unit -> string
member Method1: c: string -> string
member Method2: c: string * o: obj -> string
member Property: string
--------------------
new: unit -> Class2
val this: Class2
val c: string
Multiple items
val string: value: 'T -> string
<summary>Converts the argument to a string using <c>ToString</c>.</summary>
<remarks>For standard integer and floating point values the and any type that implements <c>IFormattable</c><c>ToString</c> conversion uses <c>CultureInfo.InvariantCulture</c>. </remarks>
<param name="value">The input value.</param>
<returns>The converted string.</returns>
<example id="string-example"><code lang="fsharp"></code></example>
--------------------
type string = System.String
<summary>An abbreviation for the CLI type <see cref="T:System.String" />.</summary>
<category>Basic Types</category>
val o: obj
type obj = System.Object
<summary>An abbreviation for the CLI type <see cref="T:System.Object" />.</summary>
<category>Basic Types</category>
val referringFunction1: unit -> string
<see cref="P:TheNamespace.Class2.Property" />
and <see cref="M:TheNamespace.Class2.OtherMethod0" />
and <see cref="M:TheNamespace.Class2.Method1(System.String)" />
and <see cref="M:TheNamespace.Class2.Method2(System.String,System.Object)" />
Multiple items
type GenericClass2<'T> =
new: unit -> GenericClass2<'T>
member GenericMethod: _c: 'T * _o: 'U -> string
member NonGenericMethod: _c: 'T -> string
member Property: string
--------------------
new: unit -> GenericClass2<'T>
val this: GenericClass2<'T>
val _c: 'T
val _o: 'U
val referringFunction2: unit -> string
See <see cref="T:TheNamespace.GenericClass2`1" />
and <see cref="P:TheNamespace.GenericClass2`1.Property" />
and <see cref="M:TheNamespace.GenericClass2`1.NonGenericMethod(`0)" />
and <see cref="M:TheNamespace.GenericClass2`1.GenericMethod``1(`0,``0)" />
[<Struct>]
type Bar = int
Bar is just an `int` and belongs to module [Foo]
Multiple items
val int: value: 'T -> int (requires member op_Explicit)
<summary>Converts the argument to signed 32-bit integer. This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>Int32.Parse()</c>
with InvariantCulture settings. Otherwise the operation requires an appropriate
static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted int</returns>
<example id="int-example"><code lang="fsharp"></code></example>
--------------------
[<Struct>]
type int = int32
<summary>An abbreviation for the CLI type <see cref="T:System.Int32" />.</summary>
<category>Basic Types</category>
--------------------
type int<'Measure> =
int
<summary>The type of 32-bit signed integer numbers, annotated with a unit of measure. The unit
of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.Int32" />.</summary>
<category>Basic Types with Units of Measure</category>
type Baz =
{ id: Bar }
Baz contains a `Foo.Bar` as its `id`
Baz.id: Bar
val f: b: Baz -> Bar
This function operates on `Baz` types.
val b: Baz
[<Struct>]
type Bar = double
This is not the same type as `Foo.Bar`
Multiple items
val double: value: 'T -> double (requires member op_Explicit)
<summary>Converts the argument to 64-bit float.</summary>
<remarks>This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>Double.Parse()</c> with InvariantCulture settings. Otherwise the operation requires and invokes a <c>ToDouble</c> method on the input type.</remarks>
<example id="double-1"><code lang="fsharp">
double 45
</code>
Evaluates to <c>45.0</c>.
</example>
<example id="double-2"><code lang="fsharp">
double 12.3f
</code>
Evaluates to <c>12.30000019</c>.
</example>
--------------------
[<Struct>]
type double = System.Double
<summary>An abbreviation for the CLI type <see cref="T:System.Double" />. Identical to <see cref="T:Microsoft.FSharp.Core.float" />.</summary>
<category>Basic Types</category>
--------------------
type double<'Measure> = float<'Measure>
<summary>The type of double-precision floating point numbers, annotated with a unit of measure.
The unit of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.Double" />.</summary>
<category index="6">Basic Types with Units of Measure</category>
val f2: b: int -> int
Using the simple name for [Bar] will fail to create a link because the name is duplicated in
[Foo.Bar] and in [Foo3.Bar]. In this case, using the full name works.
val b: int
val a: int
Multiple items
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Formatting
namespace FSharp.Formatting.ApiDocs
namespace System
namespace System.IO
val file: string
type Path =
static member ChangeExtension: path: string * extension: string -> string
static member Combine: path1: string * path2: string -> string + 3 overloads
static member EndsInDirectorySeparator: path: ReadOnlySpan<char> -> bool + 1 overload
static member GetDirectoryName: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetExtension: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetFileName: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetFileNameWithoutExtension: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetFullPath: path: string -> string + 1 overload
static member GetInvalidFileNameChars: unit -> char[]
static member GetInvalidPathChars: unit -> char[]
...
<summary>Performs operations on <see cref="T:System.String" /> instances that contain file or directory path information. These operations are performed in a cross-platform manner.</summary>
Path.Combine([<System.ParamArray>] paths: string[]) : string
Path.Combine(path1: string, path2: string) : string
Path.Combine(path1: string, path2: string, path3: string) : string
Path.Combine(path1: string, path2: string, path3: string, path4: string) : string
val input: ApiDocInput
type ApiDocInput =
{
Path: string
XmlFile: string option
SourceFolder: string option
SourceRepo: string option
Substitutions: Substitutions option
MarkdownComments: bool
Warn: bool
PublicOnly: bool
}
static member FromFile: assemblyPath: string * ?mdcomments: bool * ?substitutions: Substitutions * ?sourceRepo: string * ?sourceFolder: string * ?publicOnly: bool * ?warn: bool -> ApiDocInput
<summary>
Represents an input assembly for API doc generation
</summary>
static member ApiDocInput.FromFile: assemblyPath: string * ?mdcomments: bool * ?substitutions: FSharp.Formatting.Templating.Substitutions * ?sourceRepo: string * ?sourceFolder: string * ?publicOnly: bool * ?warn: bool -> ApiDocInput
type ApiDocs =
static member GenerateHtml: inputs: ApiDocInput list * output: string * collectionName: string * substitutions: Substitutions * ?template: string * ?root: string * ?qualify: bool * ?libDirs: string list * ?otherFlags: string list * ?urlRangeHighlight: (Uri -> int -> int -> string) * ?onError: (string -> unit) -> ApiDocModel * ApiDocsSearchIndexEntry[]
static member GenerateMarkdown: inputs: ApiDocInput list * output: string * collectionName: string * substitutions: Substitutions * ?template: string * ?root: string * ?qualify: bool * ?libDirs: string list * ?otherFlags: string list * ?urlRangeHighlight: (Uri -> int -> int -> string) * ?onError: (string -> unit) -> ApiDocModel * ApiDocsSearchIndexEntry[]
static member GenerateModel: inputs: ApiDocInput list * collectionName: string * substitutions: Substitutions * ?qualify: bool * ?libDirs: string list * ?otherFlags: string list * ?root: string * ?urlRangeHighlight: (Uri -> int -> int -> string) * ?onError: (string -> unit) * ?extension: ApiDocFileExtensions -> ApiDocModel
static member SearchIndexEntriesForModel: model: ApiDocModel -> ApiDocsSearchIndexEntry[]
<summary>
This type exposes the functionality for producing documentation model from `dll` files with associated `xml` files
generated by the F# or C# compiler. To generate documentation model, use one of the overloades of the `Generate` method.
</summary>
<namespacedoc><summary>Functionality relating to generating API documentation</summary></namespacedoc>
static member ApiDocs.GenerateHtml: inputs: ApiDocInput list * output: string * collectionName: string * substitutions: FSharp.Formatting.Templating.Substitutions * ?template: string * ?root: string * ?qualify: bool * ?libDirs: string list * ?otherFlags: string list * ?urlRangeHighlight: (System.Uri -> int -> int -> string) * ?onError: (string -> unit) -> ApiDocModel * ApiDocsSearchIndexEntry[]