If you have ever experimented with notebooks, for instance using Jupyter or VS Code, you will have experienced the benefits of literate programming. For the uninitiated, this is the practice of interleaving documentation, executable code blocks and the output of that code.
This style of programming is often used in scientific communities for its ability to document a 'train of thought', working the reader through a problem step by step with explanations accompanying the logic and results.
You may also be aware of the inline documentation features available in .NET which allow you to mark up your modules and functions with XML-tagged meta data, enabling detailed tooltip support in your IDE.
This week I am going to give a quick introduction to FSharp.Formatting, a tool which combines these documentation approaches to generate a browsable static website from your scripts and application code.
The tool is pretty configurable and has a lot of options, all described in detail on the website. Whilst browsing it I did think it could perhaps have benefitted with a 'getting started' guide which onboards you with a simple example, so that is how I will approach this post.
I am using a PowerShell console here. The commands to navigate, create directories and files etc may be different depending on your terminal.
1. Install the tool
- Create a new directory for your app and navigate to it
md fsdocs-example
cd fsdocs-example
- Add a dotnet tool manifest
dotnet new tool-manifest
2. Create a homepage
- Install the docs tool
dotnet tool install fsdocs-tool
- Create a docs folder. This is where the tool will look for script files to parse.
md docs
- Create a home page markdown doc
cd docs
new-item -name "index.md"
- Open the file you just created in an editor and paste in some markdown, such as
# Home Page
This file will load as the root of your docs
If you have VSCode installed, you can launch it in the current directory using
code .
3. Start the file watcher
- Back up to the root and start the docs tool in watch mode.
cd ..
dotnet fsdocs watch --eval
The
--eval
option here tells the tool to evaluate the output of script functions, allowing the data to be rendered in the generated docs.
You should see the console output some info about generating and preparing content, after which a browser window will be launched showing your home page. Changes to the markdown will be reflected in the browser (more or less) immediately upon save.
4. Create literative-programming style docs
Your existing terminal window will still be hosting the hot-reload process for the tool, so you will need a second instance now.
- Create a script file
cd docs
new-item -name "fsdocs-example.fsx"
- Open the script file and paste in the following
(**
# Inline documentation
This is a **markdown** script so you can use *all* the expected features.
1. You can show numbered lists
1. They auto increment
## Subheading
- bullet points
> You can use quotes
*)
let sum a b = a + b
sum 5 8
(*** include-it ***)
(**
You can interleave comments and code.
*)
printfn "This is console output"
(*** include-output ***)
You can see from this simple script that you can interleave docs and code, similarly to a notebook. the (*** include-it ***)
line renders output from fsi inline, and (*** include-output ***)
does the same with console output.
If you save the document and check the web page, you should find it now has a link to your script doc under the Documentation
menu on the left.
If it doesn't show up, try making an edit to your homepage to force a rebuild and allow the tool to pick up your new script.
Clicking through, you will see your script rendered out with the appropriate formatting and data. If you edit the script, for example changing the numbers fed into the sum
function, you will again see the web page update appropriately when you save.
5. Create a library project with doc markup
- Navigate back to the root and create a library project
cd ..
dotnet new classlib -lang "F#" -o src/Library
If you look inside the .fsproj
file, you will see a tag which indicates to the tool to include it in API doc generation
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Open up the Library.fs
file in the project and paste over the contents with the following
/// <summary>
/// A module
/// </summary>
///
/// <namespacedoc>
/// <summary>A namespace to remember</summary>
///
/// <remarks>More on that</remarks>
/// </namespacedoc>
///
module Library
/// <summary>
/// A nested module
/// </summary>
module Says =
/// <summary>
/// A function which greets you
/// </summary>
///
/// <param name="name">Your name here</param>
///
/// <returns>A friendly message</returns>
///
/// <example>
/// Try using
/// <code>
/// open Library
///
/// Says.hello "Ryan"
/// </code>
/// </example>
let hello name =
printfn "Hello %s" name
- Build the app
cd src/Library
dotnet build
6. Generate API docs and publish the site
Until now the hot-reloading website has been running from a tmp
folder created by the tool.
Once we are ready to publish it, we can do so with
cd ..\..
dotnet fsdocs build --eval
This will publish the website to folder named output
.
If you navigated to the folder and opened the index.html file, you would see an unstyled website with broken links. This is because the published site expects to be in a folder with the same name as your project and located at the root of your hard drive.
- Restart the fsdocs watcher
Stop the existing watcher process in your first terminal and restart it.
You should once again see the website update, now showing an entry under the API Reference
nav menu section titled All Namespaces
. Clicking through, you will find a full hierarchical API reference following the XML comments pasted in the previous step. There is also a search box with autocomplete suggestions.
If you make changes to the XML comments in your library, you need to rebuild the project in order for hot reload to pick up the changes.
Conclusion
I've only really scratched the surface of the tool configuration, and already you can hopefully see it is quite powerful.
A challenge of embedding notebooks as online documentation comes from requiring an environment to execute them in in order to view the output cells, albeit achievable using various services. The static website generated by the fsdocs tool is comparatively simple to host.
If you are authoring an open source library or working in a project which warrants proper documentation, perhaps because you have a large team or the code needs to live for a long time, I could imagine it being very useful.
Photo by Lanju Fotografie on Unsplash