Add custom playground guide
This commit is contained in:
parent
59d3717159
commit
363f4cd790
|
@ -33,6 +33,7 @@
|
||||||
- [MathJax Support](format/mathjax.md)
|
- [MathJax Support](format/mathjax.md)
|
||||||
- [mdBook-specific features](format/mdbook.md)
|
- [mdBook-specific features](format/mdbook.md)
|
||||||
- [Markdown](format/markdown.md)
|
- [Markdown](format/markdown.md)
|
||||||
|
- [Custom Playground](format/custom-playground.md)
|
||||||
- [Continuous Integration](continuous-integration.md)
|
- [Continuous Integration](continuous-integration.md)
|
||||||
- [For Developers](for_developers/README.md)
|
- [For Developers](for_developers/README.md)
|
||||||
- [Preprocessors](for_developers/preprocessors.md)
|
- [Preprocessors](for_developers/preprocessors.md)
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
# Custom Playground
|
||||||
|
|
||||||
|
By default, mdBook supports playground for Rust only.
|
||||||
|
If you want to add playground for another language,
|
||||||
|
you can do it by overriding script through theme customization.
|
||||||
|
|
||||||
|
In this section, we will try to add a playground for the imaginary `example` language.
|
||||||
|
|
||||||
|
## Add `playground` option to code blocks in the book
|
||||||
|
|
||||||
|
`playground` option is not officially supported.
|
||||||
|
It is used to mark the code block should be translated to playground code block by JavaScript.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```example,playground
|
||||||
|
Example Language Code
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
`editable` option can be used like below:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```example,playground,editable
|
||||||
|
Example Language Code
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
## Modify `book.js`
|
||||||
|
|
||||||
|
`book.js` can be modified through theme customization.
|
||||||
|
|
||||||
|
### Remove the access to play.rust-lang.org
|
||||||
|
|
||||||
|
If there are playgrounds in the page, `book.js` will try to fetch crates list from play.rust-lang.org.
|
||||||
|
So you shoud remove the following codes.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var playgrounds = Array.from(document.querySelectorAll(".playground"));
|
||||||
|
if (playgrounds.length > 0) {
|
||||||
|
fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': "application/json",
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
mode: 'cors',
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
// get list of crates available in the rust playground
|
||||||
|
let playground_crates = response.crates.map(item => item["id"]);
|
||||||
|
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modify `run_rust_code` function
|
||||||
|
|
||||||
|
If a play button is pushed, `run_rust_code` function will be called to get the result of the playground.
|
||||||
|
So you should modify the function for your language.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function run_rust_code(code_block) {
|
||||||
|
var result_block = code_block.querySelector(".result");
|
||||||
|
if (!result_block) {
|
||||||
|
result_block = document.createElement('code');
|
||||||
|
// If the result should be syntax highlighted,
|
||||||
|
// `language-bash` should be modified to the appropriate language.
|
||||||
|
result_block.className = 'result hljs language-bash';
|
||||||
|
|
||||||
|
code_block.append(result_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = playground_text(code_block);
|
||||||
|
|
||||||
|
// Add function to get result of the playground
|
||||||
|
result_block.innerText = example_language_run(text);
|
||||||
|
|
||||||
|
// If the result should be syntax highlighted, enable the following code.
|
||||||
|
//hljs.highlightBlock(result_block);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add code for code block transformation
|
||||||
|
|
||||||
|
The following code transforms code blocks which have `playground` option to playground code blocks.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Add <pre class="playground"> to playground codeblock
|
||||||
|
Array.from(document.querySelectorAll(".playground")).forEach((element) => {
|
||||||
|
let parent = element.parentNode;
|
||||||
|
let wrapper = document.createElement('pre');
|
||||||
|
wrapper.className = 'playground';
|
||||||
|
element.classList.remove('playground');
|
||||||
|
// set the wrapper as child (instead of the element)
|
||||||
|
parent.replaceChild(wrapper, element);
|
||||||
|
// set element as child of wrapper
|
||||||
|
wrapper.appendChild(element);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This should be executed before other processes of `codeSnippets` function.
|
||||||
|
For example, the code should be inserted to the following point.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Insert the above code
|
||||||
|
|
||||||
|
// Syntax highlighting Configuration
|
||||||
|
hljs.configure({
|
||||||
|
tabReplace: ' ', // 4 spaces
|
||||||
|
languages: [], // Languages used for auto-detection
|
||||||
|
});
|
||||||
|
|
||||||
|
let code_nodes = Array
|
||||||
|
.from(document.querySelectorAll('code'))
|
||||||
|
// Don't highlight `inline code` blocks in headers.
|
||||||
|
.filter(function (node) {return !node.parentElement.classList.contains("header"); });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tweak for editable code blocks
|
||||||
|
|
||||||
|
If the code block is editable, the syntax highlight is executed by editor.
|
||||||
|
So the normal highlight mechanism should be disabled to avoid conflict.
|
||||||
|
|
||||||
|
This is achieved by the following code.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// language-rust class needs to be removed for editable
|
||||||
|
// blocks or highlightjs will capture events
|
||||||
|
code_nodes
|
||||||
|
.filter(function (node) {return node.classList.contains("editable"); })
|
||||||
|
.forEach(function (block) { block.classList.remove('language-rust'); });
|
||||||
|
```
|
||||||
|
|
||||||
|
The language name shoud be changed to `language-example`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// language-rust class needs to be removed for editable
|
||||||
|
// blocks or highlightjs will capture events
|
||||||
|
code_nodes
|
||||||
|
.filter(function (node) {return node.classList.contains("editable"); })
|
||||||
|
.forEach(function (block) { block.classList.remove('language-example'); });
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prepare syntax highlighting
|
||||||
|
|
||||||
|
[highlight.js](https://highlightjs.org) and [Ace](https://ace.c9.io) are used for syntax highlighting.
|
||||||
|
If the code block is `editable`, `Ace` is used, and if not `highlight.js` is used.
|
||||||
|
So a syntax definition of `example` language should be prepared for each library.
|
||||||
|
|
||||||
|
You can get the following JavaScript codes by following each library's document.
|
||||||
|
|
||||||
|
* highlight.js: `build/highlight.min.js`
|
||||||
|
* Ace: `build/src-min-noconflict/mode-example.js`
|
||||||
|
|
||||||
|
`highlight.js` can be overridden by theme.
|
||||||
|
So the `build/highlight.min.js` should be moved to `theme/highlight.js` in the project.
|
||||||
|
`mode-example.js` shoud be moved to the project root because it is not overridable.
|
||||||
|
|
||||||
|
## Modify `editor.js`
|
||||||
|
|
||||||
|
The language configuration of Ace editor is set at `editor.js`.
|
||||||
|
This script can't be customized through theme.
|
||||||
|
So you should copy `editor.js` from the generated book directory to the project root.
|
||||||
|
The copied file should be modified like below:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
//editor.getSession().setMode("ace/mode/rust");
|
||||||
|
editor.getSession().setMode("ace/mode/example");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Set additional-js
|
||||||
|
|
||||||
|
You should add `editor.js` and `mode-example.js` to `additional-js` field of `book.toml`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[output.html]
|
||||||
|
additional-js = [
|
||||||
|
"mode-example.js",
|
||||||
|
"editor.js",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory structure
|
||||||
|
|
||||||
|
The final directory structure becomes below:
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
|-- book.toml
|
||||||
|
|-- editor.js
|
||||||
|
|-- mode-example.js
|
||||||
|
|-- src
|
||||||
|
`-- theme
|
||||||
|
|-- book.js
|
||||||
|
`-- highlight.js
|
||||||
|
```
|
Loading…
Reference in New Issue