use unicode_width::{UnicodeWidthStr,UnicodeWidthChar};
use super::Renderer;
use std::mem;
use std::vec;
use std::fmt::Debug;
#[derive(Debug)]
pub struct TaggedString<T:Debug> {
pub s: String,
pub tag: T,
}
#[derive(Debug)]
pub enum TaggedLineElement<T:Debug+Eq+PartialEq+Clone> {
Str(TaggedString<T>),
FragmentStart(String),
}
#[derive(Debug)]
pub struct TaggedLine<T:Debug+Eq+PartialEq+Clone> {
v: Vec<TaggedLineElement<T>>,
}
impl<T:Debug+Eq+PartialEq+Clone+Default> TaggedLine<T> {
pub fn new() -> TaggedLine<T> {
TaggedLine {
v: Vec::new(),
}
}
pub fn from_string(s: String, tag: &T) -> TaggedLine<T> {
TaggedLine {
v: vec![TaggedLineElement::Str(
TaggedString{ s: s, tag: tag.clone() })],
}
}
pub fn into_string(self) -> String {
let mut s = String::new();
for tle in self.v {
if let TaggedLineElement::Str(ts) = tle {
s.push_str(&ts.s);
}
}
s
}
pub fn is_empty(&self) -> bool {
self.v.len() == 0
}
pub fn push_str(&mut self, ts: TaggedString<T>) {
use self::TaggedLineElement::Str;
if !self.v.is_empty() {
if let Str(ref mut ts_prev) = self.v.last_mut().unwrap() {
if ts_prev.tag == ts.tag {
ts_prev.s.push_str(&ts.s);
return;
}
}
}
self.v.push(Str(ts));
}
pub fn push(&mut self, tle: TaggedLineElement<T>) {
use self::TaggedLineElement::Str;
if let Str(ts) = tle {
self.push_str(ts);
} else {
self.v.push(tle);
}
}
pub fn insert_front(&mut self, ts: TaggedString<T>) {
use self::TaggedLineElement::Str;
self.v.insert(0, Str(ts));
}
pub fn push_char(&mut self, c: char, tag: &T) {
use self::TaggedLineElement::Str;
if !self.v.is_empty() {
if let Str(ref mut ts_prev) = self.v.last_mut().unwrap() {
if ts_prev.tag == *tag {
ts_prev.s.push(c);
return;
}
}
}
let mut s = String::new();
s.push(c);
self.v.push(Str(TaggedString { s: s, tag: tag.clone() }));
}
pub fn consume(&mut self, tl: &mut TaggedLine<T>) {
for ts in tl.v.drain(..) {
self.push(ts);
}
}
pub fn drain_all(&mut self) -> vec::Drain<TaggedLineElement<T>> {
self.v.drain(..)
}
#[cfg_attr(feature="clippy", allow(needless_lifetimes))]
pub fn chars<'a>(&'a self) -> Box<dyn Iterator<Item=char>+'a> {
use self::TaggedLineElement::Str;
Box::new(self.v.iter().flat_map(|tle| {
if let Str(ts) = tle { ts.s.chars() } else { "".chars() }
}))
}
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item=&TaggedLineElement<T>>+'a> {
Box::new(self.v.iter())
}
pub fn width(&self) -> usize {
use self::TaggedLineElement::Str;
let mut result = 0;
for tle in &self.v {
if let Str(ts) = tle {
result += UnicodeWidthStr::width(ts.s.as_str());
}
}
result
}
pub fn pad_to(&mut self, width: usize) {
use self::TaggedLineElement::Str;
let my_width = self.width();
if width > my_width {
self.v.push(Str(TaggedString{
s: format!("{: <width$}", "", width=width-my_width),
tag: T::default(),
}));
}
}
}
#[derive(Debug)]
struct WrappedBlock<T:Clone+Eq+Debug+Default> {
width: usize,
text: Vec<TaggedLine<T>>,
textlen: usize,
line: TaggedLine<T>,
linelen: usize,
spacetag: Option<T>, word: TaggedLine<T>, wordlen: usize,
}
impl<T:Clone+Eq+Debug+Default> WrappedBlock<T> {
pub fn new(width: usize) -> WrappedBlock<T> {
assert!(width > 0);
WrappedBlock {
width: width,
text: Vec::new(),
textlen: 0,
line: TaggedLine::new(),
linelen: 0,
spacetag: None,
word: TaggedLine::new(),
wordlen: 0,
}
}
fn flush_word(&mut self) {
use self::TaggedLineElement::Str;
html_trace_quiet!("flush_word: word={:?}, linelen={}", self.word, self.linelen);
if !self.word.is_empty() {
let space_in_line = self.width - self.linelen;
let space_needed = self.wordlen +
if self.linelen > 0 { 1 } else { 0 }; if space_needed <= space_in_line {
if self.linelen > 0 {
self.line.push(Str(TaggedString{s: " ".into(), tag: self.spacetag.take().unwrap()}));
self.linelen += 1;
}
self.line.consume(&mut self.word);
self.linelen += self.wordlen;
} else {
self.flush_line();
if self.wordlen <= self.width {
let mut new_word = TaggedLine::new();
mem::swap(&mut new_word, &mut self.word);
mem::swap(&mut self.line, &mut new_word);
self.linelen = self.wordlen;
} else {
let mut wordbits = self.word.drain_all();
let mut opt_elt = wordbits.next();
let mut lineleft = self.width;
while let Some(elt) = opt_elt.take() {
if let Str(piece) = elt {
let w = UnicodeWidthStr::width(piece.s.as_str());
if w <= lineleft {
self.line.push(Str(piece));
lineleft -= w;
self.linelen += w;
opt_elt = wordbits.next();
} else {
let mut split_idx = 0;
for (idx,c) in piece.s.char_indices() {
let c_w = UnicodeWidthChar::width(c).unwrap();
if c_w <= lineleft {
lineleft -= c_w;
} else {
split_idx = idx;
break;
}
}
self.line.push(Str(TaggedString{
s: piece.s[..split_idx].into(),
tag: piece.tag.clone(),
}));
{
let mut tmp_line = TaggedLine::new();
mem::swap(&mut tmp_line, &mut self.line);
self.text.push(tmp_line);
}
lineleft = self.width;
self.linelen = 0;
opt_elt = Some(Str(TaggedString{
s: piece.s[split_idx..].into(),
tag: piece.tag,
}));
}
} else {
self.line.push(elt);
}
}
}
}
}
self.wordlen = 0;
}
fn flush_line(&mut self) {
if !self.line.is_empty() {
let mut tmp_line = TaggedLine::new();
mem::swap(&mut tmp_line, &mut self.line);
self.text.push(tmp_line);
self.linelen = 0;
}
}
fn flush(&mut self) {
self.flush_word();
self.flush_line();
}
pub fn into_lines(mut self) -> Vec<TaggedLine<T>> {
self.flush();
self.text
}
pub fn add_text(&mut self, text: &str, tag: &T) {
html_trace!("WrappedBlock::add_text({}), {:?}", text, tag);
for c in text.chars() {
if c.is_whitespace() {
self.flush_word();
self.spacetag = Some(tag.clone());
} else if let Some(charwidth) = UnicodeWidthChar::width(c) {
self.word.push_char(c, tag);
self.wordlen += charwidth;
}
html_trace_quiet!(" Added char {:?}, wordlen={}", c, self.wordlen);
}
}
pub fn add_preformatted_text(&mut self, text: &str, tag: &T) {
html_trace!("WrappedBlock::add_preformatted_text({}), {:?}", text, tag);
self.flush_word();
for c in text.chars() {
if let Some(charwidth) = UnicodeWidthChar::width(c) {
if self.linelen + charwidth > self.width {
self.flush_line();
}
self.line.push_char(c, tag);
self.linelen += charwidth;
} else {
match c {
'\n' => {
self.flush_line();
}
'\t' => {
let tab_stop = 8;
let mut at_least_one_space = false;
while self.linelen % tab_stop != 0 || !at_least_one_space {
if self.linelen >= self.width {
self.flush_line();
} else {
self.line.push_char(' ', tag);
self.linelen += 1;
at_least_one_space = true;
}
}
}
_ => {
eprintln!("Got character: {:?}", c);
}
}
}
html_trace_quiet!(" Added char {:?}", c);
}
}
pub fn add_element(&mut self, elt: TaggedLineElement<T>) {
self.word.push(elt);
}
pub fn text_len(&self) -> usize {
self.textlen + self.linelen + self.wordlen
}
}
pub trait TextDecorator {
type Annotation: Eq+PartialEq+Debug+Clone+Default;
fn decorate_link_start(&mut self, url: &str) -> (String, Self::Annotation);
fn decorate_link_end(&mut self) -> String;
fn decorate_em_start(&mut self) -> (String, Self::Annotation);
fn decorate_em_end(&mut self) -> String;
fn decorate_strong_start(&mut self) -> (String, Self::Annotation);
fn decorate_strong_end(&mut self) -> String;
fn decorate_code_start(&mut self) -> (String, Self::Annotation);
fn decorate_code_end(&mut self) -> String;
fn decorate_preformat_first(&mut self) -> Self::Annotation;
fn decorate_preformat_cont(&mut self) -> Self::Annotation;
fn decorate_image(&mut self, title: &str) -> (String, Self::Annotation);
fn make_subblock_decorator(&self) -> Self;
fn finalise(self) -> Vec<TaggedLine<Self::Annotation>>;
}
#[derive(Copy,Clone,Debug)]
pub enum BorderSegHoriz {
Straight,
JoinAbove,
JoinBelow,
JoinCross,
}
#[derive(Clone,Debug)]
pub struct BorderHoriz {
pub segments: Vec<BorderSegHoriz>,
}
impl BorderHoriz {
pub fn new(width: usize) -> BorderHoriz {
BorderHoriz {
segments: vec![BorderSegHoriz::Straight; width],
}
}
pub fn join_above(&mut self, x: usize) {
use self::BorderSegHoriz::*;
let prev = self.segments[x];
self.segments[x] = match prev {
Straight | JoinAbove => JoinAbove,
JoinBelow | JoinCross => JoinCross,
}
}
pub fn join_below(&mut self, x: usize) {
use self::BorderSegHoriz::*;
let prev = self.segments[x];
self.segments[x] = match prev {
Straight | JoinBelow => JoinBelow,
JoinAbove | JoinCross => JoinCross,
}
}
pub fn merge_from_below(&mut self, other: &BorderHoriz, pos: usize) {
use self::BorderSegHoriz::*;
for (idx, seg) in other.segments.iter().enumerate()
{
match *seg {
Straight => (),
JoinAbove | JoinBelow | JoinCross => { self.join_below(idx+pos); },
}
}
}
pub fn merge_from_above(&mut self, other: &BorderHoriz, pos: usize) {
use self::BorderSegHoriz::*;
for (idx, seg) in other.segments.iter().enumerate()
{
match *seg {
Straight => (),
JoinAbove | JoinBelow | JoinCross => { self.join_above(idx+pos); },
}
}
}
pub fn to_vertical_lines_above(&self) -> String {
use self::BorderSegHoriz::*;
self.segments
.iter()
.map(|seg| match *seg {
Straight | JoinBelow => ' ',
JoinAbove | JoinCross => '│',
})
.collect()
}
pub fn into_string(self) -> String {
self.segments
.into_iter()
.map(|seg| match seg {
BorderSegHoriz::Straight => '─',
BorderSegHoriz::JoinAbove => 'â”´',
BorderSegHoriz::JoinBelow => '┬',
BorderSegHoriz::JoinCross => '┼',
})
.collect::<String>()
}
pub fn to_string(&self) -> String {
self.clone().into_string()
}
}
#[derive(Debug)]
pub enum RenderLine<T:PartialEq+Eq+Clone+Debug+Default> {
Text(TaggedLine<T>),
Line(BorderHoriz),
}
impl<T:PartialEq+Eq+Clone+Debug+Default> RenderLine<T> {
pub fn into_string(self) -> String {
match self {
RenderLine::Text(tagged) => tagged.into_string(),
RenderLine::Line(border) => border.into_string(),
}
}
pub fn into_tagged_line(self) -> TaggedLine<T> {
use self::TaggedLineElement::Str;
match self {
RenderLine::Text(tagged) => tagged,
RenderLine::Line(border) => {
let mut tagged = TaggedLine::new();
tagged.push(Str(TaggedString {
s: border.into_string(),
tag: T::default()
}));
tagged
}
}
}
}
pub struct TextRenderer<D:TextDecorator> {
width: usize,
lines: Vec<RenderLine<Vec<D::Annotation>>>,
at_block_end: bool,
wrapping: Option<WrappedBlock<Vec<D::Annotation>>>,
decorator: Option<D>,
ann_stack: Vec<D::Annotation>,
pre_depth: usize,
}
impl<D:TextDecorator> TextRenderer<D> {
pub fn new(width: usize, decorator: D) -> TextRenderer<D> {
html_trace!("new({})", width);
TextRenderer {
width: width,
lines: Vec::new(),
at_block_end: false,
wrapping: None,
decorator: Some(decorator),
ann_stack: Vec::new(),
pre_depth: 0,
}
}
fn ensure_wrapping_exists(&mut self) {
if self.wrapping.is_none() {
self.wrapping = Some(WrappedBlock::new(self.width));
}
}
fn current_text(&mut self) -> &mut WrappedBlock<Vec<D::Annotation>> {
self.ensure_wrapping_exists();
self.wrapping.as_mut().unwrap()
}
pub fn add_subblock(&mut self, s: &str) {
use self::TaggedLineElement::Str;
html_trace!("add_subblock({}, {})", self.width, s);
let tag = self.ann_stack.clone();
self.lines.extend(s.lines().map(|l| {
let mut line = TaggedLine::new();
line.push(Str(TaggedString{s: l.into(), tag: tag.clone()}));
RenderLine::Text(line)
}));
}
fn flush_wrapping(&mut self) {
if let Some(w) = self.wrapping.take() {
self.lines.extend(w.into_lines().into_iter().map(RenderLine::Text))
}
}
fn flush_all(&mut self) {
self.flush_wrapping();
}
pub fn into_string(self) -> String {
let mut result = String::new();
#[cfg(feature="html_trace")]
let width: usize = self.width;
for line in self.into_lines() {
result.push_str(&line.into_string());
result.push('\n');
}
html_trace!("into_string({}, {:?})", width, result);
result
}
pub fn into_lines(mut self) -> Vec<RenderLine<Vec<D::Annotation>>> {
self.flush_wrapping();
let mut trailer = self.decorator.take().unwrap().finalise();
if !trailer.is_empty() {
self.start_block();
for line in trailer.drain(0..) {
let mut output = String::new();
let mut pos = 0;
for c in line.chars() {
let c = match c {
'\n' => ' ',
x => x,
};
let c_width = UnicodeWidthChar::width(c).unwrap_or(0);
if pos + c_width > self.width {
let mut tmp_s = String::new();
mem::swap(&mut output, &mut tmp_s);
self.lines.push(RenderLine::Text(TaggedLine::from_string(tmp_s, &vec![])));
output.push(c);
pos = c_width;
} else {
output.push(c);
pos += c_width;
}
}
self.lines.push(RenderLine::Text(TaggedLine::from_string(output, &vec![])));
}
}
self.lines
}
}
impl<D:TextDecorator> Renderer for TextRenderer<D> {
fn add_empty_line(&mut self) {
html_trace!("add_empty_line()");
self.flush_all();
self.lines.push(RenderLine::Text(TaggedLine::new()));
html_trace_quiet!("add_empty_line: at_block_end <- false");
self.at_block_end = false;
html_trace_quiet!("add_empty_line: new lines: {:?}", self.lines);
}
fn new_sub_renderer(&self, width: usize) -> Self {
assert!(width > 0);
TextRenderer::new(width, self.decorator.as_ref().unwrap().make_subblock_decorator())
}
fn start_block(&mut self) {
html_trace!("start_block({})", self.width);
self.flush_all();
if !self.lines.is_empty() {
self.add_empty_line();
}
html_trace_quiet!("start_block; at_block_end <- false");
self.at_block_end = false;
}
fn new_line(&mut self) {
self.flush_all();
}
fn new_line_hard(&mut self) {
match self.wrapping {
None => self.add_empty_line(),
Some(WrappedBlock { linelen: 0, wordlen: 0, .. }) => self.add_empty_line(),
Some(_) => self.flush_all(),
}
}
fn add_horizontal_border(&mut self) {
self.flush_wrapping();
self.lines.push(RenderLine::Line(BorderHoriz::new(self.width)));
}
fn start_pre(&mut self) {
self.pre_depth += 1;
}
fn end_pre(&mut self) {
if self.pre_depth > 0 {
self.pre_depth -= 1;
} else {
panic!("Attempt to end a preformatted block which wasn't opened.");
}
}
fn add_preformatted_block(&mut self, text: &str) {
use self::TaggedLineElement::Str;
html_trace!("add_block({}, {})", self.width, text);
let mut tag_first = self.ann_stack.clone();
let mut tag_cont = self.ann_stack.clone();
tag_first.push(self.decorator.as_mut().unwrap().decorate_preformat_first());
tag_cont.push(self.decorator.as_mut().unwrap().decorate_preformat_cont());
let tag_first = tag_first;
let tag_cont = tag_cont;
let width = self.width;
self.start_block();
for formatted_line in text.lines() {
let mut acc = String::new();
let mut cur_width = 0;
let mut first = true;
for c in formatted_line.chars() {
if let Some(char_width) = UnicodeWidthChar::width(c) {
if cur_width + char_width > width {
let mut line = TaggedLine::new();
line.push(Str(TaggedString {
s: acc,
tag: if first { tag_first.clone() } else { tag_cont.clone() },
}));
self.lines.push(RenderLine::Text(line));
acc = String::new();
cur_width = 0;
first = false;
}
acc.push(c);
cur_width += char_width;
} else {
match c {
'\t' => {
let tab_stop = 8;
let wanted_pos = cur_width + tab_stop - (cur_width % tab_stop);
let spaces = if wanted_pos > width {
width - cur_width
} else {
wanted_pos - cur_width
};
acc.extend((0..spaces).map(|_| ' '));
cur_width += spaces;
},
_ => (),
}
}
}
if acc.len() > 0 {
let mut line = TaggedLine::new();
line.push(Str(TaggedString {
s: acc,
tag: if first { tag_first.clone() } else { tag_cont.clone() },
}));
self.lines.push(RenderLine::Text(line));
}
}
html_trace_quiet!("add_block: at_block_end <- true");
self.at_block_end = true;
}
fn end_block(&mut self) {
self.at_block_end = true;
}
fn add_inline_text(&mut self, text: &str) {
html_trace!("add_inline_text({}, {})", self.width, text);
if self.pre_depth == 0 && self.at_block_end && text.chars().all(char::is_whitespace) {
return;
}
if self.at_block_end {
self.start_block();
}
let _ = self.current_text();
if self.pre_depth == 0 {
self.wrapping.as_mut().unwrap().add_text(text, &self.ann_stack);
} else {
self.wrapping.as_mut().unwrap().add_preformatted_text(text, &self.ann_stack);
}
}
fn width(&self) -> usize {
self.width
}
fn add_block_line(&mut self, line: &str)
{
self.add_subblock(line);
}
fn append_subrender<'a, I>(&mut self, other: Self,
prefixes: I)
where I:Iterator<Item=&'a str>
{
use self::TaggedLineElement::Str;
self.flush_wrapping();
let tag = self.ann_stack.clone();
self.lines.extend(other.into_lines()
.into_iter()
.zip(prefixes)
.map(|(line, prefix)| {
match line {
RenderLine::Text(mut tline) => {
tline.insert_front(TaggedString{
s: prefix.to_string(),
tag: tag.clone()
});
RenderLine::Text(tline)
},
RenderLine::Line(l) => {
let mut tline = TaggedLine::new();
tline.push(Str(TaggedString {
s: prefix.to_string(),
tag: tag.clone()
}));
tline.push(Str(TaggedString {
s: l.into_string(),
tag: tag.clone()
}));
RenderLine::Text(tline)
}
}
}));
}
fn append_columns_with_borders<I>(&mut self, cols: I, collapse: bool)
where I:IntoIterator<Item=Self> {
use self::TaggedLineElement::Str;
self.flush_wrapping();
let mut next_border = BorderHoriz::new(self.width);
let mut line_sets = cols.into_iter()
.map(|sub_r| {
let width = sub_r.width;
(width, sub_r.into_lines()
.into_iter()
.map(|mut line| {
match line {
RenderLine::Text(ref mut tline) => {
tline.pad_to(width);
},
RenderLine::Line(_) => {},
}
line})
.collect())
})
.collect::<Vec<(usize, Vec<RenderLine<_>>)>>();
{
let mut pos = 0;
if let &mut RenderLine::Line(ref mut prev_border) = self.lines.last_mut().unwrap() {
for &(w, _) in &line_sets[..line_sets.len()-1] {
prev_border.join_below(pos+w);
next_border.join_above(pos+w);
pos += w + 1;
}
} else {
panic!("Expected a border line");
}
}
let mut column_padding = vec![None; line_sets.len()];
if collapse {
let mut pos = 0;
for &mut (w, ref mut sublines) in &mut line_sets {
let starts_border = if sublines.len() > 0 {
if let RenderLine::Line(_) = sublines[0] {
true
} else {
false
}
} else {
false
};
if starts_border {
if let &mut RenderLine::Line(ref mut prev_border) = self.lines.last_mut().expect("No previous line") {
if let RenderLine::Line(line) = sublines.remove(0) {
prev_border.merge_from_below(&line, pos);
}
} else {
unreachable!();
}
}
pos += w + 1;
}
let mut pos = 0;
for (col_no, &mut (w, ref mut sublines)) in line_sets.iter_mut().enumerate() {
let ends_border = if sublines.len() > 0 {
if let Some(&RenderLine::Line(_)) = sublines.last() {
true
} else {
false
}
} else {
false
};
if ends_border {
if let RenderLine::Line(line) = sublines.pop().unwrap() {
next_border.merge_from_above(&line, pos);
column_padding[col_no] = Some(line.to_vertical_lines_above())
}
}
pos += w + 1;
}
}
let cell_height = line_sets.iter()
.map(|&(_, ref v)| v.len())
.max().unwrap_or(0);
let spaces: String = (0..self.width).map(|_| ' ').collect();
let last_cellno = line_sets.len()-1;
for i in 0..cell_height {
let mut line = TaggedLine::new();
for (cellno, &mut (width, ref mut ls)) in line_sets.iter_mut().enumerate() {
if let Some(piece) = ls.get_mut(i) {
match piece {
&mut RenderLine::Text(ref mut tline) => {
line.consume(tline);
},
&mut RenderLine::Line(ref bord) => {
line.push(Str(TaggedString {
s: bord.to_string(),
tag: self.ann_stack.clone(),
}));
},
};
} else {
line.push(Str(TaggedString {
s: column_padding[cellno].as_ref().map(|s| s.clone())
.unwrap_or_else(||spaces[0..width].to_string()),
tag: self.ann_stack.clone(),
}));
}
if cellno != last_cellno {
line.push_char('│', &self.ann_stack);
}
}
self.lines.push(RenderLine::Text(line));
}
self.lines.push(RenderLine::Line(next_border));
}
fn empty(&self) -> bool {
self.lines.is_empty() && self.wrapping.is_none()
}
fn text_len(&self) -> usize {
let mut result = 0;
for line in &self.lines {
result += match *line {
RenderLine::Text(ref tline) => tline.width(),
RenderLine::Line(_) => 0, };
}
if let Some(ref w) = self.wrapping {
result += w.text_len();
}
result
}
fn start_link(&mut self, target: &str)
{
if let Some((s, annotation)) = self.decorator.as_mut().map(|d| d.decorate_link_start(target)) {
self.ann_stack.push(annotation);
self.add_inline_text(&s);
}
}
fn end_link(&mut self)
{
if let Some(s) = self.decorator.as_mut().map(|d| d.decorate_link_end()) {
self.add_inline_text(&s);
self.ann_stack.pop();
}
}
fn start_emphasis(&mut self)
{
if let Some((s, annotation)) = self.decorator.as_mut().map(|d| d.decorate_em_start()) {
self.ann_stack.push(annotation);
self.add_inline_text(&s);
}
}
fn end_emphasis(&mut self)
{
if let Some(s) = self.decorator.as_mut().map(|d| d.decorate_em_end()) {
self.add_inline_text(&s);
self.ann_stack.pop();
}
}
fn start_strong(&mut self)
{
if let Some((s, annotation)) = self.decorator.as_mut().map(|d| d.decorate_strong_start()) {
self.ann_stack.push(annotation);
self.add_inline_text(&s);
}
}
fn end_strong(&mut self)
{
if let Some(s) = self.decorator.as_mut().map(|d| d.decorate_strong_end()) {
self.add_inline_text(&s);
self.ann_stack.pop();
}
}
fn start_code(&mut self)
{
if let Some((s, annotation)) = self.decorator.as_mut().map(|d| d.decorate_code_start()) {
self.ann_stack.push(annotation);
self.add_inline_text(&s);
}
}
fn end_code(&mut self)
{
if let Some(s) = self.decorator.as_mut().map(|d| d.decorate_code_end()) {
self.add_inline_text(&s);
self.ann_stack.pop();
}
}
fn add_image(&mut self, title: &str)
{
if let Some((s, tag)) = self.decorator.as_mut().map(|d| d.decorate_image(title)) {
self.ann_stack.push(tag);
self.add_inline_text(&s);
self.ann_stack.pop();
}
}
fn record_frag_start(&mut self, fragname: &str)
{
use self::TaggedLineElement::FragmentStart;
self.ensure_wrapping_exists();
self.wrapping.as_mut().unwrap().add_element(
FragmentStart(fragname.to_string()));
}
}
#[derive(Clone)]
pub struct PlainDecorator {
links: Vec<String>,
}
impl PlainDecorator {
#[cfg_attr(feature="clippy", allow(new_without_default_derive))]
pub fn new() -> PlainDecorator {
PlainDecorator {
links: Vec::new(),
}
}
}
impl TextDecorator for PlainDecorator {
type Annotation = ();
fn decorate_link_start(&mut self, url: &str) -> (String, Self::Annotation)
{
self.links.push(url.to_string());
("[".to_string(), ())
}
fn decorate_link_end(&mut self) -> String
{
format!("][{}]", self.links.len())
}
fn decorate_em_start(&mut self) -> (String, Self::Annotation)
{
("*".to_string(), ())
}
fn decorate_em_end(&mut self) -> String
{
"*".to_string()
}
fn decorate_strong_start(&mut self) -> (String, Self::Annotation)
{
("**".to_string(), ())
}
fn decorate_strong_end(&mut self) -> String
{
"**".to_string()
}
fn decorate_code_start(&mut self) -> (String, Self::Annotation)
{
("`".to_string(), ())
}
fn decorate_code_end(&mut self) -> String
{
"`".to_string()
}
fn decorate_preformat_first(&mut self) -> Self::Annotation { () }
fn decorate_preformat_cont(&mut self) -> Self::Annotation { () }
fn decorate_image(&mut self, title: &str) -> (String, Self::Annotation)
{
(format!("[{}]", title), ())
}
fn finalise(self) -> Vec<TaggedLine<()>> {
self.links.into_iter().enumerate().map(|(idx,s)|
TaggedLine::from_string(format!("[{}] {}", idx+1, s), &())).collect()
}
fn make_subblock_decorator(&self) -> Self {
PlainDecorator::new()
}
}
#[derive(Clone)]
pub struct TrivialDecorator {}
impl TrivialDecorator {
#[cfg_attr(feature="clippy", allow(new_without_default_derive))]
pub fn new() -> TrivialDecorator {
TrivialDecorator {}
}
}
impl TextDecorator for TrivialDecorator {
type Annotation = ();
fn decorate_link_start(&mut self, _url: &str) -> (String, Self::Annotation)
{
("".to_string(), ())
}
fn decorate_link_end(&mut self) -> String
{
"".to_string()
}
fn decorate_em_start(&mut self) -> (String, Self::Annotation)
{
("".to_string(), ())
}
fn decorate_em_end(&mut self) -> String
{
"".to_string()
}
fn decorate_strong_start(&mut self) -> (String, Self::Annotation)
{
("".to_string(), ())
}
fn decorate_strong_end(&mut self) -> String
{
"".to_string()
}
fn decorate_code_start(&mut self) -> (String, Self::Annotation)
{
("".to_string(), ())
}
fn decorate_code_end(&mut self) -> String
{
"".to_string()
}
fn decorate_preformat_first(&mut self) -> Self::Annotation { () }
fn decorate_preformat_cont(&mut self) -> Self::Annotation { () }
fn decorate_image(&mut self, title: &str) -> (String, Self::Annotation)
{
(title.to_string(), ())
}
fn finalise(self) -> Vec<TaggedLine<()>> {
Vec::new()
}
fn make_subblock_decorator(&self) -> Self {
TrivialDecorator::new()
}
}
#[derive(Clone)]
pub struct RichDecorator {
}
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum RichAnnotation {
Default,
Link(String),
Image,
Emphasis,
Strong,
Code,
Preformat(bool),
}
impl Default for RichAnnotation {
fn default() -> Self {
RichAnnotation::Default
}
}
impl RichDecorator {
#[cfg_attr(feature="clippy", allow(new_without_default_derive))]
pub fn new() -> RichDecorator {
RichDecorator {
}
}
}
impl TextDecorator for RichDecorator {
type Annotation = RichAnnotation;
fn decorate_link_start(&mut self, url: &str) -> (String, Self::Annotation)
{
("".to_string(), RichAnnotation::Link(url.to_string()))
}
fn decorate_link_end(&mut self) -> String
{
"".to_string()
}
fn decorate_em_start(&mut self) -> (String, Self::Annotation)
{
("".to_string(), RichAnnotation::Emphasis)
}
fn decorate_em_end(&mut self) -> String
{
"".to_string()
}
fn decorate_strong_start(&mut self) -> (String, Self::Annotation)
{
("*".to_string(), RichAnnotation::Strong)
}
fn decorate_strong_end(&mut self) -> String
{
"*".to_string()
}
fn decorate_code_start(&mut self) -> (String, Self::Annotation)
{
("`".to_string(), RichAnnotation::Code)
}
fn decorate_code_end(&mut self) -> String
{
"`".to_string()
}
fn decorate_preformat_first(&mut self) -> Self::Annotation
{
RichAnnotation::Preformat(false)
}
fn decorate_preformat_cont(&mut self) -> Self::Annotation
{
RichAnnotation::Preformat(true)
}
fn decorate_image(&mut self, title: &str) -> (String, Self::Annotation)
{
(title.to_string(), RichAnnotation::Image)
}
fn finalise(self) -> Vec<TaggedLine<RichAnnotation>> {
Vec::new()
}
fn make_subblock_decorator(&self) -> Self {
RichDecorator::new()
}
}