feat(elements): multiple lines comment and fixed width area
This commit is contained in:
parent
74a19c2ff7
commit
a288a6d554
|
@ -11,7 +11,7 @@ pub fn parse(src: &str) -> Option<(&str, &str, usize)> {
|
||||||
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let end = memchr::memchr(b'\n', src.as_bytes()).unwrap_or_else(|| src.len());
|
let end = memchr(b'\n', src.as_bytes()).unwrap_or_else(|| src.len());
|
||||||
|
|
||||||
Some((&src[4..label], &src[label + 1..end], end))
|
Some((&src[4..label], &src[label + 1..end], end))
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub fn parse(src: &str) -> Option<(Key<'_>, &str, usize)> {
|
||||||
.unwrap_or_else(|| src.len());
|
.unwrap_or_else(|| src.len());
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
match src[2..key_end].to_uppercase().as_str() {
|
match &*src[2..key_end].to_uppercase() {
|
||||||
"AUTHOR" => Key::Author,
|
"AUTHOR" => Key::Author,
|
||||||
"CALL" => Key::Call,
|
"CALL" => Key::Call,
|
||||||
"DATE" => Key::Date,
|
"DATE" => Key::Date,
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub mod rule;
|
||||||
|
|
||||||
pub use self::keyword::Key;
|
pub use self::keyword::Key;
|
||||||
|
|
||||||
use memchr::{memchr, memchr_iter};
|
use memchr::memchr_iter;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -114,9 +114,11 @@ pub fn parse(src: &str) -> (Element<'_>, usize, Option<(Element<'_>, usize)>) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tail = &src[pos..];
|
||||||
|
|
||||||
// Unlike other element, footnote def must starts at column 0
|
// Unlike other element, footnote def must starts at column 0
|
||||||
if bytes[pos..].starts_with(b"[fn:") {
|
if tail.starts_with("[fn:") {
|
||||||
if let Some((label, cont, off)) = fn_def::parse(&src[pos..]) {
|
if let Some((label, cont, off)) = fn_def::parse(tail) {
|
||||||
brk!(Element::FnDef { label, cont }, off + 1);
|
brk!(Element::FnDef { label, cont }, off + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +136,9 @@ pub fn parse(src: &str) -> (Element<'_>, usize, Option<(Element<'_>, usize)>) {
|
||||||
|
|
||||||
pos = skip_space!(src, pos);
|
pos = skip_space!(src, pos);
|
||||||
|
|
||||||
let (is_item, ordered) = list::is_item(&src[pos..]);
|
let tail = &src[pos..];
|
||||||
|
|
||||||
|
let (is_item, ordered) = list::is_item(tail);
|
||||||
if is_item {
|
if is_item {
|
||||||
let list = Element::List {
|
let list = Element::List {
|
||||||
ident: pos - line_beg,
|
ident: pos - line_beg,
|
||||||
|
@ -155,28 +159,42 @@ pub fn parse(src: &str) -> (Element<'_>, usize, Option<(Element<'_>, usize)>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LaTeX environment
|
// TODO: LaTeX environment
|
||||||
if bytes[pos..].starts_with(b"\\begin{") {}
|
if tail.starts_with("\\begin{") {}
|
||||||
|
|
||||||
// Rule
|
// rule
|
||||||
if bytes[pos] == b'-' {
|
if tail.starts_with("-----") {
|
||||||
let off = rule::parse(&src[pos..]);
|
let off = rule::parse(tail);
|
||||||
if off != 0 {
|
if off != 0 {
|
||||||
brk!(Element::Rule, off);
|
brk!(Element::Rule, off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: multiple lines fixed width area
|
// fixed width
|
||||||
if bytes[pos..].starts_with(b": ") || bytes[pos..].starts_with(b":\n") {
|
if tail.starts_with(": ") || tail.starts_with(":\n") {
|
||||||
let eol = memchr(b'\n', &bytes[pos..])
|
let end = line_ends
|
||||||
|
.skip_while(|&i| src[i + 1..].starts_with(": ") || src[i + 1..].starts_with(":\n"))
|
||||||
|
.next()
|
||||||
.map(|i| i + 1)
|
.map(|i| i + 1)
|
||||||
.unwrap_or_else(|| src.len() - pos);
|
.unwrap_or_else(|| src.len());
|
||||||
brk!(Element::FixedWidth(&src[pos + 1..pos + eol].trim()), eol);
|
let off = end - pos;
|
||||||
|
brk!(Element::FixedWidth(&tail[0..off]), off);
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes[pos..].starts_with(b"#+") {
|
// comment
|
||||||
if let Some((name, args, cont_beg, cont_end, end)) = block::parse(&src[pos..]) {
|
if tail.starts_with("# ") || tail.starts_with("#\n") {
|
||||||
let cont = &src[pos + cont_beg..pos + cont_end];
|
let end = line_ends
|
||||||
match name.to_uppercase().as_str() {
|
.skip_while(|&i| src[i + 1..].starts_with("# ") || src[i + 1..].starts_with("#\n"))
|
||||||
|
.next()
|
||||||
|
.map(|i| i + 1)
|
||||||
|
.unwrap_or_else(|| src.len());
|
||||||
|
let off = end - pos;
|
||||||
|
brk!(Element::Comment(&tail[0..off]), off);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tail.starts_with("#+") {
|
||||||
|
if let Some((name, args, cont_beg, cont_end, end)) = block::parse(tail) {
|
||||||
|
let cont = &tail[cont_beg..cont_end];
|
||||||
|
match &*name.to_uppercase() {
|
||||||
"COMMENT" => brk!(Element::CommentBlock { args, cont }, end),
|
"COMMENT" => brk!(Element::CommentBlock { args, cont }, end),
|
||||||
"EXAMPLE" => brk!(Element::ExampleBlock { args, cont }, end),
|
"EXAMPLE" => brk!(Element::ExampleBlock { args, cont }, end),
|
||||||
"EXPORT" => brk!(Element::ExportBlock { args, cont }, end),
|
"EXPORT" => brk!(Element::ExportBlock { args, cont }, end),
|
||||||
|
@ -210,7 +228,7 @@ pub fn parse(src: &str) -> (Element<'_>, usize, Option<(Element<'_>, usize)>) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((name, args, cont_beg, cont_end, end)) = dyn_block::parse(&src[pos..]) {
|
if let Some((name, args, cont_beg, cont_end, end)) = dyn_block::parse(tail) {
|
||||||
brk!(
|
brk!(
|
||||||
Element::DynBlock {
|
Element::DynBlock {
|
||||||
name,
|
name,
|
||||||
|
@ -222,7 +240,7 @@ pub fn parse(src: &str) -> (Element<'_>, usize, Option<(Element<'_>, usize)>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((key, value, off)) = keyword::parse(&src[pos..]) {
|
if let Some((key, value, off)) = keyword::parse(tail) {
|
||||||
brk!(
|
brk!(
|
||||||
if let Key::Call = key {
|
if let Key::Call = key {
|
||||||
Element::Call { value }
|
Element::Call { value }
|
||||||
|
@ -234,15 +252,6 @@ pub fn parse(src: &str) -> (Element<'_>, usize, Option<(Element<'_>, usize)>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment
|
|
||||||
// TODO: multiple lines comment
|
|
||||||
if bytes[pos..].starts_with(b"# ") || bytes[pos..].starts_with(b"#\n") {
|
|
||||||
let eol = memchr(b'\n', &bytes[pos..])
|
|
||||||
.map(|i| i + 1)
|
|
||||||
.unwrap_or_else(|| src.len() - pos);
|
|
||||||
brk!(Element::Comment(&src[pos + 1..pos + eol].trim()), eol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// move to the beginning of the next line
|
// move to the beginning of the next line
|
||||||
if let Some(off) = line_ends.next() {
|
if let Some(off) = line_ends.next() {
|
||||||
pos = off + 1;
|
pos = off + 1;
|
||||||
|
@ -330,7 +339,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("\n\n\n: Lorem ipsum dolor sit amet.\n"),
|
parse("\n\n\n: Lorem ipsum dolor sit amet.\n"),
|
||||||
(
|
(
|
||||||
FixedWidth("Lorem ipsum dolor sit amet."),
|
FixedWidth(": Lorem ipsum dolor sit amet.\n"),
|
||||||
"\n\n\n: Lorem ipsum dolor sit amet.\n".len(),
|
"\n\n\n: Lorem ipsum dolor sit amet.\n".len(),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
@ -338,7 +347,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("\n\n\n: Lorem ipsum dolor sit amet."),
|
parse("\n\n\n: Lorem ipsum dolor sit amet."),
|
||||||
(
|
(
|
||||||
FixedWidth("Lorem ipsum dolor sit amet."),
|
FixedWidth(": Lorem ipsum dolor sit amet."),
|
||||||
"\n\n\n: Lorem ipsum dolor sit amet.".len(),
|
"\n\n\n: Lorem ipsum dolor sit amet.".len(),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
@ -352,7 +361,19 @@ mod tests {
|
||||||
end: len + 1,
|
end: len + 1,
|
||||||
},
|
},
|
||||||
2,
|
2,
|
||||||
Some((FixedWidth("Lorem ipsum dolor sit amet."), 30))
|
Some((FixedWidth(": Lorem ipsum dolor sit amet.\n"), 30))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse("\n\nLorem ipsum dolor sit amet.\n: Lorem ipsum dolor sit amet.\n:\n: Lorem ipsum dolor sit amet."),
|
||||||
|
(
|
||||||
|
Paragraph {
|
||||||
|
cont_end: len,
|
||||||
|
end: len + 1,
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
Some((FixedWidth(": Lorem ipsum dolor sit amet.\n:\n: Lorem ipsum dolor sit amet."), 61))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,12 @@ pub trait HtmlHandler<W: Write> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn handle_fixed_width(&mut self, w: &mut W, cont: &str) -> Result<()> {
|
fn handle_fixed_width(&mut self, w: &mut W, cont: &str) -> Result<()> {
|
||||||
write!(w, "<pre>{}</pre>", Escape(cont))
|
for line in cont.lines() {
|
||||||
|
// remove leading colon
|
||||||
|
write!(w, "<pre>{}</pre>", Escape(&line[1..]))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn handle_table_start(&mut self, w: &mut W) -> Result<()> {
|
fn handle_table_start(&mut self, w: &mut W) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use memchr::memchr2;
|
use memchr::memchr2;
|
||||||
|
|
||||||
const HEADLINE_DEFAULT_KEYWORDS: &'static [&'static str] =
|
const HEADLINE_DEFAULT_KEYWORDS: &[&str] =
|
||||||
&["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
|
&["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
|
|
@ -94,53 +94,54 @@ pub fn parse(src: &str) -> (Object<'_>, usize, Option<(Object<'_>, usize)>) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tail = &src[pos..];
|
||||||
match bytes[pos] {
|
match bytes[pos] {
|
||||||
b'@' if bytes[pos + 1] == b'@' => {
|
b'@' if bytes[pos + 1] == b'@' => {
|
||||||
if let Some((name, value, off)) = snippet::parse(&src[pos..]) {
|
if let Some((name, value, off)) = snippet::parse(tail) {
|
||||||
brk!(Object::Snippet { name, value }, off, pos);
|
brk!(Object::Snippet { name, value }, off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'{' if bytes[pos + 1] == b'{' && bytes[pos + 2] == b'{' => {
|
b'{' if bytes[pos + 1] == b'{' && bytes[pos + 2] == b'{' => {
|
||||||
if let Some((name, args, off)) = macros::parse(&src[pos..]) {
|
if let Some((name, args, off)) = macros::parse(tail) {
|
||||||
brk!(Object::Macros { name, args }, off, pos);
|
brk!(Object::Macros { name, args }, off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'<' if bytes[pos + 1] == b'<' => {
|
b'<' if bytes[pos + 1] == b'<' => {
|
||||||
if bytes[pos + 2] == b'<' {
|
if bytes[pos + 2] == b'<' {
|
||||||
if let Some((target, off)) = radio_target::parse(&src[pos..]) {
|
if let Some((target, off)) = radio_target::parse(tail) {
|
||||||
brk!(Object::RadioTarget { target }, off, pos);
|
brk!(Object::RadioTarget { target }, off, pos);
|
||||||
}
|
}
|
||||||
} else if bytes[pos + 2] != b'\n' {
|
} else if bytes[pos + 2] != b'\n' {
|
||||||
if let Some((target, off)) = target::parse(&src[pos..]) {
|
if let Some((target, off)) = target::parse(tail) {
|
||||||
brk!(Object::Target { target }, off, pos);
|
brk!(Object::Target { target }, off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'[' => {
|
b'[' => {
|
||||||
if bytes[pos + 1..].starts_with(b"fn:") {
|
if tail[1..].starts_with("fn:") {
|
||||||
if let Some((label, def, off)) = fn_ref::parse(&src[pos..]) {
|
if let Some((label, def, off)) = fn_ref::parse(tail) {
|
||||||
brk!(Object::FnRef { label, def }, off, pos);
|
brk!(Object::FnRef { label, def }, off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes[pos + 1] == b'[' {
|
if bytes[pos + 1] == b'[' {
|
||||||
if let Some((path, desc, off)) = link::parse(&src[pos..]) {
|
if let Some((path, desc, off)) = link::parse(tail) {
|
||||||
brk!(Object::Link { path, desc }, off, pos);
|
brk!(Object::Link { path, desc }, off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((cookie, off)) = cookie::parse(&src[pos..]) {
|
if let Some((cookie, off)) = cookie::parse(tail) {
|
||||||
brk!(Object::Cookie(cookie), off, pos);
|
brk!(Object::Cookie(cookie), off, pos);
|
||||||
}
|
}
|
||||||
// TODO: Timestamp
|
// TODO: Timestamp
|
||||||
}
|
}
|
||||||
b'{' | b' ' | b'"' | b',' | b'(' | b'\n' => {
|
b'{' | b' ' | b'"' | b',' | b'(' | b'\n' => {
|
||||||
if let Some((obj, off)) = parse_text_markup(&src[pos + 1..]) {
|
if let Some((obj, off)) = parse_text_markup(&tail[1..]) {
|
||||||
brk!(obj, off, pos + 1);
|
brk!(obj, off, pos + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some((obj, off)) = parse_text_markup(&src[pos..]) {
|
if let Some((obj, off)) = parse_text_markup(tail) {
|
||||||
brk!(obj, off, pos);
|
brk!(obj, off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue