feat(node): more headline operations
This commit is contained in:
parent
f2d0a1dd2d
commit
5db7ec7465
|
@ -79,7 +79,7 @@ pub enum Element<'a> {
|
||||||
DynBlock(DynBlock<'a>),
|
DynBlock(DynBlock<'a>),
|
||||||
FnDef(FnDef<'a>),
|
FnDef(FnDef<'a>),
|
||||||
FnRef(FnRef<'a>),
|
FnRef(FnRef<'a>),
|
||||||
Headline,
|
Headline { level: usize },
|
||||||
InlineCall(InlineCall<'a>),
|
InlineCall(InlineCall<'a>),
|
||||||
InlineSrc(InlineSrc<'a>),
|
InlineSrc(InlineSrc<'a>),
|
||||||
Keyword(Keyword<'a>),
|
Keyword(Keyword<'a>),
|
||||||
|
@ -112,9 +112,25 @@ impl Element<'_> {
|
||||||
use Element::*;
|
use Element::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
SpecialBlock(_) | QuoteBlock(_) | CenterBlock(_) | VerseBlock(_) | Bold | Document
|
SpecialBlock(_)
|
||||||
| DynBlock(_) | Headline | Italic | List(_) | ListItem(_) | Paragraph | Section
|
| QuoteBlock(_)
|
||||||
| Strike | Underline | Title(_) | Table(_) | TableRow(_) | TableCell => true,
|
| CenterBlock(_)
|
||||||
|
| VerseBlock(_)
|
||||||
|
| Bold
|
||||||
|
| Document
|
||||||
|
| DynBlock(_)
|
||||||
|
| Headline { .. }
|
||||||
|
| Italic
|
||||||
|
| List(_)
|
||||||
|
| ListItem(_)
|
||||||
|
| Paragraph
|
||||||
|
| Section
|
||||||
|
| Strike
|
||||||
|
| Underline
|
||||||
|
| Title(_)
|
||||||
|
| Table(_)
|
||||||
|
| TableRow(_)
|
||||||
|
| TableCell => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +157,7 @@ impl Element<'_> {
|
||||||
DynBlock(e) => DynBlock(e.into_owned()),
|
DynBlock(e) => DynBlock(e.into_owned()),
|
||||||
FnDef(e) => FnDef(e.into_owned()),
|
FnDef(e) => FnDef(e.into_owned()),
|
||||||
FnRef(e) => FnRef(e.into_owned()),
|
FnRef(e) => FnRef(e.into_owned()),
|
||||||
Headline => Headline,
|
Headline { level } => Headline { level },
|
||||||
InlineCall(e) => InlineCall(e.into_owned()),
|
InlineCall(e) => InlineCall(e.into_owned()),
|
||||||
InlineSrc(e) => InlineSrc(e.into_owned()),
|
InlineSrc(e) => InlineSrc(e.into_owned()),
|
||||||
Keyword(e) => Keyword(e.into_owned()),
|
Keyword(e) => Keyword(e.into_owned()),
|
||||||
|
|
|
@ -93,6 +93,20 @@ impl Title<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Title<'_> {
|
||||||
|
fn default() -> Title<'static> {
|
||||||
|
Title {
|
||||||
|
level: 1,
|
||||||
|
priority: None,
|
||||||
|
tags: Vec::new(),
|
||||||
|
keyword: None,
|
||||||
|
raw: Cow::Borrowed(""),
|
||||||
|
planning: None,
|
||||||
|
properties: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_headline<'a>(
|
fn parse_headline<'a>(
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
config: &ParseConfig,
|
config: &ParseConfig,
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub trait HtmlHandler<E: From<Error>> {
|
||||||
Bold => write!(w, "<b>")?,
|
Bold => write!(w, "<b>")?,
|
||||||
Document => write!(w, "<main>")?,
|
Document => write!(w, "<main>")?,
|
||||||
DynBlock(_dyn_block) => (),
|
DynBlock(_dyn_block) => (),
|
||||||
Headline => (),
|
Headline { .. } => (),
|
||||||
List(list) => {
|
List(list) => {
|
||||||
if list.ordered {
|
if list.ordered {
|
||||||
write!(w, "<ol>")?;
|
write!(w, "<ol>")?;
|
||||||
|
@ -166,7 +166,7 @@ pub trait HtmlHandler<E: From<Error>> {
|
||||||
Bold => write!(w, "</b>")?,
|
Bold => write!(w, "</b>")?,
|
||||||
Document => write!(w, "</main>")?,
|
Document => write!(w, "</main>")?,
|
||||||
DynBlock(_dyn_block) => (),
|
DynBlock(_dyn_block) => (),
|
||||||
Headline => (),
|
Headline { .. } => (),
|
||||||
List(list) => {
|
List(list) => {
|
||||||
if list.ordered {
|
if list.ordered {
|
||||||
write!(w, "</ol>")?;
|
write!(w, "</ol>")?;
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub trait OrgHandler<E: From<Error>> {
|
||||||
}
|
}
|
||||||
writeln!(&mut w)?;
|
writeln!(&mut w)?;
|
||||||
}
|
}
|
||||||
Headline => (),
|
Headline { .. } => (),
|
||||||
List(_list) => (),
|
List(_list) => (),
|
||||||
Italic => write!(w, "/")?,
|
Italic => write!(w, "/")?,
|
||||||
ListItem(list_item) => write!(w, "{}", list_item.bullet)?,
|
ListItem(list_item) => write!(w, "{}", list_item.bullet)?,
|
||||||
|
@ -154,7 +154,7 @@ pub trait OrgHandler<E: From<Error>> {
|
||||||
Bold => write!(w, "*")?,
|
Bold => write!(w, "*")?,
|
||||||
Document => (),
|
Document => (),
|
||||||
DynBlock(_dyn_block) => writeln!(w, "#+END:")?,
|
DynBlock(_dyn_block) => writeln!(w, "#+END:")?,
|
||||||
Headline => (),
|
Headline { .. } => (),
|
||||||
List(_list) => (),
|
List(_list) => (),
|
||||||
Italic => write!(w, "/")?,
|
Italic => write!(w, "/")?,
|
||||||
ListItem(_) => (),
|
ListItem(_) => (),
|
||||||
|
|
160
src/node.rs
160
src/node.rs
|
@ -1,27 +1,169 @@
|
||||||
use indextree::NodeId;
|
use indextree::NodeId;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use crate::config::ParseConfig;
|
||||||
use crate::elements::{Element, Title};
|
use crate::elements::{Element, Title};
|
||||||
|
use crate::parsers::{parse_container, Container, OwnedArena};
|
||||||
use crate::Org;
|
use crate::Org;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct HeadlineNode(pub(crate) NodeId);
|
pub struct HeadlineNode {
|
||||||
|
pub(crate) node: NodeId,
|
||||||
|
pub(crate) level: usize,
|
||||||
|
pub(crate) title_node: NodeId,
|
||||||
|
pub(crate) section_node: Option<NodeId>,
|
||||||
|
}
|
||||||
|
|
||||||
impl HeadlineNode {
|
impl<'a: 'b, 'b> HeadlineNode {
|
||||||
pub fn get_title<'a: 'b, 'b>(self, org: &'b Org<'a>) -> &'b Title<'a> {
|
pub(crate) fn new(node: NodeId, level: usize, org: &Org<'_>) -> HeadlineNode {
|
||||||
let title_node = org.arena[self.0].first_child().unwrap();
|
let title_node = org.arena[node].first_child().unwrap();
|
||||||
if let Element::Title(title) = org.arena[title_node].get() {
|
let section_node = if let Some(node) = org.arena[title_node].next_sibling() {
|
||||||
|
if let Element::Section = org.arena[node].get() {
|
||||||
|
Some(node)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
HeadlineNode {
|
||||||
|
node,
|
||||||
|
level,
|
||||||
|
title_node,
|
||||||
|
section_node,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn level(self) -> usize {
|
||||||
|
self.level
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title(self, org: &'b Org<'a>) -> &'b Title<'a> {
|
||||||
|
if let Element::Title(title) = org.arena[self.title_node].get() {
|
||||||
title
|
title
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_title_mut<'a: 'b, 'b>(self, org: &'b mut Org<'a>) -> &'b mut Title<'a> {
|
pub fn title_mut(self, org: &'b mut Org<'a>) -> &'b mut Title<'a> {
|
||||||
let title_node = org.arena[self.0].first_child().unwrap();
|
if let Element::Title(title) = org.arena[self.title_node].get_mut() {
|
||||||
if let Element::Title(title) = org.arena[title_node].get_mut() {
|
|
||||||
title
|
title
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_title_content<S: Into<Cow<'a, str>>>(self, content: S, org: &mut Org<'a>) {
|
||||||
|
let content = content.into();
|
||||||
|
|
||||||
|
let children: Vec<_> = self.title_node.children(&org.arena).collect();
|
||||||
|
for child in children {
|
||||||
|
child.detach(&mut org.arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
match &content {
|
||||||
|
Cow::Borrowed(content) => parse_container(
|
||||||
|
&mut org.arena,
|
||||||
|
Container::Inline {
|
||||||
|
node: self.title_node,
|
||||||
|
content,
|
||||||
|
},
|
||||||
|
&ParseConfig::default(),
|
||||||
|
),
|
||||||
|
Cow::Owned(ref content) => parse_container(
|
||||||
|
&mut OwnedArena::new(&mut org.arena),
|
||||||
|
Container::Inline {
|
||||||
|
node: self.title_node,
|
||||||
|
content,
|
||||||
|
},
|
||||||
|
&ParseConfig::default(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.title_mut(org).raw = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_section_content<S: Into<Cow<'a, str>>>(self, content: S, org: &mut Org<'a>) {
|
||||||
|
let node = if let Some(node) = self.section_node {
|
||||||
|
let children: Vec<_> = node.children(&org.arena).collect();
|
||||||
|
for child in children {
|
||||||
|
child.detach(&mut org.arena);
|
||||||
|
}
|
||||||
|
node
|
||||||
|
} else {
|
||||||
|
let node = org.arena.new_node(Element::Section);
|
||||||
|
self.node.append(node, &mut org.arena);
|
||||||
|
node
|
||||||
|
};
|
||||||
|
|
||||||
|
match content.into() {
|
||||||
|
Cow::Borrowed(content) => parse_container(
|
||||||
|
&mut org.arena,
|
||||||
|
Container::Block { node, content },
|
||||||
|
&ParseConfig::default(),
|
||||||
|
),
|
||||||
|
Cow::Owned(ref content) => parse_container(
|
||||||
|
&mut OwnedArena::new(&mut org.arena),
|
||||||
|
Container::Block { node, content },
|
||||||
|
&ParseConfig::default(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent(self, org: &Org<'_>) -> Option<HeadlineNode> {
|
||||||
|
org.arena[self.node].parent().map(|node| {
|
||||||
|
if let &Element::Headline { level } = org.arena[node].get() {
|
||||||
|
HeadlineNode::new(node, level, org)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detach(self, org: &mut Org<'_>) {
|
||||||
|
self.node.detach(&mut org.arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_detached(self, org: &Org<'_>) -> bool {
|
||||||
|
self.parent(&org).is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(self, headline: &HeadlineNode, org: &mut Org<'_>) {
|
||||||
|
if self.is_detached(org) || headline.level <= self.level {
|
||||||
|
// TODO: return an error
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.node.append(headline.node, &mut org.arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend(self, headline: &HeadlineNode, org: &mut Org<'_>) {
|
||||||
|
if self.is_detached(org) || headline.level <= self.level {
|
||||||
|
// TODO: return an error
|
||||||
|
return;
|
||||||
|
} else if let Some(node) = self.section_node {
|
||||||
|
node.insert_after(headline.node, &mut org.arena);
|
||||||
|
} else {
|
||||||
|
self.title_node.insert_after(headline.node, &mut org.arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_before(self, headline: &HeadlineNode, org: &mut Org<'_>) {
|
||||||
|
if self.is_detached(org) || headline.level < self.level {
|
||||||
|
// TODO: return an error
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.node.insert_after(headline.node, &mut org.arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_after(self, headline: &HeadlineNode, org: &mut Org<'_>) {
|
||||||
|
if self.is_detached(org) || headline.level < self.level {
|
||||||
|
// TODO: return an error
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.node.insert_after(headline.node, &mut org.arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
55
src/org.rs
55
src/org.rs
|
@ -2,7 +2,7 @@ use indextree::{Arena, NodeEdge, NodeId};
|
||||||
use std::io::{Error, Write};
|
use std::io::{Error, Write};
|
||||||
|
|
||||||
use crate::config::ParseConfig;
|
use crate::config::ParseConfig;
|
||||||
use crate::elements::Element;
|
use crate::elements::{Element, Title};
|
||||||
use crate::export::*;
|
use crate::export::*;
|
||||||
use crate::node::HeadlineNode;
|
use crate::node::HeadlineNode;
|
||||||
use crate::parsers::{parse_container, Container};
|
use crate::parsers::{parse_container, Container};
|
||||||
|
@ -19,17 +19,15 @@ pub enum Event<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Org<'_> {
|
impl Org<'_> {
|
||||||
pub fn parse(text: &str) -> Org<'_> {
|
pub fn new() -> Org<'static> {
|
||||||
Org::parse_with_config(text, &ParseConfig::default())
|
let mut arena = Arena::new();
|
||||||
|
let root = arena.new_node(Element::Document);
|
||||||
|
|
||||||
|
Org { arena, root }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_with_config<'a>(content: &'a str, config: &ParseConfig) -> Org<'a> {
|
pub fn parse(text: &str) -> Org<'_> {
|
||||||
let mut arena = Arena::new();
|
Org::parse_with_config(text, &ParseConfig::default())
|
||||||
let node = arena.new_node(Element::Document);
|
|
||||||
|
|
||||||
parse_container(&mut arena, Container::Document { content, node }, config);
|
|
||||||
|
|
||||||
Org { arena, root: node }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Event<'_>> + '_ {
|
pub fn iter(&self) -> impl Iterator<Item = Event<'_>> + '_ {
|
||||||
|
@ -44,7 +42,7 @@ impl Org<'_> {
|
||||||
.descendants(&self.arena)
|
.descendants(&self.arena)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.filter_map(move |node| match self.arena[node].get() {
|
.filter_map(move |node| match self.arena[node].get() {
|
||||||
Element::Headline => Some(HeadlineNode(node)),
|
&Element::Headline { level } => Some(HeadlineNode::new(node, level, self)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -90,6 +88,41 @@ impl Org<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Org<'a> {
|
||||||
|
pub fn parse_with_config(content: &'a str, config: &ParseConfig) -> Org<'a> {
|
||||||
|
let mut org = Org::new();
|
||||||
|
|
||||||
|
parse_container(
|
||||||
|
&mut org.arena,
|
||||||
|
Container::Document {
|
||||||
|
content,
|
||||||
|
node: org.root,
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
|
||||||
|
org
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_headline(&mut self, title: Title<'a>) -> HeadlineNode {
|
||||||
|
let title_level = title.level;
|
||||||
|
let title_raw = title.raw.clone();
|
||||||
|
let headline_node = self
|
||||||
|
.arena
|
||||||
|
.new_node(Element::Headline { level: title_level });
|
||||||
|
let title_node = self.arena.new_node(Element::Title(title));
|
||||||
|
headline_node.append(title_node, &mut self.arena);
|
||||||
|
let headline_node = HeadlineNode {
|
||||||
|
node: headline_node,
|
||||||
|
level: title_level,
|
||||||
|
title_node,
|
||||||
|
section_node: None,
|
||||||
|
};
|
||||||
|
headline_node.set_title_content(title_raw, self);
|
||||||
|
headline_node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ser")]
|
#[cfg(feature = "ser")]
|
||||||
use serde::{ser::Serializer, Serialize};
|
use serde::{ser::Serializer, Serialize};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use memchr::{memchr, memchr_iter};
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::take_while1,
|
bytes::complete::take_while1,
|
||||||
character::complete::{line_ending, not_line_ending},
|
character::complete::{line_ending, not_line_ending},
|
||||||
combinator::{map, opt, recognize, verify},
|
combinator::{opt, recognize, verify},
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
error_position,
|
error_position,
|
||||||
multi::{many0_count, many1_count},
|
multi::{many0_count, many1_count},
|
||||||
|
@ -51,6 +51,42 @@ impl<'a> ElementArena<'a> for Arena<Element<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct OwnedArena<'a, 'b, 'c> {
|
||||||
|
arena: &'b mut Arena<Element<'c>>,
|
||||||
|
phantom: PhantomData<&'a ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'c> OwnedArena<'a, 'b, 'c> {
|
||||||
|
pub fn new(arena: &'b mut Arena<Element<'c>>) -> OwnedArena<'a, 'b, 'c> {
|
||||||
|
OwnedArena {
|
||||||
|
arena,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ElementArena<'a> for OwnedArena<'a, '_, '_> {
|
||||||
|
fn push_element<T: Into<Element<'a>>>(&mut self, element: T, parent: NodeId) -> NodeId {
|
||||||
|
let node = self.arena.new_node(element.into().into_owned());
|
||||||
|
parent.append(node, self.arena);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_before_last_child<T: Into<Element<'a>>>(
|
||||||
|
&mut self,
|
||||||
|
element: T,
|
||||||
|
parent: NodeId,
|
||||||
|
) -> NodeId {
|
||||||
|
if let Some(child) = self.arena[parent].last_child() {
|
||||||
|
let node = self.arena.new_node(element.into().into_owned());
|
||||||
|
child.insert_before(node, self.arena);
|
||||||
|
node
|
||||||
|
} else {
|
||||||
|
self.push_element(element, parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Container<'a> {
|
pub enum Container<'a> {
|
||||||
// List
|
// List
|
||||||
|
@ -139,22 +175,22 @@ pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
|
||||||
|
|
||||||
let mut last_end = 0;
|
let mut last_end = 0;
|
||||||
for i in memchr_iter(b'\n', content.as_bytes()) {
|
for i in memchr_iter(b'\n', content.as_bytes()) {
|
||||||
if let Ok((mut tail, headline_content)) = parse_headline(&content[last_end..]) {
|
if let Ok((mut tail, (headline_content, level))) = parse_headline(&content[last_end..]) {
|
||||||
if last_end != 0 {
|
if last_end != 0 {
|
||||||
let node = arena.push_element(Element::Section, parent);
|
let node = arena.push_element(Element::Section, parent);
|
||||||
let content = &content[0..last_end];
|
let content = &content[0..last_end];
|
||||||
containers.push(Container::Block { content, node });
|
containers.push(Container::Block { content, node });
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = arena.push_element(Element::Headline, parent);
|
let node = arena.push_element(Element::Headline { level }, parent);
|
||||||
containers.push(Container::Headline {
|
containers.push(Container::Headline {
|
||||||
content: headline_content,
|
content: headline_content,
|
||||||
node,
|
node,
|
||||||
});
|
});
|
||||||
|
|
||||||
while let Ok((new_tail, content)) = parse_headline(tail) {
|
while let Ok((new_tail, (content, level))) = parse_headline(tail) {
|
||||||
debug_assert_ne!(tail, new_tail);
|
debug_assert_ne!(tail, new_tail);
|
||||||
let node = arena.push_element(Element::Headline, parent);
|
let node = arena.push_element(Element::Headline { level }, parent);
|
||||||
containers.push(Container::Headline { content, node });
|
containers.push(Container::Headline { content, node });
|
||||||
tail = new_tail;
|
tail = new_tail;
|
||||||
}
|
}
|
||||||
|
@ -719,24 +755,22 @@ pub fn skip_empty_lines(input: &str) -> &str {
|
||||||
.unwrap_or(input)
|
.unwrap_or(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_headline(input: &str) -> IResult<&str, &str> {
|
pub fn parse_headline(input: &str) -> IResult<&str, (&str, usize)> {
|
||||||
let (input_, level) = get_headline_level(input)?;
|
let (input_, level) = parse_headline_level(input)?;
|
||||||
map(
|
let (input_, content) = take_lines_while(move |line| {
|
||||||
take_lines_while(move |line| {
|
if let Ok((_, l)) = parse_headline_level(line) {
|
||||||
if let Ok((_, l)) = get_headline_level(line) {
|
l > level
|
||||||
l.len() > level.len()
|
} else {
|
||||||
} else {
|
true
|
||||||
true
|
}
|
||||||
}
|
})(input_)?;
|
||||||
}),
|
Ok((input_, (&input[0..level + content.len()], level)))
|
||||||
move |s: &str| &input[0..level.len() + s.len()],
|
|
||||||
)(input_)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_headline_level(input: &str) -> IResult<&str, &str> {
|
pub fn parse_headline_level(input: &str) -> IResult<&str, usize> {
|
||||||
let (input, stars) = take_while1(|c: char| c == '*')(input)?;
|
let (input, stars) = take_while1(|c: char| c == '*')(input)?;
|
||||||
if input.is_empty() || input.starts_with(' ') || input.starts_with('\n') {
|
if input.is_empty() || input.starts_with(' ') || input.starts_with('\n') {
|
||||||
Ok((input, stars))
|
Ok((input, stars.len()))
|
||||||
} else {
|
} else {
|
||||||
Err(Err::Error(error_position!(input, ErrorKind::Tag)))
|
Err(Err::Error(error_position!(input, ErrorKind::Tag)))
|
||||||
}
|
}
|
||||||
|
|
24
tests/node.rs
Normal file
24
tests/node.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use orgize::Org;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_content() {
|
||||||
|
let mut org = Org::parse(
|
||||||
|
r#"* title 1
|
||||||
|
section 1
|
||||||
|
** title 2
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let headlines: Vec<_> = org.headlines().collect();
|
||||||
|
for headline in headlines {
|
||||||
|
headline.set_title_content(String::from("a *bold* title"), &mut org);
|
||||||
|
headline.set_section_content("and a _underline_ section", &mut org);
|
||||||
|
}
|
||||||
|
let mut writer = Vec::new();
|
||||||
|
org.html(&mut writer).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
String::from_utf8(writer).unwrap(),
|
||||||
|
"<main><h1>a <b>bold</b> title</h1><section><p>and a <u>underline</u> section</p></section>\
|
||||||
|
<h2>a <b>bold</b> title</h2><section><p>and a <u>underline</u> section</p></section></main>"
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue