First attempt at support for variable-width graphemes.
Doesn't completely work, but it's on the right track.
This commit is contained in:
parent
a205dce9dd
commit
74edf72cde
|
@ -4,6 +4,30 @@ use std::mem;
|
|||
use std::str::Graphemes;
|
||||
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
|
||||
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
|
||||
pub fn as_str<'a>(&'a self) -> &'a str {
|
||||
unsafe {
|
||||
|
@ -357,6 +394,15 @@ impl Line {
|
|||
|
||||
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> {
|
||||
pub fn skip_graphemes(&mut self, n: uint) {
|
||||
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
|
||||
//=========================================================================
|
||||
|
|
|
@ -158,7 +158,7 @@ impl TermUI {
|
|||
let mut col_num = editor.view_pos.1;
|
||||
|
||||
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_col = c2.1 - c1.1;
|
||||
|
@ -169,15 +169,26 @@ impl TermUI {
|
|||
|
||||
loop {
|
||||
if let Some(line) = line_iter.next() {
|
||||
let mut g_iter = line.grapheme_iter();
|
||||
g_iter.skip_graphemes(editor.view_pos.1);
|
||||
let mut g_iter = line.grapheme_vis_iter();
|
||||
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 (line_num, col_num) == cursor_pos {
|
||||
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 {
|
||||
if (line_num, col_num) == cursor_pos {
|
||||
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;
|
||||
print_col_num += 1;
|
||||
print_col_num += width;
|
||||
|
||||
if print_col_num > max_print_col {
|
||||
break;
|
||||
|
@ -205,7 +216,6 @@ impl TermUI {
|
|||
line_num += 1;
|
||||
print_line_num += 1;
|
||||
col_num = editor.view_pos.1;
|
||||
print_col_num = c1.1;
|
||||
|
||||
if print_line_num > max_print_line {
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue
Block a user