FSharp.Formatting


BinderScriptNotebook

Literate Scripts

The following example shows most of the features that can be used in a literate F# script file with .fsx extension. Most of the features should be quite self-explanatory:

(**
# First-level heading
Some more documentation using `Markdown`.
*)

let helloWorld() = printfn "Hello world!"

(** 
## Second-level heading
With some more documentation
*)

let numbers = [ 0 .. 99 ]
(*** include-value: numbers ***)

List.sum numbers
(*** include-it ***)

The F# script files is processed as follows:

Literate Command

Description

(** ... *)

Markdown

(*** condition: prepare ***)

Utilise a code snippet when analyzing for tooltips or executing for outputs

(*** condition: ipynb ***)

Include a code snippet when making a .ipynb notebook

(*** condition: tex ***)

Include a code snippet when making a .tex output

(*** condition: html ***)

Include a code snippet when making HTML output

(*** hide ***)

Hide the subsequent snippet

(*** include-output ***)

The output of the preceeding snippet

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

The F# Interactive output of the preceeding snippet

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

The merge of console output and F# Interactive output of the preceeding snippet

(*** include-it ***)

The formatted result of the preceeding snippet

(*** include-it-raw ***)

The unformatted result of the preceeding snippet

(*** include-value: value-name ***)

The formatted value

(*** raw ***)

The subsequent code is treated as raw text

Named snippets

The command define defines a named snippet (such as final-sample) and removes the command together with the following F# code block from the main document. The snippet can then be referred to using these variations. This makes it possible to write documents without the ordering requirements of the F# language.

Literate Command

Description

(*** define: snippet-name ***)

Define a named snippet

(*** include-output: snippet-name ***)

The output of the named snippet

(*** include-fsi-output: snippet-name ***)

The F# Interactive output of the named snippet

(*** include-fsi-merged-output: snippet-name ***)

The merge of console output and F# Interactive output of the named snippet

(*** include-it: snippet-name ***)

The formatted result of the named snippet

(*** include-it-raw: snippet-name ***)

The unformatted result of the named snippet

(*** include: snippet-name ***)

Include the code of the named snippet

Hiding code snippets

The command hide specifies that the following F# code block (until the next comment or command) should be omitted from the output.

Evaluating and formatting results

The commands to evaluate and format results are explained in evaluation. You must build your documentation with evaluation turned on using --eval.

Substitutions

Substitutions are applied to content, see content.

Literate Markdown Documents

For files with .md extension, the entire file is a Markdown document, which may contain F# code snippets (but also other code snippets). As usual, snippets are indented with four spaces. In addition, the snippets can be annotated with special commands. Some of them are demonstrated in the following example:

# First-level heading

    [hide]
    let print s = printfn "%s" s

Some more documentation using `Markdown`.

    [module=Hello]
    let helloWorld() = print "Hello world!"

## Second-level heading
With some more documentation

    [lang=csharp]
    Console.WriteLine("Hello world!");

When processing the document, all F# snippets are copied to a separate file that is type-checked using the F# compiler (to obtain colours and tool tips). The commands are written on the first line of the named snippet, wrapped in [...]:

LaTeX in Literate Scripts and Markdown Documents

Literate Scripts may contain LaTeX sections in Markdown using these forms:

  1. Single line latex starting with $$.

  2. A block delimited by \begin{equation}...\end{equation} or \begin{align}...\end{align}.

  3. An indented paragraph starting with $$$. This is F#-literate-specific and corresponds to \begin{equation}...\end{equation}.

For example

$$\frac{x}{y}$$

\begin{equation}
   \frac{d}{dx} \left. \left( x \left( \left. \frac{d}{dy} x y \; \right|_{y=3} \right) \right) \right|_{x=2}
\end{equation}

Becomes

\[\frac{x}{y}\]

\[ \frac{d}{dx} \left. \left( x \left( \left. \frac{d}{dy} x y \; \right|_{y=3} \right) \right) \right|_{x=2}\]

