FSharp.Formatting


BinderScriptNotebook

Embedding script output

For literate F# scripts, you may embed the result of running the script as part of the literate output. This is a feature of the functions discussed in literate programming and it is implemented using the F# Compiler service.

Including Console Output

To include the Console output use include-output:

let test = 40 + 2

printf "A result is: %d" test
(*** include-output ***)

The script defines a variable test and then prints it. The console output is included in the output.

To include the a formatted value use include-it:

[ 0 .. 99 ]

(*** include-it ***)

To include the meta output of F# Interactive processing such as type signatures use (*** include-fsi-output ***):

let test = 40 + 3

(*** include-fsi-output ***)

To include both console otuput and F# Interactive output blended use (*** include-fsi-merged-output ***).

let test = 40 + 4
(*** include-fsi-merged-output ***)

You can use the same commands with a named snippet:

(*** include-it: test ***)
(*** include-fsi-output: test ***)
(*** include-output: test ***)

You can use the include-value command to format a specific value:

let value1 = [ 0 .. 50 ]
let value2 = [ 51 .. 100 ]
(*** include-value: value1 ***)

Using AddPrinter and AddHtmlPrinter

You can use fsi.AddPrinter, fsi.AddPrintTransformer and fsi.AddHtmlPrinter to extend the formatting of objects.

Emitting Raw Text

To emit raw text in F# literate scripts use the following:

(**
	(*** raw ***)
	Some raw text.
*)

which would emit

Some raw text.

directly into the document.

F# Formatting as a Library: Specifying the Evaluator and Formatting

If using F# Formatting as a library the embedding of F# output requires specifying an additional parameter to the parsing functions discussed in literate programming documentation. Assuming you have all the references in place, you can now create an instance of FsiEvaluator that represents a wrapper for F# interactive and pass it to all the functions that parse script files or process script files:

open FSharp.Formatting.Literate
open FSharp.Formatting.Literate.Evaluation
open FSharp.Formatting.Markdown

// Sample literate content
let content =
    """
let a = 10
(*** include-value:a ***)"""

// Create evaluator and parse script
let fsi = FsiEvaluator()

let doc = Literate.ParseScriptString(content, fsiEvaluator = fsi)

Literate.ToHtml(doc)

