This is a tool to run Markdown code blocks.
This can be used by CI to guarantee that the samples written in the README will work.
Example repo: https://github.com/lempiji/sandbox-vuepress
Run it as dub fetch md and dub run md -- README.md
Also, this README.md is executable.
You can configure how your generated D source file is run by passing in command line arguments to md:
show help
dub run md -- --help
The code block whose language is specified as d, D, sh, or bash will be executed.
If there are multiple blocks as shown below, they will be combined and executed.
block 1
import std;
auto message = "Hello, " ~ "Markdown!";block 2
writeln(message); // Hello, Markdown!Code blocks specified as disabled will not be executed, as shown below.
```d disabled
```
disabled code block
throw new Exception("disabled");Can give an independent scope to a block of code by giving it a name like the following. Even if they are written separately, they will be combined like a block if they have the same name.
```d name=test
```
import std;
auto buf = iota(10).array();
writeln(buf);If name is not specified, it is treated as the name main.
By specifying the --filter option, you can execute only code blocks with specific names.
```d name=filter_test
```
dub run md -- README.md --filter=filter_test --filter=filter_test2
import std;
auto message = "filter test";
writeln(message);import std;
auto message2 = "another filter test";
writeln(message2);You can assign multiple name attributes to a single code block.
That block will be included in every matching named block.
```d name=multi_name_test1 name=multi_name_test2
```
dub run md -- README.md --filter=multi_name_test1
dub run md -- README.md --filter=multi_name_test2
import std.stdio;writeln("multi-name test1");writeln("multi-name test2");sh and bash blocks are executed with the same name and --filter rules as D blocks.
The default name is main, and a single block can have multiple names.
```sh name=shell_sample
```
dub run md -- README.md --filter=shell_sample
dub run md -- README.md --filter=bash_sample
shared_shell="from-sh"
echo "sh:${shared_shell}"shared_bash="from-bash"parts=("A" "B")
echo "bash:${shared_bash}:${parts[1]}"Execution details:
shblocks run assh -eu <temp_script_path>bashblocks run asbash -eu -o pipefail <temp_script_path>- Scripts are executed from the current working directory where
mdis launched - Non-zero exit code is treated as an error
--build,--compiler,--arch,--dependency, and--dubsdlapply only to D execution--buildOnlyskips shell script execution--show-langprints language-aware begin/end labels as<language>:<block-name>(default keeps the original label format)
To make a single block of code run independently without being combined with other blocks, give it the attribute single.
```d single
```
single block
import std;
auto message = "single code block";
writeln(message);The tool supports several options that can be passed to the execution (dub run). These options are directly passed as arguments to dub run.
--build--compiler--arch
Example
dub run md -- README.md --build=release --compiler=ldc2 --arch=x86_64
If the current directory is a dub package, the dependency will be automatically added. (using a "path" based dependency)
For example, if this README is in the same directory as md/dub.sdl, then can import commands.main of md.
import commands.main;
import std.stdio;
writeln("current package: ", loadCurrentProjectName());It's possible to specify -d <packageName> or -d <packageName>@<versionString> such as -d mir-ion@~>2.0.16 to add further dependencies. (long name: --dependency)
It's possible to specify --dubsdl "<instruction>" to add a dub.sdl recipe line into the generated file. This option can be used multiple times to add multiple lines.
Specifying this option disables the built-in CWD package dependency addition described above.
The normal code blocks are executed as if they were written in void main() {}. The source will be generated with void main() { and } appended before and after.
If you want the code block to be interpreted as a single source file, you can add a global attribute to the code block, which will not be combined with other code blocks as with the single attribute.
```d global
```
single file
import std;
void main()
{
writeln("Hello, Markdown!");
}Create a .md directory in the temp directory and generate temporary files for each execution unit.
Execution model by language:
- D: generate a dub single-file source and run it with
dub run --single md_xxx.d(ordub build --single ...with--buildOnly) - sh: generate a script file and run
sh -eu md_xxx.sh - bash: generate a script file and run
bash -eu -o pipefail md_xxx.sh
Named blocks are combined by language + name before execution, and --filter narrows the target names.
It also automatically adds the following comment to the beginning of the source to achieve default package references.
/+ dub.sdl:
dependency "md" path="C:\\work\md"
+/A normal code block will generate the source enclosed in void main() {}.
UFCS will not work because the function definition is not a global function.
this code doesn't work
auto sum(R)(R range)
{
import std.range : ElementType;
alias E = ElementType!R;
auto result = E(0);
foreach (x; range)
{
result += x;
}
return result;
}
auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
auto result = arr.sum();It works by setting the global attribute and writing the main function appropriately.
this code works
auto sum(R)(R range)
{
import std.range : ElementType;
alias E = ElementType!R;
auto result = E(0);
foreach (x; range)
{
result += x;
}
return result;
}
void main()
{
auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
auto result = arr.sum();
}