mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Add image/video media support in AsciiDoc-to-Markdown conversion
This commit is contained in:
parent
1efcb6ad9a
commit
240fc255ef
1 changed files with 126 additions and 2 deletions
|
@ -5,6 +5,8 @@ use std::{
|
|||
};
|
||||
|
||||
const LISTING_DELIMITER: &'static str = "----";
|
||||
const IMAGE_BLOCK_PREFIX: &'static str = "image::";
|
||||
const VIDEO_BLOCK_PREFIX: &'static str = "video::";
|
||||
|
||||
struct Converter<'a, 'b, R: BufRead> {
|
||||
iter: &'a mut Peekable<Lines<R>>,
|
||||
|
@ -33,6 +35,12 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||
self.process_source_code_block(0)?;
|
||||
} else if line.starts_with(LISTING_DELIMITER) {
|
||||
self.process_listing_block(None, 0)?;
|
||||
} else if line.starts_with('.') {
|
||||
self.process_block_with_title(0)?;
|
||||
} else if line.starts_with(IMAGE_BLOCK_PREFIX) {
|
||||
self.process_image_block(None, 0)?;
|
||||
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
|
||||
self.process_video_block(None, 0)?;
|
||||
} else {
|
||||
self.process_paragraph(0)?;
|
||||
}
|
||||
|
@ -95,6 +103,15 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||
} else if line.starts_with(LISTING_DELIMITER) {
|
||||
self.write_line("", 0);
|
||||
self.process_listing_block(None, 1)?;
|
||||
} else if line.starts_with('.') {
|
||||
self.write_line("", 0);
|
||||
self.process_block_with_title(1)?;
|
||||
} else if line.starts_with(IMAGE_BLOCK_PREFIX) {
|
||||
self.write_line("", 0);
|
||||
self.process_image_block(None, 1)?;
|
||||
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
|
||||
self.write_line("", 0);
|
||||
self.process_video_block(None, 1)?;
|
||||
} else {
|
||||
self.write_line("", 0);
|
||||
self.process_paragraph(1)?;
|
||||
|
@ -145,6 +162,75 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||
bail!("not a listing block")
|
||||
}
|
||||
|
||||
fn process_block_with_title(&mut self, level: usize) -> anyhow::Result<()> {
|
||||
if let Some(Ok(line)) = self.iter.next() {
|
||||
let title =
|
||||
line.strip_prefix('.').ok_or_else(|| anyhow!("extraction of the title failed"))?;
|
||||
|
||||
let line = self
|
||||
.iter
|
||||
.peek()
|
||||
.ok_or_else(|| anyhow!("target block for the title is not found"))?;
|
||||
let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
|
||||
if line.starts_with(IMAGE_BLOCK_PREFIX) {
|
||||
return self.process_image_block(Some(title), level);
|
||||
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
|
||||
return self.process_video_block(Some(title), level);
|
||||
} else {
|
||||
bail!("title for that block type is not supported");
|
||||
}
|
||||
}
|
||||
bail!("not a title")
|
||||
}
|
||||
|
||||
fn process_image_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> {
|
||||
if let Some(Ok(line)) = self.iter.next() {
|
||||
if let Some((url, attrs)) = parse_media_block(&line, IMAGE_BLOCK_PREFIX) {
|
||||
let alt = if let Some(stripped) =
|
||||
attrs.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
|
||||
{
|
||||
stripped
|
||||
} else {
|
||||
attrs
|
||||
};
|
||||
if let Some(caption) = caption {
|
||||
self.write_caption_line(caption, level);
|
||||
}
|
||||
self.write_indent(level);
|
||||
self.output.push_str("![");
|
||||
self.output.push_str(alt);
|
||||
self.output.push_str("](");
|
||||
self.output.push_str(url);
|
||||
self.output.push_str(")\n");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
bail!("not a image block")
|
||||
}
|
||||
|
||||
fn process_video_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> {
|
||||
if let Some(Ok(line)) = self.iter.next() {
|
||||
if let Some((url, attrs)) = parse_media_block(&line, VIDEO_BLOCK_PREFIX) {
|
||||
let html_attrs = match attrs {
|
||||
"options=loop" => "controls loop",
|
||||
r#"options="autoplay,loop""# => "autoplay controls loop",
|
||||
_ => bail!("unsupported video syntax"),
|
||||
};
|
||||
if let Some(caption) = caption {
|
||||
self.write_caption_line(caption, level);
|
||||
}
|
||||
self.write_indent(level);
|
||||
self.output.push_str(r#"<video src=""#);
|
||||
self.output.push_str(url);
|
||||
self.output.push_str(r#"" "#);
|
||||
self.output.push_str(html_attrs);
|
||||
self.output.push_str(">Your browser does not support the video tag.</video>\n");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
bail!("not a video block")
|
||||
}
|
||||
|
||||
fn process_paragraph(&mut self, level: usize) -> anyhow::Result<()> {
|
||||
while let Some(line) = self.iter.peek() {
|
||||
let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
|
||||
|
@ -192,6 +278,13 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||
self.output.push('\n');
|
||||
}
|
||||
|
||||
fn write_caption_line(&mut self, caption: &str, level: usize) {
|
||||
self.write_indent(level);
|
||||
self.output.push('_');
|
||||
self.output.push_str(caption);
|
||||
self.output.push_str("_\\\n");
|
||||
}
|
||||
|
||||
fn write_indent(&mut self, level: usize) {
|
||||
for _ in 0..level {
|
||||
self.output.push_str(" ");
|
||||
|
@ -249,6 +342,17 @@ fn get_list_item(line: &str) -> Option<&str> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_media_block<'a>(line: &'a str, prefix: &str) -> Option<(&'a str, &'a str)> {
|
||||
if let Some(line) = line.strip_prefix(prefix) {
|
||||
if let Some((url, rest)) = line.split_once('[') {
|
||||
if let Some(attrs) = rest.strip_suffix(']') {
|
||||
return Some((url, attrs));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -272,8 +376,18 @@ Release: release:2022-01-01[]
|
|||
+
|
||||
image::https://example.com/animation.gif[]
|
||||
+
|
||||
image::https://example.com/animation.gif[\"alt text\"]
|
||||
+
|
||||
video::https://example.com/movie.mp4[options=loop]
|
||||
+
|
||||
video::https://example.com/movie.mp4[options=\"autoplay,loop\"]
|
||||
+
|
||||
.Image
|
||||
image::https://example.com/animation.gif[]
|
||||
+
|
||||
.Video
|
||||
video::https://example.com/movie.mp4[options=loop]
|
||||
+
|
||||
[source,bash]
|
||||
----
|
||||
rustup update nightly
|
||||
|
@ -325,9 +439,19 @@ Release: release:2022-01-01[]
|
|||
- pr:1111[] foo bar baz
|
||||
- pr:2222[] foo bar baz
|
||||
|
||||
image::https://example.com/animation.gif[]
|
||||
![](https://example.com/animation.gif)
|
||||
|
||||
video::https://example.com/movie.mp4[options=\"autoplay,loop\"]
|
||||
![alt text](https://example.com/animation.gif)
|
||||
|
||||
<video src=\"https://example.com/movie.mp4\" controls loop>Your browser does not support the video tag.</video>
|
||||
|
||||
<video src=\"https://example.com/movie.mp4\" autoplay controls loop>Your browser does not support the video tag.</video>
|
||||
|
||||
_Image_\\
|
||||
![](https://example.com/animation.gif)
|
||||
|
||||
_Video_\\
|
||||
<video src=\"https://example.com/movie.mp4\" controls loop>Your browser does not support the video tag.</video>
|
||||
|
||||
```bash
|
||||
rustup update nightly
|
||||
|
|
Loading…
Reference in a new issue