When the fsiEvaluator parameter is specified, the script is evaluated and so you can use additional commands such as include-value. When the evaluator is not specified, it is not created automatically and so the functionality is not available (this way, you won't accidentally run unexpected code!)

If you specify the fsiEvaluator parameter, but don't want a specific snippet to be evaluated (because it might throw an exception, for example), you can use the (*** do-not-eval ***) command.

The constructor of FsiEvaluator takes command line parameters for fsi.exe that can be used to specify, for example, defined symbols and other attributes for F# Interactive.

You can also subscribe to the EvaluationFailed event which is fired whenever the evaluation of an expression fails. You can use that to do tests that verify that all off the code in your documentation executes without errors.

F# Formatting as a Library: Custom formatting functions

As mentioned earlier, values are formatted using a simple "%A" formatter by default. However, you can specify a formatting function that provides a nicer formatting for values of certain types. For example, let's say that we would want to format F# lists such as [1; 2; 3] as HTML ordered lists <ol>.

This can be done by calling FsiEvaluator.RegisterTransformation on the FsiEvaluator instance:

// Create evaluator & register simple formatter for lists
let fsiEvaluator = FsiEvaluator()

fsiEvaluator.RegisterTransformation(fun (o, ty, _executionCount) ->
    // If the type of value is an F# list, format it nicely
    if ty.IsGenericType
       && ty.GetGenericTypeDefinition() = typedefof<list<_>> then
        let items =
            // Get items as objects and create paragraph for each item
            [ for it in Seq.cast<obj> (unbox o) -> [ Paragraph([ Literal(it.ToString(), None) ], None) ] ]
        // Return option value (success) with ordered list
        Some [ ListBlock(MarkdownListKind.Ordered, items, None) ]
    else
        None)

The function is called with two arguments - o is the value to be formatted and ty is the static type of the value (as inferred by the F# compiler). The sample checks that the type of the value is a list (containing values of any type) and then it casts all values in the list to obj (for simplicity). Then we generate Markdown blocks representing an ordered list. This means that the code will work for both LaTeX and HTML formatting - but if you only need one, you can simply produce HTML and embed it in InlineHtmlBlock.

To use the new FsiEvaluator, we can use the same style as earlier. This time, we format a simple list containing strings:

let listy =
    """
### Formatting demo
let test = ["one";"two";"three"]
(*** include-value:test ***)"""

let docOl = Literate.ParseScriptString(listy, fsiEvaluator = fsiEvaluator)

Literate.ToHtml(docOl)

The resulting HTML formatting of the document contains the snippet that defines test, followed by a nicely formatted ordered list:

Formatting demo

1: 
let test = ["one";"two";"three"]
  1. one

  2. two

  3. three

val test: int
val printf: format: Printf.TextWriterFormat<'T> -> 'T
<summary>Print to <c>stdout</c> using the given format.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
<example>See <c>Printf.printf</c> (link: <see cref="M:Microsoft.FSharp.Core.PrintfModule.PrintFormat``1" />) for examples.</example>
val value1: int list
val value2: int list
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Formatting
namespace FSharp.Formatting.Literate
namespace FSharp.Formatting.Literate.Evaluation
namespace FSharp.Formatting.Markdown
val content: string
val fsi: FsiEvaluator
Multiple items
type FsiEvaluator = interface IFsiEvaluator new: ?options: string[] * ?fsiObj: obj * ?addHtmlPrinter: bool * ?discardStdOut: bool * ?disableFsiObj: bool * ?onError: (string -> unit) -> FsiEvaluator member RegisterTransformation: f: (obj * Type * int -> MarkdownParagraph list option) -> unit member EvaluationFailed: IEvent<FsiEvaluationFailedInfo>
<summary> A wrapper for F# interactive service that is used to evaluate inline snippets </summary>

--------------------
new: ?options: string[] * ?fsiObj: obj * ?addHtmlPrinter: bool * ?discardStdOut: bool * ?disableFsiObj: bool * ?onError: (string -> unit) -> FsiEvaluator
val doc: LiterateDocument
type Literate = static member ConvertMarkdownFile: input: string * ?template: string * ?output: string * ?outputKind: OutputKind * ?prefix: string * ?fscOptions: string * ?lineNumbers: bool * ?references: bool * ?substitutions: (ParamKey * string) list * ?generateAnchors: bool * ?imageSaver: (string -> string) * ?rootInputFolder: string * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) * ?onError: (string -> unit) -> unit static member ConvertScriptFile: input: string * ?template: string * ?output: string * ?outputKind: OutputKind * ?prefix: string * ?fscOptions: string * ?lineNumbers: bool * ?references: bool * ?fsiEvaluator: IFsiEvaluator * ?substitutions: (ParamKey * string) list * ?generateAnchors: bool * ?imageSaver: (string -> string) * ?rootInputFolder: string * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) * ?onError: (string -> unit) -> unit static member ParseAndCheckScriptFile: path: string * ?fscOptions: string * ?definedSymbols: string list * ?references: bool * ?fsiEvaluator: IFsiEvaluator * ?parseOptions: MarkdownParseOptions * ?rootInputFolder: string * ?onError: (string -> unit) -> LiterateDocument static member ParseMarkdownFile: path: string * ?fscOptions: string * ?definedSymbols: string list * ?references: bool * ?fsiEvaluator: IFsiEvaluator * ?parseOptions: MarkdownParseOptions * ?rootInputFolder: string * ?onError: (string -> unit) -> LiterateDocument static member ParseMarkdownString: content: string * ?path: string * ?fscOptions: string * ?definedSymbols: string list * ?references: bool * ?fsiEvaluator: IFsiEvaluator * ?parseOptions: MarkdownParseOptions * ?rootInputFolder: string * ?onError: (string -> unit) -> LiterateDocument static member ParseScriptString: content: string * ?path: string * ?fscOptions: string * ?definedSymbols: string list * ?references: bool * ?fsiEvaluator: IFsiEvaluator * ?parseOptions: MarkdownParseOptions * ?rootInputFolder: string * ?onError: (string -> unit) -> LiterateDocument static member ToFsx: doc: LiterateDocument * ?substitutions: (ParamKey * string) list * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) -> string static member ToHtml: doc: LiterateDocument * ?prefix: string * ?lineNumbers: bool * ?generateAnchors: bool * ?substitutions: (ParamKey * string) list * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) * ?tokenKindToCss: (TokenKind -> string) -> string static member ToLatex: doc: LiterateDocument * ?prefix: string * ?lineNumbers: bool * ?generateAnchors: bool * ?substitutions: (ParamKey * string) list * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) -> string static member ToPynb: doc: LiterateDocument * ?substitutions: (ParamKey * string) list * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) -> string ...
<summary> This type provides three simple methods for calling the literate programming tool. The <c>ConvertMarkdownFile</c> and <c>ConvertScriptFile</c> methods process a single Markdown document and F# script, respectively. The <c>ConvertDirectory</c> method handles an entire directory tree (looking for <c>*.fsx</c> and <c>*.md</c> files). </summary>
<namespacedoc><summary>Functionality to support literate programming for F# scripts</summary></namespacedoc>
static member Literate.ParseScriptString: content: string * ?path: string * ?fscOptions: string * ?definedSymbols: string list * ?references: bool * ?fsiEvaluator: IFsiEvaluator * ?parseOptions: MarkdownParseOptions * ?rootInputFolder: string * ?onError: (string -> unit) -> LiterateDocument
static member Literate.ToHtml: doc: LiterateDocument * ?prefix: string * ?lineNumbers: bool * ?generateAnchors: bool * ?substitutions: (FSharp.Formatting.Templating.ParamKey * string) list * ?crefResolver: (string -> (string * string) option) * ?mdlinkResolver: (string -> string option) * ?tokenKindToCss: (FSharp.Formatting.CodeFormat.TokenKind -> string) -> string
val fsiEvaluator: FsiEvaluator
member FsiEvaluator.RegisterTransformation: f: (obj * System.Type * int -> MarkdownParagraph list option) -> unit
val o: obj
val ty: System.Type
val _executionCount: int
property System.Type.IsGenericType: bool with get
System.Type.GetGenericTypeDefinition() : System.Type
val typedefof<'T> : System.Type
<summary>Generate a System.Type representation for a type definition. If the input type is a generic type instantiation then return the generic type definition associated with all such instantiations.</summary>
<example id="typedefof-example"><code lang="fsharp"> typeof&lt;int list;&gt; // Evaluates to Microsoft.FSharp.Collections.FSharpList`1[System.Int32] typedefof&lt;int list;&gt; // Evaluates to Microsoft.FSharp.Collections.FSharpList`1[T] /// </code></example>
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 items: MarkdownParagraph list list
val it: obj
module Seq from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.seq`1" />.</summary>
val cast: source: System.Collections.IEnumerable -> seq<'T>
<summary>Wraps a loosely-typed System.Collections sequence as a typed sequence.</summary>
<remarks>The use of this function usually requires a type annotation. An incorrect type annotation may result in runtime type errors. Individual IEnumerator values generated from the returned sequence should not be accessed concurrently.</remarks>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
<example id="cast-1"><code lang="fsharp"> [box 1; box 2; box 3] |&gt; Seq.cast&lt;int&gt; </code> Evaluates to a sequence yielding the same results as <c>seq { 1; 2; 3 }</c>, explicitly typed as <c>seq&lt;int&gt;</c>. </example>
type obj = System.Object
<summary>An abbreviation for the CLI type <see cref="T:System.Object" />.</summary>
<category>Basic Types</category>
val unbox: value: obj -> 'T
<summary>Unbox a strongly typed value.</summary>
<param name="value">The boxed value.</param>
<returns>The unboxed result.</returns>
<example id="unbox-example"><code lang="fsharp"> let x: int = 123 let obj1 = box x // obj1 is a generic object type unbox&lt;int&gt; obj1 // Evaluates to 123 (int) unbox&lt;double&gt; obj1 // Throws System.InvalidCastException </code></example>
union case MarkdownParagraph.Paragraph: body: MarkdownSpans * range: MarkdownRange option -> MarkdownParagraph
Multiple items
union case MarkdownSpan.Literal: text: string * range: MarkdownRange option -> MarkdownSpan

--------------------
type LiteralAttribute = inherit Attribute new: unit -> LiteralAttribute
<summary>Adding this attribute to a value causes it to be compiled as a CLI constant literal.</summary>
<category>Attributes</category>


--------------------
new: unit -> LiteralAttribute
System.Object.ToString() : string
union case Option.None: Option<'T>
<summary>The representation of "No value"</summary>
union case Option.Some: Value: 'T -> Option<'T>
<summary>The representation of "Value of type 'T"</summary>
<param name="Value">The input value.</param>
<returns>An option representing the value.</returns>
union case MarkdownParagraph.ListBlock: kind: MarkdownListKind * items: MarkdownParagraphs list * range: MarkdownRange option -> MarkdownParagraph
<summary> A Markdown List block </summary>
type MarkdownListKind = | Ordered | Unordered
<summary> A list kind can be Ordered or Unordered corresponding to <c>&lt;ol&gt;</c> and <c>&lt;ul&gt;</c> elements </summary>
union case MarkdownListKind.Ordered: MarkdownListKind
val listy: string
val docOl: LiterateDocument