Add {{#shiftinclude}} to L/R shift on include
Syntax is the same as {{#include}} except with a shift value and colon before the remaining arguments, e.g. {{#include -2:somefile.rs:myanchor}} A positive value for the shift prepends spaces to each line. A negative value for the shift removes chars from the beginning of each line (including non-whitespace chars, although this will emit an error log).
This commit is contained in:
parent
5a35144d4f
commit
6c7b25945d
|
@ -211,6 +211,18 @@ This is the full file.
|
|||
|
||||
Lines containing anchor patterns inside the included anchor are ignored.
|
||||
|
||||
## Including a file but changing its indentation
|
||||
|
||||
Sometimes it is useful to include a file or part of a file but with the indentation shifted,
|
||||
using the following syntax:
|
||||
|
||||
```hbs
|
||||
\{{#shiftinclude -2:file.rs}}
|
||||
```
|
||||
|
||||
A positive number for the shift will prepend spaces to all lines; a negative number will remove
|
||||
the corresponding number of characters from the beginning of each line.
|
||||
|
||||
## 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
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::errors::*;
|
||||
use crate::utils::{
|
||||
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
||||
take_rustdoc_include_lines,
|
||||
take_anchored_lines_with_shift, take_lines_with_shift, take_rustdoc_include_anchored_lines,
|
||||
take_rustdoc_include_lines, Shift,
|
||||
};
|
||||
use regex::{CaptureMatches, Captures, Regex};
|
||||
use std::cmp::Ordering;
|
||||
use std::fs;
|
||||
use std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeTo};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -20,6 +21,8 @@ const MAX_LINK_NESTED_DEPTH: usize = 10;
|
|||
///
|
||||
/// - `{{# include}}` - Insert an external file of any type. Include the whole file, only particular
|
||||
///. lines, or only between the specified anchors.
|
||||
/// - `{{# shiftinclude}}` - Insert content from an external file like include but shift the content
|
||||
///. left or right by a specified amount.
|
||||
/// - `{{# 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
|
||||
|
@ -135,7 +138,7 @@ where
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
enum LinkType<'a> {
|
||||
Escaped,
|
||||
Include(PathBuf, RangeOrAnchor),
|
||||
Include(PathBuf, RangeOrAnchor, Shift),
|
||||
Playground(PathBuf, Vec<&'a str>),
|
||||
RustdocInclude(PathBuf, RangeOrAnchor),
|
||||
Title(&'a str),
|
||||
|
@ -206,7 +209,7 @@ impl<'a> LinkType<'a> {
|
|||
let base = base.as_ref();
|
||||
match self {
|
||||
LinkType::Escaped => None,
|
||||
LinkType::Include(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::Include(p, _, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::Playground(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::RustdocInclude(p, _) => Some(return_relative_path(base, &p)),
|
||||
LinkType::Title(_) => None,
|
||||
|
@ -257,7 +260,27 @@ fn parse_include_path(path: &str) -> LinkType<'static> {
|
|||
let path = parts.next().unwrap().into();
|
||||
let range_or_anchor = parse_range_or_anchor(parts.next());
|
||||
|
||||
LinkType::Include(path, range_or_anchor)
|
||||
LinkType::Include(path, range_or_anchor, Shift::None)
|
||||
}
|
||||
|
||||
fn parse_shift_include_path(params: &str) -> LinkType<'static> {
|
||||
let mut params = params.splitn(2, ':');
|
||||
let param0 = params.next().unwrap();
|
||||
let shift: isize = param0.parse().unwrap_or_else(|e| {
|
||||
log::error!("failed to parse shift amount: {e:?}");
|
||||
0
|
||||
});
|
||||
let shift = match shift.cmp(&0) {
|
||||
Ordering::Greater => Shift::Right(shift as usize),
|
||||
Ordering::Equal => Shift::None,
|
||||
Ordering::Less => Shift::Left(-shift as usize),
|
||||
};
|
||||
let mut parts = params.next().unwrap().splitn(2, ':');
|
||||
|
||||
let path = parts.next().unwrap().into();
|
||||
let range_or_anchor = parse_range_or_anchor(parts.next());
|
||||
|
||||
LinkType::Include(path, range_or_anchor, shift)
|
||||
}
|
||||
|
||||
fn parse_rustdoc_include_path(path: &str) -> LinkType<'static> {
|
||||
|
@ -289,6 +312,7 @@ impl<'a> Link<'a> {
|
|||
let props: Vec<&str> = path_props.collect();
|
||||
|
||||
match (typ.as_str(), file_arg) {
|
||||
("shiftinclude", Some(pth)) => Some(parse_shift_include_path(pth)),
|
||||
("include", Some(pth)) => Some(parse_include_path(pth)),
|
||||
("playground", Some(pth)) => Some(LinkType::Playground(pth.into(), props)),
|
||||
("playpen", Some(pth)) => {
|
||||
|
@ -328,13 +352,17 @@ impl<'a> Link<'a> {
|
|||
match self.link_type {
|
||||
// omit the escape char
|
||||
LinkType::Escaped => Ok(self.link_text[1..].to_owned()),
|
||||
LinkType::Include(ref pat, ref range_or_anchor) => {
|
||||
LinkType::Include(ref pat, ref range_or_anchor, shift) => {
|
||||
let target = base.join(pat);
|
||||
|
||||
fs::read_to_string(&target)
|
||||
.map(|s| match range_or_anchor {
|
||||
RangeOrAnchor::Range(range) => take_lines(&s, range.clone()),
|
||||
RangeOrAnchor::Anchor(anchor) => take_anchored_lines(&s, anchor),
|
||||
RangeOrAnchor::Range(range) => {
|
||||
take_lines_with_shift(&s, range.clone(), shift)
|
||||
}
|
||||
RangeOrAnchor::Anchor(anchor) => {
|
||||
take_anchored_lines_with_shift(&s, anchor, shift)
|
||||
}
|
||||
})
|
||||
.with_context(|| {
|
||||
format!(
|
||||
|
@ -544,7 +572,8 @@ mod tests {
|
|||
end_index: 48,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(9..20))
|
||||
RangeOrAnchor::Range(LineRange::from(9..20)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs:10:20}}",
|
||||
}]
|
||||
|
@ -563,7 +592,8 @@ mod tests {
|
|||
end_index: 45,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(9..10))
|
||||
RangeOrAnchor::Range(LineRange::from(9..10)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs:10}}",
|
||||
}]
|
||||
|
@ -582,7 +612,8 @@ mod tests {
|
|||
end_index: 46,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(9..))
|
||||
RangeOrAnchor::Range(LineRange::from(9..)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs:10:}}",
|
||||
}]
|
||||
|
@ -601,7 +632,8 @@ mod tests {
|
|||
end_index: 46,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(..20))
|
||||
RangeOrAnchor::Range(LineRange::from(..20)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs::20}}",
|
||||
}]
|
||||
|
@ -620,7 +652,8 @@ mod tests {
|
|||
end_index: 44,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(..))
|
||||
RangeOrAnchor::Range(LineRange::from(..)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs::}}",
|
||||
}]
|
||||
|
@ -639,7 +672,8 @@ mod tests {
|
|||
end_index: 42,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(..))
|
||||
RangeOrAnchor::Range(LineRange::from(..)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs}}",
|
||||
}]
|
||||
|
@ -658,7 +692,8 @@ mod tests {
|
|||
end_index: 49,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Anchor(String::from("anchor"))
|
||||
RangeOrAnchor::Anchor(String::from("anchor")),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs:anchor}}",
|
||||
}]
|
||||
|
@ -717,12 +752,12 @@ mod tests {
|
|||
fn test_find_all_link_types() {
|
||||
let s =
|
||||
"Some random text with escaped playground {{#include file.rs}} and \\{{#contents are \
|
||||
insignifficant in escaped link}} some more\n text {{#playground my.rs editable \
|
||||
insignifficant in escaped link}} some more\n shifted {{#shiftinclude -2:file.rs}} text {{#playground my.rs editable \
|
||||
no_run should_panic}} ...";
|
||||
|
||||
let res = find_links(s).collect::<Vec<_>>();
|
||||
println!("\nOUTPUT: {:?}\n", res);
|
||||
assert_eq!(res.len(), 3);
|
||||
assert_eq!(res.len(), 4);
|
||||
assert_eq!(
|
||||
res[0],
|
||||
Link {
|
||||
|
@ -730,7 +765,8 @@ mod tests {
|
|||
end_index: 61,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(..))
|
||||
RangeOrAnchor::Range(LineRange::from(..)),
|
||||
Shift::None
|
||||
),
|
||||
link_text: "{{#include file.rs}}",
|
||||
}
|
||||
|
@ -747,8 +783,21 @@ mod tests {
|
|||
assert_eq!(
|
||||
res[2],
|
||||
Link {
|
||||
start_index: 133,
|
||||
end_index: 183,
|
||||
start_index: 135,
|
||||
end_index: 163,
|
||||
link_type: LinkType::Include(
|
||||
PathBuf::from("file.rs"),
|
||||
RangeOrAnchor::Range(LineRange::from(..)),
|
||||
Shift::Left(2)
|
||||
),
|
||||
link_text: "{{#shiftinclude -2:file.rs}}",
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
res[3],
|
||||
Link {
|
||||
start_index: 170,
|
||||
end_index: 220,
|
||||
link_type: LinkType::Playground(
|
||||
PathBuf::from("my.rs"),
|
||||
vec!["editable", "no_run", "should_panic"]
|
||||
|
@ -765,7 +814,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull))
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -777,7 +827,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull))
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -789,7 +840,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull))
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -801,7 +853,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull))
|
||||
RangeOrAnchor::Range(LineRange::from(RangeFull)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -813,7 +866,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..5))
|
||||
RangeOrAnchor::Range(LineRange::from(4..5)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -825,7 +879,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(0..1))
|
||||
RangeOrAnchor::Range(LineRange::from(0..1)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -837,7 +892,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(0..1))
|
||||
RangeOrAnchor::Range(LineRange::from(0..1)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -849,7 +905,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..))
|
||||
RangeOrAnchor::Range(LineRange::from(4..)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -861,7 +918,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..))
|
||||
RangeOrAnchor::Range(LineRange::from(4..)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -873,7 +931,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(..5))
|
||||
RangeOrAnchor::Range(LineRange::from(..5)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -885,7 +944,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..10))
|
||||
RangeOrAnchor::Range(LineRange::from(4..10)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -897,7 +957,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Anchor("-5".to_string())
|
||||
RangeOrAnchor::Anchor("-5".to_string()),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -909,7 +970,8 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Anchor("-5.7".to_string())
|
||||
RangeOrAnchor::Anchor("-5.7".to_string()),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -921,7 +983,21 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Anchor("some-anchor".to_string())
|
||||
RangeOrAnchor::Anchor("some-anchor".to_string()),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_with_shifted_anchor() {
|
||||
let link_type = parse_shift_include_path("17:arbitrary:some-anchor");
|
||||
assert_eq!(
|
||||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Anchor("some-anchor".to_string()),
|
||||
Shift::Right(17)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -933,7 +1009,47 @@ mod tests {
|
|||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..10))
|
||||
RangeOrAnchor::Range(LineRange::from(4..10)),
|
||||
Shift::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_start_and_end_shifted_left_range() {
|
||||
let link_type = parse_shift_include_path("-2:arbitrary:5:10");
|
||||
assert_eq!(
|
||||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..10)),
|
||||
Shift::Left(2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_start_and_end_shifted_right_range() {
|
||||
let link_type = parse_shift_include_path("2:arbitrary:5:10");
|
||||
assert_eq!(
|
||||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..10)),
|
||||
Shift::Right(2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_start_and_end_plus_shifted_right_range() {
|
||||
let link_type = parse_shift_include_path("+2:arbitrary:5:10");
|
||||
assert_eq!(
|
||||
link_type,
|
||||
LinkType::Include(
|
||||
PathBuf::from("arbitrary"),
|
||||
RangeOrAnchor::Range(LineRange::from(4..10)),
|
||||
Shift::Right(2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ use std::fmt::Write;
|
|||
use std::path::Path;
|
||||
|
||||
pub use self::string::{
|
||||
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
||||
take_rustdoc_include_lines,
|
||||
take_anchored_lines, take_anchored_lines_with_shift, take_lines, take_lines_with_shift,
|
||||
take_rustdoc_include_anchored_lines, take_rustdoc_include_lines, Shift,
|
||||
};
|
||||
|
||||
/// Replaces multiple consecutive whitespace characters with a single space character.
|
||||
|
|
|
@ -1,27 +1,63 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Bound::{Excluded, Included, Unbounded};
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
/// Indication of whether to shift included text.
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum Shift {
|
||||
None,
|
||||
Left(usize),
|
||||
Right(usize),
|
||||
}
|
||||
|
||||
fn shift_line(l: &str, shift: Shift) -> Cow<'_, str> {
|
||||
match shift {
|
||||
Shift::None => Cow::Borrowed(l),
|
||||
Shift::Right(shift) => {
|
||||
let indent = " ".repeat(shift);
|
||||
Cow::Owned(format!("{indent}{l}"))
|
||||
}
|
||||
Shift::Left(skip) => {
|
||||
if l.chars().take(skip).any(|c| !c.is_whitespace()) {
|
||||
log::error!("left-shifting away non-whitespace");
|
||||
}
|
||||
let rest = l.chars().skip(skip).collect::<String>();
|
||||
Cow::Owned(rest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shift_lines(lines: &[String], shift: Shift) -> Vec<Cow<'_, str>> {
|
||||
lines.iter().map(|l| shift_line(l, shift)).collect()
|
||||
}
|
||||
|
||||
/// Take a range of lines from a string.
|
||||
pub fn take_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {
|
||||
take_lines_with_shift(s, range, Shift::None)
|
||||
}
|
||||
|
||||
/// Take a range of lines from a string, shifting all lines left or right.
|
||||
pub fn take_lines_with_shift<R: RangeBounds<usize>>(s: &str, range: R, shift: Shift) -> String {
|
||||
let start = match range.start_bound() {
|
||||
Excluded(&n) => n + 1,
|
||||
Included(&n) => n,
|
||||
Unbounded => 0,
|
||||
};
|
||||
let lines = s.lines().skip(start);
|
||||
match range.end_bound() {
|
||||
let retained = match range.end_bound() {
|
||||
Excluded(end) => lines
|
||||
.take(end.saturating_sub(start))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
.map(|l| l.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
Included(end) => lines
|
||||
.take((end + 1).saturating_sub(start))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
Unbounded => lines.collect::<Vec<_>>().join("\n"),
|
||||
}
|
||||
.map(|l| l.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
Unbounded => lines.map(|l| l.to_string()).collect::<Vec<_>>(),
|
||||
};
|
||||
shift_lines(&retained, shift).join("\n")
|
||||
}
|
||||
|
||||
static ANCHOR_START: Lazy<Regex> =
|
||||
|
@ -32,7 +68,13 @@ static ANCHOR_END: Lazy<Regex> =
|
|||
/// Take anchored lines from a string.
|
||||
/// Lines containing anchor are ignored.
|
||||
pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
|
||||
let mut retained = Vec::<&str>::new();
|
||||
take_anchored_lines_with_shift(s, anchor, Shift::None)
|
||||
}
|
||||
|
||||
/// Take anchored lines from a string, shifting all lines left or right.
|
||||
/// Lines containing anchor are ignored.
|
||||
pub fn take_anchored_lines_with_shift(s: &str, anchor: &str, shift: Shift) -> String {
|
||||
let mut retained = Vec::<String>::new();
|
||||
let mut anchor_found = false;
|
||||
|
||||
for l in s.lines() {
|
||||
|
@ -45,7 +87,7 @@ pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
|
|||
}
|
||||
None => {
|
||||
if !ANCHOR_START.is_match(l) {
|
||||
retained.push(l);
|
||||
retained.push(l.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +98,7 @@ pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
retained.join("\n")
|
||||
shift_lines(&retained, shift).join("\n")
|
||||
}
|
||||
|
||||
/// Keep lines contained within the range specified as-is.
|
||||
|
@ -118,10 +160,24 @@ pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
|
||||
take_rustdoc_include_lines,
|
||||
shift_line, take_anchored_lines, take_anchored_lines_with_shift, take_lines,
|
||||
take_lines_with_shift, take_rustdoc_include_anchored_lines, take_rustdoc_include_lines,
|
||||
Shift,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn shift_line_test() {
|
||||
let s = " Line with 4 space intro";
|
||||
assert_eq!(shift_line(s, Shift::None), s);
|
||||
assert_eq!(shift_line(s, Shift::Left(4)), "Line with 4 space intro");
|
||||
assert_eq!(shift_line(s, Shift::Left(2)), " Line with 4 space intro");
|
||||
assert_eq!(shift_line(s, Shift::Left(6)), "ne with 4 space intro");
|
||||
assert_eq!(
|
||||
shift_line(s, Shift::Right(2)),
|
||||
" Line with 4 space intro"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled
|
||||
fn take_lines_test() {
|
||||
|
@ -135,6 +191,56 @@ mod tests {
|
|||
assert_eq!(take_lines(s, ..100), s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled
|
||||
fn take_lines_with_shift_test() {
|
||||
let s = " Lorem\n ipsum\n dolor\n sit\n amet";
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, 1..3, Shift::None),
|
||||
" ipsum\n dolor"
|
||||
);
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, 1..3, Shift::Left(2)),
|
||||
"ipsum\n dolor"
|
||||
);
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, 1..3, Shift::Right(2)),
|
||||
" ipsum\n dolor"
|
||||
);
|
||||
assert_eq!(take_lines_with_shift(s, 3.., Shift::None), " sit\n amet");
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, 3.., Shift::Right(1)),
|
||||
" sit\n amet"
|
||||
);
|
||||
assert_eq!(take_lines_with_shift(s, 3.., Shift::Left(1)), " sit\n amet");
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, ..3, Shift::None),
|
||||
" Lorem\n ipsum\n dolor"
|
||||
);
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, ..3, Shift::Right(4)),
|
||||
" Lorem\n ipsum\n dolor"
|
||||
);
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, ..3, Shift::Left(4)),
|
||||
"rem\nsum\ndolor"
|
||||
);
|
||||
assert_eq!(take_lines_with_shift(s, .., Shift::None), s);
|
||||
// corner cases
|
||||
assert_eq!(take_lines_with_shift(s, 4..3, Shift::None), "");
|
||||
assert_eq!(take_lines_with_shift(s, 4..3, Shift::Left(2)), "");
|
||||
assert_eq!(take_lines_with_shift(s, 4..3, Shift::Right(2)), "");
|
||||
assert_eq!(take_lines_with_shift(s, ..100, Shift::None), s);
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, ..100, Shift::Right(2)),
|
||||
" Lorem\n ipsum\n dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_lines_with_shift(s, ..100, Shift::Left(2)),
|
||||
"Lorem\nipsum\n dolor\nsit\namet"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_anchored_lines_test() {
|
||||
let s = "Lorem\nipsum\ndolor\nsit\namet";
|
||||
|
@ -164,6 +270,144 @@ mod tests {
|
|||
assert_eq!(take_anchored_lines(s, "something"), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_anchored_lines_with_shift_test() {
|
||||
let s = "Lorem\nipsum\ndolor\nsit\namet";
|
||||
assert_eq!(take_anchored_lines_with_shift(s, "test", Shift::None), "");
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Right(2)),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(2)),
|
||||
""
|
||||
);
|
||||
|
||||
let s = "Lorem\nipsum\ndolor\nANCHOR_END: test\nsit\namet";
|
||||
assert_eq!(take_anchored_lines_with_shift(s, "test", Shift::None), "");
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Right(2)),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(2)),
|
||||
""
|
||||
);
|
||||
|
||||
let s = " Lorem\n ipsum\n ANCHOR: test\n dolor\n sit\n amet";
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::None),
|
||||
" dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Right(2)),
|
||||
" dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(2)),
|
||||
"dolor\nsit\namet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::None),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::Right(2)),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::Left(2)),
|
||||
""
|
||||
);
|
||||
|
||||
let s = " Lorem\n ipsum\n ANCHOR: test\n dolor\n sit\n amet\n ANCHOR_END: test\n lorem\n ipsum";
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::None),
|
||||
" dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Right(2)),
|
||||
" dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(2)),
|
||||
"dolor\nsit\namet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(4)),
|
||||
"lor\nt\net"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(44)),
|
||||
"\n\n"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::None),
|
||||
""
|
||||
);
|
||||
|
||||
let s = " Lorem\n ANCHOR: test\n ipsum\n ANCHOR: test\n dolor\n sit\n amet\n ANCHOR_END: test\n lorem\n ipsum";
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::None),
|
||||
" ipsum\n dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Right(2)),
|
||||
" ipsum\n dolor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(2)),
|
||||
"ipsum\ndolor\nsit\namet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::None),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::Right(2)),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::Left(2)),
|
||||
""
|
||||
);
|
||||
|
||||
// Include non-ASCII.
|
||||
let s = " Lorem\n ANCHOR: test2\n ípsum\n ANCHOR: test\n dôlor\n sit\n amet\n ANCHOR_END: test\n lorem\n ANCHOR_END:test2\n ipsum";
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test2", Shift::None),
|
||||
" ípsum\n dôlor\n sit\n amet\n lorem"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test2", Shift::Right(2)),
|
||||
" ípsum\n dôlor\n sit\n amet\n lorem"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test2", Shift::Left(2)),
|
||||
"ípsum\ndôlor\nsit\namet\nlorem"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test2", Shift::Left(4)),
|
||||
"sum\nlor\nt\net\nrem"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::None),
|
||||
" dôlor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Right(2)),
|
||||
" dôlor\n sit\n amet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "test", Shift::Left(2)),
|
||||
"dôlor\nsit\namet"
|
||||
);
|
||||
assert_eq!(
|
||||
take_anchored_lines_with_shift(s, "something", Shift::None),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled
|
||||
fn take_rustdoc_include_lines_test() {
|
||||
|
|
|
@ -9,3 +9,11 @@ assert!(!$TEST_STATUS);
|
|||
// unique-string-for-anchor-test
|
||||
assert!($TEST_STATUS);
|
||||
// ANCHOR_END: myanchor
|
||||
|
||||
pub mod indent {
|
||||
// ANCHOR: indentedanchor
|
||||
pub fn indented_function() {
|
||||
// This extra indent remains
|
||||
}
|
||||
// ANCHOR_END: indentedanchor
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@ assert!($TEST_STATUS);
|
|||
{{#include nested-test-with-anchors.rs:myanchor}}
|
||||
```
|
||||
|
||||
## Includes can be shifted
|
||||
|
||||
```rust
|
||||
{{#shiftinclude +2:nested-test-with-anchors.rs:myanchor}}
|
||||
```
|
||||
|
||||
## Rustdoc include adds the rest of the file as hidden
|
||||
|
||||
```rust
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue