First attempt at support for variable-width graphemes.

Doesn't completely work, but it's on the right track.
This commit is contained in:
Nathan Vegdahl 2015-01-01 17:35:34 -08:00
parent a205dce9dd
commit 74edf72cde
2 changed files with 113 additions and 7 deletions

View File

@ -4,6 +4,30 @@ use std::mem;
use std::str::Graphemes; use std::str::Graphemes;
use string_utils::{grapheme_count, grapheme_pos_to_byte_pos, is_line_ending}; use string_utils::{grapheme_count, grapheme_pos_to_byte_pos, is_line_ending};
const TAB_WIDTH: uint = 4;
/// Returns the visual width of a grapheme given a starting
/// position on a line.
fn grapheme_vis_width_at_vis_pos(g: &str, pos: uint) -> uint {
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);
}
}
}
}
/// A single line of text /// A single line of text
pub struct Line { pub struct Line {
@ -208,6 +232,19 @@ impl Line {
} }
/// Returns the visual cell width of the line
pub fn vis_width(&self) -> uint {
let mut width = 0;
for g in self.as_str().graphemes(true) {
let w = grapheme_vis_width_at_vis_pos(g, width);
width += w;
}
return width;
}
/// Returns an immutable string slice into the text block's memory /// Returns an immutable string slice into the text block's memory
pub fn as_str<'a>(&'a self) -> &'a str { pub fn as_str<'a>(&'a self) -> &'a str {
unsafe { unsafe {
@ -357,6 +394,15 @@ impl Line {
return iter; return iter;
} }
/// Returns an iterator over the graphemes of the line
pub fn grapheme_vis_iter<'a>(&'a self) -> LineGraphemeVisIter<'a> {
LineGraphemeVisIter {
graphemes: self.grapheme_iter(),
vis_pos: 0,
}
}
} }
@ -456,7 +502,9 @@ pub struct LineGraphemeIter<'a> {
impl<'a> LineGraphemeIter<'a> { impl<'a> LineGraphemeIter<'a> {
pub fn skip_graphemes(&mut self, n: uint) { pub fn skip_graphemes(&mut self, n: uint) {
for _ in range(0, n) { for _ in range(0, n) {
self.next(); if let None = self.next() {
break;
}
} }
} }
} }
@ -488,6 +536,54 @@ impl<'a> Iterator<&'a str> for LineGraphemeIter<'a> {
/// An iterator over the graphemes of a Line. This iterator yields not just
/// the grapheme, but also it's beginning visual position in the line and its
/// visual width.
pub struct LineGraphemeVisIter<'a> {
graphemes: LineGraphemeIter<'a>,
vis_pos: uint,
}
impl<'a> LineGraphemeVisIter<'a> {
pub fn skip_graphemes(&mut self, n: uint) {
for _ in range(0, n) {
if let None = self.next() {
break;
}
}
}
pub fn skip_vis_positions(&mut self, n: uint) {
let mut i = 0;
while i < n {
if let Some((_, _, width)) = self.next() {
i += width;
}
else {
break;
}
}
}
}
impl<'a> Iterator<(&'a str, uint, uint)> for LineGraphemeVisIter<'a> {
fn next(&mut self) -> Option<(&'a str, uint, uint)> {
if let Some(g) = self.graphemes.next() {
let pos = self.vis_pos;
let width = grapheme_vis_width_at_vis_pos(g, self.vis_pos);
self.vis_pos += width;
return Some((g, pos, width));
}
else {
return None;
}
}
}
//========================================================================= //=========================================================================
// Line tests // Line tests
//========================================================================= //=========================================================================

View File

@ -158,7 +158,7 @@ impl TermUI {
let mut col_num = editor.view_pos.1; let mut col_num = editor.view_pos.1;
let mut print_line_num = c1.0; let mut print_line_num = c1.0;
let mut print_col_num = c1.1; let mut print_col_num;
let max_print_line = c2.0 - c1.0; let max_print_line = c2.0 - c1.0;
let max_print_col = c2.1 - c1.1; let max_print_col = c2.1 - c1.1;
@ -169,15 +169,26 @@ impl TermUI {
loop { loop {
if let Some(line) = line_iter.next() { if let Some(line) = line_iter.next() {
let mut g_iter = line.grapheme_iter(); let mut g_iter = line.grapheme_vis_iter();
g_iter.skip_graphemes(editor.view_pos.1); g_iter.skip_vis_positions(editor.view_pos.1);
for (g, pos, width) in g_iter {
print_col_num = pos - editor.view_pos.1;
for g in g_iter {
if is_line_ending(g) { if is_line_ending(g) {
if (line_num, col_num) == cursor_pos { if (line_num, col_num) == cursor_pos {
self.rb.print(print_col_num, print_line_num, rustbox::RB_NORMAL, Color::Black, Color::White, " "); self.rb.print(print_col_num, print_line_num, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
} }
} }
else if g == "\t" {
for i in range(print_col_num, print_col_num + width) {
self.rb.print(i, print_line_num, rustbox::RB_NORMAL, Color::White, Color::Black, " ");
}
if (line_num, col_num) == cursor_pos {
self.rb.print(print_col_num, print_line_num, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
}
}
else { else {
if (line_num, col_num) == cursor_pos { if (line_num, col_num) == cursor_pos {
self.rb.print(print_col_num, print_line_num, rustbox::RB_NORMAL, Color::Black, Color::White, g); self.rb.print(print_col_num, print_line_num, rustbox::RB_NORMAL, Color::Black, Color::White, g);
@ -188,7 +199,7 @@ impl TermUI {
} }
col_num += 1; col_num += 1;
print_col_num += 1; print_col_num += width;
if print_col_num > max_print_col { if print_col_num > max_print_col {
break; break;
@ -205,7 +216,6 @@ impl TermUI {
line_num += 1; line_num += 1;
print_line_num += 1; print_line_num += 1;
col_num = editor.view_pos.1; col_num = editor.view_pos.1;
print_col_num = c1.1;
if print_line_num > max_print_line { if print_line_num > max_print_line {
break; break;