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:0.7.29
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.8.0
object Printer {
def printHello(): Unit = pprint.pprintln("Hello")
}
```
## Plain `scala` example
```scala
//> using dep com.lihaoyi::os-lib:0.8.1
println(os.pwd)
```
## `scala test` example
```scala test
//> using dep org.scalameta::munit:1.0.0-M7
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