mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Add support for list nesting in AsciiDoc-to-Markdown conversion
Support for following list item types are also added: - `-`-prefixed unordered list items - `.`-prefixed ordered list items
This commit is contained in:
parent
240fc255ef
commit
0eb537fa31
@ -83,38 +83,42 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_list(&mut self) -> anyhow::Result<()> {
|
fn process_list(&mut self) -> anyhow::Result<()> {
|
||||||
|
let mut nesting = ListNesting::new();
|
||||||
while let Some(line) = self.iter.next() {
|
while let Some(line) = self.iter.next() {
|
||||||
let line = line?;
|
let line = line?;
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(item) = get_list_item(&line) {
|
if let Some((marker, item)) = get_list_item(&line) {
|
||||||
self.write_list_item(item);
|
nesting.set_current(marker);
|
||||||
|
self.write_list_item(item, &nesting);
|
||||||
} else if line == "+" {
|
} else if line == "+" {
|
||||||
let line = self
|
let line = self
|
||||||
.iter
|
.iter
|
||||||
.peek()
|
.peek()
|
||||||
.ok_or_else(|| anyhow!("list continuation unexpectedly terminated"))?;
|
.ok_or_else(|| anyhow!("list continuation unexpectedly terminated"))?;
|
||||||
let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
|
let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
|
||||||
|
|
||||||
|
let indent = nesting.indent();
|
||||||
if line.starts_with('[') {
|
if line.starts_with('[') {
|
||||||
self.write_line("", 0);
|
self.write_line("", 0);
|
||||||
self.process_source_code_block(1)?;
|
self.process_source_code_block(indent)?;
|
||||||
} else if line.starts_with(LISTING_DELIMITER) {
|
} else if line.starts_with(LISTING_DELIMITER) {
|
||||||
self.write_line("", 0);
|
self.write_line("", 0);
|
||||||
self.process_listing_block(None, 1)?;
|
self.process_listing_block(None, indent)?;
|
||||||
} else if line.starts_with('.') {
|
} else if line.starts_with('.') {
|
||||||
self.write_line("", 0);
|
self.write_line("", 0);
|
||||||
self.process_block_with_title(1)?;
|
self.process_block_with_title(indent)?;
|
||||||
} else if line.starts_with(IMAGE_BLOCK_PREFIX) {
|
} else if line.starts_with(IMAGE_BLOCK_PREFIX) {
|
||||||
self.write_line("", 0);
|
self.write_line("", 0);
|
||||||
self.process_image_block(None, 1)?;
|
self.process_image_block(None, indent)?;
|
||||||
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
|
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
|
||||||
self.write_line("", 0);
|
self.write_line("", 0);
|
||||||
self.process_video_block(None, 1)?;
|
self.process_video_block(None, indent)?;
|
||||||
} else {
|
} else {
|
||||||
self.write_line("", 0);
|
self.write_line("", 0);
|
||||||
self.process_paragraph(1)?;
|
self.process_paragraph(indent)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bail!("not a list block")
|
bail!("not a list block")
|
||||||
@ -263,8 +267,8 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_title(&mut self, level: usize, title: &str) {
|
fn write_title(&mut self, indent: usize, title: &str) {
|
||||||
for _ in 0..level {
|
for _ in 0..indent {
|
||||||
self.output.push('#');
|
self.output.push('#');
|
||||||
}
|
}
|
||||||
self.output.push(' ');
|
self.output.push(' ');
|
||||||
@ -272,27 +276,29 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
|
|||||||
self.output.push('\n');
|
self.output.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_list_item(&mut self, item: &str) {
|
fn write_list_item(&mut self, item: &str, nesting: &ListNesting) {
|
||||||
self.output.push_str("- ");
|
let (marker, indent) = nesting.marker();
|
||||||
|
self.write_indent(indent);
|
||||||
|
self.output.push_str(marker);
|
||||||
self.output.push_str(item);
|
self.output.push_str(item);
|
||||||
self.output.push('\n');
|
self.output.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_caption_line(&mut self, caption: &str, level: usize) {
|
fn write_caption_line(&mut self, caption: &str, indent: usize) {
|
||||||
self.write_indent(level);
|
self.write_indent(indent);
|
||||||
self.output.push('_');
|
self.output.push('_');
|
||||||
self.output.push_str(caption);
|
self.output.push_str(caption);
|
||||||
self.output.push_str("_\\\n");
|
self.output.push_str("_\\\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_indent(&mut self, level: usize) {
|
fn write_indent(&mut self, indent: usize) {
|
||||||
for _ in 0..level {
|
for _ in 0..indent {
|
||||||
self.output.push_str(" ");
|
self.output.push(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_line(&mut self, line: &str, level: usize) {
|
fn write_line(&mut self, line: &str, indent: usize) {
|
||||||
self.write_indent(level);
|
self.write_indent(indent);
|
||||||
self.output.push_str(line);
|
self.output.push_str(line);
|
||||||
self.output.push('\n');
|
self.output.push('\n');
|
||||||
}
|
}
|
||||||
@ -312,15 +318,31 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_title(line: &str) -> Option<(usize, &str)> {
|
fn get_title(line: &str) -> Option<(usize, &str)> {
|
||||||
const MARKER: char = '=';
|
strip_prefix_symbol(line, '=')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_list_item(line: &str) -> Option<(ListMarker, &str)> {
|
||||||
|
const HYPHYEN_MARKER: &'static str = "- ";
|
||||||
|
if let Some(text) = line.strip_prefix(HYPHYEN_MARKER) {
|
||||||
|
Some((ListMarker::Hyphen, text))
|
||||||
|
} else if let Some((count, text)) = strip_prefix_symbol(line, '*') {
|
||||||
|
Some((ListMarker::Asterisk(count), text))
|
||||||
|
} else if let Some((count, text)) = strip_prefix_symbol(line, '.') {
|
||||||
|
Some((ListMarker::Dot(count), text))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip_prefix_symbol(line: &str, symbol: char) -> Option<(usize, &str)> {
|
||||||
let mut iter = line.chars();
|
let mut iter = line.chars();
|
||||||
if iter.next()? != MARKER {
|
if iter.next()? != symbol {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut count = 1;
|
let mut count = 1;
|
||||||
loop {
|
loop {
|
||||||
match iter.next() {
|
match iter.next() {
|
||||||
Some(MARKER) => {
|
Some(ch) if ch == symbol => {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
Some(' ') => {
|
Some(' ') => {
|
||||||
@ -332,16 +354,6 @@ fn get_title(line: &str) -> Option<(usize, &str)> {
|
|||||||
Some((count, iter.as_str()))
|
Some((count, iter.as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_list_item(line: &str) -> Option<&str> {
|
|
||||||
const MARKER: &'static str = "* ";
|
|
||||||
if line.starts_with(MARKER) {
|
|
||||||
let item = &line[MARKER.len()..];
|
|
||||||
Some(item)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_media_block<'a>(line: &'a str, prefix: &str) -> Option<(&'a str, &'a 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(line) = line.strip_prefix(prefix) {
|
||||||
if let Some((url, rest)) = line.split_once('[') {
|
if let Some((url, rest)) = line.split_once('[') {
|
||||||
@ -353,6 +365,55 @@ fn parse_media_block<'a>(line: &'a str, prefix: &str) -> Option<(&'a str, &'a st
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ListNesting(Vec<ListMarker>);
|
||||||
|
|
||||||
|
impl ListNesting {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self(Vec::<ListMarker>::with_capacity(6))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_current(&mut self, marker: ListMarker) {
|
||||||
|
let Self(markers) = self;
|
||||||
|
if let Some(index) = markers.iter().position(|m| *m == marker) {
|
||||||
|
markers.truncate(index + 1);
|
||||||
|
} else {
|
||||||
|
markers.push(marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indent(&self) -> usize {
|
||||||
|
self.0.iter().map(|m| m.in_markdown().len()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn marker(&self) -> (&str, usize) {
|
||||||
|
let Self(markers) = self;
|
||||||
|
let indent = markers.iter().take(markers.len() - 1).map(|m| m.in_markdown().len()).sum();
|
||||||
|
let marker = match markers.last() {
|
||||||
|
None => "",
|
||||||
|
Some(marker) => marker.in_markdown(),
|
||||||
|
};
|
||||||
|
(marker, indent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum ListMarker {
|
||||||
|
Asterisk(usize),
|
||||||
|
Hyphen,
|
||||||
|
Dot(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListMarker {
|
||||||
|
fn in_markdown(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
ListMarker::Asterisk(_) => "- ",
|
||||||
|
ListMarker::Hyphen => "- ",
|
||||||
|
ListMarker::Dot(_) => "1. ",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -372,7 +433,19 @@ Release: release:2022-01-01[]
|
|||||||
== New Features
|
== New Features
|
||||||
|
|
||||||
* pr:1111[] foo bar baz
|
* pr:1111[] foo bar baz
|
||||||
* pr:2222[] foo bar baz
|
- hyphen-prefixed list item
|
||||||
|
* nested list item
|
||||||
|
** `foo` -> `foofoo`
|
||||||
|
** `bar` -> `barbar`
|
||||||
|
* listing in the secondary level
|
||||||
|
. install
|
||||||
|
. add to config
|
||||||
|
+
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{\"foo\":\"bar\"}
|
||||||
|
----
|
||||||
|
* list item with continuation
|
||||||
+
|
+
|
||||||
image::https://example.com/animation.gif[]
|
image::https://example.com/animation.gif[]
|
||||||
+
|
+
|
||||||
@ -400,15 +473,10 @@ This is a plain listing.
|
|||||||
paragraph
|
paragraph
|
||||||
paragraph
|
paragraph
|
||||||
|
|
||||||
== Fixes
|
== Another Section
|
||||||
|
|
||||||
* pr:3333[] foo bar baz
|
* foo bar baz
|
||||||
* pr:4444[] foo bar baz
|
* foo bar baz
|
||||||
|
|
||||||
== Internal Improvements
|
|
||||||
|
|
||||||
* pr:5555[] foo bar baz
|
|
||||||
* pr:6666[] foo bar baz
|
|
||||||
|
|
||||||
The highlight of the month is probably pr:1111[].
|
The highlight of the month is probably pr:1111[].
|
||||||
|
|
||||||
@ -437,7 +505,18 @@ Release: release:2022-01-01[]
|
|||||||
## New Features
|
## New Features
|
||||||
|
|
||||||
- pr:1111[] foo bar baz
|
- pr:1111[] foo bar baz
|
||||||
- pr:2222[] foo bar baz
|
- hyphen-prefixed list item
|
||||||
|
- nested list item
|
||||||
|
- `foo` -> `foofoo`
|
||||||
|
- `bar` -> `barbar`
|
||||||
|
- listing in the secondary level
|
||||||
|
1. install
|
||||||
|
1. add to config
|
||||||
|
|
||||||
|
```json
|
||||||
|
{\"foo\":\"bar\"}
|
||||||
|
```
|
||||||
|
- list item with continuation
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -464,15 +543,10 @@ Release: release:2022-01-01[]
|
|||||||
paragraph
|
paragraph
|
||||||
paragraph
|
paragraph
|
||||||
|
|
||||||
## Fixes
|
## Another Section
|
||||||
|
|
||||||
- pr:3333[] foo bar baz
|
- foo bar baz
|
||||||
- pr:4444[] foo bar baz
|
- foo bar baz
|
||||||
|
|
||||||
## Internal Improvements
|
|
||||||
|
|
||||||
- pr:5555[] foo bar baz
|
|
||||||
- pr:6666[] foo bar baz
|
|
||||||
|
|
||||||
The highlight of the month is probably pr:1111[].
|
The highlight of the month is probably pr:1111[].
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user