termimad/code.rs
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
use {
crate::*,
minimad::Alignment,
};
/// a sequence of lines whose line-style is Code
#[derive(Debug)]
pub struct CodeBlock {
pub start: usize,
pub height: usize, // number of lines
pub width: usize, // length in chars of the widest line
}
impl CodeBlock {
/// ensure all lines of the block have the same width
pub fn justify(&self, lines: &mut [FmtLine<'_>]) {
for line in lines.iter_mut().skip(self.start).take(self.height) {
if let FmtLine::Normal(ref mut fc) = line {
fc.spacing = Some(Spacing {
width: self.width,
align: Alignment::Left,
});
}
}
}
}
const fn code_line_length(line: &FmtLine<'_>) -> Option<usize> {
match line {
FmtLine::Normal(fc) => match fc.kind {
CompositeKind::Code => Some(fc.visible_length),
_ => None,
},
_ => None,
}
}
/// find ranges of code lines in a text.
///
/// Warning: the indices in a codeblock are invalid as
/// soon as lines are inserted or removed. This function
/// should normally not be used from another module or lib
pub fn find_blocks(lines: &[FmtLine<'_>]) -> Vec<CodeBlock> {
let mut blocks: Vec<CodeBlock> = Vec::new();
let mut current: Option<CodeBlock> = None;
for (idx, line) in lines.iter().enumerate() {
if let Some(ll) = code_line_length(line) {
match current.as_mut() {
Some(b) => {
b.height += 1;
b.width = b.width.max(ll);
}
None => {
current = Some(CodeBlock {
start: idx,
height: 1,
width: ll,
});
}
}
} else if let Some(c) = current.take() {
blocks.push(c);
}
}
if let Some(c) = current.take() {
blocks.push(c);
}
blocks
}
/// ensure the widths of all lines in a code block are
/// the same line.
pub fn justify_blocks(lines: &mut [FmtLine<'_>]) {
let blocks = find_blocks(lines);
for b in blocks {
b.justify(lines);
}
}