Markdown ⚡️
Markdown support is an experimental feature.
Please bear in mind that non-ideal user experience should be expected. If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team on GitHub.
Scala CLI can compile, run, test, and package markdown (.md) sources.
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.
Markdown inputs
On-disk markdown sources
You can pass local .md inputs by passing their path to Scala CLI (as you would for any other kind of input).
# Simple snippet
```scala
println("Hello")
```
scala-cli --power dir/hello.md
.md sources inside of directories are ignored by default, unless the --enable-markdown option is passed.
scala-cli --power dir --enable-markdown
Zipped archives
Scala CLI can run .md sources inside a .zip archive.
Same as with directories, .md sources inside zipped archives are ignored by default, unless
the --enable-markdown option is passed.
scala-cli --power archive-with-markdown.zip --enable-markdown
Remote inputs
Running unverified code from the Internet can be very handy for trusted sources, but it can also be really dangerous, since Scala CLI does not provide any sandboxing at this moment.
Make sure that you trust the code that you are about to run.
URLs
You can also pass a URL pointing to a .md file to run it with Scala CLI.
scala-cli --power https://gist.githubusercontent.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839/raw/4c5ce7593e19f1390555221e0d076f4b02f4b4fd/example.md
Hello
Github Gist
Scala CLI accepts GitHub Gist URLs.
The gist is technically treated as a zipped archive (which it is downloaded as), so it is necessary to pass
the --enable-markdown option alongside the gist URL to run any contained Markdown sources.
scala-cli --power https://gist.github.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839 --enable-markdown
Hello
You can find more information on running GitHub Gists in the gists cookbook.
Piped Markdown code
Instead of passing paths to your Markdown sources, you can also pipe your code via standard input:
echo '# Example Snippet
```scala
println("Hello")
```' | scala-cli --power _.md
Hello
You can find more information on piped sources in the piping guide.
Markdown code as a command line snippet
It is also possible to pass Markdown code as a snippet directly from the command line.
scala-cli --power run --markdown-snippet '# Markdown snippet
with a code block
```scala
println("Hello")
```'
Hello
You can find more information on command line snippets in the snippets guide.
Markdown code blocks
Plain scala snippets
# 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 --power 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.
# Main class 1
```scala
println("1")
```
# Main class 2
```scala
println("2")
```
scala-cli --power Main1.md Main2.md
[error] Found several main classes: Main1_md, Main2_md
When multiple such sources are passed as inputs, the main class has to be passed explicitly with the --main-class
option.
scala-cli --power Main1.md Main2.md --main-class Main1_md
1
You can always check what main classes are available in the context with the --list-main-classes option.
scala-cli --power Main1.md Main2.md --list-main-classes
Main1_md Main2_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.
# `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 --power 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.
# `test` example
This is a simple example of an `.md` file with a test Scala snippet.
```scala test
//> using dep org.scalameta::munit:1.0.2
class Test extends munit.FunSuite {
test("example test") {
assert(true)
}
}
```
scala-cli --power 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).
# `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 --power ResetExample.md
Hello
world
!
ignore in scala snippets
You can mark a scala code block with the ignore keyword, indicating that this snippet should not be run. This block will be ignored completely and will not be compiled or executed.
Below is an example of an .md file with an ignored scala snippet:
# `ignore` example
## Normal snippet
```scala
val message = "Hello world!"
println(message)
```
## Ignored snippet
```scala ignore
val message = "This must be ignored"
println(message)
1 / 0
```
scala-cli --power IgnoreExample.md
We see output only from the scala snippet:
Hello world!
After execution of the above example, you can see that the ignored snippet was not run.
All errors and exceptions that would have been thrown by the ignore snippet are also absent.
shebang header and Markdown code blocks
The shebang line in scala code blocks inside a markdown input are always ignored.
You can use them (i.e. to give an example of their usage), but they do not change how the code is handled.
## Self executable Scala script
```scala
#!/usr/bin/env -S scala-cli shebang
println("Hello world")
```
using directives and Markdown code blocks
It is possible to define using directives at the beginning of a scala code block inside a markdown input.
This is supported for all scala code block flavours.
# Using directives in `.md` inputs
## `scala raw` example
```scala raw
//> using dep com.lihaoyi::pprint:0.9.0
object Printer {
def printHello(): Unit = pprint.pprintln("Hello")
}
```
## Plain `scala` example
```scala
//> using dep com.lihaoyi::os-lib:0.11.3
println(os.pwd)
```
## `scala test` example
```scala test
//> using dep org.scalameta::munit:1.0.2
class Test extends munit.FunSuite {
test("foo") {
assert(true)
println("Hello from tests")
}
}
```
## Relying on directives from other snippets
Directives from other snippets apply to the whole context.
As a result, nothing really stops you from using a dependency
from an earlier code block.
```scala
Printer.printHello()
pprint.pprintln("world")
```
scala snippets inside of a Markdown input are not isolated. Each using directive applies to the whole project's
context. A directive defined in a later snippet within the same source may override another defined in an earlier one.
## 1
```scala
//> using scala 2.12.17
println(util.Properties.versionNumberString)
```
## 2
```scala
//> using scala 2.13.10
println(util.Properties.versionNumberString)
```
In this example, the directive from the second scala snippet will override the previous one and Scala 2.13.10 will
be used for both.
scala-cli --power OverriddenDirective.md
Compiling project (Scala 2.13.10, JVM)
Compiled project (Scala 2.13.10, JVM)
2.13.10
2.13.10
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.
## Scope 0
```scala
def hello: String = "Hello"
```
## Still scope 0, since `reset` wasn't used yet
```scala
def space: String = " "
```
## Scope 1
```scala reset
def world: String = "world"
```
object Main extends App {
val hello = markdown.Example_md.Scope.hello
val space = markdown.Example_md.Scope.space
val world = markdown.Example_md.Scope1.world
println(s"$hello$space$world")
}
scala-cli --power src Main.scala --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.
# `raw` snippet
```scala raw
object Something {
def message: String = "Hello"
}
```
scala-cli --power RawSnippetToReferTo.md -e 'println(Something.message)'
Hello