The LaTeX will also be used in HTML and iPython notebook outputs.

Making literate scripts work for different outputs

Literate scripts and markdown can by turned into LaTex, Python Notebooks and F# scripts.

A header may be needed to get the code to load, a typical example is this:

(*** condition: prepare ***)
#nowarn "211"
#I "../src/FSharp.Formatting/bin/Release/netstandard2.1"
#r "FSharp.Formatting.Common.dll"
#r "FSharp.Formatting.Markdown.dll"
#r "FSharp.Formatting.CodeFormat.dll"
#r "FSharp.Formatting.Literate.dll"
(*** condition: fsx ***)
#if FSX
#r "nuget: FSharp.Formatting,{{package-version}}"
#endif // FSX
(*** condition: ipynb ***)
#if IPYNB
#r "nuget: FSharp.Formatting,{{package-version}}"
#endif // IPYNB

Processing literate files programatically

To process file Use the two static methods to turn single documents into HTML as follows using functionality from the Literate type:

open System.IO
open FSharp.Formatting.Literate

let source = __SOURCE_DIRECTORY__
let template = Path.Combine(source, "template.html")

let script = Path.Combine(source, "../docs/script.fsx")
Literate.ConvertScriptFile(script, template)

let doc = Path.Combine(source, "../docs/document.md")
Literate.ConvertMarkdownFile(doc, template)

The following sample also uses optional parameter parameters to specify additional keywords that will be replaced in the template file (this matches the template-project.html file which is included as a sample in the package):

// Load the template & specify project information
let projTemplate = source + "template-project.html"
let projInfo =
  [ "fsdocs-authors", "Tomas Petricek"
    "fsdocs-source-link", "https://github.com/fsprojects/FSharp.Formatting"
    "fsdocs-collection-name", "F# Formatting" ]

The methods used above (Literate.ConvertScriptFile, Literate.ConvertMarkdownFile) produce HTML output by default, but they can be also used to produce LaTeX output. This is done by setting the output kind. The following example shows how to call the methods to generate LaTeX documents:

let templateTex = Path.Combine(source, "template.tex")

let scriptTex = Path.Combine(source, "../docs/script.fsx")
Literate.ConvertScriptFile(scriptTex, templateTex, outputKind=OutputKind.Latex)

let docTex = Path.Combine(source, "../docs/document.md")
Literate.ConvertMarkdownFile(docTex, templateTex, outputKind=OutputKind.Latex)

The methods used above (ConvertScriptFile, ConvertMarkdownFile) can also produce iPython Notebook output. This is done by setting the named parameter format to OutputKind.Pynb:

// Process script file, Markdown document and a directory
let scriptPynb = Path.Combine(source, "../docs/script.fsx")
Literate.ConvertScriptFile(scriptPynb, outputKind=OutputKind.Pynb)

let docPynb = Path.Combine(source, "../docs/document.md")
Literate.ConvertMarkdownFile(docPynb, outputKind=OutputKind.Pynb)

All of the three methods discussed in the previous two sections take a number of optional parameters that can be used to tweak how the formatting works

