Skip to content

Commit

Permalink
infra: support non-numbered and no-caption listings
Browse files Browse the repository at this point in the history
When I originally built this, I thought *all* “listings” had numbers and
captions, but it turns out that there are a number of places in the book
where having the overall `figure`-driven output, i.e. with a file name,
is desirable even though there is no number or caption.

A potential enhancement later would be to require a caption if a number
is present, since that seems to be what the book actually does.
  • Loading branch information
chriskrycho committed Jul 15, 2024
1 parent 28230e6 commit 4d35744
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 56 deletions.
108 changes: 52 additions & 56 deletions packages/mdbook-trpl-listing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn rewrite_listing(src: &str, mode: Mode) -> Result<String, String> {
match ev {
Event::Html(tag) => {
if tag.starts_with("<Listing") {
let listing_result = Tokenizer::from(tag.as_ref())
let listing = Tokenizer::from(tag.as_ref())
.flatten()
.fold(ListingBuilder::new(), |builder, token| {
match token {
Expand All @@ -177,26 +177,19 @@ fn rewrite_listing(src: &str, mode: Mode) -> Result<String, String> {
})
.build();

match listing_result {
Ok(listing) => {
let opening_event = match mode {
Mode::Default => {
let opening_html =
listing.opening_html();
Event::Html(opening_html.into())
}
Mode::Simple => {
let opening_text =
listing.opening_text();
Event::Text(opening_text.into())
}
};

state.current_listing = Some(listing);
state.events.push(Ok(opening_event));
let opening_event = match mode {
Mode::Default => {
let opening_html = listing.opening_html();
Event::Html(opening_html.into())
}
Err(reason) => state.events.push(Err(reason)),
}
Mode::Simple => {
let opening_text = listing.opening_text();
Event::Text(opening_text.into())
}
};

state.current_listing = Some(listing);
state.events.push(Ok(opening_event));
} else if tag.starts_with("</Listing>") {
let trailing = if !tag.ends_with('>') {
tag.replace("</Listing>", "")
Expand Down Expand Up @@ -259,8 +252,8 @@ fn rewrite_listing(src: &str, mode: Mode) -> Result<String, String> {

#[derive(Debug)]
struct Listing {
number: String,
caption: String,
number: Option<String>,
caption: Option<String>,
file_name: Option<String>,
}

Expand All @@ -277,12 +270,21 @@ impl Listing {
}

fn closing_html(&self, trailing: &str) -> String {
format!(
r#"<figcaption>Listing {number}: {caption}</figcaption>
</figure>{trailing}"#,
number = self.number,
caption = self.caption
)
match (&self.number, &self.caption) {
(Some(number), Some(caption)) => format!(
r#"<figcaption>Listing {number}: {caption}</figcaption>
</figure>{trailing}"#
),
(None, Some(caption)) => format!(
r#"<figcaption>{caption}</figcaption>
</figure>{trailing}"#
),
(Some(number), None) => format!(
r#"<figcaption>Listing {number}</figcaption>
</figure>{trailing}"#
),
(None, None) => format!("</figure>{trailing}"),
}
}

fn opening_text(&self) -> String {
Expand All @@ -293,11 +295,14 @@ impl Listing {
}

fn closing_text(&self, trailing: &str) -> String {
format!(
"Listing {number}: {caption}{trailing}",
number = self.number,
caption = self.caption,
)
match (&self.number, &self.caption) {
(Some(number), Some(caption)) => {
format!("Listing {number}: {caption}{trailing}")
}
(None, Some(caption)) => format!("{caption}{trailing}"),
(Some(number), None) => format!("Listing {number}{trailing}"),
(None, None) => trailing.into(),
}
}
}

Expand Down Expand Up @@ -331,32 +336,23 @@ impl<'a> ListingBuilder<'a> {
self
}

fn build(self) -> Result<Listing, String> {
let number = self
.number
.ok_or_else(|| String::from("Missing number"))?
.to_owned();

let caption = self
.caption
.map(|caption_source| {
let events = new_cmark_parser(caption_source, true);
let mut buf = String::with_capacity(caption_source.len() * 2);
html::push_html(&mut buf, events);

// This is not particularly principled, but since the only
// place it is used is here, for caption source handling, it
// is “fine”.
buf.replace("<p>", "").replace("</p>", "").replace('\n', "")
})
.ok_or_else(|| String::from("Missing caption"))?
.to_owned();
fn build(self) -> Listing {
let caption = self.caption.map(|caption_source| {
let events = new_cmark_parser(caption_source, true);
let mut buf = String::with_capacity(caption_source.len() * 2);
html::push_html(&mut buf, events);

Ok(Listing {
number,
// This is not particularly principled, but since the only
// place it is used is here, for caption source handling, it
// is “fine”.
buf.replace("<p>", "").replace("</p>", "").replace('\n', "")
});

Listing {
number: self.number.map(String::from),
caption,
file_name: self.file_name.map(String::from),
})
}
}
}

Expand Down
27 changes: 27 additions & 0 deletions packages/mdbook-trpl-listing/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,32 @@ This is the closing."#
);
}

#[test]
fn without_number() {
let result = rewrite_listing(
r#"<Listing file-name="src/main.rs">
```rust
fn main() {}
```
</Listing>"#,
Mode::Default,
);

assert!(result.is_ok());
assert_eq!(
result.unwrap(),
r#"<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
````rust
fn main() {}
````
</figure>"#
);
}

#[cfg(test)]
mod config;

0 comments on commit 4d35744

Please sign in to comment.