Implement a `rustdoc_include` preprocessor (#1003)
* Allow underscores in the link type name * Add some tests for include anchors * Include parts of Rust files and hide the rest Fixes #618. * Increase min supported Rust version to 1.35 * Add a test for a behavior of rustdoc_include I want to depend on At first I thought this was a bug, but then I looked at some use cases we have in TRPL and decided this was a feature that I'd like to use.
This commit is contained in:
parent
8cdeb121c5
commit
ac1749ff2f
|
@ -17,7 +17,7 @@ matrix:
|
||||||
env: TARGET=x86_64-unknown-linux-gnu
|
env: TARGET=x86_64-unknown-linux-gnu
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
env: TARGET=x86_64-unknown-linux-gnu
|
env: TARGET=x86_64-unknown-linux-gnu
|
||||||
- rust: 1.34.0 # Minimum required Rust version
|
- rust: 1.35.0 # Minimum required Rust version
|
||||||
env: TARGET=x86_64-unknown-linux-gnu
|
env: TARGET=x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
- rust: stable
|
- rust: stable
|
||||||
|
|
|
@ -41,7 +41,7 @@ There are multiple ways to install mdBook.
|
||||||
|
|
||||||
2. **From Crates.io**
|
2. **From Crates.io**
|
||||||
|
|
||||||
This requires at least [Rust] 1.34 and Cargo to be installed. Once you have installed
|
This requires at least [Rust] 1.35 and Cargo to be installed. Once you have installed
|
||||||
Rust, type the following in the terminal:
|
Rust, type the following in the terminal:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -81,7 +81,7 @@ This controls the build process of your book.
|
||||||
|
|
||||||
The following preprocessors are available and included by default:
|
The following preprocessors are available and included by default:
|
||||||
|
|
||||||
- `links`: Expand the `{{ #playpen }}` and `{{ #include }}` handlebars
|
- `links`: Expand the `{{ #playpen }}`, `{{ #include }}`, and `{{ #rustdoc_include }}` handlebars
|
||||||
helpers in a chapter to include the contents of a file.
|
helpers in a chapter to include the contents of a file.
|
||||||
- `index`: Convert all chapter files named `README.md` into `index.md`. That is
|
- `index`: Convert all chapter files named `README.md` into `index.md`. That is
|
||||||
to say, all `README.md` would be rendered to an index file `index.html` in the
|
to say, all `README.md` would be rendered to an index file `index.html` in the
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
## Hiding code lines
|
## Hiding code lines
|
||||||
|
|
||||||
There is a feature in mdBook that lets you hide code lines by prepending them
|
There is a feature in mdBook that lets you hide code lines by prepending them
|
||||||
with a `#`.
|
with a `#` [in the same way that Rustdoc does][rustdoc-hide].
|
||||||
|
|
||||||
|
[rustdoc-hide]: https://doc.rust-lang.org/stable/rustdoc/documentation-tests.html#hiding-portions-of-the-example
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# fn main() {
|
# fn main() {
|
||||||
|
@ -107,6 +109,70 @@ This is the full file.
|
||||||
|
|
||||||
Lines containing anchor patterns inside the included anchor are ignored.
|
Lines containing anchor patterns inside the included anchor are ignored.
|
||||||
|
|
||||||
|
## Including a file but initially hiding all except specified lines
|
||||||
|
|
||||||
|
The `rustdoc_include` helper is for including code from external Rust files that contain complete
|
||||||
|
examples, but only initially showing particular lines specified with line numbers or anchors in the
|
||||||
|
same way as with `include`.
|
||||||
|
|
||||||
|
The lines not in the line number range or between the anchors will still be included, but they will
|
||||||
|
be prefaced with `#`. This way, a reader can expand the snippet to see the complete example, and
|
||||||
|
Rustdoc will use the complete example when you run `mdbook test`.
|
||||||
|
|
||||||
|
For example, consider a file named `file.rs` that contains this Rust program:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let x = add_one(2);
|
||||||
|
assert_eq!(x, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_one(num: i32) -> i32 {
|
||||||
|
num + 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We can include a snippet that initially shows only line 2 by using this syntax:
|
||||||
|
|
||||||
|
````hbs
|
||||||
|
To call the `add_one` function, we pass it an `i32` and bind the returned value to `x`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
\{{#rustdoc_include file.rs:2}}
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
This would have the same effect as if we had manually inserted the code and hidden all but line 2
|
||||||
|
using `#`:
|
||||||
|
|
||||||
|
````hbs
|
||||||
|
To call the `add_one` function, we pass it an `i32` and bind the returned value to `x`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# fn main() {
|
||||||
|
let x = add_one(2);
|
||||||
|
# assert_eq!(x, 3);
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# fn add_one(num: i32) -> i32 {
|
||||||
|
# num + 1
|
||||||
|
#}
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
That is, it looks like this (click the "expand" icon to see the rest of the file):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# fn main() {
|
||||||
|
let x = add_one(2);
|
||||||
|
# assert_eq!(x, 3);
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# fn add_one(num: i32) -> i32 {
|
||||||
|
# num + 1
|
||||||
|
#}
|
||||||
|
```
|
||||||
|
|
||||||
## Inserting runnable Rust files
|
## Inserting runnable Rust files
|
||||||
|
|
||||||
With the following syntax, you can insert runnable Rust files into your book:
|
With the following syntax, you can insert runnable Rust files into your book:
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::utils::{take_anchored_lines, take_lines};
|
use crate::utils::{
|
||||||
|
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
||||||
|
take_rustdoc_include_lines,
|
||||||
|
};
|
||||||
use regex::{CaptureMatches, Captures, Regex};
|
use regex::{CaptureMatches, Captures, Regex};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeTo};
|
use std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeTo};
|
||||||
|
@ -11,8 +14,15 @@ use crate::book::{Book, BookItem};
|
||||||
const ESCAPE_CHAR: char = '\\';
|
const ESCAPE_CHAR: char = '\\';
|
||||||
const MAX_LINK_NESTED_DEPTH: usize = 10;
|
const MAX_LINK_NESTED_DEPTH: usize = 10;
|
||||||
|
|
||||||
/// A preprocessor for expanding the `{{# playpen}}` and `{{# include}}`
|
/// A preprocessor for expanding helpers in a chapter. Supported helpers are:
|
||||||
/// helpers in a chapter.
|
///
|
||||||
|
/// - `{{# include}}` - Insert an external file of any type. Include the whole file, only particular
|
||||||
|
///. lines, or only between the specified anchors.
|
||||||
|
/// - `{{# rustdoc_include}}` - Insert an external Rust file, showing the particular lines
|
||||||
|
///. specified or the lines between specified anchors, and include the rest of the file behind `#`.
|
||||||
|
/// This hides the lines from initial display but shows them when the reader expands the code
|
||||||
|
/// block and provides them to Rustdoc for testing.
|
||||||
|
/// - `{{# playpen}}` - Insert runnable Rust files
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LinkPreprocessor;
|
pub struct LinkPreprocessor;
|
||||||
|
|
||||||
|
@ -104,6 +114,7 @@ enum LinkType<'a> {
|
||||||
Escaped,
|
Escaped,
|
||||||
Include(PathBuf, RangeOrAnchor),
|
Include(PathBuf, RangeOrAnchor),
|
||||||
Playpen(PathBuf, Vec<&'a str>),
|
Playpen(PathBuf, Vec<&'a str>),
|
||||||
|
RustdocInclude(PathBuf, RangeOrAnchor),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
@ -172,6 +183,7 @@ impl<'a> LinkType<'a> {
|
||||||
LinkType::Escaped => None,
|
LinkType::Escaped => None,
|
||||||
LinkType::Include(p, _) => Some(return_relative_path(base, &p)),
|
LinkType::Include(p, _) => Some(return_relative_path(base, &p)),
|
||||||
LinkType::Playpen(p, _) => Some(return_relative_path(base, &p)),
|
LinkType::Playpen(p, _) => Some(return_relative_path(base, &p)),
|
||||||
|
LinkType::RustdocInclude(p, _) => Some(return_relative_path(base, &p)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,6 +234,15 @@ fn parse_include_path(path: &str) -> LinkType<'static> {
|
||||||
LinkType::Include(path, range_or_anchor)
|
LinkType::Include(path, range_or_anchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_rustdoc_include_path(path: &str) -> LinkType<'static> {
|
||||||
|
let mut parts = path.splitn(2, ':');
|
||||||
|
|
||||||
|
let path = parts.next().unwrap().into();
|
||||||
|
let range_or_anchor = parse_range_or_anchor(parts.next());
|
||||||
|
|
||||||
|
LinkType::RustdocInclude(path, range_or_anchor)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
struct Link<'a> {
|
struct Link<'a> {
|
||||||
start_index: usize,
|
start_index: usize,
|
||||||
|
@ -241,6 +262,7 @@ impl<'a> Link<'a> {
|
||||||
match (typ.as_str(), file_arg) {
|
match (typ.as_str(), file_arg) {
|
||||||
("include", Some(pth)) => Some(parse_include_path(pth)),
|
("include", Some(pth)) => Some(parse_include_path(pth)),
|
||||||
("playpen", Some(pth)) => Some(LinkType::Playpen(pth.into(), props)),
|
("playpen", Some(pth)) => Some(LinkType::Playpen(pth.into(), props)),
|
||||||
|
("rustdoc_include", Some(pth)) => Some(parse_rustdoc_include_path(pth)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,6 +303,26 @@ impl<'a> Link<'a> {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
LinkType::RustdocInclude(ref pat, ref range_or_anchor) => {
|
||||||
|
let target = base.join(pat);
|
||||||
|
|
||||||
|
fs::read_to_string(&target)
|
||||||
|
.map(|s| match range_or_anchor {
|
||||||
|
RangeOrAnchor::Range(range) => {
|
||||||
|
take_rustdoc_include_lines(&s, range.clone())
|
||||||
|
}
|
||||||
|
RangeOrAnchor::Anchor(anchor) => {
|
||||||
|
take_rustdoc_include_anchored_lines(&s, anchor)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.chain_err(|| {
|
||||||
|
format!(
|
||||||
|
"Could not read file for link {} ({})",
|
||||||
|
self.link_text,
|
||||||
|
target.display(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
LinkType::Playpen(ref pat, ref attrs) => {
|
LinkType::Playpen(ref pat, ref attrs) => {
|
||||||
let target = base.join(pat);
|
let target = base.join(pat);
|
||||||
|
|
||||||
|
@ -326,7 +368,7 @@ fn find_links(contents: &str) -> LinkIter<'_> {
|
||||||
\\\{\{\#.*\}\} # match escaped link
|
\\\{\{\#.*\}\} # match escaped link
|
||||||
| # or
|
| # or
|
||||||
\{\{\s* # link opening parens and whitespace
|
\{\{\s* # link opening parens and whitespace
|
||||||
\#([a-zA-Z0-9]+) # link type
|
\#([a-zA-Z0-9_]+) # link type
|
||||||
\s+ # separating whitespace
|
\s+ # separating whitespace
|
||||||
([a-zA-Z0-9\s_.\-:/\\]+) # link target path and space separated properties
|
([a-zA-Z0-9\s_.\-:/\\]+) # link target path and space separated properties
|
||||||
\s*\}\} # whitespace and link closing parens"
|
\s*\}\} # whitespace and link closing parens"
|
||||||
|
|
|
@ -11,7 +11,10 @@ use std::borrow::Cow;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub use self::string::{take_anchored_lines, take_lines};
|
pub use self::string::{
|
||||||
|
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
||||||
|
take_rustdoc_include_lines,
|
||||||
|
};
|
||||||
|
|
||||||
/// Replaces multiple consecutive whitespace characters with a single space character.
|
/// Replaces multiple consecutive whitespace characters with a single space character.
|
||||||
pub fn collapse_whitespace(text: &str) -> Cow<'_, str> {
|
pub fn collapse_whitespace(text: &str) -> Cow<'_, str> {
|
||||||
|
|
|
@ -18,33 +18,33 @@ pub fn take_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ANCHOR_START: Regex = Regex::new(r"ANCHOR:\s*(?P<anchor_name>[\w_-]+)").unwrap();
|
||||||
|
static ref ANCHOR_END: Regex = Regex::new(r"ANCHOR_END:\s*(?P<anchor_name>[\w_-]+)").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// Take anchored lines from a string.
|
/// Take anchored lines from a string.
|
||||||
/// Lines containing anchor are ignored.
|
/// Lines containing anchor are ignored.
|
||||||
pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
|
pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
|
||||||
lazy_static! {
|
|
||||||
static ref RE_START: Regex = Regex::new(r"ANCHOR:\s*(?P<anchor_name>[\w_-]+)").unwrap();
|
|
||||||
static ref RE_END: Regex = Regex::new(r"ANCHOR_END:\s*(?P<anchor_name>[\w_-]+)").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut retained = Vec::<&str>::new();
|
let mut retained = Vec::<&str>::new();
|
||||||
let mut anchor_found = false;
|
let mut anchor_found = false;
|
||||||
|
|
||||||
for l in s.lines() {
|
for l in s.lines() {
|
||||||
if anchor_found {
|
if anchor_found {
|
||||||
match RE_END.captures(l) {
|
match ANCHOR_END.captures(l) {
|
||||||
Some(cap) => {
|
Some(cap) => {
|
||||||
if &cap["anchor_name"] == anchor {
|
if &cap["anchor_name"] == anchor {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if !RE_START.is_match(l) {
|
if !ANCHOR_START.is_match(l) {
|
||||||
retained.push(l);
|
retained.push(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(cap) = RE_START.captures(l) {
|
if let Some(cap) = ANCHOR_START.captures(l) {
|
||||||
if &cap["anchor_name"] == anchor {
|
if &cap["anchor_name"] == anchor {
|
||||||
anchor_found = true;
|
anchor_found = true;
|
||||||
}
|
}
|
||||||
|
@ -55,9 +55,70 @@ pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
|
||||||
retained.join("\n")
|
retained.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Keep lines contained within the range specified as-is.
|
||||||
|
/// For any lines not in the range, include them but use `#` at the beginning. This will hide the
|
||||||
|
/// lines from initial display but include them when expanding the code snippet or testing with
|
||||||
|
/// rustdoc.
|
||||||
|
pub fn take_rustdoc_include_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {
|
||||||
|
let mut output = String::with_capacity(s.len());
|
||||||
|
|
||||||
|
for (index, line) in s.lines().enumerate() {
|
||||||
|
if !range.contains(&index) {
|
||||||
|
output.push_str("# ");
|
||||||
|
}
|
||||||
|
output.push_str(line);
|
||||||
|
output.push_str("\n");
|
||||||
|
}
|
||||||
|
output.pop();
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Keep lines between the anchor comments specified as-is.
|
||||||
|
/// For any lines not between the anchors, include them but use `#` at the beginning. This will
|
||||||
|
/// hide the lines from initial display but include them when expanding the code snippet or testing
|
||||||
|
/// with rustdoc.
|
||||||
|
pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
|
||||||
|
let mut output = String::with_capacity(s.len());
|
||||||
|
let mut within_anchored_section = false;
|
||||||
|
|
||||||
|
for l in s.lines() {
|
||||||
|
if within_anchored_section {
|
||||||
|
match ANCHOR_END.captures(l) {
|
||||||
|
Some(cap) => {
|
||||||
|
if &cap["anchor_name"] == anchor {
|
||||||
|
within_anchored_section = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if !ANCHOR_START.is_match(l) {
|
||||||
|
output.push_str(l);
|
||||||
|
output.push_str("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(cap) = ANCHOR_START.captures(l) {
|
||||||
|
if &cap["anchor_name"] == anchor {
|
||||||
|
within_anchored_section = true;
|
||||||
|
}
|
||||||
|
} else if !ANCHOR_END.is_match(l) {
|
||||||
|
output.push_str("# ");
|
||||||
|
output.push_str(l);
|
||||||
|
output.push_str("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.pop();
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{take_anchored_lines, take_lines};
|
use super::{
|
||||||
|
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
||||||
|
take_rustdoc_include_lines,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn take_lines_test() {
|
fn take_lines_test() {
|
||||||
|
@ -99,4 +160,93 @@ mod tests {
|
||||||
assert_eq!(take_anchored_lines(s, "test"), "dolor\nsit\namet");
|
assert_eq!(take_anchored_lines(s, "test"), "dolor\nsit\namet");
|
||||||
assert_eq!(take_anchored_lines(s, "something"), "");
|
assert_eq!(take_anchored_lines(s, "something"), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_rustdoc_include_lines_test() {
|
||||||
|
let s = "Lorem\nipsum\ndolor\nsit\namet";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_lines(s, 1..3),
|
||||||
|
"# Lorem\nipsum\ndolor\n# sit\n# amet"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_lines(s, 3..),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\nsit\namet"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_lines(s, ..3),
|
||||||
|
"Lorem\nipsum\ndolor\n# sit\n# amet"
|
||||||
|
);
|
||||||
|
assert_eq!(take_rustdoc_include_lines(s, ..), s);
|
||||||
|
// corner cases
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_lines(s, 4..3),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
|
||||||
|
);
|
||||||
|
assert_eq!(take_rustdoc_include_lines(s, ..100), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_rustdoc_include_anchored_lines_test() {
|
||||||
|
let s = "Lorem\nipsum\ndolor\nsit\namet";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = "Lorem\nipsum\ndolor\nANCHOR_END: test\nsit\namet";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = "Lorem\nipsum\nANCHOR: test\ndolor\nsit\namet";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\n# ipsum\ndolor\nsit\namet"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "something"),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = "Lorem\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nipsum";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\n# ipsum\ndolor\nsit\namet\n# lorem\n# ipsum"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "something"),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet\n# lorem\n# ipsum"
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = "Lorem\nANCHOR: test\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nipsum";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\nipsum\ndolor\nsit\namet\n# lorem\n# ipsum"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "something"),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet\n# lorem\n# ipsum"
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = "Lorem\nANCHOR: test2\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nANCHOR_END:test2\nipsum";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test2"),
|
||||||
|
"# Lorem\nipsum\ndolor\nsit\namet\nlorem\n# ipsum"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\n# ipsum\ndolor\nsit\namet\n# lorem\n# ipsum"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "something"),
|
||||||
|
"# Lorem\n# ipsum\n# dolor\n# sit\n# amet\n# lorem\n# ipsum"
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = "Lorem\nANCHOR: test\nipsum\nANCHOR_END: test\ndolor\nANCHOR: test\nsit\nANCHOR_END: test\namet";
|
||||||
|
assert_eq!(
|
||||||
|
take_rustdoc_include_anchored_lines(s, "test"),
|
||||||
|
"# Lorem\nipsum\n# dolor\nsit\n# amet"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,13 @@ impl DummyBook {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let sub_pattern = if self.passing_test { "true" } else { "false" };
|
let sub_pattern = if self.passing_test { "true" } else { "false" };
|
||||||
let files_containing_tests = ["src/first/nested.md", "src/first/nested-test.rs"];
|
let files_containing_tests = [
|
||||||
|
"src/first/nested.md",
|
||||||
|
"src/first/nested-test.rs",
|
||||||
|
"src/first/nested-test-with-anchors.rs",
|
||||||
|
"src/first/partially-included-test.rs",
|
||||||
|
"src/first/partially-included-test-with-anchors.rs",
|
||||||
|
];
|
||||||
for file in &files_containing_tests {
|
for file in &files_containing_tests {
|
||||||
let path_containing_tests = temp.path().join(file);
|
let path_containing_tests = temp.path().join(file);
|
||||||
replace_pattern_in_file(&path_containing_tests, "$TEST_STATUS", sub_pattern)?;
|
replace_pattern_in_file(&path_containing_tests, "$TEST_STATUS", sub_pattern)?;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// The next line will cause a `testing` test to fail if the anchor feature is broken in such a way
|
||||||
|
// that the whole file gets mistakenly included.
|
||||||
|
assert!(!$TEST_STATUS);
|
||||||
|
|
||||||
|
// ANCHOR: myanchor
|
||||||
|
// ANCHOR: unendinganchor
|
||||||
|
// The next line will cause a `rendered_output` test to fail if the anchor feature is broken in
|
||||||
|
// such a way that the content between anchors isn't included.
|
||||||
|
// unique-string-for-anchor-test
|
||||||
|
assert!($TEST_STATUS);
|
||||||
|
// ANCHOR_END: myanchor
|
|
@ -11,3 +11,21 @@ assert!($TEST_STATUS);
|
||||||
```rust
|
```rust
|
||||||
{{#include nested-test.rs}}
|
{{#include nested-test.rs}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Anchors include the part of a file between special comments
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#include nested-test-with-anchors.rs:myanchor}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rustdoc include adds the rest of the file as hidden
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#rustdoc_include partially-included-test.rs:5:7}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rustdoc include works with anchors too
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#rustdoc_include partially-included-test-with-anchors.rs:rustdoc-include-anchor}}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
fn some_other_function() {
|
||||||
|
// ANCHOR: unused-anchor-that-should-be-stripped
|
||||||
|
assert!($TEST_STATUS);
|
||||||
|
// ANCHOR_END: unused-anchor-that-should-be-stripped
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANCHOR: rustdoc-include-anchor
|
||||||
|
fn main() {
|
||||||
|
some_other_function();
|
||||||
|
}
|
||||||
|
// ANCHOR_END: rustdoc-include-anchor
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn some_function() {
|
||||||
|
assert!($TEST_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
some_function();
|
||||||
|
}
|
|
@ -147,6 +147,32 @@ fn rendered_code_has_playpen_stuff() {
|
||||||
assert_contains_strings(book_js, &[".playpen"]);
|
assert_contains_strings(book_js, &[".playpen"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anchors_include_text_between_but_not_anchor_comments() {
|
||||||
|
let temp = DummyBook::new().build().unwrap();
|
||||||
|
let md = MDBook::load(temp.path()).unwrap();
|
||||||
|
md.build().unwrap();
|
||||||
|
|
||||||
|
let nested = temp.path().join("book/first/nested.html");
|
||||||
|
let text_between_anchors = vec!["unique-string-for-anchor-test"];
|
||||||
|
let anchor_text = vec!["ANCHOR"];
|
||||||
|
|
||||||
|
assert_contains_strings(nested.clone(), &text_between_anchors);
|
||||||
|
assert_doesnt_contain_strings(nested, &anchor_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rustdoc_include_hides_the_unspecified_part_of_the_file() {
|
||||||
|
let temp = DummyBook::new().build().unwrap();
|
||||||
|
let md = MDBook::load(temp.path()).unwrap();
|
||||||
|
md.build().unwrap();
|
||||||
|
|
||||||
|
let nested = temp.path().join("book/first/nested.html");
|
||||||
|
let text = vec!["# fn some_function() {", "# fn some_other_function() {"];
|
||||||
|
|
||||||
|
assert_contains_strings(nested, &text);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chapter_content_appears_in_rendered_document() {
|
fn chapter_content_appears_in_rendered_document() {
|
||||||
let content = vec![
|
let content = vec![
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue