From 2bf638f9d00184f753b10cd56df4993e5abb59dd Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Thu, 4 Apr 2024 07:52:02 +0100 Subject: [PATCH] Support escaped command Fixes #16 --- src/main.rs | 52 +++++++++++++++++++++++++++++++++++- testbook/expected_index.html | 1 + testbook/src/chapter1.md | 3 +-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index cc3c452..029a2a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,12 +109,17 @@ const HIDDEN: &str = "hi"; /// Command for a visible index entry, italicized. const ITALIC: &str = "ii"; +/// Escape character. +const ESCAPE_CHAR: char = '\\'; + lazy_static! { /// Regular expression to match indexing commands. static ref INDEX_RE: Regex = Regex::new( r"(?x) # insignificant whitespace mode (?s) # dot matches newline + \\\{\{[^}]*\}\} # match escaped link + | # or \{\{ # opening braces (?P[hi]?i) # visibility command (i, hi, ii) : # separator @@ -150,7 +155,7 @@ pub struct Index { see_instead: HashMap, /// Index entries that should appear in the index as sub-entries underneath the specified top-level entry. nest_under: HashMap, - /// Whether to skip a ", " prefix in sub-entries where the prefix matches the top-level entry. + /// Whether to skip a "head, " prefix in sub-entries where the prefix matches the top-level entry. suppress_head: bool, /// Emit chapter names as the link text in the generated index. use_chapter_names: bool, @@ -250,6 +255,11 @@ impl Index { let mut entries = self.entries.borrow_mut(); INDEX_RE .replace_all(content, |caps: ®ex::Captures| { + if let Some(mat) = caps.get(0) { + if mat.as_str().starts_with(ESCAPE_CHAR) { + return mat.as_str()[1..].to_owned(); + } + } // Retrieve the content of the markup. For a visible index entry, this is // rendered in the output. let viz = caps.name("viz").unwrap().as_str(); @@ -573,6 +583,7 @@ mod tests { VISIBLE, "trailing space ", ), + ("blah {{i:normal}} blah \\{{i:escaped}}", VISIBLE, "normal"), ]; for (input, want_viz, want_content) in tests { let got = INDEX_RE.captures_iter(input).next().unwrap(); @@ -582,4 +593,43 @@ mod tests { assert_eq!(got_content, want_content, "for input '{input}'"); } } + + #[test] + fn test_escaped_matches() { + let tests = [ + "blah \\{{i:simple}} blah", + "blah\\{{i:simple}}blah", + "blah \\{{hi:simple}} blah", + "blah \\{{ii:simple}} blah", + "blah \\{{i:`code`}} blah", + "blah \\{{i:interior space}} blah", + "blah \\{{i:interior\nnewline}} blah", + "blah \\{{i: leading space}} blah", + "blah \\{{i:trailing space }} blah", + ]; + for input in tests { + let got = INDEX_RE.captures_iter(input).next().unwrap(); + assert!( + got.get(0).unwrap().as_str().starts_with(ESCAPE_CHAR), + "got {:?} for input '{}'", + got, + input + ); + assert!(got.name("viz").is_none(), "for input '{}'", input); + assert!(got.name("content").is_none(), "for input '{}'", input); + } + } + + #[test] + fn test_escaped_and_unescaped() { + let input = "blah \\{{i:escaped}} blah {{i:second}}"; + let mut iter = INDEX_RE.captures_iter(input); + let got1 = iter.next().unwrap(); + assert!(got1.get(0).unwrap().as_str().starts_with(ESCAPE_CHAR),); + let got2 = iter.next().unwrap(); + let got2_viz = got2.name("viz").unwrap().as_str(); + assert_eq!(got2_viz, VISIBLE); + let got2_content = got2.name("content").unwrap().as_str(); + assert_eq!(got2_content, "second"); + } } diff --git a/testbook/expected_index.html b/testbook/expected_index.html index f642ebd..fe5bc79 100644 --- a/testbook/expected_index.html +++ b/testbook/expected_index.html @@ -176,6 +176,7 @@

Test Book for mdbook-indexing

Index

*, 1
_, 1
+backslash, 1
Borrow, 1
borrow, 1
code, 1
diff --git a/testbook/src/chapter1.md b/testbook/src/chapter1.md index f0e650f..78e2717 100644 --- a/testbook/src/chapter1.md +++ b/testbook/src/chapter1.md @@ -20,5 +20,4 @@ Different sorts of {{i:test}}: - {{hi:test, fuzz}} fuzz test - {{hi: test, doc}} doc test -To use the markup {{ii:as is}}, put in an HTML entity reference instead of the markup (e.g. `{` instead -of `{`). +To use the markup \{{ii:as is}}, put a {{i:backslash}} before the markup.