159 lines
4.3 KiB
Rust
159 lines
4.3 KiB
Rust
use string_utils::{is_line_ending};
|
|
use buffer::line::{Line, LineGraphemeIter};
|
|
use buffer::line_formatter::{LineFormatter, RoundingBehavior};
|
|
|
|
//===================================================================
|
|
// LineFormatter implementation for terminals/consoles.
|
|
//===================================================================
|
|
|
|
pub struct GUILineFormatter {
|
|
pub tab_width: u8,
|
|
}
|
|
|
|
|
|
impl GUILineFormatter {
|
|
pub fn new(tab_width: u8) -> GUILineFormatter {
|
|
GUILineFormatter {
|
|
tab_width: tab_width,
|
|
}
|
|
}
|
|
|
|
|
|
/// 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 GUILineFormatter {
|
|
fn single_line_height(&self) -> usize {
|
|
return 1;
|
|
}
|
|
|
|
fn dimensions(&self, line: &Line) -> (usize, usize) {
|
|
return (1, self.vis_width(line));
|
|
}
|
|
|
|
|
|
fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize) {
|
|
let mut pos = 0;
|
|
let mut iter = line.grapheme_iter();
|
|
|
|
for _ in range(0, index) {
|
|
if let Some(g) = iter.next() {
|
|
let w = grapheme_vis_width_at_vis_pos(g, pos, self.tab_width as usize);
|
|
pos += w;
|
|
}
|
|
else {
|
|
panic!("GUILineFormatter::index_to_v2d(): index past end of line.");
|
|
}
|
|
}
|
|
|
|
return (0, pos);
|
|
}
|
|
|
|
|
|
fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
|
|
let mut pos = 0;
|
|
let mut i = 0;
|
|
let mut iter = line.grapheme_iter();
|
|
|
|
while pos < v2d.1 {
|
|
if let Some(g) = iter.next() {
|
|
let w = grapheme_vis_width_at_vis_pos(g, pos, self.tab_width as usize);
|
|
if (w + pos) > v2d.1 {
|
|
let d1 = v2d.1 - pos;
|
|
let d2 = (pos + w) - v2d.1;
|
|
if d2 < d1 {
|
|
i += 1;
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
pos += w;
|
|
i += 1;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 GUILineFormatter,
|
|
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 pos = self.pos;
|
|
|
|
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize);
|
|
self.pos = (self.pos.0, self.pos.1 + width);
|
|
|
|
return Some((g, (pos.0, pos.1), 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);
|
|
}
|
|
}
|
|
}
|
|
}
|