From 6c7b25945d1d576d8f615e0678d397446f03eb71 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Fri, 1 Mar 2024 11:33:59 +0000 Subject: [PATCH 1/4] 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). --- guide/src/format/mdbook.md | 12 + src/preprocess/links.rs | 186 ++- src/utils/mod.rs | 4 +- src/utils/string.rs | 268 +++- .../src/first/nested-test-with-anchors.rs | 8 + tests/dummy_book/src/first/nested.md | 6 + tests/searchindex_fixture.json | 1168 ++++++++++------- 7 files changed, 1115 insertions(+), 537 deletions(-) diff --git a/guide/src/format/mdbook.md b/guide/src/format/mdbook.md index df24340c47..78143dfad0 100644 --- a/guide/src/format/mdbook.md +++ b/guide/src/format/mdbook.md @@ -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 diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 0af211960a..938a6e304e 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -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::>(); 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) ) ); } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2b17cc7d84..28b558c5ac 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -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. diff --git a/src/utils/string.rs b/src/utils/string.rs index 6dafe2603a..ad856e8111 100644 --- a/src/utils/string.rs +++ b/src/utils/string.rs @@ -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::(); + Cow::Owned(rest) + } + } +} + +fn shift_lines(lines: &[String], shift: Shift) -> Vec> { + lines.iter().map(|l| shift_line(l, shift)).collect() +} + /// Take a range of lines from a string. pub fn take_lines>(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>(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::>() - .join("\n"), + .map(|l| l.to_string()) + .collect::>(), Included(end) => lines .take((end + 1).saturating_sub(start)) - .collect::>() - .join("\n"), - Unbounded => lines.collect::>().join("\n"), - } + .map(|l| l.to_string()) + .collect::>(), + Unbounded => lines.map(|l| l.to_string()).collect::>(), + }; + shift_lines(&retained, shift).join("\n") } static ANCHOR_START: Lazy = @@ -32,7 +68,13 @@ static ANCHOR_END: Lazy = /// 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::::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() { diff --git a/tests/dummy_book/src/first/nested-test-with-anchors.rs b/tests/dummy_book/src/first/nested-test-with-anchors.rs index 783ab14de3..b637545d8a 100644 --- a/tests/dummy_book/src/first/nested-test-with-anchors.rs +++ b/tests/dummy_book/src/first/nested-test-with-anchors.rs @@ -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 +} diff --git a/tests/dummy_book/src/first/nested.md b/tests/dummy_book/src/first/nested.md index ae90763a06..1591a734fb 100644 --- a/tests/dummy_book/src/first/nested.md +++ b/tests/dummy_book/src/first/nested.md @@ -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 diff --git a/tests/searchindex_fixture.json b/tests/searchindex_fixture.json index 06bfb2abbe..3713240ec8 100644 --- a/tests/searchindex_fixture.json +++ b/tests/searchindex_fixture.json @@ -7,6 +7,7 @@ "first/nested.html#nested-chapter", "first/nested.html#some-section", "first/nested.html#anchors-include-the-part-of-a-file-between-special-comments", + "first/nested.html#includes-can-be-shifted", "first/nested.html#rustdoc-include-adds-the-rest-of-the-file-as-hidden", "first/nested.html#rustdoc-include-works-with-anchors-too", "first/includes.html#includes", @@ -45,62 +46,62 @@ "title": 1 }, "10": { - "body": 21, + "body": 0, "breadcrumbs": 4, "title": 1 }, "11": { + "body": 21, + "breadcrumbs": 4, + "title": 1 + }, + "12": { "body": 44, "breadcrumbs": 3, "title": 2 }, - "12": { + "13": { "body": 3, "breadcrumbs": 5, "title": 2 }, - "13": { + "14": { "body": 4, "breadcrumbs": 4, "title": 1 }, - "14": { + "15": { "body": 12, "breadcrumbs": 4, "title": 1 }, - "15": { + "16": { "body": 2, "breadcrumbs": 4, "title": 1 }, - "16": { + "17": { "body": 3, "breadcrumbs": 4, "title": 1 }, - "17": { + "18": { "body": 29, "breadcrumbs": 6, "title": 3 }, - "18": { + "19": { "body": 6, "breadcrumbs": 3, "title": 2 }, - "19": { - "body": 5, - "breadcrumbs": 6, - "title": 2 - }, "2": { "body": 2, "breadcrumbs": 4, "title": 2 }, "20": { - "body": 0, + "body": 5, "breadcrumbs": 6, "title": 2 }, @@ -125,35 +126,40 @@ "title": 2 }, "25": { + "body": 0, + "breadcrumbs": 6, + "title": 2 + }, + "26": { "body": 0, "breadcrumbs": 7, "title": 3 }, - "26": { + "27": { "body": 20, "breadcrumbs": 4, "title": 2 }, - "27": { + "28": { "body": 18, "breadcrumbs": 9, "title": 5 }, - "28": { + "29": { "body": 0, "breadcrumbs": 5, "title": 1 }, - "29": { - "body": 10, - "breadcrumbs": 2, - "title": 1 - }, "3": { "body": 0, "breadcrumbs": 3, "title": 1 }, + "30": { + "body": 10, + "breadcrumbs": 2, + "title": 1 + }, "4": { "body": 4, "breadcrumbs": 6, @@ -170,19 +176,19 @@ "title": 7 }, "7": { + "body": 21, + "breadcrumbs": 6, + "title": 2 + }, + "8": { "body": 6, "breadcrumbs": 10, "title": 6 }, - "8": { + "9": { "body": 6, "breadcrumbs": 8, "title": 4 - }, - "9": { - "body": 0, - "breadcrumbs": 4, - "title": 1 } }, "docs": { @@ -199,64 +205,64 @@ "title": "Introduction" }, "10": { + "body": "", + "breadcrumbs": "First Chapter » Includes » Includes", + "id": "10", + "title": "Includes" + }, + "11": { "body": "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode No Headers Duplicate Headers Heading Attributes Second Chapter Nested Chapter Conclusion", "breadcrumbs": "First Chapter » Includes » Summary", - "id": "10", + "id": "11", "title": "Summary" }, - "11": { + "12": { "body": "Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world Around the world, around the world", "breadcrumbs": "First Chapter » Recursive", - "id": "11", + "id": "12", "title": "First Chapter" }, - "12": { + "13": { "body": "Tests for some markdown output.", "breadcrumbs": "First Chapter » Markdown » Markdown tests", - "id": "12", + "id": "13", "title": "Markdown tests" }, - "13": { + "14": { "body": "foo bar baz bim", "breadcrumbs": "First Chapter » Markdown » Tables", - "id": "13", + "id": "14", "title": "Tables" }, - "14": { + "15": { "body": "Footnote example [1] , or with a word [2] . This is a footnote. A longer footnote. With multiple lines. Third line.", "breadcrumbs": "First Chapter » Markdown » Footnotes", - "id": "14", + "id": "15", "title": "Footnotes" }, - "15": { + "16": { "body": "strikethrough example", "breadcrumbs": "First Chapter » Markdown » Strikethrough", - "id": "15", + "id": "16", "title": "Strikethrough" }, - "16": { + "17": { "body": "Apples Broccoli Carrots", "breadcrumbs": "First Chapter » Markdown » Tasklisks", - "id": "16", + "id": "17", "title": "Tasklisks" }, - "17": { + "18": { "body": "Please be careful editing, this contains carefully crafted characters. Two byte character: spatiëring Combining character: spatiëring Three byte character: 书こんにちは Four byte character: 𐌀‮𐌁‮𐌂‮𐌃‮𐌄‮𐌅‮𐌆‮𐌇‮𐌈‬ Right-to-left: مرحبا Emoticons: 🔊 😍 💜 1️⃣ right-to-left mark: hello באמת!‏ Zalgo: ǫ̛̖̱̗̝͈̋͒͋̏ͥͫ̒̆ͩ̏͌̾͊͐ͪ̾̚", "breadcrumbs": "First Chapter » Unicode » Unicode stress tests", - "id": "17", + "id": "18", "title": "Unicode stress tests" }, - "18": { + "19": { "body": "Capybara capybara capybara. Capybara capybara capybara. ThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex.", "breadcrumbs": "First Chapter » No Headers", - "id": "18", - "title": "First Chapter" - }, - "19": { - "body": "This page validates behaviour of duplicate headers.", - "breadcrumbs": "First Chapter » Duplicate Headers » Duplicate headers", "id": "19", - "title": "Duplicate headers" + "title": "First Chapter" }, "2": { "body": "more text.", @@ -265,10 +271,10 @@ "title": "First Chapter" }, "20": { - "body": "", - "breadcrumbs": "First Chapter » Duplicate Headers » Header Text", + "body": "This page validates behaviour of duplicate headers.", + "breadcrumbs": "First Chapter » Duplicate Headers » Duplicate headers", "id": "20", - "title": "Header Text" + "title": "Duplicate headers" }, "21": { "body": "", @@ -278,51 +284,51 @@ }, "22": { "body": "", - "breadcrumbs": "First Chapter » Duplicate Headers » header-text", + "breadcrumbs": "First Chapter » Duplicate Headers » Header Text", "id": "22", - "title": "header-text" + "title": "Header Text" }, "23": { "body": "", - "breadcrumbs": "First Chapter » Heading Attributes » Heading Attributes", + "breadcrumbs": "First Chapter » Duplicate Headers » header-text", "id": "23", - "title": "Heading Attributes" + "title": "header-text" }, "24": { "body": "", - "breadcrumbs": "First Chapter » Heading Attributes » Heading with classes", + "breadcrumbs": "First Chapter » Heading Attributes » Heading Attributes", "id": "24", - "title": "Heading with classes" + "title": "Heading Attributes" }, "25": { "body": "", - "breadcrumbs": "First Chapter » Heading Attributes » Heading with id and classes", + "breadcrumbs": "First Chapter » Heading Attributes » Heading with classes", "id": "25", - "title": "Heading with id and classes" + "title": "Heading with classes" }, "26": { + "body": "", + "breadcrumbs": "First Chapter » Heading Attributes » Heading with id and classes", + "id": "26", + "title": "Heading with id and classes" + }, + "27": { "body": "This makes sure you can insert runnable Rust files. fn main() { println!(\"Hello World!\");\n#\n# // You can even hide lines! :D\n# println!(\"I am hidden! Expand the code snippet to see me\");\n}", "breadcrumbs": "Second Chapter » Second Chapter", - "id": "26", + "id": "27", "title": "Second Chapter" }, - "27": { + "28": { "body": "When we link to the first section , it should work on both the print page and the non-print page. A fragment link should work. Link outside . Some image HTML Link", "breadcrumbs": "Second Chapter » Nested Chapter » Testing relative links for the print page", - "id": "27", + "id": "28", "title": "Testing relative links for the print page" }, - "28": { + "29": { "body": "", "breadcrumbs": "Second Chapter » Nested Chapter » Some section", - "id": "28", - "title": "Some section" - }, - "29": { - "body": "I put <HTML> in here! Sneaky inline event alert(\"inline\");. But regular inline is indexed.", - "breadcrumbs": "Conclusion » Conclusion", "id": "29", - "title": "Conclusion" + "title": "Some section" }, "3": { "body": "", @@ -330,6 +336,12 @@ "id": "3", "title": "Some Section" }, + "30": { + "body": "I put <HTML> in here! Sneaky inline event alert(\"inline\");. But regular inline is indexed.", + "breadcrumbs": "Conclusion » Conclusion", + "id": "30", + "title": "Conclusion" + }, "4": { "body": "This file has some testable code. assert!(true);", "breadcrumbs": "First Chapter » Nested Chapter » Nested Chapter", @@ -349,25 +361,25 @@ "title": "Anchors include the part of a file between special comments" }, "7": { - "body": "# fn some_function() {\n# assert!(true);\n# }\n# fn main() { some_function();\n}", - "breadcrumbs": "First Chapter » Nested Chapter » Rustdoc include adds the rest of the file as hidden", + "body": "// 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!(true);", + "breadcrumbs": "First Chapter » Nested Chapter » Includes can be shifted", "id": "7", - "title": "Rustdoc include adds the rest of the file as hidden" + "title": "Includes can be shifted" }, "8": { - "body": "# fn some_other_function() {\n# assert!(true);\n# }\n# fn main() { some_other_function();\n}", - "breadcrumbs": "First Chapter » Nested Chapter » Rustdoc include works with anchors too", + "body": "# fn some_function() {\n# assert!(true);\n# }\n# fn main() { some_function();\n}", + "breadcrumbs": "First Chapter » Nested Chapter » Rustdoc include adds the rest of the file as hidden", "id": "8", - "title": "Rustdoc include works with anchors too" + "title": "Rustdoc include adds the rest of the file as hidden" }, "9": { - "body": "", - "breadcrumbs": "First Chapter » Includes » Includes", + "body": "# fn some_other_function() {\n# assert!(true);\n# }\n# fn main() { some_other_function();\n}", + "breadcrumbs": "First Chapter » Nested Chapter » Rustdoc include works with anchors too", "id": "9", - "title": "Includes" + "title": "Rustdoc include works with anchors too" } }, - "length": 30, + "length": 31, "save": true }, "fields": [ @@ -381,10 +393,10 @@ "1": { "df": 2, "docs": { - "14": { + "15": { "tf": 1.0 }, - "17": { + "18": { "tf": 1.0 } } @@ -392,7 +404,7 @@ "2": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -402,7 +414,7 @@ "d": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.0 } } @@ -441,7 +453,7 @@ "n": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -471,12 +483,15 @@ "df": 0, "docs": {}, "r": { - "df": 2, + "df": 3, "docs": { "6": { "tf": 2.0 }, - "8": { + "7": { + "tf": 1.7320508075688772 + }, + "9": { "tf": 1.0 } } @@ -496,7 +511,7 @@ "l": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -516,7 +531,7 @@ "d": { "df": 1, "docs": { - "11": { + "12": { "tf": 4.69041575982343 } } @@ -551,7 +566,7 @@ "df": 0, "docs": {}, "u": { - "df": 5, + "df": 6, "docs": { "4": { "tf": 1.0 @@ -567,6 +582,9 @@ }, "8": { "tf": 1.0 + }, + "9": { + "tf": 1.0 } } } @@ -602,10 +620,10 @@ "t": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "23": { + "24": { "tf": 1.0 } } @@ -626,7 +644,7 @@ "r": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -634,7 +652,7 @@ "z": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -664,7 +682,7 @@ "r": { "df": 1, "docs": { - "19": { + "20": { "tf": 1.0 } } @@ -690,10 +708,13 @@ "df": 0, "docs": {}, "n": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.4142135623730951 + }, + "7": { + "tf": 1.0 } } } @@ -708,7 +729,7 @@ "m": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -726,7 +747,7 @@ "0": { "tf": 1.0 }, - "10": { + "11": { "tf": 1.0 } } @@ -738,7 +759,7 @@ "h": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -762,7 +783,7 @@ "i": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -782,10 +803,13 @@ "df": 0, "docs": {}, "n": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -802,7 +826,7 @@ "e": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.7320508075688772 } } @@ -826,7 +850,7 @@ "a": { "df": 1, "docs": { - "18": { + "19": { "tf": 2.449489742783178 } } @@ -848,7 +872,7 @@ "e": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } }, @@ -867,7 +891,7 @@ "i": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -886,7 +910,7 @@ "t": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -898,13 +922,16 @@ "df": 0, "docs": {}, "s": { - "df": 2, + "df": 3, "docs": { "0": { "tf": 1.0 }, "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -928,19 +955,19 @@ "r": { "df": 6, "docs": { - "10": { + "11": { "tf": 2.0 }, - "11": { + "12": { "tf": 1.0 }, - "18": { + "19": { "tf": 1.0 }, "2": { "tf": 1.0 }, - "26": { + "27": { "tf": 1.0 }, "4": { @@ -959,7 +986,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 2.23606797749979 } } @@ -985,10 +1012,10 @@ "s": { "df": 2, "docs": { - "24": { + "25": { "tf": 1.0 }, - "25": { + "26": { "tf": 1.0 } } @@ -1005,7 +1032,7 @@ "e": { "df": 2, "docs": { - "26": { + "27": { "tf": 1.0 }, "4": { @@ -1026,7 +1053,7 @@ "n": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1069,10 +1096,10 @@ "s": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "29": { + "30": { "tf": 1.0 } } @@ -1092,7 +1119,7 @@ "n": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1108,10 +1135,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -1130,7 +1160,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1144,7 +1174,7 @@ "d": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } }, @@ -1163,7 +1193,7 @@ "0": { "tf": 1.0 }, - "10": { + "11": { "tf": 1.0 } } @@ -1180,10 +1210,10 @@ "c": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "19": { + "20": { "tf": 1.4142135623730951 } } @@ -1207,7 +1237,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1235,7 +1265,7 @@ "n": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1257,14 +1287,14 @@ "n": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } }, "t": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -1285,10 +1315,10 @@ "l": { "df": 2, "docs": { - "14": { + "15": { "tf": 1.0 }, - "15": { + "16": { "tf": 1.0 } } @@ -1306,7 +1336,7 @@ "d": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -1328,10 +1358,13 @@ "df": 0, "docs": {}, "l": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -1350,10 +1383,13 @@ "df": 0, "docs": {}, "r": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -1375,7 +1411,7 @@ "0": { "tf": 1.0 }, - "26": { + "27": { "tf": 1.0 }, "4": { @@ -1384,7 +1420,7 @@ "6": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.0 } } @@ -1399,19 +1435,19 @@ "t": { "df": 5, "docs": { - "10": { + "11": { "tf": 1.0 }, - "11": { + "12": { "tf": 1.0 }, - "18": { + "19": { "tf": 1.0 }, "2": { "tf": 1.0 }, - "27": { + "28": { "tf": 1.0 } } @@ -1422,13 +1458,13 @@ "n": { "df": 3, "docs": { - "26": { + "27": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.4142135623730951 }, - "8": { + "9": { "tf": 1.4142135623730951 } } @@ -1439,7 +1475,7 @@ "o": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } }, @@ -1455,7 +1491,7 @@ "t": { "df": 1, "docs": { - "14": { + "15": { "tf": 2.0 } } @@ -1470,7 +1506,7 @@ "r": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1496,7 +1532,7 @@ "t": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -1538,10 +1574,7 @@ "d": { "df": 4, "docs": { - "10": { - "tf": 1.0 - }, - "23": { + "11": { "tf": 1.0 }, "24": { @@ -1549,6 +1582,9 @@ }, "25": { "tf": 1.0 + }, + "26": { + "tf": 1.0 } }, "e": { @@ -1557,20 +1593,20 @@ "r": { "df": 5, "docs": { - "10": { - "tf": 1.4142135623730951 - }, - "19": { + "11": { "tf": 1.4142135623730951 }, "20": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "21": { "tf": 1.0 }, "22": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } } @@ -1590,7 +1626,7 @@ "o": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -1614,7 +1650,7 @@ "0": { "tf": 1.0 }, - "29": { + "30": { "tf": 1.0 } } @@ -1632,10 +1668,10 @@ "n": { "df": 2, "docs": { - "26": { + "27": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.0 } } @@ -1647,7 +1683,7 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -1665,7 +1701,7 @@ "l": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -1677,7 +1713,7 @@ "d": { "df": 1, "docs": { - "25": { + "26": { "tf": 1.0 } } @@ -1691,7 +1727,7 @@ "g": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -1709,16 +1745,19 @@ "docs": {}, "u": { "d": { - "df": 5, + "df": 6, "docs": { "10": { "tf": 1.0 }, + "11": { + "tf": 1.0 + }, "6": { "tf": 1.4142135623730951 }, "7": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "8": { "tf": 1.0 @@ -1745,7 +1784,7 @@ "0": { "tf": 1.0 }, - "29": { + "30": { "tf": 1.0 } } @@ -1763,7 +1802,7 @@ "n": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.4142135623730951 } } @@ -1782,7 +1821,7 @@ "t": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -1834,7 +1873,7 @@ "1": { "tf": 1.0 }, - "10": { + "11": { "tf": 1.0 } } @@ -1858,10 +1897,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -1899,7 +1941,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.4142135623730951 } } @@ -1913,23 +1955,26 @@ "df": 0, "docs": {}, "e": { - "df": 3, + "df": 4, "docs": { - "14": { + "15": { "tf": 1.4142135623730951 }, - "26": { + "27": { "tf": 1.0 }, "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } }, "k": { "df": 1, "docs": { - "27": { + "28": { "tf": 2.23606797749979 } } @@ -1951,7 +1996,7 @@ "r": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -1983,7 +2028,7 @@ "t": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -2011,13 +2056,13 @@ "n": { "df": 3, "docs": { - "26": { + "27": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.0 }, - "8": { + "9": { "tf": 1.0 } } @@ -2029,7 +2074,7 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -2051,10 +2096,10 @@ "n": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "12": { + "13": { "tf": 1.4142135623730951 } } @@ -2064,7 +2109,7 @@ }, "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -2107,7 +2152,7 @@ "l": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -2130,7 +2175,7 @@ "t": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.4142135623730951 }, "4": { @@ -2143,10 +2188,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -2158,7 +2206,7 @@ "n": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -2183,7 +2231,7 @@ "t": { "df": 1, "docs": { - "12": { + "13": { "tf": 1.0 } } @@ -2197,7 +2245,7 @@ "d": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -2219,10 +2267,10 @@ "e": { "df": 2, "docs": { - "19": { + "20": { "tf": 1.0 }, - "27": { + "28": { "tf": 1.7320508075688772 } } @@ -2253,7 +2301,7 @@ "s": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -2335,7 +2383,7 @@ "t": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.7320508075688772 } }, @@ -2363,7 +2411,7 @@ "o": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -2375,7 +2423,7 @@ "i": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -2401,7 +2449,7 @@ "t": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -2424,7 +2472,7 @@ "s": { "df": 1, "docs": { - "10": { + "11": { "tf": 1.0 } } @@ -2447,7 +2495,7 @@ "r": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -2461,7 +2509,7 @@ "l": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -2497,10 +2545,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -2528,7 +2579,7 @@ "t": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.0 } } @@ -2547,7 +2598,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.4142135623730951 } } @@ -2573,7 +2624,7 @@ "l": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -2597,10 +2648,10 @@ "c": { "df": 2, "docs": { - "7": { + "8": { "tf": 1.0 }, - "8": { + "9": { "tf": 1.0 } } @@ -2611,7 +2662,7 @@ }, "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -2633,10 +2684,10 @@ "d": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "26": { + "27": { "tf": 1.0 } } @@ -2657,10 +2708,10 @@ "n": { "df": 4, "docs": { - "27": { + "28": { "tf": 1.0 }, - "28": { + "29": { "tf": 1.0 }, "3": { @@ -2680,12 +2731,32 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } } }, + "h": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + } + } + } + }, "n": { "df": 0, "docs": {}, @@ -2699,7 +2770,7 @@ "i": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -2724,7 +2795,7 @@ "t": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -2757,7 +2828,7 @@ "t": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.4142135623730951 } } @@ -2797,7 +2868,7 @@ "t": { "df": 1, "docs": { - "8": { + "9": { "tf": 1.4142135623730951 } } @@ -2841,7 +2912,7 @@ "r": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -2854,7 +2925,7 @@ "r": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -2905,7 +2976,7 @@ "s": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -2942,7 +3013,7 @@ "h": { "df": 1, "docs": { - "15": { + "16": { "tf": 1.4142135623730951 } } @@ -2959,10 +3030,13 @@ "df": 0, "docs": {}, "g": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -2975,10 +3049,13 @@ "df": 0, "docs": {}, "h": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -2998,7 +3075,7 @@ "i": { "df": 1, "docs": { - "10": { + "11": { "tf": 1.0 } } @@ -3015,7 +3092,7 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -3031,7 +3108,7 @@ "l": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -3057,7 +3134,7 @@ "k": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -3093,19 +3170,22 @@ "df": 0, "docs": {} }, - "df": 4, + "df": 5, "docs": { - "12": { + "13": { "tf": 1.4142135623730951 }, - "17": { + "18": { "tf": 1.0 }, - "27": { + "28": { "tf": 1.0 }, "6": { "tf": 1.4142135623730951 + }, + "7": { + "tf": 1.4142135623730951 } } } @@ -3122,14 +3202,14 @@ "2": { "tf": 1.0 }, - "20": { - "tf": 1.0 - }, "21": { "tf": 1.0 }, "22": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } } @@ -3145,7 +3225,7 @@ "d": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -3163,7 +3243,7 @@ "e": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -3177,7 +3257,7 @@ "o": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -3198,10 +3278,10 @@ "d": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "17": { + "18": { "tf": 1.0 } } @@ -3216,10 +3296,13 @@ "df": 0, "docs": {}, "u": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -3238,7 +3321,7 @@ "d": { "df": 1, "docs": { - "19": { + "20": { "tf": 1.0 } } @@ -3256,10 +3339,13 @@ "df": 0, "docs": {}, "y": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -3273,7 +3359,7 @@ "d": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -3283,10 +3369,10 @@ "k": { "df": 2, "docs": { - "27": { + "28": { "tf": 1.4142135623730951 }, - "8": { + "9": { "tf": 1.0 } } @@ -3295,10 +3381,10 @@ "d": { "df": 2, "docs": { - "11": { + "12": { "tf": 4.69041575982343 }, - "26": { + "27": { "tf": 1.0 } } @@ -3322,7 +3408,7 @@ "o": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -3340,10 +3426,10 @@ "1": { "df": 2, "docs": { - "14": { + "15": { "tf": 1.0 }, - "17": { + "18": { "tf": 1.0 } } @@ -3351,7 +3437,7 @@ "2": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -3361,7 +3447,7 @@ "d": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.4142135623730951 } } @@ -3400,7 +3486,7 @@ "n": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -3430,12 +3516,15 @@ "df": 0, "docs": {}, "r": { - "df": 2, + "df": 3, "docs": { "6": { "tf": 2.23606797749979 }, - "8": { + "7": { + "tf": 1.7320508075688772 + }, + "9": { "tf": 1.4142135623730951 } } @@ -3455,7 +3544,7 @@ "l": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -3475,7 +3564,7 @@ "d": { "df": 1, "docs": { - "11": { + "12": { "tf": 4.69041575982343 } } @@ -3510,7 +3599,7 @@ "df": 0, "docs": {}, "u": { - "df": 5, + "df": 6, "docs": { "4": { "tf": 1.0 @@ -3526,6 +3615,9 @@ }, "8": { "tf": 1.0 + }, + "9": { + "tf": 1.0 } } } @@ -3561,16 +3653,16 @@ "t": { "df": 4, "docs": { - "10": { + "11": { "tf": 1.0 }, - "23": { + "24": { "tf": 1.7320508075688772 }, - "24": { + "25": { "tf": 1.0 }, - "25": { + "26": { "tf": 1.0 } } @@ -3591,7 +3683,7 @@ "r": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -3599,7 +3691,7 @@ "z": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -3629,7 +3721,7 @@ "r": { "df": 1, "docs": { - "19": { + "20": { "tf": 1.0 } } @@ -3655,10 +3747,13 @@ "df": 0, "docs": {}, "n": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.7320508075688772 + }, + "7": { + "tf": 1.0 } } } @@ -3673,7 +3768,7 @@ "m": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -3691,7 +3786,7 @@ "0": { "tf": 1.7320508075688772 }, - "10": { + "11": { "tf": 1.0 } } @@ -3703,7 +3798,7 @@ "h": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -3727,7 +3822,7 @@ "i": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -3747,10 +3842,13 @@ "df": 0, "docs": {}, "n": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -3767,7 +3865,7 @@ "e": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.7320508075688772 } } @@ -3791,7 +3889,7 @@ "a": { "df": 1, "docs": { - "18": { + "19": { "tf": 2.449489742783178 } } @@ -3813,7 +3911,7 @@ "e": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } }, @@ -3832,7 +3930,7 @@ "i": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -3851,7 +3949,7 @@ "t": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -3863,13 +3961,16 @@ "df": 0, "docs": {}, "s": { - "df": 2, + "df": 3, "docs": { "0": { "tf": 1.0 }, "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -3891,16 +3992,16 @@ "df": 0, "docs": {}, "r": { - "df": 27, + "df": 28, "docs": { "10": { - "tf": 2.23606797749979 + "tf": 1.0 }, "11": { - "tf": 1.4142135623730951 + "tf": 2.23606797749979 }, "12": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "13": { "tf": 1.0 @@ -3918,10 +4019,10 @@ "tf": 1.0 }, "18": { - "tf": 1.4142135623730951 + "tf": 1.0 }, "19": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "2": { "tf": 1.7320508075688772 @@ -3945,14 +4046,17 @@ "tf": 1.0 }, "26": { - "tf": 1.7320508075688772 + "tf": 1.0 }, "27": { - "tf": 1.4142135623730951 + "tf": 1.7320508075688772 }, "28": { "tf": 1.4142135623730951 }, + "29": { + "tf": 1.4142135623730951 + }, "3": { "tf": 1.0 }, @@ -3972,7 +4076,7 @@ "tf": 1.4142135623730951 }, "9": { - "tf": 1.0 + "tf": 1.4142135623730951 } } } @@ -3987,7 +4091,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 2.23606797749979 } } @@ -4013,10 +4117,10 @@ "s": { "df": 2, "docs": { - "24": { + "25": { "tf": 1.4142135623730951 }, - "25": { + "26": { "tf": 1.4142135623730951 } } @@ -4033,7 +4137,7 @@ "e": { "df": 2, "docs": { - "26": { + "27": { "tf": 1.0 }, "4": { @@ -4054,7 +4158,7 @@ "n": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4097,10 +4201,10 @@ "s": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "29": { + "30": { "tf": 1.7320508075688772 } } @@ -4120,7 +4224,7 @@ "n": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4136,10 +4240,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -4158,7 +4265,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4172,7 +4279,7 @@ "d": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } }, @@ -4191,7 +4298,7 @@ "0": { "tf": 1.7320508075688772 }, - "10": { + "11": { "tf": 1.0 } } @@ -4208,20 +4315,20 @@ "c": { "df": 5, "docs": { - "10": { + "11": { "tf": 1.0 }, - "19": { - "tf": 2.0 - }, "20": { - "tf": 1.0 + "tf": 2.0 }, "21": { "tf": 1.0 }, "22": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } }, @@ -4244,7 +4351,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4272,7 +4379,7 @@ "n": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4294,14 +4401,14 @@ "n": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } }, "t": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -4322,10 +4429,10 @@ "l": { "df": 2, "docs": { - "14": { + "15": { "tf": 1.0 }, - "15": { + "16": { "tf": 1.0 } } @@ -4343,7 +4450,7 @@ "d": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -4365,10 +4472,13 @@ "df": 0, "docs": {}, "l": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -4387,10 +4497,13 @@ "df": 0, "docs": {}, "r": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -4412,7 +4525,7 @@ "0": { "tf": 1.0 }, - "26": { + "27": { "tf": 1.0 }, "4": { @@ -4421,7 +4534,7 @@ "6": { "tf": 1.4142135623730951 }, - "7": { + "8": { "tf": 1.4142135623730951 } } @@ -4434,16 +4547,16 @@ "df": 0, "docs": {}, "t": { - "df": 25, + "df": 26, "docs": { "10": { - "tf": 1.4142135623730951 + "tf": 1.0 }, "11": { "tf": 1.4142135623730951 }, "12": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "13": { "tf": 1.0 @@ -4461,10 +4574,10 @@ "tf": 1.0 }, "18": { - "tf": 1.4142135623730951 + "tf": 1.0 }, "19": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "2": { "tf": 1.7320508075688772 @@ -4487,7 +4600,10 @@ "25": { "tf": 1.0 }, - "27": { + "26": { + "tf": 1.0 + }, + "28": { "tf": 1.0 }, "3": { @@ -4519,13 +4635,13 @@ "n": { "df": 3, "docs": { - "26": { + "27": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.4142135623730951 }, - "8": { + "9": { "tf": 1.4142135623730951 } } @@ -4536,7 +4652,7 @@ "o": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } }, @@ -4552,7 +4668,7 @@ "t": { "df": 1, "docs": { - "14": { + "15": { "tf": 2.23606797749979 } } @@ -4567,7 +4683,7 @@ "r": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4593,7 +4709,7 @@ "t": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -4635,17 +4751,17 @@ "d": { "df": 4, "docs": { - "10": { + "11": { "tf": 1.0 }, - "23": { - "tf": 1.7320508075688772 - }, "24": { "tf": 1.7320508075688772 }, "25": { "tf": 1.7320508075688772 + }, + "26": { + "tf": 1.7320508075688772 } }, "e": { @@ -4654,23 +4770,23 @@ "r": { "df": 6, "docs": { - "10": { + "11": { "tf": 1.4142135623730951 }, - "18": { - "tf": 1.0 - }, "19": { - "tf": 2.0 + "tf": 1.0 }, "20": { - "tf": 1.7320508075688772 + "tf": 2.0 }, "21": { "tf": 1.7320508075688772 }, "22": { "tf": 1.7320508075688772 + }, + "23": { + "tf": 1.7320508075688772 } } } @@ -4690,7 +4806,7 @@ "o": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -4714,7 +4830,7 @@ "0": { "tf": 1.0 }, - "29": { + "30": { "tf": 1.0 } } @@ -4732,10 +4848,10 @@ "n": { "df": 2, "docs": { - "26": { + "27": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.4142135623730951 } } @@ -4747,7 +4863,7 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -4765,7 +4881,7 @@ "l": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -4777,7 +4893,7 @@ "d": { "df": 1, "docs": { - "25": { + "26": { "tf": 1.4142135623730951 } } @@ -4791,7 +4907,7 @@ "g": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -4809,22 +4925,25 @@ "docs": {}, "u": { "d": { - "df": 5, + "df": 6, "docs": { "10": { + "tf": 1.7320508075688772 + }, + "11": { "tf": 1.4142135623730951 }, "6": { "tf": 1.7320508075688772 }, "7": { - "tf": 1.4142135623730951 + "tf": 1.7320508075688772 }, "8": { "tf": 1.4142135623730951 }, "9": { - "tf": 1.7320508075688772 + "tf": 1.4142135623730951 } } }, @@ -4845,7 +4964,7 @@ "0": { "tf": 1.0 }, - "29": { + "30": { "tf": 1.0 } } @@ -4863,7 +4982,7 @@ "n": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.4142135623730951 } } @@ -4882,7 +5001,7 @@ "t": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -4934,7 +5053,7 @@ "1": { "tf": 1.7320508075688772 }, - "10": { + "11": { "tf": 1.0 } } @@ -4958,10 +5077,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -4999,7 +5121,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.4142135623730951 } } @@ -5013,23 +5135,26 @@ "df": 0, "docs": {}, "e": { - "df": 3, + "df": 4, "docs": { - "14": { + "15": { "tf": 1.4142135623730951 }, - "26": { + "27": { "tf": 1.0 }, "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } }, "k": { "df": 1, "docs": { - "27": { + "28": { "tf": 2.449489742783178 } } @@ -5051,7 +5176,7 @@ "r": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -5083,7 +5208,7 @@ "t": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -5111,13 +5236,13 @@ "n": { "df": 3, "docs": { - "26": { + "27": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.0 }, - "8": { + "9": { "tf": 1.0 } } @@ -5129,7 +5254,7 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -5151,14 +5276,11 @@ "n": { "df": 6, "docs": { - "10": { + "11": { "tf": 1.0 }, - "12": { - "tf": 2.0 - }, "13": { - "tf": 1.0 + "tf": 2.0 }, "14": { "tf": 1.0 @@ -5168,6 +5290,9 @@ }, "16": { "tf": 1.0 + }, + "17": { + "tf": 1.0 } } } @@ -5176,7 +5301,7 @@ }, "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -5219,7 +5344,7 @@ "l": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -5240,15 +5365,15 @@ "df": 0, "docs": {}, "t": { - "df": 8, + "df": 9, "docs": { - "10": { + "11": { "tf": 1.4142135623730951 }, - "27": { + "28": { "tf": 1.0 }, - "28": { + "29": { "tf": 1.0 }, "4": { @@ -5265,6 +5390,9 @@ }, "8": { "tf": 1.0 + }, + "9": { + "tf": 1.0 } } } @@ -5273,10 +5401,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -5288,7 +5419,7 @@ "n": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -5313,7 +5444,7 @@ "t": { "df": 1, "docs": { - "12": { + "13": { "tf": 1.0 } } @@ -5327,7 +5458,7 @@ "d": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -5349,10 +5480,10 @@ "e": { "df": 2, "docs": { - "19": { + "20": { "tf": 1.0 }, - "27": { + "28": { "tf": 2.0 } } @@ -5383,7 +5514,7 @@ "s": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -5465,7 +5596,7 @@ "t": { "df": 1, "docs": { - "27": { + "28": { "tf": 2.0 } }, @@ -5493,7 +5624,7 @@ "o": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -5505,7 +5636,7 @@ "i": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -5531,7 +5662,7 @@ "t": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -5554,10 +5685,10 @@ "s": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "11": { + "12": { "tf": 1.0 } } @@ -5580,7 +5711,7 @@ "r": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -5594,7 +5725,7 @@ "l": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.4142135623730951 } } @@ -5630,10 +5761,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -5661,7 +5795,7 @@ "t": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.4142135623730951 } } @@ -5680,7 +5814,7 @@ "t": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.4142135623730951 } } @@ -5706,7 +5840,7 @@ "l": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -5730,10 +5864,10 @@ "c": { "df": 2, "docs": { - "7": { + "8": { "tf": 1.4142135623730951 }, - "8": { + "9": { "tf": 1.4142135623730951 } } @@ -5744,7 +5878,7 @@ }, "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -5766,16 +5900,16 @@ "d": { "df": 4, "docs": { - "10": { + "11": { "tf": 1.0 }, - "26": { + "27": { "tf": 1.7320508075688772 }, - "27": { + "28": { "tf": 1.0 }, - "28": { + "29": { "tf": 1.0 } } @@ -5796,10 +5930,10 @@ "n": { "df": 4, "docs": { - "27": { + "28": { "tf": 1.0 }, - "28": { + "29": { "tf": 1.4142135623730951 }, "3": { @@ -5819,12 +5953,32 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } } }, + "h": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.4142135623730951 + } + } + } + } + } + }, "n": { "df": 0, "docs": {}, @@ -5838,7 +5992,7 @@ "i": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -5863,7 +6017,7 @@ "t": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -5896,7 +6050,7 @@ "t": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.4142135623730951 } } @@ -5936,7 +6090,7 @@ "t": { "df": 1, "docs": { - "8": { + "9": { "tf": 1.4142135623730951 } } @@ -5980,7 +6134,7 @@ "r": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -5993,7 +6147,7 @@ "r": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -6044,7 +6198,7 @@ "s": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.4142135623730951 } } @@ -6081,7 +6235,7 @@ "h": { "df": 1, "docs": { - "15": { + "16": { "tf": 1.7320508075688772 } } @@ -6098,10 +6252,13 @@ "df": 0, "docs": {}, "g": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -6114,10 +6271,13 @@ "df": 0, "docs": {}, "h": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -6137,7 +6297,7 @@ "i": { "df": 1, "docs": { - "10": { + "11": { "tf": 1.4142135623730951 } } @@ -6154,7 +6314,7 @@ "e": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -6170,7 +6330,7 @@ "l": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.4142135623730951 } } @@ -6196,7 +6356,7 @@ "k": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.4142135623730951 } } @@ -6232,19 +6392,22 @@ "df": 0, "docs": {} }, - "df": 4, + "df": 5, "docs": { - "12": { + "13": { "tf": 1.7320508075688772 }, - "17": { + "18": { "tf": 1.4142135623730951 }, - "27": { + "28": { "tf": 1.4142135623730951 }, "6": { "tf": 1.4142135623730951 + }, + "7": { + "tf": 1.4142135623730951 } } } @@ -6261,14 +6424,14 @@ "2": { "tf": 1.0 }, - "20": { - "tf": 1.4142135623730951 - }, "21": { "tf": 1.4142135623730951 }, "22": { "tf": 1.4142135623730951 + }, + "23": { + "tf": 1.4142135623730951 } } } @@ -6284,7 +6447,7 @@ "d": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -6302,7 +6465,7 @@ "e": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -6316,7 +6479,7 @@ "o": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -6337,10 +6500,10 @@ "d": { "df": 2, "docs": { - "10": { + "11": { "tf": 1.0 }, - "17": { + "18": { "tf": 1.7320508075688772 } } @@ -6355,10 +6518,13 @@ "df": 0, "docs": {}, "u": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -6377,7 +6543,7 @@ "d": { "df": 1, "docs": { - "19": { + "20": { "tf": 1.0 } } @@ -6395,10 +6561,13 @@ "df": 0, "docs": {}, "y": { - "df": 1, + "df": 2, "docs": { "6": { "tf": 1.0 + }, + "7": { + "tf": 1.0 } } } @@ -6412,7 +6581,7 @@ "d": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -6422,10 +6591,10 @@ "k": { "df": 2, "docs": { - "27": { + "28": { "tf": 1.4142135623730951 }, - "8": { + "9": { "tf": 1.4142135623730951 } } @@ -6434,10 +6603,10 @@ "d": { "df": 2, "docs": { - "11": { + "12": { "tf": 4.69041575982343 }, - "26": { + "27": { "tf": 1.0 } } @@ -6461,7 +6630,7 @@ "o": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -6481,7 +6650,7 @@ "d": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.0 } } @@ -6507,7 +6676,7 @@ "6": { "tf": 1.0 }, - "8": { + "9": { "tf": 1.0 } } @@ -6537,7 +6706,7 @@ "t": { "df": 1, "docs": { - "23": { + "24": { "tf": 1.0 } } @@ -6618,16 +6787,16 @@ "r": { "df": 5, "docs": { - "11": { + "12": { "tf": 1.0 }, - "18": { + "19": { "tf": 1.0 }, "2": { "tf": 1.0 }, - "26": { + "27": { "tf": 1.0 }, "4": { @@ -6652,10 +6821,10 @@ "s": { "df": 2, "docs": { - "24": { + "25": { "tf": 1.0 }, - "25": { + "26": { "tf": 1.0 } } @@ -6705,7 +6874,7 @@ "s": { "df": 1, "docs": { - "29": { + "30": { "tf": 1.0 } } @@ -6750,7 +6919,7 @@ "c": { "df": 1, "docs": { - "19": { + "20": { "tf": 1.0 } } @@ -6779,7 +6948,7 @@ "6": { "tf": 1.0 }, - "7": { + "8": { "tf": 1.0 } } @@ -6794,10 +6963,10 @@ "t": { "df": 3, "docs": { - "11": { + "12": { "tf": 1.0 }, - "18": { + "19": { "tf": 1.0 }, "2": { @@ -6826,7 +6995,7 @@ "t": { "df": 1, "docs": { - "14": { + "15": { "tf": 1.0 } } @@ -6845,14 +7014,14 @@ "d": { "df": 3, "docs": { - "23": { - "tf": 1.0 - }, "24": { "tf": 1.0 }, "25": { "tf": 1.0 + }, + "26": { + "tf": 1.0 } }, "e": { @@ -6861,9 +7030,6 @@ "r": { "df": 4, "docs": { - "19": { - "tf": 1.0 - }, "20": { "tf": 1.0 }, @@ -6872,6 +7038,9 @@ }, "22": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } } @@ -6894,7 +7063,7 @@ "n": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.0 } } @@ -6912,7 +7081,7 @@ "d": { "df": 1, "docs": { - "25": { + "26": { "tf": 1.0 } } @@ -6928,8 +7097,11 @@ "docs": {}, "u": { "d": { - "df": 4, + "df": 5, "docs": { + "10": { + "tf": 1.0 + }, "6": { "tf": 1.0 }, @@ -6997,7 +7169,7 @@ "k": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -7025,7 +7197,7 @@ "n": { "df": 1, "docs": { - "12": { + "13": { "tf": 1.0 } } @@ -7071,7 +7243,7 @@ "e": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -7104,7 +7276,7 @@ "t": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -7122,7 +7294,7 @@ "l": { "df": 1, "docs": { - "27": { + "28": { "tf": 1.0 } } @@ -7133,7 +7305,7 @@ "t": { "df": 1, "docs": { - "7": { + "8": { "tf": 1.0 } } @@ -7154,10 +7326,10 @@ "c": { "df": 2, "docs": { - "7": { + "8": { "tf": 1.0 }, - "8": { + "9": { "tf": 1.0 } } @@ -7186,7 +7358,7 @@ "d": { "df": 1, "docs": { - "26": { + "27": { "tf": 1.0 } } @@ -7207,7 +7379,7 @@ "n": { "df": 3, "docs": { - "28": { + "29": { "tf": 1.0 }, "3": { @@ -7225,6 +7397,26 @@ "df": 0, "docs": {} }, + "h": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + } + } + } + }, "p": { "df": 0, "docs": {}, @@ -7268,7 +7460,7 @@ "s": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -7305,7 +7497,7 @@ "h": { "df": 1, "docs": { - "15": { + "16": { "tf": 1.0 } } @@ -7337,7 +7529,7 @@ "i": { "df": 1, "docs": { - "10": { + "11": { "tf": 1.0 } } @@ -7358,7 +7550,7 @@ "l": { "df": 1, "docs": { - "13": { + "14": { "tf": 1.0 } } @@ -7384,7 +7576,7 @@ "k": { "df": 1, "docs": { - "16": { + "17": { "tf": 1.0 } } @@ -7406,13 +7598,13 @@ "t": { "df": 3, "docs": { - "12": { + "13": { "tf": 1.0 }, - "17": { + "18": { "tf": 1.0 }, - "27": { + "28": { "tf": 1.0 } } @@ -7424,14 +7616,14 @@ "t": { "df": 3, "docs": { - "20": { - "tf": 1.0 - }, "21": { "tf": 1.0 }, "22": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } } @@ -7452,7 +7644,7 @@ "d": { "df": 1, "docs": { - "17": { + "18": { "tf": 1.0 } } @@ -7478,7 +7670,7 @@ "k": { "df": 1, "docs": { - "8": { + "9": { "tf": 1.0 } } From a886ff55f855dd4e0bd74a57d09fc532e15fa73d Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Sat, 2 Mar 2024 08:18:18 +0000 Subject: [PATCH 2/4] Support {{#shiftinclude auto}} As well as allowing explicitly-specified shift amounts, also support an "auto" option that strips common leftmost whitespace from an inclusion. --- guide/src/format/mdbook.md | 6 + src/preprocess/links.rs | 46 +++++-- src/utils/string.rs | 127 ++++++++++++++--- tests/dummy_book/src/first/nested.md | 4 + tests/searchindex_fixture.json | 198 ++++++++++++++++++++++++++- 5 files changed, 353 insertions(+), 28 deletions(-) diff --git a/guide/src/format/mdbook.md b/guide/src/format/mdbook.md index 78143dfad0..667700c24f 100644 --- a/guide/src/format/mdbook.md +++ b/guide/src/format/mdbook.md @@ -223,6 +223,12 @@ using the following syntax: 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. +The special `auto` value will remove common initial whitespace from all lines. + +```hbs +\{{#shiftinclude auto:file.rs:indentedanchor}} +``` + ## 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 diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 938a6e304e..0497f86890 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -266,14 +266,18 @@ fn parse_include_path(path: &str) -> LinkType<'static> { 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 shift = if param0 == "auto" { + Shift::Auto + } else { + let shift: isize = param0.parse().unwrap_or_else(|e| { + log::error!("failed to parse shift amount: {e:?}"); + 0 + }); + 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, ':'); @@ -1002,6 +1006,19 @@ mod tests { ); } + #[test] + fn parse_with_auto_shifted_anchor() { + let link_type = parse_shift_include_path("auto:arbitrary:some-anchor"); + assert_eq!( + link_type, + LinkType::Include( + PathBuf::from("arbitrary"), + RangeOrAnchor::Anchor("some-anchor".to_string()), + Shift::Auto + ) + ); + } + #[test] fn parse_with_more_than_three_colons_ignores_everything_after_third_colon() { let link_type = parse_include_path("arbitrary:5:10:17:anything:"); @@ -1053,4 +1070,17 @@ mod tests { ) ); } + + #[test] + fn parse_start_and_end_auto_shifted_range() { + let link_type = parse_shift_include_path("auto:arbitrary:5:10"); + assert_eq!( + link_type, + LinkType::Include( + PathBuf::from("arbitrary"), + RangeOrAnchor::Range(LineRange::from(4..10)), + Shift::Auto + ) + ); + } } diff --git a/src/utils/string.rs b/src/utils/string.rs index ad856e8111..bbb80f514d 100644 --- a/src/utils/string.rs +++ b/src/utils/string.rs @@ -10,16 +10,58 @@ pub enum Shift { None, Left(usize), Right(usize), + /// Strip leftmost whitespace that is common to all lines. + Auto, } -fn shift_line(l: &str, shift: Shift) -> Cow<'_, str> { +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +enum ExplicitShift { + None, + Left(usize), + Right(usize), +} + +fn common_leading_ws(lines: &[String]) -> String { + let mut common_ws: Option = None; + for line in lines { + if line.is_empty() { + // Don't include empty lines in the calculation. + continue; + } + let ws = line.chars().take_while(|c| c.is_whitespace()); + if let Some(common) = common_ws { + common_ws = Some( + common + .chars() + .zip(ws) + .take_while(|(a, b)| a == b) + .map(|(a, _b)| a) + .collect(), + ); + } else { + common_ws = Some(ws.collect()) + } + } + common_ws.unwrap_or_else(String::new) +} + +fn calculate_shift(lines: &[String], shift: Shift) -> ExplicitShift { match shift { - Shift::None => Cow::Borrowed(l), - Shift::Right(shift) => { + Shift::None => ExplicitShift::None, + Shift::Left(l) => ExplicitShift::Left(l), + Shift::Right(r) => ExplicitShift::Right(r), + Shift::Auto => ExplicitShift::Left(common_leading_ws(lines).len()), + } +} + +fn shift_line(l: &str, shift: ExplicitShift) -> Cow<'_, str> { + match shift { + ExplicitShift::None => Cow::Borrowed(l), + ExplicitShift::Right(shift) => { let indent = " ".repeat(shift); Cow::Owned(format!("{indent}{l}")) } - Shift::Left(skip) => { + ExplicitShift::Left(skip) => { if l.chars().take(skip).any(|c| !c.is_whitespace()) { log::error!("left-shifting away non-whitespace"); } @@ -30,6 +72,7 @@ fn shift_line(l: &str, shift: Shift) -> Cow<'_, str> { } fn shift_lines(lines: &[String], shift: Shift) -> Vec> { + let shift = calculate_shift(lines, shift); lines.iter().map(|l| shift_line(l, shift)).collect() } @@ -160,20 +203,44 @@ pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String { #[cfg(test)] mod tests { use super::{ - 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, + common_leading_ws, 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, ExplicitShift, Shift, }; + #[test] + fn common_leading_ws_test() { + let tests = [ + ([" line1", " line2", " line3"], " "), + ([" line1", " line2", "line3"], ""), + (["\t\tline1", "\t\t line2", "\t\tline3"], "\t\t"), + (["\t line1", " \tline2", " \t\tline3"], ""), + ]; + for (lines, want) in tests { + let lines = lines.into_iter().map(|l| l.to_string()).collect::>(); + let got = common_leading_ws(&lines); + assert_eq!(got, want, "for input {lines:?}"); + } + } + #[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, ExplicitShift::None), s); + assert_eq!( + shift_line(s, ExplicitShift::Left(4)), + "Line with 4 space intro" + ); + assert_eq!( + shift_line(s, ExplicitShift::Left(2)), + " Line with 4 space intro" + ); + assert_eq!( + shift_line(s, ExplicitShift::Left(6)), + "ne with 4 space intro" + ); assert_eq!( - shift_line(s, Shift::Right(2)), + shift_line(s, ExplicitShift::Right(2)), " Line with 4 space intro" ); } @@ -207,6 +274,10 @@ mod tests { take_lines_with_shift(s, 1..3, Shift::Right(2)), " ipsum\n dolor" ); + assert_eq!( + take_lines_with_shift(s, 1..3, Shift::Auto), + "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)), @@ -217,6 +288,10 @@ mod tests { take_lines_with_shift(s, ..3, Shift::None), " Lorem\n ipsum\n dolor" ); + assert_eq!( + take_lines_with_shift(s, ..3, Shift::Auto), + "Lorem\nipsum\n dolor" + ); assert_eq!( take_lines_with_shift(s, ..3, Shift::Right(4)), " Lorem\n ipsum\n dolor" @@ -226,6 +301,10 @@ mod tests { "rem\nsum\ndolor" ); assert_eq!(take_lines_with_shift(s, .., Shift::None), s); + assert_eq!( + take_lines_with_shift(s, .., Shift::Auto), + "Lorem\nipsum\n dolor\nsit\namet" + ); // 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)), ""); @@ -307,6 +386,10 @@ mod tests { take_anchored_lines_with_shift(s, "test", Shift::Left(2)), "dolor\nsit\namet" ); + assert_eq!( + take_anchored_lines_with_shift(s, "test", Shift::Auto), + "dolor\nsit\namet" + ); assert_eq!( take_anchored_lines_with_shift(s, "something", Shift::None), "" @@ -333,6 +416,10 @@ mod tests { take_anchored_lines_with_shift(s, "test", Shift::Left(2)), "dolor\nsit\namet" ); + assert_eq!( + take_anchored_lines_with_shift(s, "test", Shift::Auto), + "dolor\nsit\namet" + ); assert_eq!( take_anchored_lines_with_shift(s, "test", Shift::Left(4)), "lor\nt\net" @@ -346,18 +433,22 @@ mod tests { "" ); - 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"; + let s = " Lorem\n ANCHOR: test\n ipsum\n ANCHOR: test\n dolor\n\n\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" + " ipsum\n dolor\n\n\n sit\n amet" ); assert_eq!( take_anchored_lines_with_shift(s, "test", Shift::Right(2)), - " ipsum\n dolor\n sit\n amet" + " ipsum\n dolor\n \n \n sit\n amet" ); assert_eq!( take_anchored_lines_with_shift(s, "test", Shift::Left(2)), - "ipsum\ndolor\nsit\namet" + "ipsum\ndolor\n\n\nsit\namet" + ); + assert_eq!( + take_anchored_lines_with_shift(s, "test", Shift::Auto), + "ipsum\ndolor\n\n\nsit\namet" ); assert_eq!( take_anchored_lines_with_shift(s, "something", Shift::None), @@ -371,6 +462,10 @@ mod tests { take_anchored_lines_with_shift(s, "something", Shift::Left(2)), "" ); + assert_eq!( + take_anchored_lines_with_shift(s, "something", Shift::Auto), + "" + ); // 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"; diff --git a/tests/dummy_book/src/first/nested.md b/tests/dummy_book/src/first/nested.md index 1591a734fb..8e581e00d6 100644 --- a/tests/dummy_book/src/first/nested.md +++ b/tests/dummy_book/src/first/nested.md @@ -24,6 +24,10 @@ assert!($TEST_STATUS); {{#shiftinclude +2:nested-test-with-anchors.rs:myanchor}} ``` +```rust +{{#shiftinclude auto:nested-test-with-anchors.rs:indentedanchor}} +``` + ## Rustdoc include adds the rest of the file as hidden ```rust diff --git a/tests/searchindex_fixture.json b/tests/searchindex_fixture.json index 3713240ec8..beb6f43e84 100644 --- a/tests/searchindex_fixture.json +++ b/tests/searchindex_fixture.json @@ -176,7 +176,7 @@ "title": 7 }, "7": { - "body": 21, + "body": 27, "breadcrumbs": 6, "title": 2 }, @@ -361,7 +361,7 @@ "title": "Anchors include the part of a file between special comments" }, "7": { - "body": "// 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!(true);", + "body": "// 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!(true); pub fn indented_function() { // This extra indent remains\n}", "breadcrumbs": "First Chapter » Nested Chapter » Includes can be shifted", "id": "7", "title": "Includes can be shifted" @@ -1347,6 +1347,22 @@ }, "df": 0, "docs": {} + }, + "t": { + "df": 0, + "docs": {}, + "r": { + "a": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + }, + "df": 0, + "docs": {} + } } } }, @@ -1456,11 +1472,14 @@ } }, "n": { - "df": 3, + "df": 4, "docs": { "27": { "tf": 1.0 }, + "7": { + "tf": 1.0 + }, "8": { "tf": 1.4142135623730951 }, @@ -1778,6 +1797,54 @@ "e": { "df": 0, "docs": {}, + "n": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + }, + "e": { + "d": { + "_": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "u": { + "df": 0, + "docs": {}, + "n": { + "c": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + } + }, + "df": 0, + "docs": {} + } + } + } + }, + "df": 0, + "docs": {} + }, + "df": 0, + "docs": {} + } + } + }, "x": { "df": 2, "docs": { @@ -2444,6 +2511,14 @@ } }, "u": { + "b": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + }, "df": 0, "docs": {}, "t": { @@ -2514,6 +2589,26 @@ } } }, + "m": { + "a": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "n": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + } + } + }, + "df": 0, + "docs": {} + }, "n": { "d": { "df": 0, @@ -4461,6 +4556,22 @@ }, "df": 0, "docs": {} + }, + "t": { + "df": 0, + "docs": {}, + "r": { + "a": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + }, + "df": 0, + "docs": {} + } } } }, @@ -4633,11 +4744,14 @@ } }, "n": { - "df": 3, + "df": 4, "docs": { "27": { "tf": 1.0 }, + "7": { + "tf": 1.0 + }, "8": { "tf": 1.4142135623730951 }, @@ -4958,6 +5072,54 @@ "e": { "df": 0, "docs": {}, + "n": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + }, + "e": { + "d": { + "_": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "u": { + "df": 0, + "docs": {}, + "n": { + "c": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + } + }, + "df": 0, + "docs": {} + } + } + } + }, + "df": 0, + "docs": {} + }, + "df": 0, + "docs": {} + } + } + }, "x": { "df": 2, "docs": { @@ -5657,6 +5819,14 @@ } }, "u": { + "b": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + }, "df": 0, "docs": {}, "t": { @@ -5730,6 +5900,26 @@ } } }, + "m": { + "a": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "n": { + "df": 1, + "docs": { + "7": { + "tf": 1.0 + } + } + } + } + }, + "df": 0, + "docs": {} + }, "n": { "d": { "df": 0, From af77b41d7f3ec4705569d1089af314578f2e2e34 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Sun, 23 Jun 2024 16:03:55 +0100 Subject: [PATCH 3/4] Don't remove non-whitespace on left shift --- guide/src/format/mdbook.md | 3 ++- src/utils/string.rs | 29 ++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/guide/src/format/mdbook.md b/guide/src/format/mdbook.md index 667700c24f..d1fe11c820 100644 --- a/guide/src/format/mdbook.md +++ b/guide/src/format/mdbook.md @@ -221,7 +221,8 @@ using the following syntax: ``` 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. +the corresponding number of whitespace characters from the beginning of each line (leaving +intact any non-whitespace). The special `auto` value will remove common initial whitespace from all lines. diff --git a/src/utils/string.rs b/src/utils/string.rs index bbb80f514d..773c8b579c 100644 --- a/src/utils/string.rs +++ b/src/utils/string.rs @@ -14,10 +14,17 @@ pub enum Shift { Auto, } +/// Indication of how much to shift included text. #[derive(PartialEq, Eq, Debug, Clone, Copy)] enum ExplicitShift { + /// Don't shift. None, + /// Shift left by removing the given number of leading whitespace chars. + /// + /// Does not remove leading non-whitespace chars, i.e. lines with fewer leading whitespace + /// chars get a smaller shift. Left(usize), + /// Shift right by the given amount, inserting spaces on the left. Right(usize), } @@ -62,10 +69,14 @@ fn shift_line(l: &str, shift: ExplicitShift) -> Cow<'_, str> { Cow::Owned(format!("{indent}{l}")) } ExplicitShift::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::(); + let mut count = 0; + let rest = l + .chars() + .skip_while(|c| { + count += 1; + c.is_whitespace() && count <= skip + }) + .collect::(); Cow::Owned(rest) } } @@ -237,7 +248,7 @@ mod tests { ); assert_eq!( shift_line(s, ExplicitShift::Left(6)), - "ne with 4 space intro" + "Line with 4 space intro" ); assert_eq!( shift_line(s, ExplicitShift::Right(2)), @@ -298,7 +309,7 @@ mod tests { ); assert_eq!( take_lines_with_shift(s, ..3, Shift::Left(4)), - "rem\nsum\ndolor" + "Lorem\nipsum\ndolor" ); assert_eq!(take_lines_with_shift(s, .., Shift::None), s); assert_eq!( @@ -422,11 +433,11 @@ mod tests { ); assert_eq!( take_anchored_lines_with_shift(s, "test", Shift::Left(4)), - "lor\nt\net" + "dolor\nsit\namet" ); assert_eq!( take_anchored_lines_with_shift(s, "test", Shift::Left(44)), - "\n\n" + "dolor\nsit\namet" ); assert_eq!( take_anchored_lines_with_shift(s, "something", Shift::None), @@ -483,7 +494,7 @@ mod tests { ); assert_eq!( take_anchored_lines_with_shift(s, "test2", Shift::Left(4)), - "sum\nlor\nt\net\nrem" + "ípsum\ndôlor\nsit\namet\nlorem" ); assert_eq!( take_anchored_lines_with_shift(s, "test", Shift::None), From 8e6c317cc5dabf229f355aa377c11de5f44d16b8 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Sun, 23 Jun 2024 16:25:01 +0100 Subject: [PATCH 4/4] Skip invalid shiftinclude Rather than defaulting to a shift of 0, instead just skip over a shiftinclude command with an invalid shift specifier. --- src/preprocess/links.rs | 47 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 0497f86890..493b3740bd 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -263,16 +263,19 @@ fn parse_include_path(path: &str) -> LinkType<'static> { LinkType::Include(path, range_or_anchor, Shift::None) } -fn parse_shift_include_path(params: &str) -> LinkType<'static> { +fn parse_shift_include_path(params: &str) -> Option> { let mut params = params.splitn(2, ':'); let param0 = params.next().unwrap(); let shift = if param0 == "auto" { Shift::Auto } else { - let shift: isize = param0.parse().unwrap_or_else(|e| { - log::error!("failed to parse shift amount: {e:?}"); - 0 - }); + let shift: isize = match param0.parse() { + Ok(v) => v, + Err(e) => { + log::error!("failed to parse shift amount: {e:?}"); + return None; + } + }; match shift.cmp(&0) { Ordering::Greater => Shift::Right(shift as usize), Ordering::Equal => Shift::None, @@ -284,7 +287,7 @@ fn parse_shift_include_path(params: &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, shift) + Some(LinkType::Include(path, range_or_anchor, shift)) } fn parse_rustdoc_include_path(path: &str) -> LinkType<'static> { @@ -316,7 +319,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)), + ("shiftinclude", Some(pth)) => 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)) => { @@ -998,11 +1001,11 @@ mod tests { let link_type = parse_shift_include_path("17:arbitrary:some-anchor"); assert_eq!( link_type, - LinkType::Include( + Some(LinkType::Include( PathBuf::from("arbitrary"), RangeOrAnchor::Anchor("some-anchor".to_string()), Shift::Right(17) - ) + )) ); } @@ -1011,11 +1014,11 @@ mod tests { let link_type = parse_shift_include_path("auto:arbitrary:some-anchor"); assert_eq!( link_type, - LinkType::Include( + Some(LinkType::Include( PathBuf::from("arbitrary"), RangeOrAnchor::Anchor("some-anchor".to_string()), Shift::Auto - ) + )) ); } @@ -1037,11 +1040,11 @@ mod tests { let link_type = parse_shift_include_path("-2:arbitrary:5:10"); assert_eq!( link_type, - LinkType::Include( + Some(LinkType::Include( PathBuf::from("arbitrary"), RangeOrAnchor::Range(LineRange::from(4..10)), Shift::Left(2) - ) + )) ); } @@ -1050,11 +1053,11 @@ mod tests { let link_type = parse_shift_include_path("2:arbitrary:5:10"); assert_eq!( link_type, - LinkType::Include( + Some(LinkType::Include( PathBuf::from("arbitrary"), RangeOrAnchor::Range(LineRange::from(4..10)), Shift::Right(2) - ) + )) ); } @@ -1063,11 +1066,11 @@ mod tests { let link_type = parse_shift_include_path("+2:arbitrary:5:10"); assert_eq!( link_type, - LinkType::Include( + Some(LinkType::Include( PathBuf::from("arbitrary"), RangeOrAnchor::Range(LineRange::from(4..10)), Shift::Right(2) - ) + )) ); } @@ -1076,11 +1079,17 @@ mod tests { let link_type = parse_shift_include_path("auto:arbitrary:5:10"); assert_eq!( link_type, - LinkType::Include( + Some(LinkType::Include( PathBuf::from("arbitrary"), RangeOrAnchor::Range(LineRange::from(4..10)), Shift::Auto - ) + )) ); } + + #[test] + fn parse_invalid_shifted_anchor() { + let link_type = parse_shift_include_path("bogus:arbitrary:5:10"); + assert_eq!(link_type, None); + } }