val helloWorld : unit -> unit
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
val numbers : int list
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetReverseIndex : rank:int * offset:int -> int
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    static member Cons : head:'T * tail:'T list -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    ...
val sum : list:'T list -> 'T (requires member ( + ) and member get_Zero)
namespace System
namespace System.IO
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Formatting
namespace FSharp.Formatting.Literate
val source : string
val template : 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 []
  ...
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 script : string
type Literate =
  private new : unit -> Literate
  static member ConvertMarkdownFile : input:string * ?template:string * ?output:string * ?outputKind:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?fscOptions:string * ?lineNumbers:bool * ?references:bool * ?substitutions:Substitutions * ?generateAnchors:bool * ?rootInputFolder:string * ?crefResolver:(string -> (string * string) option) -> unit
  static member ConvertScriptFile : input:string * ?template:string * ?output:string * ?outputKind:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?fscOptions:string * ?lineNumbers:bool * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?substitutions:Substitutions * ?generateAnchors:bool * ?rootInputFolder:string * ?crefResolver:(string -> (string * string) option) -> unit
  static member private FormatLiterateNodes : doc:LiterateDocument * ?outputKind:OutputKind * ?prefix:string * ?lineNumbers:bool * ?generateAnchors:bool * ?tokenKindToCss:(TokenKind -> string) * ?crefResolver:(string -> (string * string) option) -> LiterateDocument
  static member ParseAndCheckScriptFile : path:string * ?formatAgent:CodeFormatAgent * ?fscOptions:string * ?definedSymbols:string list * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?parseOptions:MarkdownParseOptions * ?rootInputFolder:string -> LiterateDocument
  static member private ParseAndTransformMarkdownFile : input:string * ?output:string * ?outputKind:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?fscOptions:string * ?lineNumbers:bool * ?references:bool * ?substitutions:Substitutions * ?generateAnchors:bool * ?customizeDocument:(LiterateProcessingContext -> LiterateDocument -> LiterateDocument) * ?tokenKindToCss:(TokenKind -> string) * ?imageSaver:(string -> string) * ?rootInputFolder:string * ?crefResolver:(string -> (string * string) option) * ?parseOptions:MarkdownParseOptions -> LiterateDocModel
  static member private ParseAndTransformScriptFile : input:string * ?output:string * ?outputKind:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?fscOptions:string * ?lineNumbers:bool * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?substitutions:Substitutions * ?generateAnchors:bool * ?customizeDocument:(LiterateProcessingContext -> LiterateDocument -> LiterateDocument) * ?tokenKindToCss:(TokenKind -> string) * ?imageSaver:(string -> string) * ?rootInputFolder:string * ?crefResolver:(string -> (string * string) option) -> LiterateDocModel
  static member ParseMarkdownFile : path:string * ?formatAgent:CodeFormatAgent * ?fscOptions:string * ?definedSymbols:string list * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?parseOptions:MarkdownParseOptions * ?rootInputFolder:string -> LiterateDocument
  static member ParseMarkdownString : content:string * ?path:string * ?formatAgent:CodeFormatAgent * ?fscOptions:string * ?definedSymbols:string list * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?parseOptions:MarkdownParseOptions * ?rootInputFolder:string -> LiterateDocument
  static member ParseScriptString : content:string * ?path:string * ?formatAgent:CodeFormatAgent * ?fscOptions:string * ?definedSymbols:string list * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?parseOptions:MarkdownParseOptions * ?rootInputFolder:string -> LiterateDocument
  ...
static member Literate.ConvertScriptFile : input:string * ?template:string * ?output:string * ?outputKind:OutputKind * ?formatAgent:FSharp.Formatting.CodeFormat.CodeFormatAgent * ?prefix:string * ?fscOptions:string * ?lineNumbers:bool * ?references:bool * ?fsiEvaluator:Evaluation.IFsiEvaluator * ?substitutions:FSharp.Formatting.Templating.Substitutions * ?generateAnchors:bool * ?rootInputFolder:string * ?crefResolver:(string -> (string * string) option) -> unit
val doc : string
static member Literate.ConvertMarkdownFile : input:string * ?template:string * ?output:string * ?outputKind:OutputKind * ?formatAgent:FSharp.Formatting.CodeFormat.CodeFormatAgent * ?prefix:string * ?fscOptions:string * ?lineNumbers:bool * ?references:bool * ?substitutions:FSharp.Formatting.Templating.Substitutions * ?generateAnchors:bool * ?rootInputFolder:string * ?crefResolver:(string -> (string * string) option) -> unit
val projTemplate : string
val projInfo : (string * string) list
val templateTex : string
val scriptTex : string
type OutputKind =
  | Html
  | Latex
  | Pynb
  | Fsx
  | Md
    member Extension : string
union case OutputKind.Latex: OutputKind
val docTex : string
val scriptPynb : string
union case OutputKind.Pynb: OutputKind
val docPynb : string