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 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
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user