When you’re writing documentation, it’s a good idea to use tools to keep code examples accurate. We’ve started to adopt tut, and that’s what this post is about.
What is tut?
tut is a Typelevel project for documentation. It does one thing, and it does it well: it reads markdown and runs code blocks through the REPL.
The key feature is this: any unexpected error interpreting your code will cause the tut sbt command to fail. In other words, your code has to compile and run in order for your documentation to pass the build step.
You can see examples of tut output in the doobie documentation.
Writing with tut
tut interprets fenced code blocks. I know them from Github, and they look like this:
```scala
val x = 1 + 2
```
Github, and other sites, will render that markdown with Scala syntax highlighting:
val x = 1 + 2
Where tut fits in is adding new commands.
In place of scala
we write tut
:
```tut
val x = 1 + 2
```
The result will be another markdown file. tut replaces the code blocks with the interpreted output, itself in a code block:
```scala
scala> val x = 1 + 2
x: Int = 3
```
And that markdown you can render however you want.
More Styles
There are a variety of modifiers to control the tut output.
We’ve contributed a tut:book
style for our documentation:
```tut:book
val x = 1 + 2
```
We want to show the REPL output, but that can get in the way when copying and pasting. So we comment all the REPL output:
val x = 1 + 2
// x: Int = 3
Running tut
You can add tut to your project as an sbt plugin:
$ cat project/plugins.sbt
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0")
You can configure what files to process and where to write processed markdown:
$ cat build.sbt
scalaVersion := "2.11.7"
tutSettings
// Maybe I want output to go here:
tutTargetDirectory := file(".")
The plugin adds the command tut
to process all files, with each file getting a separate REPL.
There are other modifiers and commands outlined in the project README.
Errors
Sometimes you’ll want to show an error, and tut supports that via the tut:fail
modifier.
For example, we demand the following will fail:
```tut:fail
// An example of a compiler error message:
List(1,2,3).sort
```
This produces the following rendered markdown:
scala> // An example of a compiler error message:
| List(1,2,3).sort
<console>:13: error: value sort is not a member of List[Int]
List(1,2,3).sort
^
…but our build will succeed. If that block unexpectedly succeeded, our build would fail.
Assertions
You can also add hidden checks. Maybe you want to ensure the results you’re showing are the results you’re expecting:
```tut:invisible
assert(x == 3)
```
tut:invisible
produces no output.
However, if it turned out x
wasn’t 3, the build would fail.
Summary
tut helps produce good quality, correct, documentation. We’re adopting it for our books.
There are plenty of other tools that can do similar things. Dexy is another good one to look at.
But tut happens to fit well with the workflow we use. Maybe it’ll work well for you.
Once you’ve tried it out, if you want to get more involved, join the tut gitter channel.