led/src/term_ui/formatter.rs

167 lines
4.4 KiB
Rust

use std::cmp::max;
use string_utils::{is_line_ending};
use buffer::line::{Line, LineGraphemeIter};
use buffer::line_formatter::{LineFormatter, RoundingBehavior};
//===================================================================
// LineFormatter implementation for terminals/consoles.
//===================================================================
pub struct ConsoleLineFormatter {
pub tab_width: u8,
pub wrap_width: usize,
}
impl ConsoleLineFormatter {
pub fn new(tab_width: u8) -> ConsoleLineFormatter {
ConsoleLineFormatter {
tab_width: tab_width,
wrap_width: 40,
}
}
/// Returns the visual cell width of a line
pub fn vis_width(&self, line: &Line) -> usize {
let mut width = 0;
for g in line.grapheme_iter() {
let w = grapheme_vis_width_at_vis_pos(g, width, self.tab_width as usize);
width += w;
}
return width;
}
pub fn vis_grapheme_iter<'b>(&'b self, line: &'b Line) -> ConsoleLineFormatterVisIter<'b> {
ConsoleLineFormatterVisIter {
grapheme_iter: line.grapheme_iter(),
f: self,
pos: (0, 0),
}
}
}
impl<'a> LineFormatter for ConsoleLineFormatter {
fn single_line_height(&self) -> usize {
return 1;
}
fn dimensions(&self, line: &Line) -> (usize, usize) {
let mut dim: (usize, usize) = (0, 0);
for (_, pos, width) in self.vis_grapheme_iter(line) {
dim = (max(dim.0, pos.0), max(dim.1, pos.1 + width));
}
dim.0 += 1;
return dim;
}
fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize) {
let mut pos = (0, 0);
let mut i = 0;
let mut last_width = 0;
for (_, _pos, width) in self.vis_grapheme_iter(line) {
pos = _pos;
last_width = width;
i += 1;
if i > index {
return pos;
}
}
return (pos.0, pos.1 + last_width);
}
fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
// TODO: handle rounding modes
let mut i = 0;
for (_, pos, _) in self.vis_grapheme_iter(line) {
if pos.0 > v2d.0 {
break;
}
else if pos.0 == v2d.0 && pos.1 >= v2d.1 {
break;
}
i += 1;
}
return i;
}
}
//===================================================================
// An iterator that iterates over the graphemes in a line in a
// manner consistent with the ConsoleFormatter.
//===================================================================
pub struct ConsoleLineFormatterVisIter<'a> {
grapheme_iter: LineGraphemeIter<'a>,
f: &'a ConsoleLineFormatter,
pos: (usize, usize),
}
impl<'a> Iterator for ConsoleLineFormatterVisIter<'a> {
type Item = (&'a str, (usize, usize), usize);
fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> {
if let Some(g) = self.grapheme_iter.next() {
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize);
if (self.pos.1 + width) > self.f.wrap_width {
let pos = (self.pos.0 + 1, 0);
self.pos = (self.pos.0 + 1, width);
return Some((g, pos, width));
}
else {
let pos = self.pos;
self.pos = (self.pos.0, self.pos.1 + width);
return Some((g, pos, width));
}
}
else {
return None;
}
}
}
//===================================================================
// Helper functions
//===================================================================
/// Returns the visual width of a grapheme given a starting
/// position on a line.
fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize {
match g {
"\t" => {
let ending_pos = ((pos / tab_width) + 1) * tab_width;
return ending_pos - pos;
},
_ => {
if is_line_ending(g) {
return 1;
}
else {
return g.width(true);
}
}
}
}