Skip to main content

Markdown (experimental)

Scala CLI can compile, run, test, and package markdown (.md) sources.

note

This feature is a work in progress and should currently be treated as experimental. Markdown sources are ignored by default unless passed explicitly as inputs. You can enable including non-explicit .md inputs by passing the --enable-markdown option.

Plain scala snippets

Example.md
# Example

This is a simple example of an `.md` file with a Scala snippet.

```scala
val message = "Hello from Markdown"
println(message)
```

Plain scala snippets are treated similarly to .sc scripts in that any kind of statement is accepted at the top-level.

scala-cli run Example.md
Hello from Markdown

Similarly to .sc scripts, when multiple .md files with plain scala snippets are being run, each of them will have its own main class, that can be run.

scala-cli Example1.md Example2.md
[error]  Found several main classes: Example1_md, Example2_md

When multiple such sources are passed as inputs, the main class has to be passed explicitly with the --main-class option.

scala-cli Example1.md Example2.md --main-class Example1_md
Hello from Markdown

You can always check what main classes are available in the context with the --list-main-classes option.

scala-cli Example1.md Example2.md --list-main-classes
Example1_md Example2_md

scala raw snippets

You can mark a scala code block with the raw keyword, indicating that this snippet should not be wrapped as a script and should instead be treated as is. This is the equivalent of code in a .scala file. For a raw snippet to be runnable a main class has to be included.

RawExample.md
# `raw` example

This is a simple example of an `.md` file with a raw Scala snippet.

```scala raw
object Main extends App {
val message = "Hello from Markdown"
println(message)
}
```
scala-cli RawExample.md
Hello from Markdown

scala test snippets

It is possible to run tests from scala code blocks marked as test. This is similar to raw snippets in that the code is not wrapped and is treated as is.

You can run scala test code blocks with the test sub-command.

TestExample.md
# `test` example
This is a simple example of an `.md` file with a test Scala snippet.

```scala test
//> using lib "org.scalameta::munit:0.7.29"
class Test extends munit.FunSuite {
test("example test") {
assert(true)
}
}
```
scala-cli test TestExample.md
Test:
+ example test

reset scope for scala snippets

When multiple plain scala snippets are used in a single .md file, by default they are actually treated as a single script. They share context and when run, are executed one after another, as if they were all in a single .sc file.

If you want a snippet to use a fresh context instead, you can rely on the reset keyword. This allows you to start a fresh scope for the marked snippet (and any coming after it).

ResetExample.md
# `reset` scope
This is an example of an `.md` file with multiple `scala` snippets with separate scopes

## Scope 1
```scala
val message = "Hello"
```

## Still scope 1, since `reset` wasn't used yet
```scala
println(message)
```

## Scope 2
```scala reset
val message = "world"
println(message)
```

## Scope 3
```scala reset
val message = "!"
println(message)
```
scala-cli test ResetExample.md
Hello
world
!

Referring to code from Markdown

Plain scala code blocks

Referring to code from plain scala snippets in Markdown requires using their package name. Similarly to scripts, the package is inferred based on the relative path to the source file in your project.

You also have to point to the Scope under which the code is located. Scopes are numbered according to their order in a given .md file (starting from 0 for the first plain scala snippet): Scope{scopeNumber}. The snippetNumber is omitted for the first script code block (0). In other words, the first scope is just Scope, the second is Scope1, then Scope2 and so on.

src/markdown/Example.md
## Scope 1
```scala
def hello: String = "Hello"
```

## Still scope 1, since `reset` wasn't used yet
```scala
def space: String = " "
```

## Scope 2
```scala reset
def world: String = "world"
```
Main.scala
object Main extends App {
val hello = src.markdown.Example_md.Scope.hello
val space = src.markdown.Example_md.Scope.space
val world = src.markdown.Example_md.Scope.world
println(s"$hello$space$world)
}
scala-cli . --enable-markdown --main-class Main
Hello world

scala raw and scala test code blocks

You can refer to code from scala raw and scala test snippets as if they were the contents of a .scala file.

RawSnippetToReferTo.md
# `raw` snippet
```scala raw
object Something {
def message: String = "Hello"
}
```
scala-cli RawSnippetToReferTo.md -e 'println(Something.message)'
Hello