WIP getting graphemes working again.
This commit is contained in:
parent
ca948588e6
commit
2e9c2b2704
|
@ -69,9 +69,20 @@ impl Buffer {
|
||||||
self.text.len_chars()
|
self.text.len_chars()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grapheme_count(&self) -> usize {
|
pub fn nth_next_grapheme(&self, char_idx: usize, n: usize) -> usize {
|
||||||
// TODO: be correct
|
let mut char_idx = char_idx;
|
||||||
self.text.len_chars()
|
for _ in 0..n {
|
||||||
|
char_idx = self.text.next_grapheme_boundary(char_idx);
|
||||||
|
}
|
||||||
|
char_idx
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nth_prev_grapheme(&self, char_idx: usize, n: usize) -> usize {
|
||||||
|
let mut char_idx = char_idx;
|
||||||
|
for _ in 0..n {
|
||||||
|
char_idx = self.text.prev_grapheme_boundary(char_idx);
|
||||||
|
}
|
||||||
|
char_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_count(&self) -> usize {
|
pub fn line_count(&self) -> usize {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use formatter::LineFormatter;
|
||||||
use formatter::RoundingBehavior::*;
|
use formatter::RoundingBehavior::*;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use string_utils::{grapheme_count, str_to_line_ending, LineEnding};
|
use string_utils::{char_count, str_to_line_ending, LineEnding};
|
||||||
use utils::digit_count;
|
use utils::digit_count;
|
||||||
use self::cursor::CursorSet;
|
use self::cursor::CursorSet;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub struct Editor<T: LineFormatter> {
|
||||||
|
|
||||||
// The dimensions and position of just the text view portion of the editor
|
// The dimensions and position of just the text view portion of the editor
|
||||||
pub view_dim: (usize, usize), // (height, width)
|
pub view_dim: (usize, usize), // (height, width)
|
||||||
pub view_pos: (usize, usize), // (grapheme index, visual horizontal offset)
|
pub view_pos: (usize, usize), // (char index, visual horizontal offset)
|
||||||
|
|
||||||
// The editing cursor position
|
// The editing cursor position
|
||||||
pub cursors: CursorSet,
|
pub cursors: CursorSet,
|
||||||
|
@ -324,24 +324,24 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
// there are no cursors currently in view, and should jump to
|
// there are no cursors currently in view, and should jump to
|
||||||
// the closest cursor.
|
// the closest cursor.
|
||||||
|
|
||||||
// Find the first and last grapheme index visible within the editor.
|
// Find the first and last char index visible within the editor.
|
||||||
let g_first =
|
let c_first =
|
||||||
self.formatter
|
self.formatter
|
||||||
.index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor);
|
.index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor);
|
||||||
let mut g_last = self.formatter.index_offset_vertical_v2d(
|
let mut c_last = self.formatter.index_offset_vertical_v2d(
|
||||||
&self.buffer,
|
&self.buffer,
|
||||||
g_first,
|
c_first,
|
||||||
self.view_dim.0 as isize,
|
self.view_dim.0 as isize,
|
||||||
(Floor, Floor),
|
(Floor, Floor),
|
||||||
);
|
);
|
||||||
g_last =
|
c_last =
|
||||||
self.formatter
|
self.formatter
|
||||||
.index_set_horizontal_v2d(&self.buffer, g_last, self.view_dim.1, Floor);
|
.index_set_horizontal_v2d(&self.buffer, c_last, self.view_dim.1, Floor);
|
||||||
|
|
||||||
// Adjust the view depending on where the cursor is
|
// Adjust the view depending on where the cursor is
|
||||||
if self.cursors[0].range.0 < g_first {
|
if self.cursors[0].range.0 < c_first {
|
||||||
self.view_pos.0 = self.cursors[0].range.0;
|
self.view_pos.0 = self.cursors[0].range.0;
|
||||||
} else if self.cursors[0].range.0 > g_last {
|
} else if self.cursors[0].range.0 >= c_last {
|
||||||
self.view_pos.0 = self.formatter.index_offset_vertical_v2d(
|
self.view_pos.0 = self.formatter.index_offset_vertical_v2d(
|
||||||
&self.buffer,
|
&self.buffer,
|
||||||
self.cursors[0].range.0,
|
self.cursors[0].range.0,
|
||||||
|
@ -354,7 +354,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
pub fn insert_text_at_cursor(&mut self, text: &str) {
|
pub fn insert_text_at_cursor(&mut self, text: &str) {
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
let str_len = grapheme_count(text);
|
let str_len = char_count(text);
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
|
@ -421,13 +421,6 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
self.remove_text_behind_cursor(1);
|
self.remove_text_behind_cursor(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_text_at_grapheme(&mut self, text: &str, pos: usize) {
|
|
||||||
self.dirty = true;
|
|
||||||
let buf_len = self.buffer.grapheme_count();
|
|
||||||
self.buffer
|
|
||||||
.insert_text(text, if pos < buf_len { pos } else { buf_len });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_text_behind_cursor(&mut self, grapheme_count: usize) {
|
pub fn remove_text_behind_cursor(&mut self, grapheme_count: usize) {
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
|
@ -443,7 +436,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = min(c.range.0, grapheme_count);
|
let len = c.range.0 - self.buffer.nth_prev_grapheme(c.range.0, grapheme_count);
|
||||||
|
|
||||||
// Remove text
|
// Remove text
|
||||||
self.buffer.remove_text_before(c.range.0, len);
|
self.buffer.remove_text_before(c.range.0, len);
|
||||||
|
@ -475,16 +468,11 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
c.range.1 -= min(c.range.1, offset);
|
c.range.1 -= min(c.range.1, offset);
|
||||||
|
|
||||||
// Do nothing if there's nothing to delete.
|
// Do nothing if there's nothing to delete.
|
||||||
if c.range.1 == self.buffer.grapheme_count() {
|
if c.range.1 == self.buffer.char_count() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_len = if self.buffer.grapheme_count() > c.range.1 {
|
let len = self.buffer.nth_next_grapheme(c.range.1, grapheme_count) - c.range.1;
|
||||||
self.buffer.grapheme_count() - c.range.1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
let len = min(max_len, grapheme_count);
|
|
||||||
|
|
||||||
// Remove text
|
// Remove text
|
||||||
self.buffer.remove_text_after(c.range.1, len);
|
self.buffer.remove_text_after(c.range.1, len);
|
||||||
|
@ -548,7 +536,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor_to_end_of_buffer(&mut self) {
|
pub fn cursor_to_end_of_buffer(&mut self) {
|
||||||
let end = self.buffer.grapheme_count();
|
let end = self.buffer.char_count();
|
||||||
|
|
||||||
self.cursors = CursorSet::new();
|
self.cursors = CursorSet::new();
|
||||||
self.cursors[0].range = (end, end);
|
self.cursors[0].range = (end, end);
|
||||||
|
@ -560,12 +548,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
|
|
||||||
pub fn cursor_left(&mut self, n: usize) {
|
pub fn cursor_left(&mut self, n: usize) {
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
if c.range.0 >= n {
|
c.range.0 = self.buffer.nth_prev_grapheme(c.range.0, n);
|
||||||
c.range.0 -= n;
|
|
||||||
} else {
|
|
||||||
c.range.0 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
c.range.1 = c.range.0;
|
c.range.1 = c.range.0;
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
}
|
}
|
||||||
|
@ -576,12 +559,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
|
|
||||||
pub fn cursor_right(&mut self, n: usize) {
|
pub fn cursor_right(&mut self, n: usize) {
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
c.range.1 += n;
|
c.range.1 = self.buffer.nth_next_grapheme(c.range.1, n);
|
||||||
|
|
||||||
if c.range.1 > self.buffer.grapheme_count() {
|
|
||||||
c.range.1 = self.buffer.grapheme_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
c.range.0 = c.range.1;
|
c.range.0 = c.range.1;
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
}
|
}
|
||||||
|
@ -629,7 +607,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
(Round, Round),
|
(Round, Round),
|
||||||
);
|
);
|
||||||
|
|
||||||
if temp_index == self.buffer.grapheme_count() {
|
if temp_index == self.buffer.char_count() {
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
} else {
|
} else {
|
||||||
temp_index = self.formatter.index_set_horizontal_v2d(
|
temp_index = self.formatter.index_set_horizontal_v2d(
|
||||||
|
|
|
@ -26,13 +26,13 @@ pub trait LineFormatter {
|
||||||
where
|
where
|
||||||
T: Iterator<Item = &'a str>;
|
T: Iterator<Item = &'a str>;
|
||||||
|
|
||||||
/// Converts a grapheme index within a text into a visual 2d position.
|
/// Converts a char index within a text into a visual 2d position.
|
||||||
/// The text to be formatted is passed as a grapheme iterator.
|
/// The text to be formatted is passed as a grapheme iterator.
|
||||||
fn index_to_v2d<'a, T>(&'a self, g_iter: T, index: usize) -> (usize, usize)
|
fn index_to_v2d<'a, T>(&'a self, g_iter: T, char_idx: usize) -> (usize, usize)
|
||||||
where
|
where
|
||||||
T: Iterator<Item = &'a str>;
|
T: Iterator<Item = &'a str>;
|
||||||
|
|
||||||
/// Converts a visual 2d position into a grapheme index within a text.
|
/// Converts a visual 2d position into a char index within a text.
|
||||||
/// The text to be formatted is passed as a grapheme iterator.
|
/// The text to be formatted is passed as a grapheme iterator.
|
||||||
fn v2d_to_index<'a, T>(
|
fn v2d_to_index<'a, T>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -43,8 +43,8 @@ pub trait LineFormatter {
|
||||||
where
|
where
|
||||||
T: Iterator<Item = &'a str>;
|
T: Iterator<Item = &'a str>;
|
||||||
|
|
||||||
fn index_to_horizontal_v2d(&self, buf: &Buffer, index: usize) -> usize {
|
fn index_to_horizontal_v2d(&self, buf: &Buffer, char_idx: usize) -> usize {
|
||||||
let (line_i, col_i) = buf.index_to_line_col(index);
|
let (line_i, col_i) = buf.index_to_line_col(char_idx);
|
||||||
let line = buf.get_line(line_i);
|
let line = buf.get_line(line_i);
|
||||||
|
|
||||||
// Find the right block in the line, and the index within that block
|
// Find the right block in the line, and the index within that block
|
||||||
|
@ -57,12 +57,12 @@ pub trait LineFormatter {
|
||||||
return self.index_to_v2d(g_iter, col_i_adjusted).1;
|
return self.index_to_v2d(g_iter, col_i_adjusted).1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a grapheme index and a visual vertical offset, and returns the grapheme
|
/// Takes a char index and a visual vertical offset, and returns the char
|
||||||
/// index after that visual offset is applied.
|
/// index after that visual offset is applied.
|
||||||
fn index_offset_vertical_v2d(
|
fn index_offset_vertical_v2d(
|
||||||
&self,
|
&self,
|
||||||
buf: &Buffer,
|
buf: &Buffer,
|
||||||
index: usize,
|
char_idx: usize,
|
||||||
offset: isize,
|
offset: isize,
|
||||||
rounding: (RoundingBehavior, RoundingBehavior),
|
rounding: (RoundingBehavior, RoundingBehavior),
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
@ -70,7 +70,7 @@ pub trait LineFormatter {
|
||||||
// TODO: do this with bidirectional line iterator
|
// TODO: do this with bidirectional line iterator
|
||||||
|
|
||||||
// Get the line and block index of the given index
|
// Get the line and block index of the given index
|
||||||
let (mut line_i, mut col_i) = buf.index_to_line_col(index);
|
let (mut line_i, mut col_i) = buf.index_to_line_col(char_idx);
|
||||||
|
|
||||||
// Find the right block in the line, and the index within that block
|
// Find the right block in the line, and the index within that block
|
||||||
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
||||||
|
@ -104,7 +104,7 @@ pub trait LineFormatter {
|
||||||
|
|
||||||
// Check for off-the-end
|
// Check for off-the-end
|
||||||
if is_last_block && (line_i + 1) >= buf.line_count() {
|
if is_last_block && (line_i + 1) >= buf.line_count() {
|
||||||
return buf.grapheme_count();
|
return buf.char_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_last_block {
|
if is_last_block {
|
||||||
|
@ -153,17 +153,17 @@ pub trait LineFormatter {
|
||||||
return buf.line_col_to_index((line_i, col_i));
|
return buf.line_col_to_index((line_i, col_i));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a grapheme index and a desired visual horizontal position, and
|
/// Takes a char index and a desired visual horizontal position, and
|
||||||
/// returns a grapheme index on the same visual line as the given index,
|
/// returns a char index on the same visual line as the given index,
|
||||||
/// but offset to have the desired horizontal position.
|
/// but offset to have the desired horizontal position.
|
||||||
fn index_set_horizontal_v2d(
|
fn index_set_horizontal_v2d(
|
||||||
&self,
|
&self,
|
||||||
buf: &Buffer,
|
buf: &Buffer,
|
||||||
index: usize,
|
char_idx: usize,
|
||||||
horizontal: usize,
|
horizontal: usize,
|
||||||
rounding: RoundingBehavior,
|
rounding: RoundingBehavior,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let (line_i, col_i) = buf.index_to_line_col(index);
|
let (line_i, col_i) = buf.index_to_line_col(char_idx);
|
||||||
let line = buf.get_line(line_i);
|
let line = buf.get_line(line_i);
|
||||||
|
|
||||||
// Find the right block in the line, and the index within that block
|
// Find the right block in the line, and the index within that block
|
||||||
|
@ -189,7 +189,7 @@ pub trait LineFormatter {
|
||||||
new_col_i = line.len_chars() - 1;
|
new_col_i = line.len_chars() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (index + new_col_i) - col_i;
|
return (char_idx + new_col_i) - col_i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -345,8 +345,8 @@ impl TermUI {
|
||||||
// Percentage position in document
|
// Percentage position in document
|
||||||
// TODO: use view instead of cursor for calculation if there is more
|
// TODO: use view instead of cursor for calculation if there is more
|
||||||
// than one cursor.
|
// than one cursor.
|
||||||
let percentage: usize = if editor.buffer.grapheme_count() > 0 {
|
let percentage: usize = if editor.buffer.char_count() > 0 {
|
||||||
(((editor.cursors[0].range.0 as f32) / (editor.buffer.grapheme_count() as f32)) * 100.0)
|
(((editor.cursors[0].range.0 as f32) / (editor.buffer.char_count() as f32)) * 100.0)
|
||||||
as usize
|
as usize
|
||||||
} else {
|
} else {
|
||||||
100
|
100
|
||||||
|
@ -584,7 +584,7 @@ impl TermUI {
|
||||||
// Calculate the cell coordinates at which to draw the cursor
|
// Calculate the cell coordinates at which to draw the cursor
|
||||||
let pos_x = editor
|
let pos_x = editor
|
||||||
.formatter
|
.formatter
|
||||||
.index_to_horizontal_v2d(&self.editor.buffer, self.editor.buffer.grapheme_count());
|
.index_to_horizontal_v2d(&self.editor.buffer, self.editor.buffer.char_count());
|
||||||
let px = pos_x as isize + screen_col - editor.view_pos.1 as isize;
|
let px = pos_x as isize + screen_col - editor.view_pos.1 as isize;
|
||||||
let py = screen_line - 1;
|
let py = screen_line - 1;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user