mdbook-lang
mdbook-lang
是一个mdbook
预处理器插件和多编程语言playground
服务器,支持在浏览器中通过与playground
交互、运行mdbook
电子书嵌入的多种编程语言代码,并展示结果。而该playground
服务器可以自行部署或本地部署,可以容易扩展到其他编程语言。
本软件受mdbook
和mdbook-repl
启发,但mdbook
基于https://play.rust-lang.org实现的playground,目前仅支持rust
语言;而mdbook-repl
主要支持python
、javascript
和typescript
等解释型语言,两者都依赖在线playground
服务器,使得mdbook
支持的编程语言不易扩展。而本软件借助自主部署的编译器环境为mdbook
电子书嵌入的多编程语言代码段
架起浏览器和编译器之间的桥梁,且便于扩展,也给出了多电子书和沙箱安全等配置。
如下电子书中的C/C++
代码,在安装了mdbook-lang
主机环境中,是否让其提供支持的区别,使用mdbook-lang
支持:
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
不使用mdbook-lang
支持:
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
在启用mdbook-lang
支持的代码中,以ACE Editor
作为代码编辑器,可对其进行可配置的编辑、重置和运行等。您可以直接在浏览器中修改并执行代码,实时查看输出结果。如果您是教师为学生布置练习,还可以控制剪切、复制和粘贴等操作,并禁用浏览器调试功能,以实现更受控的教学环境。
本软件仍处于持续开发和优化中,未来将支持更多的编程语言。
用法
本工具是一个mdbook
预处理插件和可独立运行的playground
编译和运行服务器。
安装
有两种方式可以安装 mdbook-lang
:
- 如果你已经有 Rust 环境,可以通过
cargo
工具安装mdbook-lang
:
cargo install mdbook-lang
- 或者你可以从 github 页面下载二进制文件,并将该二进制文件最终存放路径加入到用户或全局
PATH
环境变量中。
你可以通过以下命令检查安装情况:
mdbook-lang --version
配置
安装完成后,通过 mdbook-lang install
命令自动在书籍的 book.toml
文件中配置 mdbook-lang
插件,使得现有基于mdbook
工具的电子书启用该插件,该默认配置根据需要可进行修改。
$ mdbook-lang install /path/to/your/book
该语句将设置 mdbook-lang
预处理插件和编译服务器的 url
参数。
例如:
[book]
authors = ["gaoxu.jeffrey"]
language = "en"
multilingual = false
src = "src"
title = "mdbook-lang example"
[preprocessor]
[preprocessor.lang]
command = "mdbook-lang"
server = "http://127.0.0.1:3333/api/v1/build-code"
cpp-enable = true
java-enable = true
go-enable = true
python-enable = true
javascript-enable = true
typescript-enable = true
scheme-enable = true
editable = true
disable-devtool-auto = false
disable-menu = false
clear-log = false
disable-select = false
disable-copy = false
disable-cut = false
disable-paste = false
ace-strict = false
[output]
[output.html]
additional-js = ["jquery.js", "disable-devtool.js", "lang.js"]
additional-css = ["lang.css"]
server
: 编译服务器的url
。
现已部署了一个编译服务器,你可以直接修改 server
的值进行测试。
server = "https://183.205.132.14:3000/playground/api/v1/build-code"
注意 你需要先在浏览器中打开 https://183.205.132.14:3000 并忽略或关闭安全警告,否则在电子书中运行编程语言代码时无法访问该服务。
-
language-enable
:启用
某种语言,默认值为true
。 -
disable-devtool-auto
: 禁用浏览器调试器功能,默认值为false
,即不禁用。 -
ace-strict
: 启用ACE
编辑器的严格模式,默认值为false
。启用后,ACE
编辑器将不允许电子书读者在玩转代码时在ACE
编辑环境中剪切、复制和粘贴代码等功能。
运行编译服务器
仅 Windows 7/8/10/11 需要安装/卸载服务
所有命令都以 管理员
身份运行。
- 安装
mdbook-lang
编译、运行服务器软件为Windows操作系统中的一个服务:
C:\Windows\System32>mdbook-lang server install --hostname 127.0.0.1 --port 3333
或者当install
无参数时候,采用默认参数: hostname
为127.0.0.1
、 port
为3333
:
C:\Windows\System32>mdbook-lang server install
当不再需要 mdbook-lang
服务或想更改 hostname
和/或 port
时,应先 以子server
的子命令命令uninstall
卸载服务,再以不同参数通过install
重新安装服务 :
永久删除服务:
mdbook-lang uninstall
如果你想更改 hostname
和/或 port
:
mdbook-lang server uninstall
mdbook-lang server install --hostname 0.0.0.0
类 Unix 和 Windows 7/8/10/11操作系统
启动 mdbook-lang 编译服务器
用以下命令启动编译服务器:
mdbook-lang server start
或者在类 Unix 系统中使用 --hostname
或简写 -n
和 --port
或简写 -p
参数启动编译服务器:
mdbook-lang server start --hostname 127.0.0.1 -port 3333
对于 Windows,需要具有管理员权限。
- 启动服务
C:\Windows\System32>mdbook-lang server start
或者通过操作系统提供的 服务管理器
或 任务管理器
图形界面工具启动。
stop/restart/status
子命令
- 停止服务器
mdbook-lang server stop
- 重启服务器
mdbook-lang server restart
- 检查服务器状态
mdbook-lang server status
运行 mdbook,并打开默认浏览器,阅读电子书,playing内嵌代码段:
mdbook serve -o
选项
在mdbook-lang
所支持的语言的markdown代码块中,可使用扩展的选项,完成定制功能:
norun
norun
选项会使代码块不被mdbook-lang
预处理器渲染。如果你想展示一些不应被执行的代码示例,并且 language-enable=true
时,可以使用该选项。
```java,norun
// java codeblock with norun option
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
```
这样该代码块不会被预处理器渲染:
// java codeblock with norun option
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
```java
// java codeblock without norun option
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
```
java-enable=true
,且无nolang
选项时该代码块会被预处理器渲染:
// java codeblock with norun option
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
快捷键
操作系统 | 快捷键 | 说明 |
---|---|---|
Windows | Ctrl-Enter | 运行代码 |
Mac | Command-Enter | 运行代码 |
Windows | Ctrl-Shift-Enter | 清除代码编译或运行结果 |
Mac | Command-Shift-Enter | 清除代码编译或运行结果 |
语言扩展
该预处理器只识别特定语言的特定扩展。例如,C++ 代码只能用 c++ 或 cpp 代码块,Python 代码只能用 python 或 py 代码块。
完整扩展名列表如下:
语言 | 扩展名 | 编译器 |
---|---|---|
C++ | cpp, c++, c | clang++ |
Java | java | sun jdk/openjdk |
Go | go | golang |
Python | py, python | python2, python3(python 目录需在 PATH 环境变量中) |
JavaScript | js, javascript | node.js |
TypeScript | ts, typescript | node.js, tsc |
Scheme | lisp, scheme | gambit-scheme(gsi 目录需在 PATH 环境变量中) |
如需支持某些语言,你需要在编译服务器主机上安装相应的编译器。
性能
通过浏览器远程执行代码块的速度非常快,仅受网络延迟和编译服务器的负载影响。
A高级用法
通过nginx反向代理部署多本电子书
你可以在同一台主机上部署多本书籍,只需一个具有一个公网IP地址和一个端口号的物理服务器、编译服务器mdbook-lang和 nginx。
安装 nginx
不同平台的安装方式有所不同,具体参考install nginx
ubuntu
sudo apt-get install nginx
centos
sudo yum install nginx
macos
brew install nginx
windows
choco install nginx
configure nginx
sudo vim /etc/nginx/conf.d/mdbook-lang.conf
and add the following content:
# /etc/nginx/conf.d/mdbook.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
# nginx listen port
listen 3000;
server_name 0.0.0.0;
# compiler server
location /playground/{
proxy_pass http://127.0.0.1:3333/;
}
# java object oriented programming mdbook
location /joop/{
proxy_pass http://127.0.0.1:2000/;
}
location /joop/__livereload{
proxy_pass http://127.0.0.1:2000/__livereload/;
}
# rust-course mdbook
location /rust-course/{
proxy_pass http://127.0.0.1:2001/;
}
location /rust-course/__livereload{
proxy_pass http://127.0.0.1:2001/__livereload/;
}
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
启动 nginx
sudo nginx -s reload
启动编译服务器
- 类Unix操作系统:Linux/MacOS/FreeBSD/NetBSD等
mdbook-lang server --hostname 127.0.0.1 --port 3333
- Windows7/8/10/11操作系统
mdbook-lang server start
如果在windows系统中提示没有安装服务,则执行如下命令,先将编译服务器安装为系统服务:
mbook-lang server install
启动电子书
$ cd /path/to/joop/
$ mdbook serve start --hostname 127.0.0.1 --port 2000 > joop-mdbook.log 2> &1 &
$ cd /path/to/rust-course/
mdbook serve start --hostname 127.0.0.1 --port 2001 > rust-course.log > 2&1 &
如果电子书没有安装mdbook-lnag
支持,则需要在包含有book.toml
的电子书根目录中执行命令:
mdbook-lang install
修改book.toml中的编译服务器配置
server = "https://183.205.132.14:3000/playground/api/v1/build-code"
通过浏览器访问电子书
- 通过浏览器访问joop:http://127.0.0.1:3000/joop
- 通过浏览器访问rust-course:http://127.0.0.1:3000/rust-course
沙箱安全
安装沙箱工具:firejail
ubuntu
sudo apt-get install firejail
centos
sudo yum install firejail
macos
brew install firejail
配置沙箱工具:firejail
在合适位置新建沙箱配置文件:
sudo vim /etc/firejail/mdbook-lang-server.profile
文件内容如下
# /etc/firejail/mdbook-lang-server.profile
include disable-common.inc
# include disable-exec.inc
# noexec ${HOME}
noexec ${RUNUSER}
noexec /dev/shm
noexec /var
# must be canceled for c/c++ execute output.exe in /tmp
# noexec /tmp
include disable-passwdmgr.inc
include disable-programs.inc
quiet
net none
nodbus
nodvd
nogroups
nonewprivs
noroot
nosound
notv
nou2f
novideo
protocol inet,inet6
seccomp
shell none
# tracelog
disable-mnt
private
# for debug porpose
# private-bin ls
# allow c/c++ tools chain
private-bin mdbook-lang
private-bin clang++
private-bin ld
# allow java tools chain
private-bin java
private-bin javac
# allow python tools chain
private-bin python2
private-bin python3
private-bin python
# allow go tools chain
private-bin go
# allow javascript/typescript tools chain
private-bin node
private-bin tsc
# allow lisp/scheme tools chain
private-bin scheme-r5rs
# allow c/c++ output executable
private-bin output.exe
# must be canceled for java/javac
# private-lib
blacklist /opt
blacklist /etc/nginx
blacklist /etc/firejail
blacklist /etc
blacklist /sbin
# checked for java,c/c++,
blacklist /bin
# for python interpreter
# blacklist /usr/bin
blacklist /usr/sbin
blacklist /usr/libexec
blacklist /usr/local/sbin
blacklist /usr/local/lib
blacklist /usr/local/libexec
blacklist /usr/libexec/firejail
blacklist /usr/libexec/firejail/firejail-config
blacklist /usr/libexec/firejail/firejail-profile
blacklist /usr/libexec/firejail/firejail-shell
blacklist /usr/libexec/firejail/firejail-shell-wrapper
blacklist /usr/libexec/firejail/firejail
configure envs
sudo vim ~/.bashrc
and add the following content:
export MDBOOKLANG_SERVER_SANDBOX_CMD="firejail"
export MDBOOKLANG_SERVER_SANDBOX_ARGS="--profile=/etc/firejail/mdbook-lang-server.profile:--quiet"
Note: sandbox is for single or multiple books, local or remote server is ok.
Enjoy it!
沙箱配置
添加白名单
如果所部署的python编程环境不在系统路径,而是在${HOME}
目录,除了将其添加到$HOME/.bash
的PATH
环境变量之外,需要使用命令安装相应包:
- 采用系统
python
环境,用非超管账户安装包,则额外包安装路径为:${HOME}/.local/lib/python3.10/site-packages
$ python -m pip install numpy
则需要在firejail配置尾部添加语句,注意python
版本号:
whitelist ${HOME}/.local/lib/python3.10/site-packages
添加编译器白名单
如果安装了其他编译器,需要在firejail配置文件中添加:
private-bin your-compiler-file-name
C/C++
C/C++ 是一种系统编程语言,能编写在几乎任何平台上运行的程序。它是一种功能强大的编程语言,常用于开发底层程序,如操作系统、驱动程序和游戏。同时,它也是学习编程的优秀选择。
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
Go
Go 是一种静态类型、编译型编程语言,由 Google 的 Robert Griesemer、Rob Pike 和 Ken Thompson 设计。它在语法上与 C 类似,但具有内存安全、垃圾回收、结构化类型和基于 CSP 的并发特性。
Go 常用于系统编程、网络编程和分布式系统开发,也被广泛应用于构建 Web 应用、微服务和云原生应用。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Java
Java 是一种多范式编程语言,支持面向对象、命令式和函数式等编程风格。它是一种编译型语言,其源代码会被编译为字节码,可以在任何 Java 虚拟机(JVM)上运行,而不受底层计算机架构的影响。
Java 被广泛用于开发各种应用,包括桌面应用、Web 应用、移动应用和企业级应用。它也常用于开发大规模分布式系统(如互联网应用)以及实时系统(如视频游戏)。
编译器/运行服务器使用 javac[.exe]
编译器编译java
源代码代码为字节码,通过 java[.exe]
解释器解释运行字节码。编译器/运行服务器会查找 public class
以确定不要保存的正确文件名,并查找 main class
和 main
方法来运行程序。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
你不能在同一个文件中编写多个 public class
,否则编译器会报错。例如,下面的代码会导致错误:
public class HelloWorld {
public static void main(String[] args) {
new Fibonacci().run(10);
}
}
public class Fibonacci {
public void run(int n) {
int n = 10;
int a = 0, b = 1;
for (int i = 0; i < n; i++) {
System.out.println(a);
int temp = a;
a = b;
b = temp + b;
}
}
}
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
public class Sum {
public static void main(String[] args) {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
System.out.println("Sum of first 100 numbers is: " + sum);
}
}
另外,编译器或运行服务器会查找包含 main
方法的主类(main class
)来运行程序。如果存在多个主类,编译器或运行服务器通常会按照类名的字母表顺序选择第一个找到的主类来执行。
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
class Sum {
public static void main(String[] args) {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
System.out.println("Sum of first 100 numbers is: " + sum);
}
}
The output of the above code will be:
Hello, World!
此处的main
方法,实际函数头部声明应为:public static void main(String[] args)
。包含了主方法的类称为是主类。如下几个代码不包含主方法、所在类也不是主类
class HelloWorld {
public void main(String[] args) {
System.out.println("Hello, World!");
}
}
class Sum {
public static int main(String[] args) {
int n = 100;
int sum = 0;
for (int i = 0; i <= n; i++) {
sum += i;
}
System.out.println("Sum of first " + n + " numbers is: " + sum);
}
}
class Fib {
public static void main(String args) {
int fib[] = new int[30];
fib[0] = fib[1] = 1;
for (int i = 2; i < 30; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
for (int i = 0; i < 30; i++) {
System.out.println(fib[i]);
}
}
}
class Mul {
public static long main() {
long n = 30;
long m = 1;
for (int i = 1; i <= n; i++) {
m *= i;
}
return m;
}
}
Python
Python 是一种高级的、解释型的编程语言,广泛应用于 Web 开发、数据分析、人工智能、机器人、嵌入式和自动化等领域。它以简洁、易读和多功能著称。
print("Hello, World!")
Javascript
JavaScript 是一种高级的、解释型的动态编程语言,主要用于创建交互式网页。它是一种多功能语言,可用于前端和后端开发。
console.log("Hello, World!");
Typescript
TypeScript 是一种强类型、面向对象的编程语言,设计用于将改语言编译为 JavaScript。它是 JavaScript 的超集,这意味着任何有效的 JavaScript 代码也是有效的 TypeScript 代码。
TypeScript 为 JavaScript 增加了静态类型,这意味着变量必须声明为特定的类型。这有助于在编译时而不是运行时捕获错误。
let person: { name: string; age: number; isStudent: boolean } = {
name: "John",
age: 30,
isStudent: true,
}
console.log(person);
Scheme
Scheme 是一种易于学习和使用的编程语言。它属于 Lisp 语言家族,是 Lisp 的一种方言。Scheme 是一门函数式编程语言,这意味着它通过函数来进行计算。
;scheme,lisp
(define (greet-world)
(let ((southern-germany "Grüß Gott!")
(chinese "世界,你好")
(english "World, hello"))
(let ((regions (list southern-germany chinese english)))
(for-each (lambda (region)
(display region)
(newline))
regions))))
(greet-world)