Fix book links (#1227)

This commit is contained in:
Casey Rodarmor 2022-06-12 18:02:09 -07:00 committed by GitHub
parent 2710df4207
commit c710d5a685
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 173 additions and 40 deletions

View File

@ -80,6 +80,16 @@ jobs:
with: with:
mdbook-version: latest mdbook-version: latest
- name: Install `mdbook-linkcheck`
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
mkdir -p mdbook-linkcheck
cd mdbook-linkcheck
wget https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip
unzip mdbook-linkcheck.x86_64-unknown-linux-gnu.zip
chmod +x mdbook-linkcheck
pwd >> $GITHUB_PATH
- name: Build book - name: Build book
if: ${{ matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'ubuntu-latest' }}
run: | run: |

3
.gitignore vendored
View File

@ -1,6 +1,8 @@
/.vagrant /.vagrant
/README.html /README.html
/book/en/build
/book/en/src /book/en/src
/book/zh/build
/book/zh/src /book/zh/src
/fuzz/artifacts /fuzz/artifacts
/fuzz/corpus /fuzz/corpus
@ -9,4 +11,3 @@
/test-utilities/Cargo.lock /test-utilities/Cargo.lock
/test-utilities/target /test-utilities/target
/tmp /tmp
/www/man

View File

@ -313,7 +313,7 @@ Kakoune supports `justfile` syntax highlighting out of the box, thanks to TeddyD
### Sublime Text ### Sublime Text
A syntax file for Sublime Text written by TonioGela is available in [extras/just.sublime-syntax](extras/just.sublime-syntax). A syntax file for Sublime Text written by TonioGela is available in [extras/just.sublime-syntax](https://github.com/casey/just/blob/master/extras/just.sublime-syntax).
### Other Editors ### Other Editors
@ -940,7 +940,7 @@ $ just system-info
This is an x86_64 machine This is an x86_64 machine
``` ```
The `os_family()` function can be used to create cross-platform `justfile`s that work on various operating systems. For an example, see [cross-platform.just](examples/cross-platform.just) file. The `os_family()` function can be used to create cross-platform `justfile`s that work on various operating systems. For an example, see [cross-platform.just](https://github.com/casey/just/blob/master/examples/cross-platform.just) file.
#### Environment Variables #### Environment Variables
@ -1946,7 +1946,7 @@ complete -F _just -o bashdefault -o default j
### Shell Completion Scripts ### Shell Completion Scripts
Shell completion scripts for Bash, Zsh, Fish, PowerShell, and Elvish are available in the [completions](completions) directory. Please refer to your shell's documentation for how to install them. Shell completion scripts for Bash, Zsh, Fish, PowerShell, and Elvish are available in the [completions](https://github.com/casey/just/tree/master/completions) directory. Please refer to your shell's documentation for how to install them.
The `just` binary can also generate the same completion scripts at runtime, using the `--completions` command: The `just` binary can also generate the same completion scripts at runtime, using the `--completions` command:
@ -1956,11 +1956,11 @@ $ just --completions zsh > just.zsh
### Grammar ### Grammar
A non-normative grammar of `justfile`s can be found in [GRAMMAR.md](GRAMMAR.md). A non-normative grammar of `justfile`s can be found in [GRAMMAR.md](https://github.com/casey/just/blob/master/GRAMMAR.md).
### just.sh ### just.sh
Before `just` was a fancy Rust program it was a tiny shell script that called `make`. You can find the old version in [extras/just.sh](extras/just.sh). Before `just` was a fancy Rust program it was a tiny shell script that called `make`. You can find the old version in [extras/just.sh](https://github.com/casey/just/blob/master/extras/just.sh).
### User `justfile`s ### User `justfile`s
@ -2099,7 +2099,7 @@ Some ideas for recipes:
Even for small, personal projects it's nice to be able to remember commands by name instead of ^Reverse searching your shell history, and it's a huge boon to be able to go into an old project written in a random language with a mysterious build system and know that all the commands you need to do whatever you need to do are in the `justfile`, and that if you type `just` something useful (or at least interesting!) will probably happen. Even for small, personal projects it's nice to be able to remember commands by name instead of ^Reverse searching your shell history, and it's a huge boon to be able to go into an old project written in a random language with a mysterious build system and know that all the commands you need to do whatever you need to do are in the `justfile`, and that if you type `just` something useful (or at least interesting!) will probably happen.
For ideas for recipes, check out [this project's `justfile`](justfile), or some of the `justfile`s [out in the wild](https://github.com/search?o=desc&q=filename%3Ajustfile&s=indexed&type=Code). For ideas for recipes, check out [this project's `justfile`](https://github.com/casey/just/blob/master/justfile), or some of the `justfile`s [out in the wild](https://github.com/search?o=desc&q=filename%3Ajustfile&s=indexed&type=Code).
Anyways, I think that's about it for this incredibly long-winded README. Anyways, I think that's about it for this incredibly long-winded README.

View File

@ -309,7 +309,7 @@ Kakoune 已经内置支持 `justfile` 语法高亮,这要感谢 TeddyDD。
### Sublime Text ### Sublime Text
由 TonioGela 编写的 Sublime Text 的语法高亮文件在 [extras/just.sublim-syntax](extras/just.sublim-syntax) 中提供。 由 TonioGela 编写的 Sublime Text 的语法高亮文件在 [extras/just.sublim-syntax](https://github.com/casey/just/blob/master/extras/just.sublime-syntax) 中提供。
### 其它编辑器 ### 其它编辑器
@ -936,7 +936,7 @@ $ just system-info
This is an x86_64 machine This is an x86_64 machine
``` ```
`os_family()` 函数可以用来创建跨平台的 `justfile`,使其可以在不同的操作系统上工作。一个例子,见 [cross-platform.just](examples/cross-platform.just) 文件。 `os_family()` 函数可以用来创建跨平台的 `justfile`,使其可以在不同的操作系统上工作。一个例子,见 [cross-platform.just](https://github.com/casey/just/blob/master/examples/cross-platform.just) 文件。
#### 环境变量 #### 环境变量
@ -1941,7 +1941,7 @@ complete -F _just -o bashdefault -o default j
### Shell 自动补全脚本 ### Shell 自动补全脚本
Bash、Zsh、Fish、PowerShell 和 Elvish 的 Shell 自动补全脚本可以在 [自动补全](completions) 目录下找到。关于如何安装它们,请参考你的 Shell 文档。 Bash、Zsh、Fish、PowerShell 和 Elvish 的 Shell 自动补全脚本可以在 [自动补全](https://github.com/casey/just/tree/master/completions) 目录下找到。关于如何安装它们,请参考你的 Shell 文档。
`just` 二进制文件也可以在运行时生成相同的自动补全脚本,使用 `--completions` 命令即可,如下: `just` 二进制文件也可以在运行时生成相同的自动补全脚本,使用 `--completions` 命令即可,如下:
@ -1951,11 +1951,11 @@ $ just --completions zsh > just.zsh
### 语法 ### 语法
在 [GRAMMAR.md](GRAMMAR.md) 中可以找到一个非正式的 `justfile` 语法说明。 在 [GRAMMAR.md](https://github.com/casey/just/blob/master/GRAMMAR.md) 中可以找到一个非正式的 `justfile` 语法说明。
### just.sh ### just.sh
`just` 成为一个精致的 Rust 程序之前,它是一个很小的 Shell 脚本,叫 `make`。你可以在 [extras/just.sh](extras/just.sh) 中找到旧版本。 `just` 成为一个精致的 Rust 程序之前,它是一个很小的 Shell 脚本,叫 `make`。你可以在 [extras/just.sh](https://github.com/casey/just/blob/master/extras/just.sh) 中找到旧版本。
### 用户 `justfile` ### 用户 `justfile`
@ -2094,7 +2094,7 @@ make: `test' is up to date.
即使是小型的个人项目,能够通过名字记住命令,而不是通过 ^Reverse 搜索你的 Shell 历史,这也是一个巨大的福音,能够进入一个用任意语言编写的旧项目,并知道你需要用到的所有命令都在 `justfile` 中,如果你输入 `just`,就可能会输出一些有用的(或至少是有趣的!)信息。 即使是小型的个人项目,能够通过名字记住命令,而不是通过 ^Reverse 搜索你的 Shell 历史,这也是一个巨大的福音,能够进入一个用任意语言编写的旧项目,并知道你需要用到的所有命令都在 `justfile` 中,如果你输入 `just`,就可能会输出一些有用的(或至少是有趣的!)信息。
关于配方的想法,请查看 [这个项目的 `justfile`](justfile),或一些 [在其他项目里](https://github.com/search?o=desc&q=filename%3Ajustfile&s=indexed&type=Code) 的 `justfile` 关于配方的想法,请查看 [这个项目的 `justfile`](https://github.com/casey/just/blob/master/justfile),或一些 [在其他项目里](https://github.com/search?o=desc&q=filename%3Ajustfile&s=indexed&type=Code) 的 `justfile`
总之,我想这个令人难以置信地啰嗦的 README 就到此为止了。 总之,我想这个令人难以置信地啰嗦的 README 就到此为止了。

View File

@ -1,13 +1,12 @@
use { use {
pulldown_cmark::{ pulldown_cmark::{CowStr, Event, HeadingLevel, Options, Parser, Tag},
Event,
HeadingLevel::{H2, H3},
Options, Parser, Tag,
},
pulldown_cmark_to_cmark::cmark, pulldown_cmark_to_cmark::cmark,
std::{error::Error, fs}, std::{collections::BTreeMap, error::Error, fs, ops::Deref},
}; };
type Result<T = ()> = std::result::Result<T, Box<dyn Error>>;
#[derive(Copy, Clone, Debug)]
enum Language { enum Language {
English, English,
Chinese, Chinese,
@ -36,7 +35,61 @@ impl Language {
} }
} }
fn main() -> Result<(), Box<dyn Error>> { #[derive(Debug)]
struct Chapter<'a> {
level: HeadingLevel,
events: Vec<Event<'a>>,
index: usize,
language: Language,
}
impl<'a> Chapter<'a> {
fn title(&self) -> String {
if self.index == 0 {
return self.language.introduction().into();
}
self
.events
.iter()
.skip_while(|event| !matches!(event, Event::Start(Tag::Heading(..))))
.skip(1)
.take_while(|event| !matches!(event, Event::End(Tag::Heading(..))))
.filter_map(|event| match event {
Event::Code(content) | Event::Text(content) => Some(content.deref()),
_ => None,
})
.collect()
}
fn number(&self) -> usize {
self.index + 1
}
fn markdown(&self) -> Result<String> {
let mut markdown = String::new();
cmark(self.events.iter(), &mut markdown)?;
if self.index == 0 {
markdown = markdown.split_inclusive('\n').skip(1).collect::<String>();
}
Ok(markdown)
}
}
fn slug(s: &str) -> String {
let mut slug = String::new();
for c in s.chars() {
match c {
'A'..='Z' => slug.extend(c.to_lowercase()),
' ' => slug.push('-'),
'?' | '.' | '' => {}
_ => slug.push(c),
}
}
slug
}
fn main() -> Result {
for language in [Language::English, Language::Chinese] { for language in [Language::English, Language::Chinese] {
let src = format!("book/{}/src", language.code()); let src = format!("book/{}/src", language.code());
fs::remove_dir_all(&src).ok(); fs::remove_dir_all(&src).ok();
@ -44,34 +97,88 @@ fn main() -> Result<(), Box<dyn Error>> {
let txt = fs::read_to_string(format!("README{}.md", language.suffix()))?; let txt = fs::read_to_string(format!("README{}.md", language.suffix()))?;
let mut chapters = vec![(1usize, Vec::new())]; let mut chapters = vec![Chapter {
level: HeadingLevel::H1,
events: Vec::new(),
index: 0,
language,
}];
for event in Parser::new_ext(&txt, Options::all()) { for event in Parser::new_ext(&txt, Options::all()) {
if let Event::Start(Tag::Heading(level @ (H2 | H3), ..)) = event { if let Event::Start(Tag::Heading(level @ (HeadingLevel::H2 | HeadingLevel::H3), ..)) = event {
chapters.push((if level == H2 { 2 } else { 3 }, Vec::new())); let index = chapters.last().unwrap().index + 1;
chapters.push(Chapter {
level,
events: Vec::new(),
index,
language,
});
}
chapters.last_mut().unwrap().events.push(event);
}
let mut links = BTreeMap::new();
for chapter in &chapters {
let mut current = None;
for event in &chapter.events {
match event {
Event::Start(Tag::Heading(..)) => current = Some(Vec::new()),
Event::End(Tag::Heading(level, ..)) => {
let events = current.unwrap();
let title = events
.iter()
.filter_map(|event| match event {
Event::Code(content) | Event::Text(content) => Some(content.deref()),
_ => None,
})
.collect::<String>();
let slug = slug(&title);
let link = if let HeadingLevel::H1 | HeadingLevel::H2 | HeadingLevel::H3 = level {
format!("chapter_{}.html", chapter.number())
} else {
format!("chapter_{}.html#{}", chapter.number(), slug)
};
links.insert(slug, link);
current = None;
}
_ => {
if let Some(events) = &mut current {
events.push(event.clone());
}
}
}
}
}
for chapter in &mut chapters {
for event in &mut chapter.events {
if let Event::Start(Tag::Link(_, dest, _)) | Event::End(Tag::Link(_, dest, _)) = event {
if let Some(anchor) = dest.clone().strip_prefix('#') {
*dest = CowStr::Borrowed(&links[anchor]);
}
}
} }
chapters.last_mut().unwrap().1.push(event);
} }
let mut summary = String::new(); let mut summary = String::new();
for (i, (level, chapter)) in chapters.into_iter().enumerate() { for chapter in chapters {
let mut txt = String::new(); let path = format!("{}/chapter_{}.md", src, chapter.number());
cmark(chapter.iter(), &mut txt)?; fs::write(&path, &chapter.markdown()?)?;
let title = if i == 0 { let indent = match chapter.level {
txt = txt.split_inclusive('\n').skip(1).collect::<String>(); HeadingLevel::H1 => 0,
language.introduction() HeadingLevel::H2 => 1,
} else { HeadingLevel::H3 => 2,
txt.lines().next().unwrap().split_once(' ').unwrap().1 HeadingLevel::H4 => 3,
HeadingLevel::H5 => 4,
HeadingLevel::H6 => 5,
}; };
let path = format!("{}/chapter_{}.md", src, i + 1);
fs::write(&path, &txt)?;
summary.push_str(&format!( summary.push_str(&format!(
"{}- [{}](chapter_{}.md)\n", "{}- [{}](chapter_{}.md)\n",
" ".repeat((level.saturating_sub(1)) * 4), " ".repeat(indent * 4),
title, chapter.title(),
i + 1 chapter.number()
)); ));
} }

View File

@ -5,4 +5,8 @@ src = "src"
title = "Just Programmer's Manual" title = "Just Programmer's Manual"
[build] [build]
build-dir = "../../www/man/en" build-dir = "build"
[output.html]
[output.linkcheck]

View File

@ -5,4 +5,8 @@ src = "src"
title = "Just 用户指南" title = "Just 用户指南"
[build] [build]
build-dir = "../../www/man/zh" build-dir = "build"
[output.html]
[output.linkcheck]

View File

@ -165,6 +165,11 @@ watch-readme:
generate-completions: generate-completions:
./bin/generate-completions ./bin/generate-completions
build-book:
cargo run --package generate-book
mdbook build book/en
mdbook build book/zh
# run all polyglot recipes # run all polyglot recipes
polyglot: _python _js _perl _sh _ruby polyglot: _python _js _perl _sh _ruby
# (recipes that start with `_` are hidden from --list) # (recipes that start with `_` are hidden from --list)

1
www/man/en Symbolic link
View File

@ -0,0 +1 @@
../../book/en/build/html/

1
www/man/zh Symbolic link
View File

@ -0,0 +1 @@
../../book/zh/build/html/