1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use super::*;
use crate::walkers::*;
use failure::Error;
use oatie::doc::*;
use oatie::rtf::*;
use oatie::stepper::DocStepper;

// For function reuse
#[derive(Debug)]
pub enum StyleOp {
    AddStyle(RtfStyle, Option<String>),
    RemoveStyle(RtfStyle),
}

// TODO consider removing this and just use restyle
pub fn apply_style(
    ctx: ActionContext,
    style: RtfStyle,
    value: Option<String>,
) -> Result<Op<RtfSchema>, Error> {
    restyle(ctx, vec![StyleOp::AddStyle(style, value)])
}

// TODO consider removing this and just use restyle
pub fn remove_styles(ctx: ActionContext, styles: StyleSet) -> Result<Op<RtfSchema>, Error> {
    restyle(
        ctx,
        styles
            .styles()
            .into_iter()
            .map(|style| StyleOp::RemoveStyle(style))
            .collect(),
    )
}

pub fn restyle(ctx: ActionContext, ops: Vec<StyleOp>) -> Result<Op<RtfSchema>, Error> {
    // Find start and end carets, or return if either are missing.
    let (walker_start, walker_end) = match (ctx.get_walker(Pos::Start), ctx.get_walker(Pos::End)) {
        (Ok(walker_start), Ok(walker_end)) => (walker_start, walker_end),
        _ => {
            return Ok(Op::empty());
        }
    };

    // Calculate delta.
    let delta = walker_end.delta(&walker_start).unwrap_or(0);
    if delta == 0 {
        return Ok(Op::empty());
    }

    // Create style sets for adding or removing styles.
    let mut add_styles = hashset![];
    for op in &ops {
        if let &StyleOp::AddStyle(ref style, _) = op {
            add_styles.insert(style.to_owned());
        }
    }
    let mut remove_styles = hashset![];
    for op in &ops {
        if let &StyleOp::RemoveStyle(ref style) = op {
            remove_styles.insert(style.to_owned());
        }
    }

    let mut writer = walker_start.to_writer();

    // Remove styles.
    if !remove_styles.is_empty() {
        let mut doc1: DocStepper<RtfSchema> = walker_start.doc().to_owned();
        let doc2: DocStepper<RtfSchema> = walker_end.doc().to_owned();
        while doc1 != doc2 {
            match doc1.head() {
                Some(DocGroup(..)) => {
                    writer.del.begin();
                    doc1.enter();
                }
                Some(DocText(_, ref text)) => {
                    writer.del.place(&DelStyles(
                        text.char_len(),
                        StyleSet::from(remove_styles.clone()),
                    ));
                    doc1.skip(text.char_len());
                }
                None => {
                    writer.del.exit();
                    doc1.exit();
                }
            }
        }
    }

    // Add styles.
    if !add_styles.is_empty() {
        let mut doc1 = walker_start.doc().to_owned();
        let doc2 = walker_end.doc().to_owned();
        while doc1 != doc2 {
            match doc1.head() {
                Some(DocGroup(..)) => {
                    writer.add.begin();
                    doc1.enter();
                }
                Some(DocText(_, ref text)) => {
                    writer.add.place(&AddStyles(
                        text.char_len(),
                        StyleSet::from(add_styles.clone()),
                    ));
                    doc1.skip(text.char_len());
                }
                None => {
                    writer.add.exit();
                    doc1.exit();
                }
            }
        }
    }

    Ok(writer.exit_result())
}