Updated to ropey git master, and got everything working again.
However, everything is very, very slow now.
This commit is contained in:
parent
16f2aab223
commit
de625e71dc
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -3,7 +3,7 @@ name = "Led"
|
|||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ropey 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ropey 0.6.3 (git+https://github.com/cessen/ropey)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -87,11 +87,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "ropey"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "0.6.3"
|
||||
source = "git+https://github.com/cessen/ropey#e9b361b5b45d3e389bbfcb4f75127bec69e0b199"
|
||||
dependencies = [
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -209,7 +208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
|
||||
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
|
||||
"checksum ropey 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "661796e716a1916c9fd80457ee204b05b47afa4c1f1ceec3c7fd40e59f0e31f1"
|
||||
"checksum ropey 0.6.3 (git+https://github.com/cessen/ropey)" = "<none>"
|
||||
"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
|
||||
"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
|
||||
"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
|
||||
|
|
|
@ -9,7 +9,7 @@ name = "led"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
ropey = "0.6"
|
||||
ropey = { git = "https://github.com/cessen/ropey", branch = "master" }
|
||||
unicode-segmentation = "1.2"
|
||||
unicode-width = "0.1"
|
||||
serde = "1.*"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,8 +7,8 @@ use formatter::LineFormatter;
|
|||
use formatter::RoundingBehavior::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::cmp::{max, min};
|
||||
use string_utils::{char_count, str_to_line_ending, LineEnding};
|
||||
use utils::digit_count;
|
||||
use string_utils::{char_count, rope_slice_to_line_ending, LineEnding};
|
||||
use utils::{digit_count, RopeGraphemes};
|
||||
use self::cursor::CursorSet;
|
||||
|
||||
mod cursor;
|
||||
|
@ -100,17 +100,15 @@ impl<T: LineFormatter> Editor<T> {
|
|||
for line in self.buffer.line_iter().take(100) {
|
||||
// Get the line ending
|
||||
let ending = if line.len_chars() == 1 {
|
||||
let g = line.slice((line.len_chars() - 1)..)
|
||||
.graphemes()
|
||||
let g = RopeGraphemes::new(&line.slice((line.len_chars() - 1)..))
|
||||
.last()
|
||||
.unwrap();
|
||||
str_to_line_ending(g)
|
||||
rope_slice_to_line_ending(&g)
|
||||
} else if line.len_chars() > 1 {
|
||||
let g = line.slice((line.len_chars() - 2)..)
|
||||
.graphemes()
|
||||
let g = RopeGraphemes::new(&line.slice((line.len_chars() - 2)..))
|
||||
.last()
|
||||
.unwrap();
|
||||
str_to_line_ending(g)
|
||||
rope_slice_to_line_ending(&g)
|
||||
} else {
|
||||
LineEnding::None
|
||||
};
|
||||
|
@ -626,7 +624,7 @@ impl<T: LineFormatter> Editor<T> {
|
|||
|
||||
pub fn page_up(&mut self) {
|
||||
let move_amount =
|
||||
self.view_dim.0 - max((self.view_dim.0 / 8), self.formatter.single_line_height());
|
||||
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.buffer,
|
||||
self.view_pos.0,
|
||||
|
@ -642,7 +640,7 @@ impl<T: LineFormatter> Editor<T> {
|
|||
|
||||
pub fn page_down(&mut self) {
|
||||
let move_amount =
|
||||
self.view_dim.0 - max((self.view_dim.0 / 8), self.formatter.single_line_height());
|
||||
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.buffer,
|
||||
self.view_pos.0,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::cmp::min;
|
||||
use ropey::RopeSlice;
|
||||
use buffer::Buffer;
|
||||
use utils::RopeGraphemes;
|
||||
|
||||
// Maximum graphemes in a line before a soft line break is forced.
|
||||
// This is necessary to prevent pathological formatting cases which
|
||||
|
@ -24,13 +26,13 @@ pub trait LineFormatter {
|
|||
/// The text to be formatted is passed as a grapheme iterator.
|
||||
fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize)
|
||||
where
|
||||
T: Iterator<Item = &'a str>;
|
||||
T: Iterator<Item = RopeSlice<'a>>;
|
||||
|
||||
/// Converts a char index within a text into a visual 2d position.
|
||||
/// The text to be formatted is passed as a grapheme iterator.
|
||||
fn index_to_v2d<'a, T>(&'a self, g_iter: T, char_idx: usize) -> (usize, usize)
|
||||
where
|
||||
T: Iterator<Item = &'a str>;
|
||||
T: Iterator<Item = RopeSlice<'a>>;
|
||||
|
||||
/// Converts a visual 2d position into a char index within a text.
|
||||
/// The text to be formatted is passed as a grapheme iterator.
|
||||
|
@ -41,7 +43,7 @@ pub trait LineFormatter {
|
|||
rounding: (RoundingBehavior, RoundingBehavior),
|
||||
) -> usize
|
||||
where
|
||||
T: Iterator<Item = &'a str>;
|
||||
T: Iterator<Item = RopeSlice<'a>>;
|
||||
|
||||
fn index_to_horizontal_v2d(&self, buf: &Buffer, char_idx: usize) -> usize {
|
||||
let (line_i, col_i) = buf.index_to_line_col(char_idx);
|
||||
|
@ -53,7 +55,7 @@ pub trait LineFormatter {
|
|||
// Get an iter into the right block
|
||||
let a = line_block * LINE_BLOCK_LENGTH;
|
||||
let b = min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH);
|
||||
let g_iter = line.slice(a..b).graphemes();
|
||||
let g_iter = RopeGraphemes::new(&line.slice(a..b));
|
||||
return self.index_to_v2d(g_iter, col_i_adjusted).1;
|
||||
}
|
||||
|
||||
|
@ -77,10 +79,10 @@ pub trait LineFormatter {
|
|||
|
||||
let mut line = buf.get_line(line_i);
|
||||
let (mut y, x) = self.index_to_v2d(
|
||||
line.slice(
|
||||
RopeGraphemes::new(&line.slice(
|
||||
(line_block * LINE_BLOCK_LENGTH)
|
||||
..min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH),
|
||||
).graphemes(),
|
||||
)),
|
||||
col_i_adjusted,
|
||||
);
|
||||
|
||||
|
@ -90,10 +92,10 @@ pub trait LineFormatter {
|
|||
let mut block_index: usize = line_block;
|
||||
loop {
|
||||
line = buf.get_line(line_i);
|
||||
let (h, _) = self.dimensions(line.slice(
|
||||
let (h, _) = self.dimensions(RopeGraphemes::new(&line.slice(
|
||||
(block_index * LINE_BLOCK_LENGTH)
|
||||
..min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH),
|
||||
).graphemes());
|
||||
)));
|
||||
|
||||
if new_y >= 0 && new_y < h as isize {
|
||||
y = new_y as usize;
|
||||
|
@ -127,10 +129,10 @@ pub trait LineFormatter {
|
|||
} else {
|
||||
block_index -= 1;
|
||||
}
|
||||
let (h, _) = self.dimensions(line.slice(
|
||||
let (h, _) = self.dimensions(RopeGraphemes::new(&line.slice(
|
||||
(block_index * LINE_BLOCK_LENGTH)
|
||||
..min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH),
|
||||
).graphemes());
|
||||
)));
|
||||
new_y += h as isize;
|
||||
} else {
|
||||
unreachable!();
|
||||
|
@ -145,7 +147,7 @@ pub trait LineFormatter {
|
|||
..min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH),
|
||||
);
|
||||
let block_col_i = min(
|
||||
self.v2d_to_index(block_slice.graphemes(), (y, x), rounding),
|
||||
self.v2d_to_index(RopeGraphemes::new(&block_slice), (y, x), rounding),
|
||||
LINE_BLOCK_LENGTH - 1,
|
||||
);
|
||||
col_i = (block_index * LINE_BLOCK_LENGTH) + block_col_i;
|
||||
|
@ -173,11 +175,11 @@ pub trait LineFormatter {
|
|||
|
||||
// Calculate the horizontal position
|
||||
let (v, _) = self.index_to_v2d(
|
||||
line.slice(start_index..end_index).graphemes(),
|
||||
RopeGraphemes::new(&line.slice(start_index..end_index)),
|
||||
col_i_adjusted,
|
||||
);
|
||||
let block_col_i = self.v2d_to_index(
|
||||
line.slice(start_index..end_index).graphemes(),
|
||||
RopeGraphemes::new(&line.slice(start_index..end_index)),
|
||||
(v, horizontal),
|
||||
(RoundingBehavior::Floor, rounding),
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//! Misc helpful utility functions for TextBuffer related stuff.
|
||||
|
||||
use std::iter::repeat;
|
||||
use ropey::RopeSlice;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub fn is_line_ending(text: &str) -> bool {
|
||||
|
@ -13,6 +14,10 @@ pub fn is_line_ending(text: &str) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rope_slice_is_line_ending(text: &RopeSlice) -> bool {
|
||||
rope_slice_to_line_ending(text) != LineEnding::None
|
||||
}
|
||||
|
||||
pub fn is_whitespace(text: &str) -> bool {
|
||||
// TODO: this is a naive categorization of whitespace characters.
|
||||
// For better categorization these should be split up into groups
|
||||
|
@ -44,6 +49,34 @@ pub fn is_whitespace(text: &str) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rope_slice_is_whitespace(text: &RopeSlice) -> bool {
|
||||
// TODO: this is a naive categorization of whitespace characters.
|
||||
// For better categorization these should be split up into groups
|
||||
// based on e.g. breaking vs non-breaking spaces, among other things.
|
||||
|
||||
text == "\u{0020}" // SPACE
|
||||
|| text == "\u{0009}" // CHARACTER TABULATION
|
||||
|| text == "\u{00A0}" // NO-BREAK SPACE
|
||||
//|| "\u{1680}" // OGHAM SPACE MARK (here for completeness, but usually displayed as a dash, not as whitespace)
|
||||
|| text == "\u{180E}" // MONGOLIAN VOWEL SEPARATOR
|
||||
|| text == "\u{2000}" // EN QUAD
|
||||
|| text == "\u{2001}" // EM QUAD
|
||||
|| text == "\u{2002}" // EN SPACE
|
||||
|| text == "\u{2003}" // EM SPACE
|
||||
|| text == "\u{2004}" // THREE-PER-EM SPACE
|
||||
|| text == "\u{2005}" // FOUR-PER-EM SPACE
|
||||
|| text == "\u{2006}" // SIX-PER-EM SPACE
|
||||
|| text == "\u{2007}" // FIGURE SPACE
|
||||
|| text == "\u{2008}" // PUNCTUATION SPACE
|
||||
|| text == "\u{2009}" // THIN SPACE
|
||||
|| text == "\u{200A}" // HAIR SPACE
|
||||
|| text == "\u{200B}" // ZERO WIDTH SPACE
|
||||
|| text == "\u{202F}" // NARROW NO-BREAK SPACE
|
||||
|| text == "\u{205F}" // MEDIUM MATHEMATICAL SPACE
|
||||
|| text == "\u{3000}" // IDEOGRAPHIC SPACE
|
||||
|| text == "\u{FEFF}" // ZERO WIDTH NO-BREAK SPACE
|
||||
}
|
||||
|
||||
pub fn line_ending_count(text: &str) -> usize {
|
||||
let mut count = 0;
|
||||
for g in UnicodeSegmentation::graphemes(text, true) {
|
||||
|
@ -286,6 +319,29 @@ pub fn str_to_line_ending(g: &str) -> LineEnding {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rope_slice_to_line_ending(g: &RopeSlice) -> LineEnding {
|
||||
if g == "\u{000D}\u{000A}" {
|
||||
LineEnding::CRLF
|
||||
} else if g == "\u{000A}" {
|
||||
LineEnding::LF
|
||||
} else if g == "\u{000B}" {
|
||||
LineEnding::VT
|
||||
} else if g == "\u{000C}" {
|
||||
LineEnding::FF
|
||||
} else if g == "\u{000D}" {
|
||||
LineEnding::CR
|
||||
} else if g == "\u{0085}" {
|
||||
LineEnding::NEL
|
||||
} else if g == "\u{2028}" {
|
||||
LineEnding::LS
|
||||
} else if g == "\u{2029}" {
|
||||
LineEnding::PS
|
||||
} else {
|
||||
// Not a line ending
|
||||
LineEnding::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_ending_to_str(ending: LineEnding) -> &'static str {
|
||||
LINE_ENDINGS[ending as usize]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::cmp::max;
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use string_utils::{is_line_ending, is_whitespace};
|
||||
use ropey::RopeSlice;
|
||||
use utils::grapheme_width;
|
||||
use string_utils::{rope_slice_is_line_ending, rope_slice_is_whitespace};
|
||||
use formatter::{LineFormatter, RoundingBehavior};
|
||||
|
||||
pub enum WrapType {
|
||||
|
@ -47,7 +48,7 @@ impl ConsoleLineFormatter {
|
|||
|
||||
pub fn iter<'a, T>(&'a self, g_iter: T) -> ConsoleLineFormatterVisIter<'a, T>
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
ConsoleLineFormatterVisIter::<'a, T> {
|
||||
grapheme_iter: g_iter,
|
||||
|
@ -68,7 +69,7 @@ impl LineFormatter for ConsoleLineFormatter {
|
|||
|
||||
fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize)
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
let mut dim: (usize, usize) = (0, 0);
|
||||
|
||||
|
@ -83,7 +84,7 @@ impl LineFormatter for ConsoleLineFormatter {
|
|||
|
||||
fn index_to_v2d<'a, T>(&'a self, g_iter: T, char_idx: usize) -> (usize, usize)
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
let mut pos = (0, 0);
|
||||
let mut i = 0;
|
||||
|
@ -109,7 +110,7 @@ impl LineFormatter for ConsoleLineFormatter {
|
|||
_: (RoundingBehavior, RoundingBehavior),
|
||||
) -> usize
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
// TODO: handle rounding modes
|
||||
let mut prev_i = 0;
|
||||
|
@ -137,7 +138,7 @@ impl LineFormatter for ConsoleLineFormatter {
|
|||
// ===================================================================
|
||||
pub struct ConsoleLineFormatterVisIter<'a, T>
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
grapheme_iter: T,
|
||||
f: &'a ConsoleLineFormatter,
|
||||
|
@ -146,15 +147,15 @@ where
|
|||
indent: usize,
|
||||
indent_found: bool,
|
||||
|
||||
word_buf: Vec<&'a str>,
|
||||
word_buf: Vec<RopeSlice<'a>>,
|
||||
word_i: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> ConsoleLineFormatterVisIter<'a, T>
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
fn next_nowrap(&mut self, g: &'a str) -> Option<(&'a str, (usize, usize), usize)> {
|
||||
fn next_nowrap(&mut self, g: RopeSlice<'a>) -> Option<(RopeSlice<'a>, (usize, usize), usize)> {
|
||||
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize);
|
||||
|
||||
let pos = self.pos;
|
||||
|
@ -164,9 +165,9 @@ where
|
|||
|
||||
fn next_charwrap(
|
||||
&mut self,
|
||||
g: &'a str,
|
||||
g: RopeSlice<'a>,
|
||||
wrap_width: usize,
|
||||
) -> Option<(&'a str, (usize, usize), usize)> {
|
||||
) -> Option<(RopeSlice<'a>, (usize, usize), usize)> {
|
||||
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize);
|
||||
|
||||
if (self.pos.1 + width) > wrap_width {
|
||||
|
@ -198,7 +199,7 @@ where
|
|||
}
|
||||
} else {
|
||||
if !self.indent_found {
|
||||
if is_whitespace(g) {
|
||||
if rope_slice_is_whitespace(&g) {
|
||||
self.indent += width;
|
||||
} else {
|
||||
self.indent_found = true;
|
||||
|
@ -214,11 +215,11 @@ where
|
|||
|
||||
impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T>
|
||||
where
|
||||
T: Iterator<Item = &'a str>,
|
||||
T: Iterator<Item = RopeSlice<'a>>,
|
||||
{
|
||||
type Item = (&'a str, (usize, usize), usize);
|
||||
type Item = (RopeSlice<'a>, (usize, usize), usize);
|
||||
|
||||
fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> {
|
||||
fn next(&mut self) -> Option<(RopeSlice<'a>, (usize, usize), usize)> {
|
||||
match self.f.wrap_type {
|
||||
WrapType::NoWrap => {
|
||||
if let Some(g) = self.grapheme_iter.next() {
|
||||
|
@ -249,14 +250,14 @@ where
|
|||
self.f.tab_width as usize,
|
||||
);
|
||||
word_width += width;
|
||||
if is_whitespace(g) {
|
||||
if rope_slice_is_whitespace(&g) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if self.word_buf.len() == 0 {
|
||||
return None;
|
||||
} else if !self.indent_found && !is_whitespace(self.word_buf[0]) {
|
||||
} else if !self.indent_found && !rope_slice_is_whitespace(&self.word_buf[0]) {
|
||||
self.indent_found = true;
|
||||
}
|
||||
|
||||
|
@ -300,27 +301,22 @@ where
|
|||
|
||||
/// Returns the visual width of a grapheme given a starting
|
||||
/// position on a line.
|
||||
fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize {
|
||||
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 UnicodeWidthStr::width(g);
|
||||
}
|
||||
}
|
||||
fn grapheme_vis_width_at_vis_pos(g: RopeSlice, pos: usize, tab_width: usize) -> usize {
|
||||
if g == "\t" {
|
||||
let ending_pos = ((pos / tab_width) + 1) * tab_width;
|
||||
return ending_pos - pos;
|
||||
} else if rope_slice_is_line_ending(&g) {
|
||||
return 1;
|
||||
} else {
|
||||
return grapheme_width(&g);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(unused_imports)]
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use ropey::Rope;
|
||||
use utils::RopeGraphemes;
|
||||
use super::*;
|
||||
use formatter::{LineFormatter, LINE_BLOCK_LENGTH};
|
||||
use formatter::RoundingBehavior::{Ceiling, Floor, Round};
|
||||
|
@ -328,7 +324,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn dimensions_1() {
|
||||
let text = "Hello there, stranger!"; // 22 graphemes long
|
||||
let text = Rope::from_str("Hello there, stranger!"); // 22 graphemes long
|
||||
|
||||
let mut f = ConsoleLineFormatter::new(4);
|
||||
f.wrap_type = WrapType::CharWrap(0);
|
||||
|
@ -337,14 +333,14 @@ mod tests {
|
|||
f.set_wrap_width(80);
|
||||
|
||||
assert_eq!(
|
||||
f.dimensions(UnicodeSegmentation::graphemes(text, true)),
|
||||
f.dimensions(RopeGraphemes::new(&text.slice(..))),
|
||||
(1, 22)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimensions_2() {
|
||||
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
||||
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::CharWrap(0);
|
||||
|
@ -353,7 +349,7 @@ mod tests {
|
|||
f.set_wrap_width(12);
|
||||
|
||||
assert_eq!(
|
||||
f.dimensions(UnicodeSegmentation::graphemes(text, true)),
|
||||
f.dimensions(RopeGraphemes::new(&text.slice(..))),
|
||||
(5, 12)
|
||||
);
|
||||
}
|
||||
|
@ -361,7 +357,7 @@ mod tests {
|
|||
#[test]
|
||||
fn dimensions_3() {
|
||||
// 55 graphemes long
|
||||
let text = "税マイミ文末\
|
||||
let text = Rope::from_str("税マイミ文末\
|
||||
レ日題イぽじ\
|
||||
や男目統ス公\
|
||||
身みトしつ結\
|
||||
|
@ -370,7 +366,7 @@ mod tests {
|
|||
凱字テ式重反\
|
||||
てす献罪がご\
|
||||
く官俵呉嫁ー\
|
||||
。";
|
||||
。");
|
||||
|
||||
let mut f = ConsoleLineFormatter::new(4);
|
||||
f.wrap_type = WrapType::CharWrap(0);
|
||||
|
@ -379,7 +375,7 @@ mod tests {
|
|||
f.set_wrap_width(12);
|
||||
|
||||
assert_eq!(
|
||||
f.dimensions(UnicodeSegmentation::graphemes(text, true)),
|
||||
f.dimensions(RopeGraphemes::new(&text.slice(..))),
|
||||
(10, 12)
|
||||
);
|
||||
}
|
||||
|
@ -387,7 +383,7 @@ mod tests {
|
|||
#[test]
|
||||
fn dimensions_4() {
|
||||
// 55 graphemes long
|
||||
let text = "税マイミ文末\
|
||||
let text = Rope::from_str("税マイミ文末\
|
||||
レ日題イぽじ\
|
||||
や男目統ス公\
|
||||
身みトしつ結\
|
||||
|
@ -396,7 +392,7 @@ mod tests {
|
|||
凱字テ式重反\
|
||||
てす献罪がご\
|
||||
く官俵呉嫁ー\
|
||||
。";
|
||||
。");
|
||||
|
||||
let mut f = ConsoleLineFormatter::new(4);
|
||||
f.wrap_type = WrapType::WordWrap(0);
|
||||
|
@ -405,14 +401,14 @@ mod tests {
|
|||
f.set_wrap_width(12);
|
||||
|
||||
assert_eq!(
|
||||
f.dimensions(UnicodeSegmentation::graphemes(text, true)),
|
||||
f.dimensions(RopeGraphemes::new(&text.slice(..))),
|
||||
(10, 12)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_to_v2d_1() {
|
||||
let text = "Hello there, stranger!"; // 22 graphemes long
|
||||
let text = Rope::from_str("Hello there, stranger!"); // 22 graphemes long
|
||||
|
||||
let mut f = ConsoleLineFormatter::new(4);
|
||||
f.wrap_type = WrapType::CharWrap(0);
|
||||
|
@ -421,26 +417,26 @@ mod tests {
|
|||
f.set_wrap_width(80);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 0),
|
||||
(0, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 5),
|
||||
(0, 5)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 22),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 22),
|
||||
(0, 22)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 23),
|
||||
(0, 22)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_to_v2d_2() {
|
||||
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
||||
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::CharWrap(0);
|
||||
|
@ -449,79 +445,79 @@ mod tests {
|
|||
f.set_wrap_width(12);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 0),
|
||||
(0, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 5),
|
||||
(0, 5)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 11),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 11),
|
||||
(0, 11)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 12),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 12),
|
||||
(1, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 15),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 15),
|
||||
(1, 3)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 23),
|
||||
(1, 11)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 24),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 24),
|
||||
(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 28),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 28),
|
||||
(2, 4)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 35),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 35),
|
||||
(2, 11)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 36),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 36),
|
||||
(3, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 43),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 43),
|
||||
(3, 7)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 47),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 47),
|
||||
(3, 11)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 48),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 48),
|
||||
(4, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 50),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 50),
|
||||
(4, 2)
|
||||
);
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 56),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 56),
|
||||
(4, 8)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 57),
|
||||
f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 57),
|
||||
(4, 8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2d_to_index_1() {
|
||||
let text = "Hello there, stranger!"; // 22 graphemes long
|
||||
let text = Rope::from_str("Hello there, stranger!"); // 22 graphemes long
|
||||
|
||||
let mut f = ConsoleLineFormatter::new(4);
|
||||
f.wrap_type = WrapType::CharWrap(0);
|
||||
|
@ -531,7 +527,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -539,7 +535,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 5),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -547,7 +543,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 22),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -555,7 +551,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 23),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -563,7 +559,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(1, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -571,7 +567,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(1, 1),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -581,7 +577,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn v2d_to_index_2() {
|
||||
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
||||
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::CharWrap(0);
|
||||
|
@ -591,7 +587,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -599,7 +595,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 11),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -607,7 +603,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(0, 12),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -616,7 +612,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(1, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -624,7 +620,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(1, 11),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -632,7 +628,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(1, 12),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -641,7 +637,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(2, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -649,7 +645,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(2, 11),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -657,7 +653,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(2, 12),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -666,7 +662,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(3, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -674,7 +670,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(3, 11),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -682,7 +678,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(3, 12),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -691,7 +687,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(4, 0),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -699,7 +695,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(4, 7),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -707,7 +703,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(4, 8),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
@ -715,7 +711,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
f.v2d_to_index(
|
||||
UnicodeSegmentation::graphemes(text, true),
|
||||
RopeGraphemes::new(&text.slice(..)),
|
||||
(4, 9),
|
||||
(Floor, Floor)
|
||||
),
|
||||
|
|
|
@ -10,8 +10,8 @@ use termion::input::TermRead;
|
|||
use editor::Editor;
|
||||
use formatter::{block_index_and_offset, LineFormatter, LINE_BLOCK_LENGTH};
|
||||
use self::formatter::ConsoleLineFormatter;
|
||||
use string_utils::{is_line_ending, line_ending_to_str, LineEnding};
|
||||
use utils::digit_count;
|
||||
use string_utils::{rope_slice_is_line_ending, line_ending_to_str, LineEnding};
|
||||
use utils::{digit_count, RopeGraphemes};
|
||||
|
||||
pub mod formatter;
|
||||
mod screen;
|
||||
|
@ -367,15 +367,14 @@ impl TermUI {
|
|||
.line_col_to_index((line_index, line_block_index * LINE_BLOCK_LENGTH));
|
||||
let temp_line = editor.buffer.get_line(line_index);
|
||||
let (vis_line_offset, _) = editor.formatter.index_to_v2d(
|
||||
temp_line
|
||||
RopeGraphemes::new(&temp_line
|
||||
.slice(
|
||||
(line_block_index * LINE_BLOCK_LENGTH)
|
||||
..min(
|
||||
temp_line.len_chars(),
|
||||
(line_block_index + 1) * LINE_BLOCK_LENGTH,
|
||||
),
|
||||
)
|
||||
.graphemes(),
|
||||
)),
|
||||
editor.view_pos.0 - char_index,
|
||||
);
|
||||
|
||||
|
@ -412,9 +411,9 @@ impl TermUI {
|
|||
let mut last_pos_y = 0;
|
||||
let mut lines_traversed: usize = 0;
|
||||
let line_len = line.len_chars();
|
||||
let mut g_iter = editor.formatter.iter(line.slice(
|
||||
let mut g_iter = editor.formatter.iter(RopeGraphemes::new(&line.slice(
|
||||
(line_block_index * LINE_BLOCK_LENGTH)..line_len,
|
||||
).graphemes());
|
||||
)));
|
||||
|
||||
loop {
|
||||
if let Some((g, (pos_y, pos_x), width)) = g_iter.next() {
|
||||
|
@ -444,7 +443,7 @@ impl TermUI {
|
|||
}
|
||||
|
||||
// Actually print the character
|
||||
if is_line_ending(g) {
|
||||
if rope_slice_is_line_ending(&g) {
|
||||
if at_cursor {
|
||||
self.screen.draw(
|
||||
px as usize,
|
||||
|
@ -476,17 +475,17 @@ impl TermUI {
|
|||
}
|
||||
} else {
|
||||
if at_cursor {
|
||||
self.screen.draw(
|
||||
self.screen.draw_rope_slice(
|
||||
px as usize,
|
||||
py as usize,
|
||||
g,
|
||||
&g,
|
||||
Style(Color::Black, Color::White),
|
||||
);
|
||||
} else {
|
||||
self.screen.draw(
|
||||
self.screen.draw_rope_slice(
|
||||
px as usize,
|
||||
py as usize,
|
||||
g,
|
||||
&g,
|
||||
Style(Color::White, Color::Black),
|
||||
);
|
||||
}
|
||||
|
@ -503,9 +502,9 @@ impl TermUI {
|
|||
line_block_index += 1;
|
||||
line_g_index = 0;
|
||||
let line_len = line.len_chars();
|
||||
g_iter = editor.formatter.iter(line.slice(
|
||||
g_iter = editor.formatter.iter(RopeGraphemes::new(&line.slice(
|
||||
(line_block_index * LINE_BLOCK_LENGTH)..line_len,
|
||||
).graphemes());
|
||||
)));
|
||||
lines_traversed += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::cell::RefCell;
|
|||
use std::io;
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
use ropey::RopeSlice;
|
||||
use utils::{RopeGraphemes, grapheme_width};
|
||||
use super::smallstring::SmallString;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
@ -110,6 +112,28 @@ impl Screen {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn draw_rope_slice(&self, x: usize, y: usize, text: &RopeSlice, style: Style) {
|
||||
if y < self.h {
|
||||
let mut buf = self.buf.borrow_mut();
|
||||
let mut x = x;
|
||||
for g in RopeGraphemes::new(&text) {
|
||||
let width = grapheme_width(&g);
|
||||
if width > 0 {
|
||||
if x < self.w {
|
||||
buf[y * self.w + x] = Some((style, SmallString::from_rope_slice(&g)));
|
||||
}
|
||||
x += 1;
|
||||
for _ in 0..(width - 1) {
|
||||
if x < self.w {
|
||||
buf[y * self.w + x] = None;
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hide_cursor(&self) {
|
||||
write!(self.out.borrow_mut(), "{}", termion::cursor::Hide).unwrap();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use std::ops::Deref;
|
|||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
use ropey::RopeSlice;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
|
@ -37,6 +39,17 @@ impl SmallString {
|
|||
string
|
||||
}
|
||||
|
||||
/// Creates a new `SmallString` with the same contents as the given `&str`.
|
||||
pub fn from_rope_slice(text: &RopeSlice) -> Self {
|
||||
let mut string = SmallString::with_capacity(text.len_bytes());
|
||||
let mut idx = 0;
|
||||
for chunk in text.chunks() {
|
||||
unsafe { string.insert_bytes(idx, chunk.as_bytes()) };
|
||||
idx += chunk.len();
|
||||
}
|
||||
string
|
||||
}
|
||||
|
||||
/// Appends a `&str` to end the of the `SmallString`.
|
||||
pub fn push_str(&mut self, string: &str) {
|
||||
let len = self.len();
|
||||
|
|
167
src/utils.rs
167
src/utils.rs
|
@ -1,3 +1,7 @@
|
|||
use ropey::{RopeSlice, iter::Chunks};
|
||||
use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
pub fn digit_count(mut n: u32, b: u32) -> u32 {
|
||||
let mut d = 0;
|
||||
loop {
|
||||
|
@ -9,6 +13,169 @@ pub fn digit_count(mut n: u32, b: u32) -> u32 {
|
|||
}
|
||||
}
|
||||
|
||||
//=============================================================
|
||||
|
||||
pub fn grapheme_width(slice: &RopeSlice) -> usize {
|
||||
// TODO: use a small stack-allocated buffer to handle the common case
|
||||
// without allocation.
|
||||
let s = slice.to_string();
|
||||
return UnicodeWidthStr::width(&s[..]);
|
||||
}
|
||||
|
||||
/// Finds the previous grapheme boundary before the given char position.
|
||||
pub fn prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize {
|
||||
// Bounds check
|
||||
debug_assert!(char_idx <= slice.len_chars());
|
||||
|
||||
// We work with bytes for this, so convert.
|
||||
let byte_idx = slice.char_to_byte(char_idx);
|
||||
|
||||
// Get the chunk with our byte index in it, and calculate its starting
|
||||
// byte within the total rope slice.
|
||||
let (mut chunk, byte_offset) = slice.chunk_at_byte(byte_idx);
|
||||
let mut chunk_start_idx = byte_idx - byte_offset;
|
||||
|
||||
// Set up the grapheme cursor.
|
||||
let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true);
|
||||
|
||||
// Find the previous grapheme cluster boundary.
|
||||
loop {
|
||||
match gc.prev_boundary(chunk, chunk_start_idx) {
|
||||
Ok(None) => return 0,
|
||||
Ok(Some(n)) => return slice.byte_to_char(n),
|
||||
Err(GraphemeIncomplete::PrevChunk) => {
|
||||
chunk = slice.chunk_at_byte(chunk_start_idx - 1).0;
|
||||
chunk_start_idx -= chunk.len();
|
||||
}
|
||||
Err(GraphemeIncomplete::PreContext(n)) => {
|
||||
let (ctx_chunk, _) = slice.chunk_at_byte(n - 1);
|
||||
gc.provide_context(ctx_chunk, n - ctx_chunk.len());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the next grapheme boundary after the given char position.
|
||||
pub fn next_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize {
|
||||
// Bounds check
|
||||
debug_assert!(char_idx <= slice.len_chars());
|
||||
|
||||
// We work with bytes for this, so convert.
|
||||
let byte_idx = slice.char_to_byte(char_idx);
|
||||
|
||||
// Get the chunk with our byte index in it, and calculate its starting
|
||||
// byte within the total rope slice.
|
||||
let (mut chunk, byte_offset) = slice.chunk_at_byte(byte_idx);
|
||||
let mut chunk_start_idx = byte_idx - byte_offset;
|
||||
|
||||
// Set up the grapheme cursor.
|
||||
let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true);
|
||||
|
||||
// Find the next grapheme cluster boundary.
|
||||
loop {
|
||||
match gc.next_boundary(chunk, chunk_start_idx) {
|
||||
Ok(None) => return slice.len_chars(),
|
||||
Ok(Some(n)) => return slice.byte_to_char(n),
|
||||
Err(GraphemeIncomplete::NextChunk) => {
|
||||
chunk_start_idx += chunk.len();
|
||||
chunk = slice.chunk_at_byte(chunk_start_idx).0;
|
||||
}
|
||||
Err(GraphemeIncomplete::PreContext(n)) => {
|
||||
let (ctx_chunk, _) = slice.chunk_at_byte(n - 1);
|
||||
gc.provide_context(ctx_chunk, n - ctx_chunk.len());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the given char position is a grapheme boundary.
|
||||
pub fn is_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> bool {
|
||||
// Bounds check
|
||||
debug_assert!(char_idx <= slice.len_chars());
|
||||
|
||||
// We work with bytes for this, so convert.
|
||||
let byte_idx = slice.char_to_byte(char_idx);
|
||||
|
||||
// Get the chunk with our byte index in it, and calculate its starting
|
||||
// byte within the total rope slice.
|
||||
let (chunk, byte_offset) = slice.chunk_at_byte(byte_idx);
|
||||
let chunk_start_idx = byte_idx - byte_offset;
|
||||
|
||||
// Set up the grapheme cursor.
|
||||
let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true);
|
||||
|
||||
// Determine if the given position is a grapheme cluster boundary.
|
||||
loop {
|
||||
match gc.is_boundary(chunk, chunk_start_idx) {
|
||||
Ok(n) => return n,
|
||||
Err(GraphemeIncomplete::PreContext(n)) => {
|
||||
let (ctx_chunk, _) = slice.chunk_at_byte(n - 1);
|
||||
gc.provide_context(ctx_chunk, n - ctx_chunk.len());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the graphemes of a RopeSlice.
|
||||
pub struct RopeGraphemes<'a> {
|
||||
text: RopeSlice<'a>,
|
||||
chunks: Chunks<'a>,
|
||||
cur_chunk: &'a str,
|
||||
cur_chunk_start: usize,
|
||||
cursor: GraphemeCursor,
|
||||
}
|
||||
|
||||
impl<'a> RopeGraphemes<'a> {
|
||||
pub fn new<'b>(slice: &RopeSlice<'b>) -> RopeGraphemes<'b> {
|
||||
let mut chunks = slice.chunks();
|
||||
let first_chunk = chunks.next().unwrap_or("");
|
||||
RopeGraphemes {
|
||||
text: *slice,
|
||||
chunks: chunks,
|
||||
cur_chunk: first_chunk,
|
||||
cur_chunk_start: 0,
|
||||
cursor: GraphemeCursor::new(0, slice.len_bytes(), true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for RopeGraphemes<'a> {
|
||||
type Item = RopeSlice<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<RopeSlice<'a>> {
|
||||
let a = self.cursor.cur_cursor();
|
||||
let b;
|
||||
loop {
|
||||
match self.cursor
|
||||
.next_boundary(self.cur_chunk, self.cur_chunk_start)
|
||||
{
|
||||
Ok(None) => {
|
||||
return None;
|
||||
}
|
||||
Ok(Some(n)) => {
|
||||
b = n;
|
||||
break;
|
||||
}
|
||||
Err(GraphemeIncomplete::NextChunk) => {
|
||||
self.cur_chunk_start += self.cur_chunk.len();
|
||||
self.cur_chunk = self.chunks.next().unwrap_or("");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let a_char = self.text.byte_to_char(a);
|
||||
let b_char = self.text.byte_to_char(b);
|
||||
|
||||
Some(self.text.slice(a_char..b_char))
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Reference in New Issue
Block a user