Change how line chunking works, to favor breaking on whitespace.
This commit is contained in:
parent
18920d9e87
commit
ebfedab58c
|
@ -553,7 +553,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
|
|
||||||
pub fn cursor_up(&mut self, n: usize) {
|
pub fn cursor_up(&mut self, n: usize) {
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
let vmove = -1 * (n * self.formatter.single_line_height()) as isize;
|
let vmove = -1 * n as isize;
|
||||||
|
|
||||||
let mut temp_index = self.formatter.index_offset_vertical_v2d(
|
let mut temp_index = self.formatter.index_offset_vertical_v2d(
|
||||||
&self.buffer,
|
&self.buffer,
|
||||||
|
@ -589,7 +589,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
|
|
||||||
pub fn cursor_down(&mut self, n: usize) {
|
pub fn cursor_down(&mut self, n: usize) {
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
let vmove = (n * self.formatter.single_line_height()) as isize;
|
let vmove = n as isize;
|
||||||
|
|
||||||
let mut temp_index = self.formatter.index_offset_vertical_v2d(
|
let mut temp_index = self.formatter.index_offset_vertical_v2d(
|
||||||
&self.buffer,
|
&self.buffer,
|
||||||
|
@ -624,8 +624,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn page_up(&mut self) {
|
pub fn page_up(&mut self) {
|
||||||
let move_amount =
|
let move_amount = self.view_dim.0 - max(self.view_dim.0 / 8, 1);
|
||||||
self.view_dim.0 - max(self.view_dim.0 / 8, self.formatter.single_line_height());
|
|
||||||
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.view_pos.0,
|
self.view_pos.0,
|
||||||
|
@ -640,8 +639,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn page_down(&mut self) {
|
pub fn page_down(&mut self) {
|
||||||
let move_amount =
|
let move_amount = self.view_dim.0 - max(self.view_dim.0 / 8, 1);
|
||||||
self.view_dim.0 - max(self.view_dim.0 / 8, self.formatter.single_line_height());
|
|
||||||
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.view_pos.0,
|
self.view_pos.0,
|
||||||
|
|
146
src/formatter.rs
146
src/formatter.rs
|
@ -1,17 +1,23 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use ropey::RopeSlice;
|
use ropey::RopeSlice;
|
||||||
|
|
||||||
use crate::{buffer::Buffer, utils::RopeGraphemes};
|
use crate::{
|
||||||
|
buffer::Buffer,
|
||||||
|
utils::{is_grapheme_boundary, RopeGraphemes},
|
||||||
|
};
|
||||||
|
|
||||||
// Maximum graphemes in a line before a soft line break is forced.
|
// Maximum chars in a line before a soft line break is forced.
|
||||||
// This is necessary to prevent pathological formatting cases which
|
// This is necessary to prevent pathological formatting cases which
|
||||||
// could slow down the editor arbitrarily for arbitrarily long
|
// could slow down the editor arbitrarily for arbitrarily long
|
||||||
// lines.
|
// lines.
|
||||||
pub const LINE_BLOCK_LENGTH: usize = 1 << 12;
|
const LINE_BLOCK_LENGTH: usize = 1 << 12;
|
||||||
|
|
||||||
|
// A fudge-factor for the above block length, to allow looking for natural
|
||||||
|
// breaks.
|
||||||
|
const LINE_BLOCK_FUDGE: usize = 32;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum RoundingBehavior {
|
pub enum RoundingBehavior {
|
||||||
Round,
|
Round,
|
||||||
|
@ -20,8 +26,6 @@ pub enum RoundingBehavior {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LineFormatter {
|
pub trait LineFormatter {
|
||||||
fn single_line_height(&self) -> usize;
|
|
||||||
|
|
||||||
/// Returns the 2d visual dimensions of the given text when formatted
|
/// Returns the 2d visual dimensions of the given text when formatted
|
||||||
/// by the formatter.
|
/// by the formatter.
|
||||||
/// The text to be formatted is passed as a grapheme iterator.
|
/// The text to be formatted is passed as a grapheme iterator.
|
||||||
|
@ -46,17 +50,17 @@ pub trait LineFormatter {
|
||||||
where
|
where
|
||||||
T: Iterator<Item = RopeSlice<'a>>;
|
T: Iterator<Item = RopeSlice<'a>>;
|
||||||
|
|
||||||
|
/// Converts from char index to the horizontal 2d char index.
|
||||||
fn index_to_horizontal_v2d(&self, buf: &Buffer, char_idx: usize) -> usize {
|
fn index_to_horizontal_v2d(&self, buf: &Buffer, char_idx: usize) -> usize {
|
||||||
let (line_i, col_i) = buf.index_to_line_col(char_idx);
|
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
|
||||||
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
let (_, block_range) = block_index_and_range(&line, col_i);
|
||||||
|
let col_i_adjusted = col_i - dbg!(block_range).0;
|
||||||
|
|
||||||
// Get an iter into the right block
|
// Get an iter into the right block
|
||||||
let a = line_block * LINE_BLOCK_LENGTH;
|
let g_iter = RopeGraphemes::new(&line.slice(block_range.0..block_range.1));
|
||||||
let b = min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH);
|
|
||||||
let g_iter = RopeGraphemes::new(&line.slice(a..b));
|
|
||||||
return self.index_to_v2d(g_iter, col_i_adjusted).1;
|
return self.index_to_v2d(g_iter, col_i_adjusted).1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,16 +78,14 @@ pub trait LineFormatter {
|
||||||
|
|
||||||
// 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(char_idx);
|
let (mut line_i, mut col_i) = buf.index_to_line_col(char_idx);
|
||||||
|
let mut 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
|
||||||
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
let (line_block, block_range) = block_index_and_range(&line, col_i);
|
||||||
|
let col_i_adjusted = col_i - block_range.0;
|
||||||
|
|
||||||
let mut line = buf.get_line(line_i);
|
|
||||||
let (mut y, x) = self.index_to_v2d(
|
let (mut y, x) = self.index_to_v2d(
|
||||||
RopeGraphemes::new(&line.slice(
|
RopeGraphemes::new(&line.slice(block_range.0..block_range.1)),
|
||||||
(line_block * LINE_BLOCK_LENGTH)
|
|
||||||
..min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH),
|
|
||||||
)),
|
|
||||||
col_i_adjusted,
|
col_i_adjusted,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -93,17 +95,15 @@ pub trait LineFormatter {
|
||||||
let mut block_index: usize = line_block;
|
let mut block_index: usize = line_block;
|
||||||
loop {
|
loop {
|
||||||
line = buf.get_line(line_i);
|
line = buf.get_line(line_i);
|
||||||
let (h, _) = self.dimensions(RopeGraphemes::new(&line.slice(
|
let (block_start, block_end) = char_range_from_block_index(&line, block_index);
|
||||||
(block_index * LINE_BLOCK_LENGTH)
|
let (h, _) = self.dimensions(RopeGraphemes::new(&line.slice(block_start..block_end)));
|
||||||
..min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH),
|
|
||||||
)));
|
|
||||||
|
|
||||||
if new_y >= 0 && new_y < h as isize {
|
if new_y >= 0 && new_y < h as isize {
|
||||||
y = new_y as usize;
|
y = new_y as usize;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if new_y > 0 {
|
if new_y > 0 {
|
||||||
let is_last_block = block_index >= last_block_index(line.len_chars());
|
let is_last_block = block_index >= (block_count(&line) - 1);
|
||||||
|
|
||||||
// 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() {
|
||||||
|
@ -126,14 +126,13 @@ pub trait LineFormatter {
|
||||||
if block_index == 0 {
|
if block_index == 0 {
|
||||||
line_i -= 1;
|
line_i -= 1;
|
||||||
line = buf.get_line(line_i);
|
line = buf.get_line(line_i);
|
||||||
block_index = last_block_index(line.len_chars());
|
block_index = block_count(&line) - 1;
|
||||||
} else {
|
} else {
|
||||||
block_index -= 1;
|
block_index -= 1;
|
||||||
}
|
}
|
||||||
let (h, _) = self.dimensions(RopeGraphemes::new(&line.slice(
|
let (block_start, block_end) = char_range_from_block_index(&line, block_index);
|
||||||
(block_index * LINE_BLOCK_LENGTH)
|
let (h, _) =
|
||||||
..min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH),
|
self.dimensions(RopeGraphemes::new(&line.slice(block_start..block_end)));
|
||||||
)));
|
|
||||||
new_y += h as isize;
|
new_y += h as isize;
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
|
@ -143,15 +142,13 @@ pub trait LineFormatter {
|
||||||
|
|
||||||
// Next, convert the resulting coordinates back into buffer-wide
|
// Next, convert the resulting coordinates back into buffer-wide
|
||||||
// coordinates.
|
// coordinates.
|
||||||
let block_slice = line.slice(
|
let (block_start, block_end) = char_range_from_block_index(&line, block_index);
|
||||||
(block_index * LINE_BLOCK_LENGTH)
|
let block_slice = line.slice(block_start..block_end);
|
||||||
..min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH),
|
|
||||||
);
|
|
||||||
let block_col_i = min(
|
let block_col_i = min(
|
||||||
self.v2d_to_index(RopeGraphemes::new(&block_slice), (y, x), rounding),
|
self.v2d_to_index(RopeGraphemes::new(&block_slice), (y, x), rounding),
|
||||||
LINE_BLOCK_LENGTH - 1,
|
LINE_BLOCK_LENGTH - 1,
|
||||||
);
|
);
|
||||||
col_i = (block_index * LINE_BLOCK_LENGTH) + block_col_i;
|
col_i = block_start + block_col_i;
|
||||||
|
|
||||||
return buf.line_col_to_index((line_i, col_i));
|
return buf.line_col_to_index((line_i, col_i));
|
||||||
}
|
}
|
||||||
|
@ -170,21 +167,20 @@ pub trait LineFormatter {
|
||||||
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
|
||||||
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
let (_, block_range) = block_index_and_range(&line, col_i);
|
||||||
let start_index = line_block * LINE_BLOCK_LENGTH;
|
let col_i_adjusted = col_i - block_range.0;
|
||||||
let end_index = min(line.len_chars(), start_index + LINE_BLOCK_LENGTH);
|
|
||||||
|
|
||||||
// Calculate the horizontal position
|
// Calculate the horizontal position
|
||||||
let (v, _) = self.index_to_v2d(
|
let (v, _) = self.index_to_v2d(
|
||||||
RopeGraphemes::new(&line.slice(start_index..end_index)),
|
RopeGraphemes::new(&line.slice(block_range.0..block_range.1)),
|
||||||
col_i_adjusted,
|
col_i_adjusted,
|
||||||
);
|
);
|
||||||
let block_col_i = self.v2d_to_index(
|
let block_col_i = self.v2d_to_index(
|
||||||
RopeGraphemes::new(&line.slice(start_index..end_index)),
|
RopeGraphemes::new(&line.slice(block_range.0..block_range.1)),
|
||||||
(v, horizontal),
|
(v, horizontal),
|
||||||
(RoundingBehavior::Floor, rounding),
|
(RoundingBehavior::Floor, rounding),
|
||||||
);
|
);
|
||||||
let mut new_col_i = start_index + min(block_col_i, LINE_BLOCK_LENGTH - 1);
|
let mut new_col_i = block_range.0 + min(block_col_i, LINE_BLOCK_LENGTH - 1);
|
||||||
|
|
||||||
// Make sure we're not pushing the index off the end of the line
|
// Make sure we're not pushing the index off the end of the line
|
||||||
if (line_i + 1) < buf.line_count() && new_col_i >= line.len_chars() && line.len_chars() > 0
|
if (line_i + 1) < buf.line_count() && new_col_i >= line.len_chars() && line.len_chars() > 0
|
||||||
|
@ -196,19 +192,75 @@ pub trait LineFormatter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_index_and_offset(index: usize) -> (usize, usize) {
|
// Finds the best break at or before the given char index, bounded by
|
||||||
(index / LINE_BLOCK_LENGTH, index % LINE_BLOCK_LENGTH)
|
// the given `lower_limit`.
|
||||||
|
pub fn find_good_break(slice: &RopeSlice, lower_limit: usize, char_idx: usize) -> usize {
|
||||||
|
let char_idx = char_idx.min(slice.len_chars());
|
||||||
|
let lower_limit = lower_limit.min(slice.len_chars());
|
||||||
|
|
||||||
|
// Find a whitespace break, if any.
|
||||||
|
let mut i = char_idx;
|
||||||
|
while i > lower_limit {
|
||||||
|
match slice.char(i - 1) {
|
||||||
|
// Previous char is whitespace.
|
||||||
|
'\u{0009}' | '\u{0020}' | '\u{00A0}' | '\u{2000}' | '\u{2001}' | '\u{2002}'
|
||||||
|
| '\u{2003}' | '\u{2004}' | '\u{2005}' | '\u{2006}' | '\u{2007}' | '\u{2008}'
|
||||||
|
| '\u{2009}' | '\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' => {
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_block_index(gc: usize) -> usize {
|
// Previous char is not whitespace.
|
||||||
let mut block_count = gc / LINE_BLOCK_LENGTH;
|
_ => {
|
||||||
if (gc % LINE_BLOCK_LENGTH) > 0 {
|
i -= 1;
|
||||||
block_count += 1;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if block_count > 0 {
|
// Otherwise, at least try to find a grapheme break.
|
||||||
return block_count - 1;
|
let mut i = char_idx;
|
||||||
|
while i > lower_limit && !is_grapheme_boundary(slice, i) {
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > lower_limit {
|
||||||
|
i
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
char_idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn char_range_from_block_index(slice: &RopeSlice, block_idx: usize) -> (usize, usize) {
|
||||||
|
let start = {
|
||||||
|
let initial = LINE_BLOCK_LENGTH * block_idx;
|
||||||
|
find_good_break(slice, initial - LINE_BLOCK_FUDGE.min(initial), initial)
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = {
|
||||||
|
let initial = LINE_BLOCK_LENGTH * (block_idx + 1);
|
||||||
|
find_good_break(slice, initial - LINE_BLOCK_FUDGE.min(initial), initial)
|
||||||
|
};
|
||||||
|
|
||||||
|
(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_index_and_range(slice: &RopeSlice, char_idx: usize) -> (usize, (usize, usize)) {
|
||||||
|
let mut block_index = char_idx / LINE_BLOCK_LENGTH;
|
||||||
|
let mut range = char_range_from_block_index(slice, block_index);
|
||||||
|
if char_idx >= range.1 && range.1 < slice.len_chars() {
|
||||||
|
block_index += 1;
|
||||||
|
range = char_range_from_block_index(slice, block_index);
|
||||||
|
}
|
||||||
|
(block_index, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_count(slice: &RopeSlice) -> usize {
|
||||||
|
let char_count = slice.len_chars();
|
||||||
|
let mut last_idx = (char_count - 1.min(char_count)) / LINE_BLOCK_LENGTH;
|
||||||
|
|
||||||
|
let range = char_range_from_block_index(slice, last_idx + 1);
|
||||||
|
if range.0 < range.1 {
|
||||||
|
last_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_idx + 1
|
||||||
|
}
|
||||||
|
|
|
@ -66,10 +66,6 @@ impl ConsoleLineFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LineFormatter for ConsoleLineFormatter {
|
impl LineFormatter for ConsoleLineFormatter {
|
||||||
fn single_line_height(&self) -> usize {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize)
|
fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize)
|
||||||
where
|
where
|
||||||
T: Iterator<Item = RopeSlice<'a>>,
|
T: Iterator<Item = RopeSlice<'a>>,
|
||||||
|
@ -80,7 +76,7 @@ impl LineFormatter for ConsoleLineFormatter {
|
||||||
dim = (max(dim.0, pos.0), max(dim.1, pos.1 + width));
|
dim = (max(dim.0, pos.0), max(dim.1, pos.1 + width));
|
||||||
}
|
}
|
||||||
|
|
||||||
dim.0 += self.single_line_height();
|
dim.0 += 1;
|
||||||
|
|
||||||
return dim;
|
return dim;
|
||||||
}
|
}
|
||||||
|
@ -180,24 +176,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.f.maintain_indent {
|
if self.f.maintain_indent {
|
||||||
let pos = (
|
let pos = (self.pos.0 + 1, self.indent + self.f.wrap_additional_indent);
|
||||||
self.pos.0 + self.f.single_line_height(),
|
|
||||||
self.indent + self.f.wrap_additional_indent,
|
|
||||||
);
|
|
||||||
self.pos = (
|
self.pos = (
|
||||||
self.pos.0 + self.f.single_line_height(),
|
self.pos.0 + 1,
|
||||||
self.indent + self.f.wrap_additional_indent + width,
|
self.indent + self.f.wrap_additional_indent + width,
|
||||||
);
|
);
|
||||||
return Some((g, pos, width));
|
return Some((g, pos, width));
|
||||||
} else {
|
} else {
|
||||||
let pos = (
|
let pos = (self.pos.0 + 1, self.f.wrap_additional_indent);
|
||||||
self.pos.0 + self.f.single_line_height(),
|
self.pos = (self.pos.0 + 1, self.f.wrap_additional_indent + width);
|
||||||
self.f.wrap_additional_indent,
|
|
||||||
);
|
|
||||||
self.pos = (
|
|
||||||
self.pos.0 + self.f.single_line_height(),
|
|
||||||
self.f.wrap_additional_indent + width,
|
|
||||||
);
|
|
||||||
return Some((g, pos, width));
|
return Some((g, pos, width));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -273,15 +260,10 @@ where
|
||||||
|
|
||||||
if self.pos.1 > 0 {
|
if self.pos.1 > 0 {
|
||||||
if self.f.maintain_indent {
|
if self.f.maintain_indent {
|
||||||
self.pos = (
|
self.pos =
|
||||||
self.pos.0 + self.f.single_line_height(),
|
(self.pos.0 + 1, self.indent + self.f.wrap_additional_indent);
|
||||||
self.indent + self.f.wrap_additional_indent,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
self.pos = (
|
self.pos = (self.pos.0 + 1, self.f.wrap_additional_indent);
|
||||||
self.pos.0 + self.f.single_line_height(),
|
|
||||||
self.f.wrap_additional_indent,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,8 +303,8 @@ mod tests {
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
|
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
|
use crate::formatter::LineFormatter;
|
||||||
use crate::formatter::RoundingBehavior::{Ceiling, Floor, Round};
|
use crate::formatter::RoundingBehavior::{Ceiling, Floor, Round};
|
||||||
use crate::formatter::{LineFormatter, LINE_BLOCK_LENGTH};
|
|
||||||
use crate::utils::RopeGraphemes;
|
use crate::utils::RopeGraphemes;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -355,6 +337,19 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dimensions_3() {
|
fn dimensions_3() {
|
||||||
|
let text = Rope::from_str("Hello there, stranger! How are you doing this fine day?"); // 56 graphemes long
|
||||||
|
|
||||||
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
|
f.wrap_type = WrapType::WordWrap(0);
|
||||||
|
f.maintain_indent = false;
|
||||||
|
f.wrap_additional_indent = 0;
|
||||||
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
|
assert_eq!(f.dimensions(RopeGraphemes::new(&text.slice(..))), (6, 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dimensions_4() {
|
||||||
// 55 graphemes long
|
// 55 graphemes long
|
||||||
let text = Rope::from_str(
|
let text = Rope::from_str(
|
||||||
"税マイミ文末\
|
"税マイミ文末\
|
||||||
|
@ -379,7 +374,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dimensions_4() {
|
fn dimensions_5() {
|
||||||
// 55 graphemes long
|
// 55 graphemes long
|
||||||
let text = Rope::from_str(
|
let text = Rope::from_str(
|
||||||
"税マイミ文末\
|
"税マイミ文末\
|
||||||
|
@ -672,6 +667,36 @@ mod tests {
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 8);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn index_to_horizontal_v2d_3() {
|
||||||
|
let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long
|
||||||
|
|
||||||
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
|
f.wrap_type = WrapType::WordWrap(0);
|
||||||
|
f.maintain_indent = false;
|
||||||
|
f.wrap_additional_indent = 0;
|
||||||
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 0), 0);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 5), 5);
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 6), 0);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 12), 6);
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 13), 0);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 22), 9);
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 23), 0);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 34), 11);
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 35), 0);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 45), 10);
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 46), 0);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 55), 9);
|
||||||
|
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 9);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_set_horizontal_v2d_1() {
|
fn index_set_horizontal_v2d_1() {
|
||||||
let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long
|
let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crossterm::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::Editor,
|
editor::Editor,
|
||||||
formatter::{block_index_and_offset, LineFormatter, LINE_BLOCK_LENGTH},
|
formatter::{block_count, block_index_and_range, char_range_from_block_index, LineFormatter},
|
||||||
string_utils::{line_ending_to_str, rope_slice_is_line_ending, LineEnding},
|
string_utils::{line_ending_to_str, rope_slice_is_line_ending, LineEnding},
|
||||||
utils::{digit_count, RopeGraphemes},
|
utils::{digit_count, RopeGraphemes},
|
||||||
};
|
};
|
||||||
|
@ -487,19 +487,11 @@ impl TermUI {
|
||||||
// Calculate all the starting info
|
// Calculate all the starting info
|
||||||
let gutter_width = editor.editor_dim.1 - editor.view_dim.1;
|
let gutter_width = editor.editor_dim.1 - editor.view_dim.1;
|
||||||
let (line_index, col_i) = editor.buffer.index_to_line_col(editor.view_pos.0);
|
let (line_index, col_i) = editor.buffer.index_to_line_col(editor.view_pos.0);
|
||||||
let (mut line_block_index, _) = block_index_and_offset(col_i);
|
|
||||||
let mut char_index = editor
|
|
||||||
.buffer
|
|
||||||
.line_col_to_index((line_index, line_block_index * LINE_BLOCK_LENGTH));
|
|
||||||
let temp_line = editor.buffer.get_line(line_index);
|
let temp_line = editor.buffer.get_line(line_index);
|
||||||
|
let (mut line_block_index, block_range) = block_index_and_range(&temp_line, col_i);
|
||||||
|
let mut char_index = editor.buffer.line_col_to_index((line_index, block_range.0));
|
||||||
let (vis_line_offset, _) = editor.formatter.index_to_v2d(
|
let (vis_line_offset, _) = editor.formatter.index_to_v2d(
|
||||||
RopeGraphemes::new(&temp_line.slice(
|
RopeGraphemes::new(&temp_line.slice(block_range.0..block_range.1)),
|
||||||
(line_block_index * LINE_BLOCK_LENGTH)
|
|
||||||
..min(
|
|
||||||
temp_line.len_chars(),
|
|
||||||
(line_block_index + 1) * LINE_BLOCK_LENGTH,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
editor.view_pos.0 - char_index,
|
editor.view_pos.0 - char_index,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -527,16 +519,16 @@ impl TermUI {
|
||||||
|
|
||||||
// Loop through the graphemes of the line and print them to
|
// Loop through the graphemes of the line and print them to
|
||||||
// the screen.
|
// the screen.
|
||||||
let mut line_g_index: usize = 0;
|
let max_block_index = block_count(&line) - 1;
|
||||||
let mut last_pos_y = 0;
|
let block_range = char_range_from_block_index(&line, line_block_index);
|
||||||
let mut lines_traversed: usize = 0;
|
|
||||||
let line_len = line.len_chars();
|
|
||||||
let mut g_iter = editor.formatter.iter(RopeGraphemes::new(
|
let mut g_iter = editor.formatter.iter(RopeGraphemes::new(
|
||||||
&line.slice((line_block_index * LINE_BLOCK_LENGTH)..line_len),
|
&line.slice(block_range.0..block_range.1),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let mut last_pos_y = 0;
|
||||||
|
let mut lines_traversed: usize = 0;
|
||||||
loop {
|
loop {
|
||||||
if let Some((g, (pos_y, pos_x), width)) = g_iter.next() {
|
for (g, (pos_y, pos_x), width) in g_iter {
|
||||||
if last_pos_y != pos_y {
|
if last_pos_y != pos_y {
|
||||||
if last_pos_y < pos_y {
|
if last_pos_y < pos_y {
|
||||||
lines_traversed += pos_y - last_pos_y;
|
lines_traversed += pos_y - last_pos_y;
|
||||||
|
@ -600,19 +592,17 @@ impl TermUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
char_index += g.chars().count();
|
char_index += g.chars().count();
|
||||||
line_g_index += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if line_g_index >= LINE_BLOCK_LENGTH {
|
// Move on to the next block.
|
||||||
line_block_index += 1;
|
line_block_index += 1;
|
||||||
line_g_index = 0;
|
if line_block_index <= max_block_index {
|
||||||
let line_len = line.len_chars();
|
let block_range = char_range_from_block_index(&line, line_block_index);
|
||||||
g_iter = editor.formatter.iter(RopeGraphemes::new(
|
g_iter = editor.formatter.iter(RopeGraphemes::new(
|
||||||
&line.slice((line_block_index * LINE_BLOCK_LENGTH)..line_len),
|
&line.slice(block_range.0..block_range.1),
|
||||||
));
|
));
|
||||||
lines_traversed += 1;
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user