mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 19:26:09 +00:00
Add markdown parser
This commit is contained in:
parent
15c3e77fc4
commit
a2244f0ac1
@ -34,6 +34,7 @@ toml = "0.4"
|
|||||||
unicode-segmentation = "1.0"
|
unicode-segmentation = "1.0"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
xi-unicode = "0.1.0"
|
xi-unicode = "0.1.0"
|
||||||
|
pulldown-cmark = { version = "0.1.0", optional = true, default-features = false }
|
||||||
enumset = "0.3.3"
|
enumset = "0.3.3"
|
||||||
|
|
||||||
[dependencies.bear-lib-terminal]
|
[dependencies.bear-lib-terminal]
|
||||||
@ -75,6 +76,7 @@ termion-backend = [
|
|||||||
"chan",
|
"chan",
|
||||||
"chan-signal",
|
"chan-signal",
|
||||||
]
|
]
|
||||||
|
markdown = ["pulldown-cmark"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "cursive"
|
name = "cursive"
|
||||||
|
172
src/utils/markup/markdown.rs
Normal file
172
src/utils/markup/markdown.rs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
//! Parse markdown text.
|
||||||
|
//!
|
||||||
|
//! Needs the `markdown` feature to be enabled.
|
||||||
|
|
||||||
|
extern crate pulldown_cmark;
|
||||||
|
|
||||||
|
use self::pulldown_cmark::{Event, Tag};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use theme::{Effect, Style};
|
||||||
|
use utils::lines::spans::Span;
|
||||||
|
|
||||||
|
/// Parse markdown string into spans of styled text.
|
||||||
|
pub struct Parser<'a> {
|
||||||
|
first: bool,
|
||||||
|
stack: Vec<Style>,
|
||||||
|
parser: pulldown_cmark::Parser<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Creates a new parser with the given input text.
|
||||||
|
pub fn new(input: &'a str) -> Self {
|
||||||
|
Parser {
|
||||||
|
first: true,
|
||||||
|
parser: pulldown_cmark::Parser::new(input),
|
||||||
|
stack: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn literal_string<'b>(&self, text: String) -> Span<'b> {
|
||||||
|
Span {
|
||||||
|
text: Cow::Owned(text),
|
||||||
|
style: Style::merge(&self.stack),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn literal<'b>(&self, text: &'b str) -> Span<'b> {
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed(text),
|
||||||
|
style: Style::merge(&self.stack),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header(level: usize) -> &'static str {
|
||||||
|
&"##########"[..level]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Parser<'a> {
|
||||||
|
type Item = Span<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
let next = match self.parser.next() {
|
||||||
|
None => return None,
|
||||||
|
Some(event) => event,
|
||||||
|
};
|
||||||
|
|
||||||
|
match next {
|
||||||
|
Event::Start(tag) => match tag {
|
||||||
|
// Add to the stack!
|
||||||
|
Tag::Emphasis => {
|
||||||
|
self.stack.push(Style::from(Effect::Italic))
|
||||||
|
}
|
||||||
|
Tag::Header(level) => {
|
||||||
|
return Some(self.literal_string(format!(
|
||||||
|
"{} ",
|
||||||
|
header(level as usize)
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Tag::Rule => return Some(self.literal("---")),
|
||||||
|
Tag::BlockQuote => return Some(self.literal("> ")),
|
||||||
|
Tag::Link(_, _) => return Some(self.literal("[")),
|
||||||
|
Tag::Code => return Some(self.literal("```")),
|
||||||
|
Tag::Strong => self.stack.push(Style::from(Effect::Bold)),
|
||||||
|
Tag::Paragraph if !self.first => {
|
||||||
|
return Some(self.literal("\n\n"))
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::End(tag) => match tag {
|
||||||
|
// Remove from stack!
|
||||||
|
Tag::Paragraph if self.first => self.first = false,
|
||||||
|
Tag::Header(_) => return Some(self.literal("\n\n")),
|
||||||
|
Tag::Link(link, _) => {
|
||||||
|
return Some(self.literal_string(format!(
|
||||||
|
"]({})",
|
||||||
|
link
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Tag::Code => return Some(self.literal("```")),
|
||||||
|
Tag::Emphasis | Tag::Strong => {
|
||||||
|
self.stack.pop().unwrap();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::SoftBreak => return Some(self.literal("\n")),
|
||||||
|
Event::HardBreak => return Some(self.literal("\n")),
|
||||||
|
// Treat all text the same
|
||||||
|
Event::FootnoteReference(text)
|
||||||
|
| Event::InlineHtml(text)
|
||||||
|
| Event::Html(text)
|
||||||
|
| Event::Text(text) => {
|
||||||
|
// Return something!
|
||||||
|
return Some(Span {
|
||||||
|
text,
|
||||||
|
style: Style::merge(&self.stack),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the given markdown text into a list of spans.
|
||||||
|
///
|
||||||
|
/// This is a shortcut for `Parser::new(input).collect()`.
|
||||||
|
pub fn parse<'a>(input: &'a str) -> Vec<Span<'a>> {
|
||||||
|
Parser::new(input).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse() {
|
||||||
|
let input = r"
|
||||||
|
Attention
|
||||||
|
====
|
||||||
|
I *really* love __Cursive__!";
|
||||||
|
let spans = parse(input);
|
||||||
|
|
||||||
|
// println!("{:?}", spans);
|
||||||
|
assert_eq!(
|
||||||
|
&spans[..],
|
||||||
|
&[
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("# "),
|
||||||
|
style: Style::none(),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("Attention"),
|
||||||
|
style: Style::none(),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("\n\n"),
|
||||||
|
style: Style::none(),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("I "),
|
||||||
|
style: Style::none(),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("really"),
|
||||||
|
style: Style::from(Effect::Italic),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed(" love "),
|
||||||
|
style: Style::none(),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("Cursive"),
|
||||||
|
style: Style::from(Effect::Bold),
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
text: Cow::Borrowed("!"),
|
||||||
|
style: Style::none(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
6
src/utils/markup/mod.rs
Normal file
6
src/utils/markup/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//! Parse various text markup formats.
|
||||||
|
//!
|
||||||
|
//! Each module is optional and relies on a feature.
|
||||||
|
|
||||||
|
#[cfg(feature = "markdown")]
|
||||||
|
pub mod markdown;
|
@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
mod reader;
|
mod reader;
|
||||||
pub mod lines;
|
pub mod lines;
|
||||||
|
pub mod markup;
|
||||||
|
|
||||||
pub use self::reader::ProgressReader;
|
pub use self::reader::ProgressReader;
|
||||||
|
Loading…
Reference in New Issue
Block a user