Skip to content

Commit 71389bd

Browse files
authored
Make separate, more adapted single-page version (#339)
Fixes #297
1 parent 9f6814b commit 71389bd

File tree

5 files changed

+107
-9
lines changed

5 files changed

+107
-9
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ site-url = "/pandocs/"
3535
follow-web-links = true
3636
traverse-parent-directories = false
3737
optional = true
38-
# Do not check the "this was generated from commit N" link, as the commit may not have been pushed yet
3938
exclude = [
40-
'https://github\.com/gbdev/pandocs/tree/'
39+
'https://github\.com/gbdev/pandocs/tree/', # Do not check the "this was generated from commit N" link, as the commit may not have been pushed yet
40+
'print.html', # Do not check the print page, as it is generated separately
41+
'single.html', # Do not check the single page, as it is generated separately
4142
]

renderer/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ edition = "2018"
99
[dependencies]
1010
anyhow = "1.0.40"
1111
globwalk = "0.8.1"
12+
lazy_static = "1.4.0"
1213
# mdbook here is only used as a lib, so no need for the extra features
1314
mdbook = { version = "0.4.8", default-features = false, features = [ "search" ] }
1415
regex = "1.5"
1516
termcolor = "1.1.2"
17+
url = "2.2.2"

renderer/src/main.rs

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@
99

1010
use anyhow::Context;
1111
use globwalk::{FileType, GlobWalkerBuilder};
12+
use lazy_static::lazy_static;
1213
use mdbook::book::BookItem;
1314
use mdbook::errors::{Error, Result};
1415
use mdbook::renderer::{HtmlHandlebars, RenderContext, Renderer};
16+
use regex::Regex;
1517
use std::fs::{self, File};
1618
use std::io::{self, Write};
19+
use std::io::{BufRead, BufReader, BufWriter};
1720
use std::path::PathBuf;
1821
use std::process::Command;
1922
use std::str::FromStr;
20-
2123
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
24+
use url::Url;
2225

2326
fn main() -> Result<()> {
2427
let mut stdin = io::stdin();
@@ -78,11 +81,15 @@ impl Renderer for Pandocs {
7881
path.set_file_name("print.html");
7982
render(&mut path, "<print>", usize::MAX).context("Failed to render print page")?;
8083

84+
// Generate the single-page version
85+
let base_url = Url::parse("http://localhost/").unwrap();
86+
gen_single_page(&mut path, &base_url).context("Failed to render single-page version")?;
87+
8188
// Generate the graphs in `imgs/src/` by shelling out to Python
8289
let working_dir = ctx.destination.join("imgs");
8390
let src_dir = working_dir.join("src");
8491
let python = if cfg!(windows) { "py3" } else { "python3" };
85-
let render = |file_name, title| {
92+
let gen_graph = |file_name, title| {
8693
let mut file_name = PathBuf::from_str(file_name).unwrap(); // Can't fail
8794
let output = File::create(working_dir.join(&file_name))?;
8895

@@ -106,8 +113,8 @@ impl Renderer for Pandocs {
106113
)))
107114
}
108115
};
109-
render("MBC5_Rumble_Mild.svg", "Mild Rumble")?;
110-
render("MBC5_Rumble_Strong.svg", "Strong Rumble")?;
116+
gen_graph("MBC5_Rumble_Mild.svg", "Mild Rumble")?;
117+
gen_graph("MBC5_Rumble_Strong.svg", "Strong Rumble")?;
111118
fs::remove_dir_all(&src_dir).context(format!("Failed to remove {}", src_dir.display()))?;
112119

113120
// Scrub off files that need not be published
@@ -230,3 +237,89 @@ fn render(path: &mut PathBuf, name: &str, index: usize) -> Result<()> {
230237

231238
Ok(())
232239
}
240+
241+
/// This generates `single.html` from `print.html`.
242+
/// This does not properly parse HTML, instead relying on crude assumptions about mdBook's output.
243+
/// This is for the sake of performance, as we can afford to update this from time to time.
244+
/// Such assumptions are marked by `HACK:` comments in the function, to at least ease tracability.
245+
fn gen_single_page(path: &mut PathBuf, base_url: &Url) -> Result<()> {
246+
let print_page = BufReader::new(
247+
File::open(&path)
248+
.with_context(|| format!("Failed to open print page \"{}\"", path.display()))?,
249+
);
250+
path.set_file_name("single.html");
251+
let mut single_page = BufWriter::new(File::create(path)?);
252+
// HACK: this almost certainly forgets a bunch of HTML edge cases
253+
lazy_static! {
254+
static ref LINK_RE: Regex =
255+
Regex::new(r#"<a(?:\s+(?:href="([^"]*)"|\w+="[^"]*"))*\s*>"#).unwrap();
256+
}
257+
258+
// HACK: this assumes all link tags span a single line
259+
let mut lines = print_page.lines();
260+
while let Some(line) = lines.next().transpose()? {
261+
let mut i = 0;
262+
for url_match in LINK_RE.captures_iter(&line).filter_map(|caps| caps.get(1)) {
263+
let url = &line[url_match.range()];
264+
265+
match Url::parse(url) {
266+
Ok(_) => continue, // If not a relative URL, skip
267+
Err(url::ParseError::RelativeUrlWithoutBase) => (),
268+
Err(e) => return Err(e).with_context(|| format!("Bad link URL \"{}\"", url)), // Return other errors
269+
}
270+
271+
let url = base_url
272+
.join(url)
273+
.with_context(|| format!("Bad total URL \"{}\"", url))?;
274+
if let Some(frag) = url.fragment() {
275+
// Write everything up to the match
276+
single_page.write_all(line[i..url_match.start()].as_bytes())?;
277+
// Write the replaced match
278+
single_page.write_all("#".as_bytes())?;
279+
single_page.write_all(frag.as_bytes())?;
280+
// Start copying after the match
281+
i = url_match.end();
282+
}
283+
}
284+
285+
// Write rest of line
286+
single_page.write_all(line[i..].as_bytes())?;
287+
single_page.write_all("\n".as_bytes())?;
288+
289+
// Remove the automatic print trigger code.
290+
// HACK: this assumes the location of the script, the comment's format,
291+
// and that there is no content between this comment and the script that cannot be
292+
// passed through.
293+
if line.trim_start() == "<!-- Custom JS scripts -->" {
294+
// Pass extra scripts through unchanged
295+
// HACK: we filter scripts by assuming that mdBook's "additional JS" refs are one-line
296+
// and the printer script tag isn't.
297+
lines
298+
.by_ref()
299+
.take_while(|line| {
300+
line.as_ref().map_or(true, |line| {
301+
// HACK: this relies on the exact formatting of the auto-printer's script tag
302+
line.trim_start() != "<script type=\"text/javascript\">"
303+
})
304+
})
305+
.try_for_each(|line| {
306+
single_page.write_all(line?.as_bytes())?;
307+
single_page.write_all("\n".as_bytes())
308+
})?;
309+
310+
// Discard lines until the end of the script.
311+
// Also, check if this does discard lines; if this discards none, we have a problem.
312+
let mut auto_printer_script_lines = lines.by_ref().take_while(|line| {
313+
line.as_ref().map_or(true, |line| {
314+
// HACK: this relies on the exact formatting of the auto-printer's script end tag
315+
line.trim_start() != "</script>"
316+
})
317+
});
318+
if auto_printer_script_lines.next().is_none() {
319+
panic!("Warning: unterminated auto-printer script tag??");
320+
}
321+
}
322+
}
323+
324+
Ok(())
325+
}

src/About.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Emulator developers may be also interested in the [Game Boy: Complete Technical
1111

1212
:::
1313

14-
### Contributing
14+
## Contributing
1515

1616
This project is open source, released under the [CC0 license](https://raw.githubusercontent.com/gbdev/pandocs/master/LICENSE). Everyone is welcome to help, provide feedback and propose additions or improvements. The git repository is hosted at [github.com/gbdev/pandocs](https://github.com/gbdev/pandocs), where you can learn more about how you can [contribute](https://github.com/gbdev/pandocs/blob/master/README.MD), find detailed contribution guidelines and procedures, file Issues and send Pull Requests.
1717

@@ -20,7 +20,7 @@ There is a [Discord chat](https://gbdev.io/chat) dedicated to the gbdev communit
2020
Finally, you can also contact us and send patches via email: `pandocs (at) gbdev.io`.
2121

2222

23-
### Using this document
23+
## Using this document
2424

2525
In the top navigation bar, you will find a series of icons.
2626

@@ -32,6 +32,6 @@ You can search anywhere by pressing <kbd>s</kbd> on your keyboard or clicking th
3232

3333
The <i class="fa fa-edit"></i> icon allows you to suggest an edit on the current page by directly opening the source file in the git repository.
3434

35-
You can find a one-page printable version of Pan Docs [here](https://gbdev.io/pandocs/print.html).
35+
Also available are [one-page](single.html) and [printable](print.html) versions of this document.
3636

3737
<br><br>

0 commit comments

Comments
 (0)