Updated to work with more recent library versions. Also RustFmt.
This commit is contained in:
parent
f1ffc2867a
commit
45e6125bbc
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,3 @@
|
||||||
/target
|
/target
|
||||||
/Cargo.lock
|
*.rs.bk
|
||||||
.zedstate
|
.zedstate
|
||||||
|
|
247
Cargo.lock
generated
Normal file
247
Cargo.lock
generated
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
[root]
|
||||||
|
name = "Led"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ropey 0.3.0 (git+https://github.com/cessen/ropey.git)",
|
||||||
|
"rustbox 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docopt"
|
||||||
|
version = "0.6.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"regex 0.1.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding"
|
||||||
|
version = "0.2.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-japanese"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-korean"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-simpchinese"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-singlebyte"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-tradchinese"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding_index_tests"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gag"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tempfile 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel32-sys"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "0.1.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex-syntax 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ropey"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "git+https://github.com/cessen/ropey.git#aa4dcf8d39d77f295126eed77d56f4b93a069b49"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustbox"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gag 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"termbox-sys 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-serialize"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termbox-sys"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8-ranges"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-build"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -18,10 +18,9 @@ git = "https://github.com/cessen/ropey.git"
|
||||||
#git = "https://github.com/PistonDevelopers/freetype-rs.git"
|
#git = "https://github.com/PistonDevelopers/freetype-rs.git"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1.30"
|
|
||||||
rustc-serialize = "0.3.0"
|
rustc-serialize = "0.3.0"
|
||||||
unicode-segmentation = "0.1.*"
|
unicode-segmentation = "0.1"
|
||||||
unicode-width = "0.1.*"
|
unicode-width = "0.1"
|
||||||
docopt = "0.6.*"
|
docopt = "0.6"
|
||||||
encoding = "*"
|
encoding = "0.2"
|
||||||
rustbox = "0.6.3"
|
rustbox = "0.8"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,32 +25,30 @@ impl UndoStack {
|
||||||
stack_b: LinkedList::new(),
|
stack_b: LinkedList::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn push(&mut self, op: Operation) {
|
pub fn push(&mut self, op: Operation) {
|
||||||
self.stack_a.push_back(op);
|
self.stack_a.push_back(op);
|
||||||
self.stack_b.clear();
|
self.stack_b.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn prev(&mut self) -> Option<Operation> {
|
pub fn prev(&mut self) -> Option<Operation> {
|
||||||
if let Some(op) = self.stack_a.pop_back() {
|
if let Some(op) = self.stack_a.pop_back() {
|
||||||
self.stack_b.push_back(op.clone());
|
self.stack_b.push_back(op.clone());
|
||||||
return Some(op);
|
return Some(op);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn next(&mut self) -> Option<Operation> {
|
pub fn next(&mut self) -> Option<Operation> {
|
||||||
if let Some(op) = self.stack_b.pop_back() {
|
if let Some(op) = self.stack_b.pop_back() {
|
||||||
self.stack_a.push_back(op.clone());
|
self.stack_a.push_back(op.clone());
|
||||||
return Some(op);
|
return Some(op);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ use formatter::LineFormatter;
|
||||||
/// doesn't affect editing operations at all, but is used for cursor movement.
|
/// doesn't affect editing operations at all, but is used for cursor movement.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
pub range: (usize, usize), // start, end
|
pub range: (usize, usize), // start, end
|
||||||
pub vis_start: usize, // start
|
pub vis_start: usize, // start
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor {
|
impl Cursor {
|
||||||
|
@ -26,7 +26,7 @@ impl Cursor {
|
||||||
vis_start: 0,
|
vis_start: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_vis_start<T: LineFormatter>(&mut self, buf: &Buffer, f: &T) {
|
pub fn update_vis_start<T: LineFormatter>(&mut self, buf: &Buffer, f: &T) {
|
||||||
self.vis_start = f.index_to_horizontal_v2d(buf, self.range.0);
|
self.vis_start = f.index_to_horizontal_v2d(buf, self.range.0);
|
||||||
}
|
}
|
||||||
|
@ -36,56 +36,51 @@ impl Cursor {
|
||||||
/// A collection of cursors, managed to always be in a consistent
|
/// A collection of cursors, managed to always be in a consistent
|
||||||
/// state for multi-cursor editing.
|
/// state for multi-cursor editing.
|
||||||
pub struct CursorSet {
|
pub struct CursorSet {
|
||||||
cursors: Vec<Cursor>
|
cursors: Vec<Cursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl CursorSet {
|
impl CursorSet {
|
||||||
pub fn new() -> CursorSet {
|
pub fn new() -> CursorSet {
|
||||||
CursorSet {
|
CursorSet { cursors: vec![Cursor::new()] }
|
||||||
cursors: vec!(Cursor::new()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_cursor(&mut self, cursor: Cursor) {
|
pub fn add_cursor(&mut self, cursor: Cursor) {
|
||||||
self.cursors.push(cursor);
|
self.cursors.push(cursor);
|
||||||
self.make_consistent();
|
self.make_consistent();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn truncate(&mut self, len: usize) {
|
pub fn truncate(&mut self, len: usize) {
|
||||||
self.cursors.truncate(len);
|
self.cursors.truncate(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter<'a>(&'a self) -> Iter<'a, Cursor> {
|
pub fn iter<'a>(&'a self) -> Iter<'a, Cursor> {
|
||||||
(&self.cursors[..]).iter()
|
(&self.cursors[..]).iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, Cursor> {
|
pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, Cursor> {
|
||||||
(&mut self.cursors[..]).iter_mut()
|
(&mut self.cursors[..]).iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_consistent(&mut self) {
|
pub fn make_consistent(&mut self) {
|
||||||
// First, sort the cursors by starting position
|
// First, sort the cursors by starting position
|
||||||
self.cursors.sort_by(|a, b| {
|
self.cursors.sort_by(|a, b| {
|
||||||
if a.range.0 < b.range.0 {
|
if a.range.0 < b.range.0 {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
}
|
} else if a.range.0 > b.range.0 {
|
||||||
else if a.range.0 > b.range.0 {
|
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Next, merge overlapping cursors
|
// Next, merge overlapping cursors
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < (self.cursors.len()-1) {
|
while i < (self.cursors.len() - 1) {
|
||||||
if self.cursors[i].range.1 >= self.cursors[i+1].range.0 {
|
if self.cursors[i].range.1 >= self.cursors[i + 1].range.0 {
|
||||||
self.cursors[i].range.1 = self.cursors[i+1].range.1;
|
self.cursors[i].range.1 = self.cursors[i + 1].range.1;
|
||||||
self.cursors.remove(i+1);
|
self.cursors.remove(i + 1);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +90,7 @@ impl CursorSet {
|
||||||
|
|
||||||
impl Index<usize> for CursorSet {
|
impl Index<usize> for CursorSet {
|
||||||
type Output = Cursor;
|
type Output = Cursor;
|
||||||
|
|
||||||
fn index<'a>(&'a self, _index: usize) -> &'a Cursor {
|
fn index<'a>(&'a self, _index: usize) -> &'a Cursor {
|
||||||
&(self.cursors[_index])
|
&(self.cursors[_index])
|
||||||
}
|
}
|
||||||
|
@ -106,4 +101,4 @@ impl IndexMut<usize> for CursorSet {
|
||||||
fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut Cursor {
|
fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut Cursor {
|
||||||
&mut (self.cursors[_index])
|
&mut (self.cursors[_index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,17 @@ pub struct Editor<T: LineFormatter> {
|
||||||
pub soft_tabs: bool,
|
pub soft_tabs: bool,
|
||||||
pub soft_tab_width: u8,
|
pub soft_tab_width: u8,
|
||||||
pub dirty: bool,
|
pub dirty: bool,
|
||||||
|
|
||||||
// The dimensions of the total editor in screen space, including the
|
// The dimensions of the total editor in screen space, including the
|
||||||
// header, gutter, etc.
|
// header, gutter, etc.
|
||||||
pub editor_dim: (usize, usize),
|
pub editor_dim: (usize, usize),
|
||||||
|
|
||||||
// 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), // (grapheme index, visual horizontal offset)
|
||||||
|
|
||||||
// The editing cursor position
|
// The editing cursor position
|
||||||
pub cursors: CursorSet,
|
pub cursors: CursorSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,15 +51,15 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
cursors: CursorSet::new(),
|
cursors: CursorSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn new_from_file(formatter: T, path: &Path) -> Editor<T> {
|
pub fn new_from_file(formatter: T, path: &Path) -> Editor<T> {
|
||||||
let buf = match Buffer::new_from_file(path) {
|
let buf = match Buffer::new_from_file(path) {
|
||||||
Ok(b) => {b},
|
Ok(b) => b,
|
||||||
// TODO: handle un-openable file better
|
// TODO: handle un-openable file better
|
||||||
_ => panic!("Could not open file!"),
|
_ => panic!("Could not open file!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ed = Editor {
|
let mut ed = Editor {
|
||||||
buffer: buf,
|
buffer: buf,
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
|
@ -73,32 +73,32 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
view_pos: (0, 0),
|
view_pos: (0, 0),
|
||||||
cursors: CursorSet::new(),
|
cursors: CursorSet::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// For multiple-cursor testing
|
// For multiple-cursor testing
|
||||||
//let mut cur = Cursor::new();
|
// let mut cur = Cursor::new();
|
||||||
//cur.range.0 = 30;
|
// cur.range.0 = 30;
|
||||||
//cur.range.1 = 30;
|
// cur.range.1 = 30;
|
||||||
//cur.update_vis_start(&(ed.buffer), &(ed.formatter));
|
// cur.update_vis_start(&(ed.buffer), &(ed.formatter));
|
||||||
//ed.cursors.add_cursor(cur);
|
// ed.cursors.add_cursor(cur);
|
||||||
|
|
||||||
ed.auto_detect_line_ending();
|
ed.auto_detect_line_ending();
|
||||||
ed.auto_detect_indentation_style();
|
ed.auto_detect_indentation_style();
|
||||||
|
|
||||||
return ed;
|
return ed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn save_if_dirty(&mut self) {
|
pub fn save_if_dirty(&mut self) {
|
||||||
if self.dirty && self.file_path != PathBuf::new() {
|
if self.dirty && self.file_path != PathBuf::new() {
|
||||||
let _ = self.buffer.save_to_file(&self.file_path);
|
let _ = self.buffer.save_to_file(&self.file_path);
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn auto_detect_line_ending(&mut self) {
|
pub fn auto_detect_line_ending(&mut self) {
|
||||||
let mut line_ending_histogram: [usize; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
let mut line_ending_histogram: [usize; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
// Collect statistics
|
// Collect statistics
|
||||||
let mut line_i: usize = 0;
|
let mut line_i: usize = 0;
|
||||||
for line in self.buffer.line_iter() {
|
for line in self.buffer.line_iter() {
|
||||||
|
@ -106,48 +106,46 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
let ending = if line.grapheme_count() > 0 {
|
let ending = if line.grapheme_count() > 0 {
|
||||||
let g = line.grapheme_at_index(line.grapheme_count() - 1);
|
let g = line.grapheme_at_index(line.grapheme_count() - 1);
|
||||||
str_to_line_ending(g)
|
str_to_line_ending(g)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
LineEnding::None
|
LineEnding::None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record which line ending it is
|
// Record which line ending it is
|
||||||
match ending {
|
match ending {
|
||||||
LineEnding::None => {
|
LineEnding::None => {}
|
||||||
},
|
|
||||||
LineEnding::CRLF => {
|
LineEnding::CRLF => {
|
||||||
line_ending_histogram[0] += 1;
|
line_ending_histogram[0] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::LF => {
|
LineEnding::LF => {
|
||||||
line_ending_histogram[1] += 1;
|
line_ending_histogram[1] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::VT => {
|
LineEnding::VT => {
|
||||||
line_ending_histogram[2] += 1;
|
line_ending_histogram[2] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::FF => {
|
LineEnding::FF => {
|
||||||
line_ending_histogram[3] += 1;
|
line_ending_histogram[3] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::CR => {
|
LineEnding::CR => {
|
||||||
line_ending_histogram[4] += 1;
|
line_ending_histogram[4] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::NEL => {
|
LineEnding::NEL => {
|
||||||
line_ending_histogram[5] += 1;
|
line_ending_histogram[5] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::LS => {
|
LineEnding::LS => {
|
||||||
line_ending_histogram[6] += 1;
|
line_ending_histogram[6] += 1;
|
||||||
},
|
}
|
||||||
LineEnding::PS => {
|
LineEnding::PS => {
|
||||||
line_ending_histogram[7] += 1;
|
line_ending_histogram[7] += 1;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop after 100 lines
|
// Stop after 100 lines
|
||||||
line_i += 1;
|
line_i += 1;
|
||||||
if line_i > 100 {
|
if line_i > 100 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyze stats and make a determination
|
// Analyze stats and make a determination
|
||||||
let mut lei = 0;
|
let mut lei = 0;
|
||||||
let mut le_count = 0;
|
let mut le_count = 0;
|
||||||
|
@ -157,7 +155,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
le_count = line_ending_histogram[i];
|
le_count = line_ending_histogram[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if le_count > 0 {
|
if le_count > 0 {
|
||||||
self.line_ending_type = match lei {
|
self.line_ending_type = match lei {
|
||||||
0 => LineEnding::CRLF,
|
0 => LineEnding::CRLF,
|
||||||
|
@ -168,20 +166,20 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
5 => LineEnding::NEL,
|
5 => LineEnding::NEL,
|
||||||
6 => LineEnding::LS,
|
6 => LineEnding::LS,
|
||||||
7 => LineEnding::PS,
|
7 => LineEnding::PS,
|
||||||
|
|
||||||
_ => LineEnding::LF,
|
_ => LineEnding::LF,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn auto_detect_indentation_style(&mut self) {
|
pub fn auto_detect_indentation_style(&mut self) {
|
||||||
let mut tab_blocks: usize = 0;
|
let mut tab_blocks: usize = 0;
|
||||||
let mut space_blocks: usize = 0;
|
let mut space_blocks: usize = 0;
|
||||||
let mut space_histogram: [usize; 9] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
let mut space_histogram: [usize; 9] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
let mut last_indent = (false, 0usize); // (was_tabs, indent_count)
|
let mut last_indent = (false, 0usize); // (was_tabs, indent_count)
|
||||||
|
|
||||||
// Collect statistics
|
// Collect statistics
|
||||||
let mut line_i: usize = 0;
|
let mut line_i: usize = 0;
|
||||||
for line in self.buffer.line_iter() {
|
for line in self.buffer.line_iter() {
|
||||||
|
@ -193,64 +191,61 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
for g in g_iter {
|
for g in g_iter {
|
||||||
if g == "\t" {
|
if g == "\t" {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
if last_indent.0 && last_indent.1 < count {
|
if last_indent.0 && last_indent.1 < count {
|
||||||
tab_blocks += 1;
|
tab_blocks += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store last line info
|
// Store last line info
|
||||||
last_indent = (true, count);
|
last_indent = (true, count);
|
||||||
},
|
}
|
||||||
|
|
||||||
Some(" ") => {
|
Some(" ") => {
|
||||||
// Count leading spaces
|
// Count leading spaces
|
||||||
let mut count = 1;
|
let mut count = 1;
|
||||||
for g in g_iter {
|
for g in g_iter {
|
||||||
if g == " " {
|
if g == " " {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
if !last_indent.0 && last_indent.1 < count {
|
if !last_indent.0 && last_indent.1 < count {
|
||||||
space_blocks += 1;
|
space_blocks += 1;
|
||||||
let amount = count - last_indent.1;
|
let amount = count - last_indent.1;
|
||||||
if amount < 9 {
|
if amount < 9 {
|
||||||
space_histogram[amount] += 1;
|
space_histogram[amount] += 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
space_histogram[8] += 1;
|
space_histogram[8] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store last line info
|
// Store last line info
|
||||||
last_indent = (false, count);
|
last_indent = (false, count);
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop after 1000 lines
|
// Stop after 1000 lines
|
||||||
line_i += 1;
|
line_i += 1;
|
||||||
if line_i > 1000 {
|
if line_i > 1000 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyze stats and make a determination
|
// Analyze stats and make a determination
|
||||||
if space_blocks == 0 && tab_blocks == 0 {
|
if space_blocks == 0 && tab_blocks == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if space_blocks > (tab_blocks * 2) {
|
if space_blocks > (tab_blocks * 2) {
|
||||||
let mut width = 0;
|
let mut width = 0;
|
||||||
let mut width_count = 0;
|
let mut width_count = 0;
|
||||||
|
@ -260,22 +255,21 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
width_count = space_histogram[i];
|
width_count = space_histogram[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.soft_tabs = true;
|
self.soft_tabs = true;
|
||||||
self.soft_tab_width = width as u8;
|
self.soft_tab_width = width as u8;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.soft_tabs = false;
|
self.soft_tabs = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn update_dim(&mut self, h: usize, w: usize) {
|
pub fn update_dim(&mut self, h: usize, w: usize) {
|
||||||
self.editor_dim = (h, w);
|
self.editor_dim = (h, w);
|
||||||
self.update_view_dim();
|
self.update_view_dim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn update_view_dim(&mut self) {
|
pub fn update_view_dim(&mut self) {
|
||||||
// TODO: generalize for non-terminal UI. Maybe this isn't where it
|
// TODO: generalize for non-terminal UI. Maybe this isn't where it
|
||||||
// belongs, in fact. But for now, this is the easiest place to put
|
// belongs, in fact. But for now, this is the easiest place to put
|
||||||
|
@ -283,10 +277,11 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
let line_count_digits = digit_count(self.buffer.line_count() as u32, 10) as usize;
|
let line_count_digits = digit_count(self.buffer.line_count() as u32, 10) as usize;
|
||||||
// Minus 1 vertically for the header, minus one more than the digits in
|
// Minus 1 vertically for the header, minus one more than the digits in
|
||||||
// the line count for the gutter.
|
// the line count for the gutter.
|
||||||
self.view_dim = (self.editor_dim.0 - 1, self.editor_dim.1 - line_count_digits - 1);
|
self.view_dim = (self.editor_dim.0 - 1,
|
||||||
|
self.editor_dim.1 - line_count_digits - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn undo(&mut self) {
|
pub fn undo(&mut self) {
|
||||||
// TODO: handle multiple cursors properly
|
// TODO: handle multiple cursors properly
|
||||||
if let Some(pos) = self.buffer.undo() {
|
if let Some(pos) = self.buffer.undo() {
|
||||||
|
@ -294,16 +289,16 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
self.cursors[0].range.0 = pos;
|
self.cursors[0].range.0 = pos;
|
||||||
self.cursors[0].range.1 = pos;
|
self.cursors[0].range.1 = pos;
|
||||||
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
|
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn redo(&mut self) {
|
pub fn redo(&mut self) {
|
||||||
// TODO: handle multiple cursors properly
|
// TODO: handle multiple cursors properly
|
||||||
if let Some(pos) = self.buffer.redo() {
|
if let Some(pos) = self.buffer.redo() {
|
||||||
|
@ -311,16 +306,16 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
self.cursors[0].range.0 = pos;
|
self.cursors[0].range.0 = pos;
|
||||||
self.cursors[0].range.1 = pos;
|
self.cursors[0].range.1 = pos;
|
||||||
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
|
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Moves the editor's view the minimum amount to show the cursor
|
/// Moves the editor's view the minimum amount to show the cursor
|
||||||
pub fn move_view_to_cursor(&mut self) {
|
pub fn move_view_to_cursor(&mut self) {
|
||||||
// TODO: account for the horizontal offset of the editor view.
|
// TODO: account for the horizontal offset of the editor view.
|
||||||
|
@ -328,335 +323,373 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
// TODO: handle multiple cursors properly. Should only move if
|
// TODO: handle multiple cursors properly. Should only move if
|
||||||
// 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.
|
|
||||||
let g_first = self.formatter.index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor);
|
|
||||||
let mut g_last = self.formatter.index_offset_vertical_v2d(&self.buffer, g_first, self.view_dim.0 as isize, (Floor, Floor));
|
|
||||||
g_last = self.formatter.index_set_horizontal_v2d(&self.buffer, g_last, self.view_dim.1, Floor);
|
|
||||||
|
|
||||||
// Adjust the view depending on where the cursor is
|
// Find the first and last grapheme index visible within the editor.
|
||||||
|
let g_first = self.formatter
|
||||||
|
.index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor);
|
||||||
|
let mut g_last = self.formatter.index_offset_vertical_v2d(&self.buffer,
|
||||||
|
g_first,
|
||||||
|
self.view_dim.0 as isize,
|
||||||
|
(Floor, Floor));
|
||||||
|
g_last = self.formatter
|
||||||
|
.index_set_horizontal_v2d(&self.buffer, g_last, self.view_dim.1, Floor);
|
||||||
|
|
||||||
|
// Adjust the view depending on where the cursor is
|
||||||
if self.cursors[0].range.0 < g_first {
|
if self.cursors[0].range.0 < g_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 > g_last {
|
self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer,
|
||||||
self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer, self.cursors[0].range.0, -(self.view_dim.0 as isize), (Floor, Floor));
|
self.cursors[0].range.0,
|
||||||
|
-(self.view_dim.0 as isize),
|
||||||
|
(Floor, Floor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 = grapheme_count(text);
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
// Insert text
|
// Insert text
|
||||||
self.buffer.insert_text(text, c.range.0 + offset);
|
self.buffer.insert_text(text, c.range.0 + offset);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
// Move cursor
|
// Move cursor
|
||||||
c.range.0 += str_len + offset;
|
c.range.0 += str_len + offset;
|
||||||
c.range.1 += str_len + offset;
|
c.range.1 += str_len + offset;
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
// Update offset
|
// Update offset
|
||||||
offset += str_len;
|
offset += str_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn insert_tab_at_cursor(&mut self) {
|
pub fn insert_tab_at_cursor(&mut self) {
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
if self.soft_tabs {
|
if self.soft_tabs {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
// Update cursor with offset
|
// Update cursor with offset
|
||||||
c.range.0 += offset;
|
c.range.0 += offset;
|
||||||
c.range.1 += offset;
|
c.range.1 += offset;
|
||||||
|
|
||||||
// Figure out how many spaces to insert
|
// Figure out how many spaces to insert
|
||||||
let vis_pos = self.formatter.index_to_horizontal_v2d(&self.buffer, c.range.0);
|
let vis_pos = self.formatter.index_to_horizontal_v2d(&self.buffer, c.range.0);
|
||||||
// TODO: handle tab settings
|
// TODO: handle tab settings
|
||||||
let next_tab_stop = ((vis_pos / self.soft_tab_width as usize) + 1) * self.soft_tab_width as usize;
|
let next_tab_stop = ((vis_pos / self.soft_tab_width as usize) + 1) *
|
||||||
|
self.soft_tab_width as usize;
|
||||||
let space_count = min(next_tab_stop - vis_pos, 8);
|
let space_count = min(next_tab_stop - vis_pos, 8);
|
||||||
|
|
||||||
|
|
||||||
// Insert spaces
|
// Insert spaces
|
||||||
let space_strs = ["", " ", " ", " ", " ", " ", " ", " ", " "];
|
let space_strs = ["", " ", " ", " ", " ", " ", " ", " ",
|
||||||
|
" "];
|
||||||
self.buffer.insert_text(space_strs[space_count], c.range.0);
|
self.buffer.insert_text(space_strs[space_count], c.range.0);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
// Move cursor
|
// Move cursor
|
||||||
c.range.0 += space_count;
|
c.range.0 += space_count;
|
||||||
c.range.1 += space_count;
|
c.range.1 += space_count;
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
// Update offset
|
// Update offset
|
||||||
offset += space_count;
|
offset += space_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.insert_text_at_cursor("\t");
|
self.insert_text_at_cursor("\t");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn backspace_at_cursor(&mut self) {
|
pub fn backspace_at_cursor(&mut self) {
|
||||||
self.remove_text_behind_cursor(1);
|
self.remove_text_behind_cursor(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn insert_text_at_grapheme(&mut self, text: &str, pos: usize) {
|
pub fn insert_text_at_grapheme(&mut self, text: &str, pos: usize) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
let buf_len = self.buffer.grapheme_count();
|
let buf_len = self.buffer.grapheme_count();
|
||||||
self.buffer.insert_text(text, if pos < buf_len {pos} else {buf_len});
|
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();
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
// Update cursor with offset
|
// Update cursor with offset
|
||||||
c.range.0 -= offset;
|
c.range.0 -= offset;
|
||||||
c.range.1 -= offset;
|
c.range.1 -= offset;
|
||||||
|
|
||||||
// Do nothing if there's nothing to delete.
|
// Do nothing if there's nothing to delete.
|
||||||
if c.range.0 == 0 {
|
if c.range.0 == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = min(c.range.0, grapheme_count);
|
let len = min(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);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
// Move cursor
|
// Move cursor
|
||||||
c.range.0 -= len;
|
c.range.0 -= len;
|
||||||
c.range.1 -= len;
|
c.range.1 -= len;
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
// Update offset
|
// Update offset
|
||||||
offset += len;
|
offset += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn remove_text_in_front_of_cursor(&mut self, grapheme_count: usize) {
|
pub fn remove_text_in_front_of_cursor(&mut self, grapheme_count: usize) {
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
// Update cursor with offset
|
// Update cursor with offset
|
||||||
c.range.0 -= min(c.range.0, offset);
|
c.range.0 -= min(c.range.0, offset);
|
||||||
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.grapheme_count() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_len = if self.buffer.grapheme_count() > c.range.1 {self.buffer.grapheme_count() - c.range.1} else {0};
|
let max_len = if self.buffer.grapheme_count() > c.range.1 {
|
||||||
|
self.buffer.grapheme_count() - c.range.1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let len = min(max_len, grapheme_count);
|
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);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
// Move cursor
|
// Move cursor
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
// Update offset
|
// Update offset
|
||||||
offset += len;
|
offset += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn remove_text_inside_cursor(&mut self) {
|
pub fn remove_text_inside_cursor(&mut self) {
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for c in self.cursors.iter_mut() {
|
for c in self.cursors.iter_mut() {
|
||||||
// Update cursor with offset
|
// Update cursor with offset
|
||||||
c.range.0 -= min(c.range.0, offset);
|
c.range.0 -= min(c.range.0, offset);
|
||||||
c.range.1 -= min(c.range.1, offset);
|
c.range.1 -= min(c.range.1, offset);
|
||||||
|
|
||||||
// If selection, remove text
|
// If selection, remove text
|
||||||
if c.range.0 < c.range.1 {
|
if c.range.0 < c.range.1 {
|
||||||
let len = c.range.1 - c.range.0;
|
let len = c.range.1 - c.range.0;
|
||||||
|
|
||||||
self.buffer.remove_text_before(c.range.0, c.range.1 - c.range.0);
|
self.buffer.remove_text_before(c.range.0, c.range.1 - c.range.0);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
// Move cursor
|
// Move cursor
|
||||||
c.range.1 = c.range.0;
|
c.range.1 = c.range.0;
|
||||||
|
|
||||||
// Update offset
|
// Update offset
|
||||||
offset += len;
|
offset += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cursors.make_consistent();
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn cursor_to_beginning_of_buffer(&mut self) {
|
pub fn cursor_to_beginning_of_buffer(&mut self) {
|
||||||
self.cursors = CursorSet::new();
|
self.cursors = CursorSet::new();
|
||||||
|
|
||||||
self.cursors[0].range = (0, 0);
|
self.cursors[0].range = (0, 0);
|
||||||
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.grapheme_count();
|
||||||
|
|
||||||
self.cursors = CursorSet::new();
|
self.cursors = CursorSet::new();
|
||||||
self.cursors[0].range = (end, end);
|
self.cursors[0].range = (end, end);
|
||||||
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
self.cursors[0].update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 {
|
if c.range.0 >= n {
|
||||||
c.range.0 -= n;
|
c.range.0 -= n;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
c.range.0 = 0;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 += n;
|
||||||
|
|
||||||
if c.range.1 > self.buffer.grapheme_count() {
|
if c.range.1 > self.buffer.grapheme_count() {
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 * self.formatter.single_line_height()) as isize;
|
||||||
let mut temp_index = self.formatter.index_offset_vertical_v2d(&self.buffer, c.range.0, vmove, (Round, Round));
|
let mut temp_index = self.formatter.index_offset_vertical_v2d(&self.buffer,
|
||||||
|
c.range.0,
|
||||||
|
vmove,
|
||||||
|
(Round, Round));
|
||||||
|
|
||||||
if temp_index == 0 {
|
if temp_index == 0 {
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
} else {
|
||||||
|
temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer,
|
||||||
|
temp_index,
|
||||||
|
c.vis_start,
|
||||||
|
Round);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer, temp_index, c.vis_start, Round);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.range.0 = temp_index;
|
c.range.0 = temp_index;
|
||||||
c.range.1 = temp_index;
|
c.range.1 = temp_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 * self.formatter.single_line_height()) as isize;
|
||||||
let mut temp_index = self.formatter.index_offset_vertical_v2d(&self.buffer, c.range.0, vmove, (Round, Round));
|
let mut temp_index = self.formatter.index_offset_vertical_v2d(&self.buffer,
|
||||||
|
c.range.0,
|
||||||
|
vmove,
|
||||||
|
(Round, Round));
|
||||||
|
|
||||||
if temp_index == self.buffer.grapheme_count() {
|
if temp_index == self.buffer.grapheme_count() {
|
||||||
c.update_vis_start(&(self.buffer), &(self.formatter));
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
|
} else {
|
||||||
|
temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer,
|
||||||
|
temp_index,
|
||||||
|
c.vis_start,
|
||||||
|
Round);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer, temp_index, c.vis_start, Round);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.range.0 = temp_index;
|
c.range.0 = temp_index;
|
||||||
c.range.1 = temp_index;
|
c.range.1 = temp_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn page_up(&mut self) {
|
pub fn page_up(&mut self) {
|
||||||
let move_amount = self.view_dim.0 - max((self.view_dim.0 / 8), self.formatter.single_line_height());
|
let move_amount = self.view_dim.0 -
|
||||||
self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer, self.view_pos.0, -1 * move_amount as isize, (Round, Round));
|
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 * move_amount as isize,
|
||||||
|
(Round, Round));
|
||||||
|
|
||||||
self.cursor_up(move_amount);
|
self.cursor_up(move_amount);
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn page_down(&mut self) {
|
pub fn page_down(&mut self) {
|
||||||
let move_amount = self.view_dim.0 - max((self.view_dim.0 / 8), self.formatter.single_line_height());
|
let move_amount = self.view_dim.0 -
|
||||||
self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer, self.view_pos.0, move_amount as isize, (Round, Round));
|
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,
|
||||||
|
move_amount as isize,
|
||||||
|
(Round, Round));
|
||||||
|
|
||||||
self.cursor_down(move_amount);
|
self.cursor_down(move_amount);
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn jump_to_line(&mut self, n: usize) {
|
pub fn jump_to_line(&mut self, n: usize) {
|
||||||
let pos = self.buffer.line_col_to_index((n, 0));
|
let pos = self.buffer.line_col_to_index((n, 0));
|
||||||
self.cursors.truncate(1);
|
self.cursors.truncate(1);
|
||||||
self.cursors[0].range.0 = self.formatter.index_set_horizontal_v2d(&self.buffer, pos, self.cursors[0].vis_start, Round);
|
self.cursors[0].range.0 = self.formatter.index_set_horizontal_v2d(&self.buffer,
|
||||||
|
pos,
|
||||||
|
self.cursors[0]
|
||||||
|
.vis_start,
|
||||||
|
Round);
|
||||||
self.cursors[0].range.1 = self.cursors[0].range.0;
|
self.cursors[0].range.1 = self.cursors[0].range.0;
|
||||||
|
|
||||||
// Adjust view
|
// Adjust view
|
||||||
self.move_view_to_cursor();
|
self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
142
src/formatter.rs
142
src/formatter.rs
|
@ -20,147 +20,170 @@ pub enum RoundingBehavior {
|
||||||
|
|
||||||
pub trait LineFormatter {
|
pub trait LineFormatter {
|
||||||
fn single_line_height(&self) -> usize;
|
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.
|
||||||
fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize)
|
fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize) where T: Iterator<Item = &'a str>;
|
||||||
where T: Iterator<Item=&'a str>;
|
|
||||||
|
|
||||||
|
|
||||||
/// Converts a grapheme index within a text into a visual 2d position.
|
/// Converts a grapheme 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, index: usize) -> (usize, usize)
|
||||||
where T: Iterator<Item=&'a str>;
|
where T: Iterator<Item = &'a str>;
|
||||||
|
|
||||||
|
|
||||||
/// Converts a visual 2d position into a grapheme index within a text.
|
/// Converts a visual 2d position into a grapheme 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>(&'a self, g_iter: T, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize
|
fn v2d_to_index<'a, T>(&'a self,
|
||||||
where T: Iterator<Item=&'a str>;
|
g_iter: T,
|
||||||
|
v2d: (usize, usize),
|
||||||
|
rounding: (RoundingBehavior, RoundingBehavior))
|
||||||
|
-> usize
|
||||||
|
where T: Iterator<Item = &'a str>;
|
||||||
|
|
||||||
|
|
||||||
fn index_to_horizontal_v2d(&self, buf: &Buffer, index: usize) -> usize {
|
fn index_to_horizontal_v2d(&self, buf: &Buffer, index: usize) -> usize {
|
||||||
let (line_i, col_i) = buf.index_to_line_col(index);
|
let (line_i, col_i) = buf.index_to_line_col(index);
|
||||||
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 (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
||||||
|
|
||||||
// Get an iter into the right block
|
// Get an iter into the right block
|
||||||
let a = line_block * LINE_BLOCK_LENGTH;
|
let a = line_block * LINE_BLOCK_LENGTH;
|
||||||
let b = min(line.grapheme_count(), (line_block+1) * LINE_BLOCK_LENGTH);
|
let b = min(line.grapheme_count(), (line_block + 1) * LINE_BLOCK_LENGTH);
|
||||||
let g_iter = line.grapheme_iter_between_indices(a, b);
|
let g_iter = line.grapheme_iter_between_indices(a, b);
|
||||||
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 grapheme index and a visual vertical offset, and returns the grapheme
|
||||||
/// index after that visual offset is applied.
|
/// index after that visual offset is applied.
|
||||||
fn index_offset_vertical_v2d(&self, buf: &Buffer, index: usize, offset: isize, rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
|
fn index_offset_vertical_v2d(&self,
|
||||||
|
buf: &Buffer,
|
||||||
|
index: usize,
|
||||||
|
offset: isize,
|
||||||
|
rounding: (RoundingBehavior, RoundingBehavior))
|
||||||
|
-> usize {
|
||||||
// TODO: handle rounding modes
|
// TODO: handle rounding modes
|
||||||
// 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(index);
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
let mut line = buf.get_line(line_i);
|
let mut line = buf.get_line(line_i);
|
||||||
let (mut y, x) = self.index_to_v2d(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, min(line.grapheme_count(), (line_block+1) * LINE_BLOCK_LENGTH)), col_i_adjusted);
|
let (mut y, x) =
|
||||||
|
self.index_to_v2d(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH,
|
||||||
|
min(line.grapheme_count(),
|
||||||
|
(line_block + 1) *
|
||||||
|
LINE_BLOCK_LENGTH)),
|
||||||
|
col_i_adjusted);
|
||||||
|
|
||||||
// First, find the right line while keeping track of the vertical offset
|
// First, find the right line while keeping track of the vertical offset
|
||||||
let mut new_y = y as isize + offset;
|
let mut new_y = y as isize + offset;
|
||||||
|
|
||||||
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(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, min(line.grapheme_count(), (block_index+1) * LINE_BLOCK_LENGTH)));
|
let (h, _) =
|
||||||
|
self.dimensions(line.grapheme_iter_between_indices(block_index *
|
||||||
|
LINE_BLOCK_LENGTH,
|
||||||
|
min(line.grapheme_count(),
|
||||||
|
(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.grapheme_count());
|
let is_last_block = block_index >= last_block_index(line.grapheme_count());
|
||||||
|
|
||||||
// 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.grapheme_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_last_block {
|
if is_last_block {
|
||||||
line_i += 1;
|
line_i += 1;
|
||||||
block_index = 0;
|
block_index = 0;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
block_index += 1;
|
block_index += 1;
|
||||||
}
|
}
|
||||||
new_y -= h as isize;
|
new_y -= h as isize;
|
||||||
}
|
} else if new_y < 0 {
|
||||||
else if new_y < 0 {
|
|
||||||
// Check for off-the-end
|
// Check for off-the-end
|
||||||
if block_index == 0 && line_i == 0 {
|
if block_index == 0 && line_i == 0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.grapheme_count());
|
block_index = last_block_index(line.grapheme_count());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
block_index -= 1;
|
block_index -= 1;
|
||||||
}
|
}
|
||||||
let (h, _) = self.dimensions(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, min(line.grapheme_count(), (block_index+1) * LINE_BLOCK_LENGTH)));
|
let (h, _) = self.dimensions(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, min(line.grapheme_count(), (block_index+1) * LINE_BLOCK_LENGTH)));
|
||||||
new_y += h as isize;
|
new_y += h as isize;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(block_index * LINE_BLOCK_LENGTH, min(line.grapheme_count(), (block_index+1) * LINE_BLOCK_LENGTH));
|
let block_slice = line.slice(block_index * LINE_BLOCK_LENGTH,
|
||||||
let block_col_i = min(self.v2d_to_index(block_slice.grapheme_iter(), (y, x), rounding), LINE_BLOCK_LENGTH - 1);
|
min(line.grapheme_count(),
|
||||||
|
(block_index + 1) * LINE_BLOCK_LENGTH));
|
||||||
|
let block_col_i = min(self.v2d_to_index(block_slice.grapheme_iter(), (y, x), rounding),
|
||||||
|
LINE_BLOCK_LENGTH - 1);
|
||||||
col_i = (block_index * LINE_BLOCK_LENGTH) + block_col_i;
|
col_i = (block_index * LINE_BLOCK_LENGTH) + block_col_i;
|
||||||
|
|
||||||
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 grapheme index and a desired visual horizontal position, and
|
||||||
/// returns a grapheme index on the same visual line as the given index,
|
/// returns a grapheme 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(&self, buf: &Buffer, index: usize, horizontal: usize, rounding: RoundingBehavior) -> usize {
|
fn index_set_horizontal_v2d(&self,
|
||||||
|
buf: &Buffer,
|
||||||
|
index: usize,
|
||||||
|
horizontal: usize,
|
||||||
|
rounding: RoundingBehavior)
|
||||||
|
-> usize {
|
||||||
let (line_i, col_i) = buf.index_to_line_col(index);
|
let (line_i, col_i) = buf.index_to_line_col(index);
|
||||||
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 (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
||||||
let start_index = line_block * LINE_BLOCK_LENGTH;
|
let start_index = line_block * LINE_BLOCK_LENGTH;
|
||||||
let end_index = min(line.grapheme_count(), start_index+LINE_BLOCK_LENGTH);
|
let end_index = min(line.grapheme_count(), start_index + LINE_BLOCK_LENGTH);
|
||||||
|
|
||||||
// Calculate the horizontal position
|
// Calculate the horizontal position
|
||||||
let (v, _) = self.index_to_v2d(line.grapheme_iter_between_indices(start_index, end_index), col_i_adjusted);
|
let (v, _) = self.index_to_v2d(line.grapheme_iter_between_indices(start_index, end_index),
|
||||||
let block_col_i = self.v2d_to_index(line.grapheme_iter_between_indices(start_index, end_index), (v, horizontal), (RoundingBehavior::Floor, rounding));
|
col_i_adjusted);
|
||||||
|
let block_col_i = self.v2d_to_index(line.grapheme_iter_between_indices(start_index,
|
||||||
|
end_index),
|
||||||
|
(v, horizontal),
|
||||||
|
(RoundingBehavior::Floor, rounding));
|
||||||
let mut new_col_i = start_index + min(block_col_i, LINE_BLOCK_LENGTH - 1);
|
let mut new_col_i = start_index + 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()
|
if (line_i + 1) < buf.line_count() && new_col_i >= line.grapheme_count() &&
|
||||||
&& new_col_i >= line.grapheme_count()
|
line.grapheme_count() > 0 {
|
||||||
&& line.grapheme_count() > 0
|
|
||||||
{
|
|
||||||
new_col_i = line.grapheme_count() - 1;
|
new_col_i = line.grapheme_count() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (index + new_col_i) - col_i;
|
return (index + new_col_i) - col_i;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_index_and_offset(index: usize) -> (usize, usize) {
|
pub fn block_index_and_offset(index: usize) -> (usize, usize) {
|
||||||
|
@ -172,11 +195,10 @@ pub fn last_block_index(gc: usize) -> usize {
|
||||||
if (gc % LINE_BLOCK_LENGTH) > 0 {
|
if (gc % LINE_BLOCK_LENGTH) > 0 {
|
||||||
block_count += 1;
|
block_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if block_count > 0 {
|
if block_count > 0 {
|
||||||
return block_count - 1;
|
return block_count - 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -1,7 +1,6 @@
|
||||||
//#![feature(test)]
|
// #![feature(test)]
|
||||||
|
|
||||||
extern crate time;
|
// extern crate test;
|
||||||
//extern crate test;
|
|
||||||
extern crate rustbox;
|
extern crate rustbox;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
@ -9,16 +8,16 @@ extern crate unicode_segmentation;
|
||||||
extern crate unicode_width;
|
extern crate unicode_width;
|
||||||
extern crate encoding;
|
extern crate encoding;
|
||||||
extern crate ropey;
|
extern crate ropey;
|
||||||
//extern crate freetype;
|
// extern crate freetype;
|
||||||
//extern crate sdl2;
|
// extern crate sdl2;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use term_ui::TermUI;
|
use term_ui::TermUI;
|
||||||
use term_ui::formatter::ConsoleLineFormatter;
|
use term_ui::formatter::ConsoleLineFormatter;
|
||||||
//use gui::GUI;
|
// use gui::GUI;
|
||||||
//use gui::formatter::GUILineFormatter;
|
// use gui::formatter::GUILineFormatter;
|
||||||
|
|
||||||
mod string_utils;
|
mod string_utils;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -26,8 +25,8 @@ mod buffer;
|
||||||
mod formatter;
|
mod formatter;
|
||||||
mod editor;
|
mod editor;
|
||||||
mod term_ui;
|
mod term_ui;
|
||||||
//mod font;
|
// mod font;
|
||||||
//mod gui;
|
// mod gui;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,33 +56,31 @@ struct Args {
|
||||||
fn main() {
|
fn main() {
|
||||||
// Get command-line arguments
|
// Get command-line arguments
|
||||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
|
|
||||||
//Initialize and start UI
|
// Initialize and start UI
|
||||||
if args.flag_gui {
|
if args.flag_gui {
|
||||||
// // Load file, if specified
|
// // Load file, if specified
|
||||||
// let editor = if let Option::Some(s) = args.arg_file {
|
// let editor = if let Option::Some(s) = args.arg_file {
|
||||||
// Editor::new_from_file(GUILineFormatter::new(4), &Path::new(&s[..]))
|
// Editor::new_from_file(GUILineFormatter::new(4), &Path::new(&s[..]))
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// Editor::new(GUILineFormatter::new(4))
|
// Editor::new(GUILineFormatter::new(4))
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// // GUI
|
// // GUI
|
||||||
// sdl2::init(sdl2::INIT_VIDEO);
|
// sdl2::init(sdl2::INIT_VIDEO);
|
||||||
// let mut ui = GUI::new_from_editor(editor);
|
// let mut ui = GUI::new_from_editor(editor);
|
||||||
// ui.main_ui_loop();
|
// ui.main_ui_loop();
|
||||||
// sdl2::quit();
|
// sdl2::quit();
|
||||||
}
|
} else {
|
||||||
else {
|
// Load file, if specified
|
||||||
// Load file, if specified
|
|
||||||
let editor = if let Option::Some(s) = args.arg_file {
|
let editor = if let Option::Some(s) = args.arg_file {
|
||||||
Editor::new_from_file(ConsoleLineFormatter::new(4), &Path::new(&s[..]))
|
Editor::new_from_file(ConsoleLineFormatter::new(4), &Path::new(&s[..]))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Editor::new(ConsoleLineFormatter::new(4))
|
Editor::new(ConsoleLineFormatter::new(4))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Console UI
|
// Console UI
|
||||||
let mut ui = TermUI::new_from_editor(editor);
|
let mut ui = TermUI::new_from_editor(editor);
|
||||||
ui.main_ui_loop();
|
ui.main_ui_loop();
|
||||||
|
|
|
@ -7,16 +7,16 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub fn is_line_ending(text: &str) -> bool {
|
pub fn is_line_ending(text: &str) -> bool {
|
||||||
match text {
|
match text {
|
||||||
"\u{000D}\u{000A}"
|
"\u{000D}\u{000A}" |
|
||||||
| "\u{000A}"
|
"\u{000A}" |
|
||||||
| "\u{000B}"
|
"\u{000B}" |
|
||||||
| "\u{000C}"
|
"\u{000C}" |
|
||||||
| "\u{000D}"
|
"\u{000D}" |
|
||||||
| "\u{0085}"
|
"\u{0085}" |
|
||||||
| "\u{2028}"
|
"\u{2028}" |
|
||||||
| "\u{2029}" => true,
|
"\u{2029}" => true,
|
||||||
|
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,55 +86,55 @@ pub fn grapheme_count_is_less_than(text: &str, n: usize) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grapheme_and_line_ending_count(text: &str) -> (usize, usize) {
|
pub fn grapheme_and_line_ending_count(text: &str) -> (usize, usize) {
|
||||||
let mut grapheme_count = 0;
|
let mut grapheme_count = 0;
|
||||||
let mut line_ending_count = 0;
|
let mut line_ending_count = 0;
|
||||||
|
|
||||||
for g in UnicodeSegmentation::graphemes(text, true) {
|
for g in UnicodeSegmentation::graphemes(text, true) {
|
||||||
grapheme_count += 1;
|
grapheme_count += 1;
|
||||||
if is_line_ending(g) {
|
if is_line_ending(g) {
|
||||||
line_ending_count += 1;
|
line_ending_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (grapheme_count, line_ending_count);
|
return (grapheme_count, line_ending_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn char_pos_to_byte_pos(text: &str, pos: usize) -> usize {
|
pub fn char_pos_to_byte_pos(text: &str, pos: usize) -> usize {
|
||||||
let mut i: usize = 0;
|
let mut i: usize = 0;
|
||||||
|
|
||||||
for (offset, _) in text.char_indices() {
|
for (offset, _) in text.char_indices() {
|
||||||
if i == pos {
|
if i == pos {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == pos {
|
if i == pos {
|
||||||
return text.len();
|
return text.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("char_pos_to_byte_pos(): char position off the end of the string.");
|
panic!("char_pos_to_byte_pos(): char position off the end of the string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grapheme_pos_to_byte_pos(text: &str, pos: usize) -> usize {
|
pub fn grapheme_pos_to_byte_pos(text: &str, pos: usize) -> usize {
|
||||||
let mut i: usize = 0;
|
let mut i: usize = 0;
|
||||||
|
|
||||||
for (offset, _) in UnicodeSegmentation::grapheme_indices(text, true) {
|
for (offset, _) in UnicodeSegmentation::grapheme_indices(text, true) {
|
||||||
if i == pos {
|
if i == pos {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == pos {
|
if i == pos {
|
||||||
return text.len();
|
return text.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("grapheme_pos_to_byte_pos(): grapheme position off the end of the string.");
|
panic!("grapheme_pos_to_byte_pos(): grapheme position off the end of the string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,13 +142,13 @@ pub fn grapheme_pos_to_byte_pos(text: &str, pos: usize) -> usize {
|
||||||
pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) {
|
pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) {
|
||||||
// Find insertion position in bytes
|
// Find insertion position in bytes
|
||||||
let byte_pos = grapheme_pos_to_byte_pos(&s[..], pos);
|
let byte_pos = grapheme_pos_to_byte_pos(&s[..], pos);
|
||||||
|
|
||||||
// Get byte vec of string
|
// Get byte vec of string
|
||||||
let byte_vec = unsafe { s.as_mut_vec() };
|
let byte_vec = unsafe { s.as_mut_vec() };
|
||||||
|
|
||||||
// Grow data size
|
// Grow data size
|
||||||
byte_vec.extend(repeat(0).take(text.len()));
|
byte_vec.extend(repeat(0).take(text.len()));
|
||||||
|
|
||||||
// Move old bytes forward
|
// Move old bytes forward
|
||||||
// TODO: use copy_memory()...?
|
// TODO: use copy_memory()...?
|
||||||
let mut from = byte_vec.len() - text.len();
|
let mut from = byte_vec.len() - text.len();
|
||||||
|
@ -156,15 +156,15 @@ pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) {
|
||||||
while from > byte_pos {
|
while from > byte_pos {
|
||||||
from -= 1;
|
from -= 1;
|
||||||
to -= 1;
|
to -= 1;
|
||||||
|
|
||||||
byte_vec[to] = byte_vec[from];
|
byte_vec[to] = byte_vec[from];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy new bytes in
|
// Copy new bytes in
|
||||||
// TODO: use copy_memory()
|
// TODO: use copy_memory()
|
||||||
let mut i = byte_pos;
|
let mut i = byte_pos;
|
||||||
for g in UnicodeSegmentation::graphemes(text, true) {
|
for g in UnicodeSegmentation::graphemes(text, true) {
|
||||||
|
|
||||||
for b in g.bytes() {
|
for b in g.bytes() {
|
||||||
byte_vec[i] = b;
|
byte_vec[i] = b;
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -175,30 +175,31 @@ pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) {
|
||||||
/// Removes the text between the given grapheme indices in the given string.
|
/// Removes the text between the given grapheme indices in the given string.
|
||||||
pub fn remove_text_between_grapheme_indices(s: &mut String, pos_a: usize, pos_b: usize) {
|
pub fn remove_text_between_grapheme_indices(s: &mut String, pos_a: usize, pos_b: usize) {
|
||||||
// Bounds checks
|
// Bounds checks
|
||||||
assert!(pos_a <= pos_b, "remove_text_between_grapheme_indices(): pos_a must be less than or equal to pos_b.");
|
assert!(pos_a <= pos_b,
|
||||||
|
"remove_text_between_grapheme_indices(): pos_a must be less than or equal to pos_b.");
|
||||||
|
|
||||||
if pos_a == pos_b {
|
if pos_a == pos_b {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find removal positions in bytes
|
// Find removal positions in bytes
|
||||||
// TODO: get both of these in a single pass
|
// TODO: get both of these in a single pass
|
||||||
let byte_pos_a = grapheme_pos_to_byte_pos(&s[..], pos_a);
|
let byte_pos_a = grapheme_pos_to_byte_pos(&s[..], pos_a);
|
||||||
let byte_pos_b = grapheme_pos_to_byte_pos(&s[..], pos_b);
|
let byte_pos_b = grapheme_pos_to_byte_pos(&s[..], pos_b);
|
||||||
|
|
||||||
// Get byte vec of string
|
// Get byte vec of string
|
||||||
let byte_vec = unsafe { s.as_mut_vec() };
|
let byte_vec = unsafe { s.as_mut_vec() };
|
||||||
|
|
||||||
// Move bytes to fill in the gap left by the removed bytes
|
// Move bytes to fill in the gap left by the removed bytes
|
||||||
let mut from = byte_pos_b;
|
let mut from = byte_pos_b;
|
||||||
let mut to = byte_pos_a;
|
let mut to = byte_pos_a;
|
||||||
while from < byte_vec.len() {
|
while from < byte_vec.len() {
|
||||||
byte_vec[to] = byte_vec[from];
|
byte_vec[to] = byte_vec[from];
|
||||||
|
|
||||||
from += 1;
|
from += 1;
|
||||||
to += 1;
|
to += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove data from the end
|
// Remove data from the end
|
||||||
let final_text_size = byte_vec.len() + byte_pos_a - byte_pos_b;
|
let final_text_size = byte_vec.len() + byte_pos_a - byte_pos_b;
|
||||||
byte_vec.truncate(final_text_size);
|
byte_vec.truncate(final_text_size);
|
||||||
|
@ -209,18 +210,18 @@ pub fn remove_text_between_grapheme_indices(s: &mut String, pos_a: usize, pos_b:
|
||||||
/// while the second section of the split is returned as a new string.
|
/// while the second section of the split is returned as a new string.
|
||||||
pub fn split_string_at_grapheme_index(s1: &mut String, pos: usize) -> String {
|
pub fn split_string_at_grapheme_index(s1: &mut String, pos: usize) -> String {
|
||||||
let mut s2 = String::new();
|
let mut s2 = String::new();
|
||||||
|
|
||||||
// Code block to contain the borrow of s2
|
// Code block to contain the borrow of s2
|
||||||
{
|
{
|
||||||
let byte_pos = grapheme_pos_to_byte_pos(&s1[..], pos);
|
let byte_pos = grapheme_pos_to_byte_pos(&s1[..], pos);
|
||||||
|
|
||||||
let byte_vec_1 = unsafe { s1.as_mut_vec() };
|
let byte_vec_1 = unsafe { s1.as_mut_vec() };
|
||||||
let byte_vec_2 = unsafe { s2.as_mut_vec() };
|
let byte_vec_2 = unsafe { s2.as_mut_vec() };
|
||||||
|
|
||||||
byte_vec_2.extend((&byte_vec_1[byte_pos..]).iter().cloned());
|
byte_vec_2.extend((&byte_vec_1[byte_pos..]).iter().cloned());
|
||||||
byte_vec_1.truncate(byte_pos);
|
byte_vec_1.truncate(byte_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s2;
|
return s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,63 +235,63 @@ pub fn split_string_at_grapheme_index(s1: &mut String, pos: usize) -> String {
|
||||||
/// Also acts as an index into `LINE_ENDINGS`.
|
/// Also acts as an index into `LINE_ENDINGS`.
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum LineEnding {
|
pub enum LineEnding {
|
||||||
None = 0, // No line ending
|
None = 0, // No line ending
|
||||||
CRLF = 1, // CarriageReturn followed by LineFeed
|
CRLF = 1, // CarriageReturn followed by LineFeed
|
||||||
LF = 2, // U+000A -- LineFeed
|
LF = 2, // U+000A -- LineFeed
|
||||||
VT = 3, // U+000B -- VerticalTab
|
VT = 3, // U+000B -- VerticalTab
|
||||||
FF = 4, // U+000C -- FormFeed
|
FF = 4, // U+000C -- FormFeed
|
||||||
CR = 5, // U+000D -- CarriageReturn
|
CR = 5, // U+000D -- CarriageReturn
|
||||||
NEL = 6, // U+0085 -- NextLine
|
NEL = 6, // U+0085 -- NextLine
|
||||||
LS = 7, // U+2028 -- Line Separator
|
LS = 7, // U+2028 -- Line Separator
|
||||||
PS = 8, // U+2029 -- ParagraphSeparator
|
PS = 8, // U+2029 -- ParagraphSeparator
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn str_to_line_ending(g: &str) -> LineEnding {
|
pub fn str_to_line_ending(g: &str) -> LineEnding {
|
||||||
match g {
|
match g {
|
||||||
//==============
|
// ==============
|
||||||
// Line endings
|
// Line endings
|
||||||
//==============
|
// ==============
|
||||||
|
//
|
||||||
// CRLF
|
// CRLF
|
||||||
"\u{000D}\u{000A}" => {
|
"\u{000D}\u{000A}" => {
|
||||||
return LineEnding::CRLF;
|
return LineEnding::CRLF;
|
||||||
},
|
}
|
||||||
|
|
||||||
// LF
|
// LF
|
||||||
"\u{000A}" => {
|
"\u{000A}" => {
|
||||||
return LineEnding::LF;
|
return LineEnding::LF;
|
||||||
},
|
}
|
||||||
|
|
||||||
// VT
|
// VT
|
||||||
"\u{000B}" => {
|
"\u{000B}" => {
|
||||||
return LineEnding::VT;
|
return LineEnding::VT;
|
||||||
},
|
}
|
||||||
|
|
||||||
// FF
|
// FF
|
||||||
"\u{000C}" => {
|
"\u{000C}" => {
|
||||||
return LineEnding::FF;
|
return LineEnding::FF;
|
||||||
},
|
}
|
||||||
|
|
||||||
// CR
|
// CR
|
||||||
"\u{000D}" => {
|
"\u{000D}" => {
|
||||||
return LineEnding::CR;
|
return LineEnding::CR;
|
||||||
},
|
}
|
||||||
|
|
||||||
// NEL
|
// NEL
|
||||||
"\u{0085}" => {
|
"\u{0085}" => {
|
||||||
return LineEnding::NEL;
|
return LineEnding::NEL;
|
||||||
},
|
}
|
||||||
|
|
||||||
// LS
|
// LS
|
||||||
"\u{2028}" => {
|
"\u{2028}" => {
|
||||||
return LineEnding::LS;
|
return LineEnding::LS;
|
||||||
},
|
}
|
||||||
|
|
||||||
// PS
|
// PS
|
||||||
"\u{2029}" => {
|
"\u{2029}" => {
|
||||||
return LineEnding::PS;
|
return LineEnding::PS;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Not a line ending
|
// Not a line ending
|
||||||
_ => {
|
_ => {
|
||||||
return LineEnding::None;
|
return LineEnding::None;
|
||||||
|
@ -305,12 +306,11 @@ pub fn line_ending_to_str(ending: LineEnding) -> &'static str {
|
||||||
/// An array of string literals corresponding to the possible
|
/// An array of string literals corresponding to the possible
|
||||||
/// unicode line endings.
|
/// unicode line endings.
|
||||||
pub const LINE_ENDINGS: [&'static str; 9] = ["",
|
pub const LINE_ENDINGS: [&'static str; 9] = ["",
|
||||||
"\u{000D}\u{000A}",
|
"\u{000D}\u{000A}",
|
||||||
"\u{000A}",
|
"\u{000A}",
|
||||||
"\u{000B}",
|
"\u{000B}",
|
||||||
"\u{000C}",
|
"\u{000C}",
|
||||||
"\u{000D}",
|
"\u{000D}",
|
||||||
"\u{0085}",
|
"\u{0085}",
|
||||||
"\u{2028}",
|
"\u{2028}",
|
||||||
"\u{2029}"
|
"\u{2029}"];
|
||||||
];
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ pub enum WrapType {
|
||||||
WordWrap(usize),
|
WordWrap(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
//===================================================================
|
// ===================================================================
|
||||||
// LineFormatter implementation for terminals/consoles.
|
// LineFormatter implementation for terminals/consoles.
|
||||||
//===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
pub struct ConsoleLineFormatter {
|
pub struct ConsoleLineFormatter {
|
||||||
pub tab_width: u8,
|
pub tab_width: u8,
|
||||||
|
@ -31,24 +31,23 @@ impl ConsoleLineFormatter {
|
||||||
wrap_additional_indent: 0,
|
wrap_additional_indent: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_wrap_width(&mut self, width: usize) {
|
pub fn set_wrap_width(&mut self, width: usize) {
|
||||||
match self.wrap_type {
|
match self.wrap_type {
|
||||||
WrapType::NoWrap => {
|
WrapType::NoWrap => {}
|
||||||
},
|
|
||||||
|
|
||||||
WrapType::CharWrap(ref mut w) => {
|
WrapType::CharWrap(ref mut w) => {
|
||||||
*w = width;
|
*w = width;
|
||||||
},
|
}
|
||||||
|
|
||||||
WrapType::WordWrap(ref mut w) => {
|
WrapType::WordWrap(ref mut w) => {
|
||||||
*w = width;
|
*w = width;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter<'a, T>(&'a self, g_iter: T) -> ConsoleLineFormatterVisIter<'a, T>
|
pub fn iter<'a, T>(&'a self, g_iter: T) -> ConsoleLineFormatterVisIter<'a, T>
|
||||||
where T: Iterator<Item=&'a str>
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
ConsoleLineFormatterVisIter::<'a, T> {
|
ConsoleLineFormatterVisIter::<'a, T> {
|
||||||
grapheme_iter: g_iter,
|
grapheme_iter: g_iter,
|
||||||
|
@ -67,128 +66,135 @@ impl LineFormatter for ConsoleLineFormatter {
|
||||||
fn single_line_height(&self) -> usize {
|
fn single_line_height(&self) -> usize {
|
||||||
return 1;
|
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 T: Iterator<Item=&'a str>
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
let mut dim: (usize, usize) = (0, 0);
|
let mut dim: (usize, usize) = (0, 0);
|
||||||
|
|
||||||
for (_, pos, width) in self.iter(g_iter) {
|
for (_, pos, width) in self.iter(g_iter) {
|
||||||
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 += self.single_line_height();
|
||||||
|
|
||||||
return dim;
|
return dim;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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, index: usize) -> (usize, usize)
|
||||||
where T: Iterator<Item=&'a str>
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
let mut pos = (0, 0);
|
let mut pos = (0, 0);
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
let mut last_width = 0;
|
let mut last_width = 0;
|
||||||
|
|
||||||
for (_, _pos, width) in self.iter(g_iter) {
|
for (_, _pos, width) in self.iter(g_iter) {
|
||||||
pos = _pos;
|
pos = _pos;
|
||||||
last_width = width;
|
last_width = width;
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
if i > index {
|
if i > index {
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pos.0, pos.1 + last_width);
|
return (pos.0, pos.1 + last_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn v2d_to_index<'a, T>(&'a self, g_iter: T, v2d: (usize, usize), _: (RoundingBehavior, RoundingBehavior)) -> usize
|
fn v2d_to_index<'a, T>(&'a self,
|
||||||
where T: Iterator<Item=&'a str>
|
g_iter: T,
|
||||||
|
v2d: (usize, usize),
|
||||||
|
_: (RoundingBehavior, RoundingBehavior))
|
||||||
|
-> usize
|
||||||
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
// TODO: handle rounding modes
|
// TODO: handle rounding modes
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
||||||
for (_, pos, _) in self.iter(g_iter) {
|
for (_, pos, _) in self.iter(g_iter) {
|
||||||
if pos.0 > v2d.0 {
|
if pos.0 > v2d.0 {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
break;
|
break;
|
||||||
}
|
} else if pos.0 == v2d.0 && pos.1 >= v2d.1 {
|
||||||
else if pos.0 == v2d.0 && pos.1 >= v2d.1 {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//===================================================================
|
// ===================================================================
|
||||||
// An iterator that iterates over the graphemes in a line in a
|
// An iterator that iterates over the graphemes in a line in a
|
||||||
// manner consistent with the ConsoleFormatter.
|
// manner consistent with the ConsoleFormatter.
|
||||||
//===================================================================
|
// ===================================================================
|
||||||
pub struct ConsoleLineFormatterVisIter<'a, T>
|
pub struct ConsoleLineFormatterVisIter<'a, T>
|
||||||
where T: Iterator<Item=&'a str>
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
grapheme_iter: T,
|
grapheme_iter: T,
|
||||||
f: &'a ConsoleLineFormatter,
|
f: &'a ConsoleLineFormatter,
|
||||||
pos: (usize, usize),
|
pos: (usize, usize),
|
||||||
|
|
||||||
indent: usize,
|
indent: usize,
|
||||||
indent_found: bool,
|
indent_found: bool,
|
||||||
|
|
||||||
word_buf: Vec<&'a str>,
|
word_buf: Vec<&'a str>,
|
||||||
word_i: usize,
|
word_i: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, T> ConsoleLineFormatterVisIter<'a, T>
|
impl<'a, T> ConsoleLineFormatterVisIter<'a, T>
|
||||||
where T: Iterator<Item=&'a str>
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
fn next_nowrap(&mut self, g: &'a str) -> Option<(&'a str, (usize, usize), usize)> {
|
fn next_nowrap(&mut self, g: &'a str) -> Option<(&'a str, (usize, usize), usize)> {
|
||||||
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize);
|
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize);
|
||||||
|
|
||||||
let pos = self.pos;
|
let pos = self.pos;
|
||||||
self.pos = (self.pos.0, self.pos.1 + width);
|
self.pos = (self.pos.0, self.pos.1 + width);
|
||||||
return Some((g, pos, width));
|
return Some((g, pos, width));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn next_charwrap(&mut self, g: &'a str, wrap_width: usize) -> Option<(&'a str, (usize, usize), usize)> {
|
fn next_charwrap(&mut self,
|
||||||
|
g: &'a str,
|
||||||
|
wrap_width: usize)
|
||||||
|
-> Option<(&'a str, (usize, usize), usize)> {
|
||||||
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as 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 {
|
if (self.pos.1 + width) > wrap_width {
|
||||||
if !self.indent_found {
|
if !self.indent_found {
|
||||||
self.indent = 0;
|
self.indent = 0;
|
||||||
self.indent_found = true;
|
self.indent_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.f.maintain_indent {
|
if self.f.maintain_indent {
|
||||||
let pos = (self.pos.0 + self.f.single_line_height(), self.indent + self.f.wrap_additional_indent);
|
let pos = (self.pos.0 + self.f.single_line_height(),
|
||||||
self.pos = (self.pos.0 + self.f.single_line_height(), self.indent + self.f.wrap_additional_indent + width);
|
self.indent + self.f.wrap_additional_indent);
|
||||||
|
self.pos = (self.pos.0 + self.f.single_line_height(),
|
||||||
|
self.indent + self.f.wrap_additional_indent + width);
|
||||||
|
return Some((g, pos, width));
|
||||||
|
} else {
|
||||||
|
let pos = (self.pos.0 + self.f.single_line_height(),
|
||||||
|
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 {
|
||||||
let pos = (self.pos.0 + self.f.single_line_height(), 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if !self.indent_found {
|
if !self.indent_found {
|
||||||
if is_whitespace(g) {
|
if is_whitespace(g) {
|
||||||
self.indent += width;
|
self.indent += width;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.indent_found = true;
|
self.indent_found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = self.pos;
|
let pos = self.pos;
|
||||||
self.pos = (self.pos.0, self.pos.1 + width);
|
self.pos = (self.pos.0, self.pos.1 + width);
|
||||||
return Some((g, pos, width));
|
return Some((g, pos, width));
|
||||||
|
@ -199,7 +205,7 @@ where T: Iterator<Item=&'a str>
|
||||||
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T>
|
impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T>
|
||||||
where T: Iterator<Item=&'a str>
|
where T: Iterator<Item = &'a str>
|
||||||
{
|
{
|
||||||
type Item = (&'a str, (usize, usize), usize);
|
type Item = (&'a str, (usize, usize), usize);
|
||||||
|
|
||||||
|
@ -208,21 +214,19 @@ where T: Iterator<Item=&'a str>
|
||||||
WrapType::NoWrap => {
|
WrapType::NoWrap => {
|
||||||
if let Some(g) = self.grapheme_iter.next() {
|
if let Some(g) = self.grapheme_iter.next() {
|
||||||
return self.next_nowrap(g);
|
return self.next_nowrap(g);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
WrapType::CharWrap(wrap_width) => {
|
WrapType::CharWrap(wrap_width) => {
|
||||||
if let Some(g) = self.grapheme_iter.next() {
|
if let Some(g) = self.grapheme_iter.next() {
|
||||||
return self.next_charwrap(g, wrap_width);
|
return self.next_charwrap(g, wrap_width);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
WrapType::WordWrap(wrap_width) => {
|
WrapType::WordWrap(wrap_width) => {
|
||||||
// Get next word if necessary
|
// Get next word if necessary
|
||||||
if self.word_i >= self.word_buf.len() {
|
if self.word_i >= self.word_buf.len() {
|
||||||
|
@ -230,52 +234,54 @@ where T: Iterator<Item=&'a str>
|
||||||
self.word_buf.truncate(0);
|
self.word_buf.truncate(0);
|
||||||
while let Some(g) = self.grapheme_iter.next() {
|
while let Some(g) = self.grapheme_iter.next() {
|
||||||
self.word_buf.push(g);
|
self.word_buf.push(g);
|
||||||
let width = grapheme_vis_width_at_vis_pos(g, self.pos.1 + word_width, self.f.tab_width as usize);
|
let width = grapheme_vis_width_at_vis_pos(g,
|
||||||
|
self.pos.1 + word_width,
|
||||||
|
self.f.tab_width as usize);
|
||||||
word_width += width;
|
word_width += width;
|
||||||
if is_whitespace(g) {
|
if is_whitespace(g) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.word_buf.len() == 0 {
|
if self.word_buf.len() == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
} else if !self.indent_found && !is_whitespace(self.word_buf[0]) {
|
||||||
else if !self.indent_found && !is_whitespace(self.word_buf[0]) {
|
|
||||||
self.indent_found = true;
|
self.indent_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to next line if necessary
|
// Move to next line if necessary
|
||||||
if (self.pos.1 + word_width) > wrap_width {
|
if (self.pos.1 + word_width) > wrap_width {
|
||||||
if !self.indent_found {
|
if !self.indent_found {
|
||||||
self.indent = 0;
|
self.indent = 0;
|
||||||
self.indent_found = true;
|
self.indent_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.f.maintain_indent {
|
if self.f.maintain_indent {
|
||||||
self.pos = (self.pos.0 + self.f.single_line_height(), self.indent + self.f.wrap_additional_indent);
|
self.pos = (self.pos.0 + self.f.single_line_height(),
|
||||||
}
|
self.indent + self.f.wrap_additional_indent);
|
||||||
else {
|
} else {
|
||||||
self.pos = (self.pos.0 + self.f.single_line_height(), self.f.wrap_additional_indent);
|
self.pos = (self.pos.0 + self.f.single_line_height(),
|
||||||
|
self.f.wrap_additional_indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.word_i = 0;
|
self.word_i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over the word
|
// Iterate over the word
|
||||||
let g = self.word_buf[self.word_i];
|
let g = self.word_buf[self.word_i];
|
||||||
self.word_i += 1;
|
self.word_i += 1;
|
||||||
return self.next_charwrap(g, wrap_width);
|
return self.next_charwrap(g, wrap_width);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================================================================
|
// ===================================================================
|
||||||
// Helper functions
|
// Helper functions
|
||||||
//===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
/// Returns the visual width of a grapheme given a starting
|
/// Returns the visual width of a grapheme given a starting
|
||||||
/// position on a line.
|
/// position on a line.
|
||||||
|
@ -284,13 +290,12 @@ fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize
|
||||||
"\t" => {
|
"\t" => {
|
||||||
let ending_pos = ((pos / tab_width) + 1) * tab_width;
|
let ending_pos = ((pos / tab_width) + 1) * tab_width;
|
||||||
return ending_pos - pos;
|
return ending_pos - pos;
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if is_line_ending(g) {
|
if is_line_ending(g) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return UnicodeWidthStr::width(g);
|
return UnicodeWidthStr::width(g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,317 +312,405 @@ mod tests {
|
||||||
use formatter::{LineFormatter, LINE_BLOCK_LENGTH};
|
use formatter::{LineFormatter, LINE_BLOCK_LENGTH};
|
||||||
use formatter::RoundingBehavior::{Round, Floor, Ceiling};
|
use formatter::RoundingBehavior::{Round, Floor, Ceiling};
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dimensions_1() {
|
fn dimensions_1() {
|
||||||
let text = "Hello there, stranger!"; // 22 graphemes long
|
let text = "Hello there, stranger!"; // 22 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(80);
|
f.set_wrap_width(80);
|
||||||
|
|
||||||
assert_eq!(f.dimensions(UnicodeSegmentation::graphemes(text, true)), (1, 22));
|
assert_eq!(f.dimensions(UnicodeSegmentation::graphemes(text, true)),
|
||||||
|
(1, 22));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dimensions_2() {
|
fn dimensions_2() {
|
||||||
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(12);
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
assert_eq!(f.dimensions(UnicodeSegmentation::graphemes(text, true)), (5, 12));
|
assert_eq!(f.dimensions(UnicodeSegmentation::graphemes(text, true)),
|
||||||
|
(5, 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_to_v2d_1() {
|
fn index_to_v2d_1() {
|
||||||
let text = "Hello there, stranger!"; // 22 graphemes long
|
let text = "Hello there, stranger!"; // 22 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(80);
|
f.set_wrap_width(80);
|
||||||
|
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), (0, 0));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), (0, 5));
|
(0, 0));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 22), (0, 22));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), (0, 22));
|
(0, 5));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 22),
|
||||||
|
(0, 22));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23),
|
||||||
|
(0, 22));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_to_v2d_2() {
|
fn index_to_v2d_2() {
|
||||||
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(12);
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), (0, 0));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), (0, 5));
|
(0, 0));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 11), (0, 11));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5),
|
||||||
|
(0, 5));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 12), (1, 0));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 11),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 15), (1, 3));
|
(0, 11));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), (1, 11));
|
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 12),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 24), (2, 0));
|
(1, 0));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 28), (2, 4));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 15),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 35), (2, 11));
|
(1, 3));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 36), (3, 0));
|
(1, 11));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 43), (3, 7));
|
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 47), (3, 11));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 24),
|
||||||
|
(2, 0));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 48), (4, 0));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 28),
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 50), (4, 2));
|
(2, 4));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 56), (4, 8));
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 35),
|
||||||
|
(2, 11));
|
||||||
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 57), (4, 8));
|
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 36),
|
||||||
|
(3, 0));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 43),
|
||||||
|
(3, 7));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 47),
|
||||||
|
(3, 11));
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 48),
|
||||||
|
(4, 0));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 50),
|
||||||
|
(4, 2));
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 56),
|
||||||
|
(4, 8));
|
||||||
|
|
||||||
|
assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 57),
|
||||||
|
(4, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v2d_to_index_1() {
|
fn v2d_to_index_1() {
|
||||||
let text = "Hello there, stranger!"; // 22 graphemes long
|
let text = "Hello there, stranger!"; // 22 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(80);
|
f.set_wrap_width(80);
|
||||||
|
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,0), (Floor, Floor)), 0);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,5), (Floor, Floor)), 5);
|
(0, 0),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,22), (Floor, Floor)), 22);
|
(Floor, Floor)),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,23), (Floor, Floor)), 22);
|
0);
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (1,0), (Floor, Floor)), 22);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (1,1), (Floor, Floor)), 22);
|
(0, 5),
|
||||||
|
(Floor, Floor)),
|
||||||
|
5);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(0, 22),
|
||||||
|
(Floor, Floor)),
|
||||||
|
22);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(0, 23),
|
||||||
|
(Floor, Floor)),
|
||||||
|
22);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(1, 0),
|
||||||
|
(Floor, Floor)),
|
||||||
|
22);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(1, 1),
|
||||||
|
(Floor, Floor)),
|
||||||
|
22);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v2d_to_index_2() {
|
fn v2d_to_index_2() {
|
||||||
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(12);
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,0), (Floor, Floor)), 0);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,11), (Floor, Floor)), 11);
|
(0, 0),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (0,12), (Floor, Floor)), 11);
|
(Floor, Floor)),
|
||||||
|
0);
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (1,0), (Floor, Floor)), 12);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (1,11), (Floor, Floor)), 23);
|
(0, 11),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (1,12), (Floor, Floor)), 23);
|
(Floor, Floor)),
|
||||||
|
11);
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (2,0), (Floor, Floor)), 24);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (2,11), (Floor, Floor)), 35);
|
(0, 12),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (2,12), (Floor, Floor)), 35);
|
(Floor, Floor)),
|
||||||
|
11);
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (3,0), (Floor, Floor)), 36);
|
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (3,11), (Floor, Floor)), 47);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (3,12), (Floor, Floor)), 47);
|
(1, 0),
|
||||||
|
(Floor, Floor)),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (4,0), (Floor, Floor)), 48);
|
12);
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (4,7), (Floor, Floor)), 55);
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (4,8), (Floor, Floor)), 56);
|
(1, 11),
|
||||||
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), (4,9), (Floor, Floor)), 56);
|
(Floor, Floor)),
|
||||||
|
23);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(1, 12),
|
||||||
|
(Floor, Floor)),
|
||||||
|
23);
|
||||||
|
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(2, 0),
|
||||||
|
(Floor, Floor)),
|
||||||
|
24);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(2, 11),
|
||||||
|
(Floor, Floor)),
|
||||||
|
35);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(2, 12),
|
||||||
|
(Floor, Floor)),
|
||||||
|
35);
|
||||||
|
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(3, 0),
|
||||||
|
(Floor, Floor)),
|
||||||
|
36);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(3, 11),
|
||||||
|
(Floor, Floor)),
|
||||||
|
47);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(3, 12),
|
||||||
|
(Floor, Floor)),
|
||||||
|
47);
|
||||||
|
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(4, 0),
|
||||||
|
(Floor, Floor)),
|
||||||
|
48);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(4, 7),
|
||||||
|
(Floor, Floor)),
|
||||||
|
55);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(4, 8),
|
||||||
|
(Floor, Floor)),
|
||||||
|
56);
|
||||||
|
assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true),
|
||||||
|
(4, 9),
|
||||||
|
(Floor, Floor)),
|
||||||
|
56);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_to_horizontal_v2d_1() {
|
fn index_to_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
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(80);
|
f.set_wrap_width(80);
|
||||||
|
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 0), 0);
|
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, 5), 5);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 26), 3);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 26), 3);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 55), 32);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 55), 32);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 32);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_to_horizontal_v2d_2() {
|
fn index_to_horizontal_v2d_2() {
|
||||||
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
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(12);
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 0), 0);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 0), 0);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 11), 11);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 11), 11);
|
||||||
|
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 12), 0);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 12), 0);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 22), 10);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 22), 10);
|
||||||
|
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 23), 0);
|
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, 34), 11);
|
||||||
|
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 35), 0);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 35), 0);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 46), 11);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 46), 11);
|
||||||
|
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 47), 0);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 47), 0);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 55), 8);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 55), 8);
|
||||||
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 8);
|
assert_eq!(f.index_to_horizontal_v2d(&b, 56), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[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
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(80);
|
f.set_wrap_width(80);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 0, Floor), 0);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 0, Floor), 0);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 22, Floor), 22);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 22, Floor), 22);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 23, Floor), 22);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 23, Floor), 22);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 0, Floor), 0);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 0, Floor), 0);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 22, Floor), 22);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 22, Floor), 22);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 23, Floor), 22);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 23, Floor), 22);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 22, 0, Floor), 0);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 22, 0, Floor), 0);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 22, 22, Floor), 22);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 22, 22, Floor), 22);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 22, 23, Floor), 22);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 22, 23, Floor), 22);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 0, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 0, Floor), 23);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 32, Floor), 55);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 32, Floor), 55);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 33, Floor), 55);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 33, Floor), 55);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 28, 0, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 28, 0, Floor), 23);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 28, 32, Floor), 55);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 28, 32, Floor), 55);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 28, 33, Floor), 55);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 28, 33, Floor), 55);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 55, 0, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 55, 0, Floor), 23);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 55, 32, Floor), 55);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 55, 32, Floor), 55);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 55, 33, Floor), 55);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 55, 33, Floor), 55);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_set_horizontal_v2d_2() {
|
fn index_set_horizontal_v2d_2() {
|
||||||
let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long
|
let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(12);
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 0, Floor), 0);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 0, Floor), 0);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 11, Floor), 11);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 11, Floor), 11);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 12, Floor), 11);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 0, 12, Floor), 11);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 0, Floor), 0);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 0, Floor), 0);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 11, Floor), 11);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 11, Floor), 11);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 12, Floor), 11);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 8, 12, Floor), 11);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 11, 0, Floor), 0);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 11, 0, Floor), 0);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 11, 11, Floor), 11);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 11, 11, Floor), 11);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 11, 12, Floor), 11);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 11, 12, Floor), 11);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 12, 0, Floor), 12);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 12, 0, Floor), 12);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 12, 11, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 12, 11, Floor), 23);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 12, 12, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 12, 12, Floor), 23);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 17, 0, Floor), 12);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 17, 0, Floor), 12);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 17, 11, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 17, 11, Floor), 23);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 17, 12, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 17, 12, Floor), 23);
|
||||||
|
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 0, Floor), 12);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 0, Floor), 12);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 11, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 11, Floor), 23);
|
||||||
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 12, Floor), 23);
|
assert_eq!(f.index_set_horizontal_v2d(&b, 23, 12, Floor), 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_offset_vertical_v2d_1() {
|
fn index_offset_vertical_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
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(80);
|
f.set_wrap_width(80);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 1, (Floor, Floor)), 23);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 1, (Floor, Floor)), 23);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 23, -1, (Floor, Floor)), 0);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 23, -1, (Floor, Floor)), 0);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 2, 0, (Floor, Floor)), 2);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 2, 0, (Floor, Floor)), 2);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 2, 1, (Floor, Floor)), 25);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 2, 1, (Floor, Floor)), 25);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 25, -1, (Floor, Floor)), 2);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 25, -1, (Floor, Floor)), 2);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 22, 0, (Floor, Floor)), 22);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 22, 0, (Floor, Floor)), 22);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 22, 1, (Floor, Floor)), 45);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 22, 1, (Floor, Floor)), 45);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 45, -1, (Floor, Floor)), 22);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 45, -1, (Floor, Floor)), 22);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 54, 0, (Floor, Floor)), 54);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 54, 0, (Floor, Floor)), 54);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 54, 1, (Floor, Floor)), 55);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 54, 1, (Floor, Floor)), 55);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 54, -1, (Floor, Floor)), 22);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 54, -1, (Floor, Floor)), 22);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_offset_vertical_v2d_2() {
|
fn index_offset_vertical_v2d_2() {
|
||||||
let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long
|
let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long
|
||||||
|
|
||||||
let mut f = ConsoleLineFormatter::new(4);
|
let mut f = ConsoleLineFormatter::new(4);
|
||||||
f.wrap_type = WrapType::CharWrap(0);
|
f.wrap_type = WrapType::CharWrap(0);
|
||||||
f.maintain_indent = false;
|
f.maintain_indent = false;
|
||||||
f.wrap_additional_indent = 0;
|
f.wrap_additional_indent = 0;
|
||||||
f.set_wrap_width(12);
|
f.set_wrap_width(12);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 1, (Floor, Floor)), 12);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 1, (Floor, Floor)), 12);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 2, (Floor, Floor)), 24);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 2, (Floor, Floor)), 24);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 12, -1, (Floor, Floor)), 0);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 12, -1, (Floor, Floor)), 0);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 24, -2, (Floor, Floor)), 0);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 24, -2, (Floor, Floor)), 0);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 0, (Floor, Floor)), 4);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 0, (Floor, Floor)), 4);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 1, (Floor, Floor)), 16);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 1, (Floor, Floor)), 16);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 2, (Floor, Floor)), 28);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 2, (Floor, Floor)), 28);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 0, (Floor, Floor)), 4);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 4, 0, (Floor, Floor)), 4);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 16, -1, (Floor, Floor)), 4);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 16, -1, (Floor, Floor)), 4);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 28, -2, (Floor, Floor)), 4);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 28, -2, (Floor, Floor)), 4);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 0, (Floor, Floor)), 11);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 0, (Floor, Floor)), 11);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 1, (Floor, Floor)), 23);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 1, (Floor, Floor)), 23);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 2, (Floor, Floor)), 35);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 2, (Floor, Floor)), 35);
|
||||||
|
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 0, (Floor, Floor)), 11);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 11, 0, (Floor, Floor)), 11);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 23, -1, (Floor, Floor)), 11);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 23, -1, (Floor, Floor)), 11);
|
||||||
assert_eq!(f.index_offset_vertical_v2d(&b, 35, -2, (Floor, Floor)), 11);
|
assert_eq!(f.index_offset_vertical_v2d(&b, 35, -2, (Floor, Floor)), 11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use rustbox;
|
use rustbox;
|
||||||
use rustbox::Color;
|
use rustbox::Color;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use time::Duration;
|
use std::time::Duration;
|
||||||
use formatter::{LineFormatter, LINE_BLOCK_LENGTH, block_index_and_offset};
|
use formatter::{LineFormatter, LINE_BLOCK_LENGTH, block_index_and_offset};
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
@ -55,8 +55,8 @@ impl TermUI {
|
||||||
let w = rb.width();
|
let w = rb.width();
|
||||||
let h = rb.height();
|
let h = rb.height();
|
||||||
let mut editor = Editor::new(ConsoleLineFormatter::new(4));
|
let mut editor = Editor::new(ConsoleLineFormatter::new(4));
|
||||||
editor.update_dim(h-1, w);
|
editor.update_dim(h - 1, w);
|
||||||
|
|
||||||
TermUI {
|
TermUI {
|
||||||
rb: rb,
|
rb: rb,
|
||||||
editor: editor,
|
editor: editor,
|
||||||
|
@ -64,7 +64,7 @@ impl TermUI {
|
||||||
height: h,
|
height: h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_editor(ed: Editor<ConsoleLineFormatter>) -> TermUI {
|
pub fn new_from_editor(ed: Editor<ConsoleLineFormatter>) -> TermUI {
|
||||||
let rb = match rustbox::RustBox::init(rustbox::InitOptions {
|
let rb = match rustbox::RustBox::init(rustbox::InitOptions {
|
||||||
buffer_stderr: false,
|
buffer_stderr: false,
|
||||||
|
@ -76,8 +76,8 @@ impl TermUI {
|
||||||
let w = rb.width();
|
let w = rb.width();
|
||||||
let h = rb.height();
|
let h = rb.height();
|
||||||
let mut editor = ed;
|
let mut editor = ed;
|
||||||
editor.update_dim(h-1, w);
|
editor.update_dim(h - 1, w);
|
||||||
|
|
||||||
TermUI {
|
TermUI {
|
||||||
rb: rb,
|
rb: rb,
|
||||||
editor: editor,
|
editor: editor,
|
||||||
|
@ -85,24 +85,24 @@ impl TermUI {
|
||||||
height: h,
|
height: h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_ui_loop(&mut self) {
|
pub fn main_ui_loop(&mut self) {
|
||||||
// Quitting flag
|
// Quitting flag
|
||||||
let mut quit = false;
|
let mut quit = false;
|
||||||
let mut resize: Option<(usize, usize)> = None;
|
let mut resize: Option<(usize, usize)> = None;
|
||||||
|
|
||||||
self.editor.update_dim(self.height-1, self.width);
|
self.editor.update_dim(self.height - 1, self.width);
|
||||||
self.editor.formatter.set_wrap_width(self.width as usize);
|
self.editor.formatter.set_wrap_width(self.width as usize);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Draw the editor to screen
|
// Draw the editor to screen
|
||||||
self.editor.update_view_dim();
|
self.editor.update_view_dim();
|
||||||
self.editor.formatter.set_wrap_width(self.editor.view_dim.1);
|
self.editor.formatter.set_wrap_width(self.editor.view_dim.1);
|
||||||
self.rb.clear();
|
self.rb.clear();
|
||||||
self.draw_editor(&self.editor, (0, 0), (self.height-1, self.width-1));
|
self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1));
|
||||||
self.rb.present();
|
self.rb.present();
|
||||||
|
|
||||||
|
|
||||||
// Handle events. We block on the first event, so that the
|
// Handle events. We block on the first event, so that the
|
||||||
// program doesn't loop like crazy, but then continue pulling
|
// program doesn't loop like crazy, but then continue pulling
|
||||||
// events in a non-blocking way until we run out of events
|
// events in a non-blocking way until we run out of events
|
||||||
|
@ -111,135 +111,140 @@ impl TermUI {
|
||||||
loop {
|
loop {
|
||||||
match e {
|
match e {
|
||||||
Ok(rustbox::Event::KeyEventRaw(_, key, character)) => {
|
Ok(rustbox::Event::KeyEventRaw(_, key, character)) => {
|
||||||
//println!(" {} {} {}", modifier, key, character);
|
// println!(" {} {} {}", modifier, key, character);
|
||||||
match key {
|
match key {
|
||||||
K_CTRL_Q => {
|
K_CTRL_Q => {
|
||||||
quit = true;
|
quit = true;
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
|
|
||||||
K_CTRL_S => {
|
K_CTRL_S => {
|
||||||
self.editor.save_if_dirty();
|
self.editor.save_if_dirty();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_CTRL_Z => {
|
K_CTRL_Z => {
|
||||||
self.editor.undo();
|
self.editor.undo();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_CTRL_Y => {
|
K_CTRL_Y => {
|
||||||
self.editor.redo();
|
self.editor.redo();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_CTRL_L => {
|
K_CTRL_L => {
|
||||||
self.go_to_line_ui_loop();
|
self.go_to_line_ui_loop();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_PAGEUP => {
|
K_PAGEUP => {
|
||||||
self.editor.page_up();
|
self.editor.page_up();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_PAGEDOWN => {
|
K_PAGEDOWN => {
|
||||||
self.editor.page_down();
|
self.editor.page_down();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_UP => {
|
K_UP => {
|
||||||
self.editor.cursor_up(1);
|
self.editor.cursor_up(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
K_DOWN => {
|
K_DOWN => {
|
||||||
self.editor.cursor_down(1);
|
self.editor.cursor_down(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
K_LEFT => {
|
K_LEFT => {
|
||||||
self.editor.cursor_left(1);
|
self.editor.cursor_left(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
K_RIGHT => {
|
K_RIGHT => {
|
||||||
self.editor.cursor_right(1);
|
self.editor.cursor_right(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
K_ENTER => {
|
K_ENTER => {
|
||||||
let nl = line_ending_to_str(self.editor.line_ending_type);
|
let nl = line_ending_to_str(self.editor.line_ending_type);
|
||||||
self.editor.insert_text_at_cursor(nl);
|
self.editor.insert_text_at_cursor(nl);
|
||||||
},
|
}
|
||||||
|
|
||||||
K_SPACE => {
|
K_SPACE => {
|
||||||
self.editor.insert_text_at_cursor(" ");
|
self.editor.insert_text_at_cursor(" ");
|
||||||
},
|
}
|
||||||
|
|
||||||
K_TAB => {
|
K_TAB => {
|
||||||
self.editor.insert_tab_at_cursor();
|
self.editor.insert_tab_at_cursor();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_BACKSPACE => {
|
K_BACKSPACE => {
|
||||||
self.editor.backspace_at_cursor();
|
self.editor.backspace_at_cursor();
|
||||||
},
|
}
|
||||||
|
|
||||||
K_DELETE => {
|
K_DELETE => {
|
||||||
self.editor.remove_text_in_front_of_cursor(1);
|
self.editor.remove_text_in_front_of_cursor(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Character
|
// Character
|
||||||
0 => {
|
0 => {
|
||||||
if let Option::Some(c) = char::from_u32(character) {
|
if let Option::Some(c) = char::from_u32(character) {
|
||||||
self.editor.insert_text_at_cursor(&c.to_string()[..]);
|
self.editor.insert_text_at_cursor(&c.to_string()[..]);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
Ok(rustbox::Event::ResizeEvent(w, h)) => {
|
Ok(rustbox::Event::ResizeEvent(w, h)) => {
|
||||||
resize = Some((h as usize, w as usize));
|
resize = Some((h as usize, w as usize));
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e = self.rb.peek_event(Duration::milliseconds(0), true); // Get next event (if any)
|
e = self.rb.peek_event(Duration::from_millis(0), true); // Get next event (if any)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((h, w)) = resize {
|
if let Some((h, w)) = resize {
|
||||||
self.width = w as usize;
|
self.width = w as usize;
|
||||||
self.height = h as usize;
|
self.height = h as usize;
|
||||||
self.editor.update_dim(self.height, self.width);
|
self.editor.update_dim(self.height, self.width);
|
||||||
}
|
}
|
||||||
resize = None;
|
resize = None;
|
||||||
|
|
||||||
// Quit if quit flag is set
|
// Quit if quit flag is set
|
||||||
if quit {
|
if quit {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn go_to_line_ui_loop(&mut self) {
|
fn go_to_line_ui_loop(&mut self) {
|
||||||
let foreground = Color::Black;
|
let foreground = Color::Black;
|
||||||
let background = Color::Cyan;
|
let background = Color::Cyan;
|
||||||
|
|
||||||
let mut cancel = false;
|
let mut cancel = false;
|
||||||
let mut confirm = false;
|
let mut confirm = false;
|
||||||
let prefix = "Jump to line: ";
|
let prefix = "Jump to line: ";
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Draw the editor to screen
|
// Draw the editor to screen
|
||||||
self.editor.update_view_dim();
|
self.editor.update_view_dim();
|
||||||
self.editor.formatter.set_wrap_width(self.editor.view_dim.1);
|
self.editor.formatter.set_wrap_width(self.editor.view_dim.1);
|
||||||
self.rb.clear();
|
self.rb.clear();
|
||||||
self.draw_editor(&self.editor, (0, 0), (self.height-1, self.width-1));
|
self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1));
|
||||||
for i in 0..self.width {
|
for i in 0..self.width {
|
||||||
self.rb.print(i, 0, rustbox::RB_NORMAL, foreground, background, " ");
|
self.rb.print(i, 0, rustbox::RB_NORMAL, foreground, background, " ");
|
||||||
}
|
}
|
||||||
self.rb.print(1, 0, rustbox::RB_NORMAL, foreground, background, prefix);
|
self.rb.print(1, 0, rustbox::RB_NORMAL, foreground, background, prefix);
|
||||||
self.rb.print(prefix.len() + 1, 0, rustbox::RB_NORMAL, foreground, background, &line[..]);
|
self.rb.print(prefix.len() + 1,
|
||||||
|
0,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
foreground,
|
||||||
|
background,
|
||||||
|
&line[..]);
|
||||||
self.rb.present();
|
self.rb.present();
|
||||||
|
|
||||||
|
|
||||||
// Handle events. We block on the first event, so that the
|
// Handle events. We block on the first event, so that the
|
||||||
// program doesn't loop like crazy, but then continue pulling
|
// program doesn't loop like crazy, but then continue pulling
|
||||||
// events in a non-blocking way until we run out of events
|
// events in a non-blocking way until we run out of events
|
||||||
|
@ -252,17 +257,17 @@ impl TermUI {
|
||||||
K_ESC => {
|
K_ESC => {
|
||||||
cancel = true;
|
cancel = true;
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
|
|
||||||
K_ENTER => {
|
K_ENTER => {
|
||||||
confirm = true;
|
confirm = true;
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
|
|
||||||
K_BACKSPACE => {
|
K_BACKSPACE => {
|
||||||
line.pop();
|
line.pop();
|
||||||
},
|
}
|
||||||
|
|
||||||
// Character
|
// Character
|
||||||
0 => {
|
0 => {
|
||||||
if let Option::Some(c) = char::from_u32(character) {
|
if let Option::Some(c) = char::from_u32(character) {
|
||||||
|
@ -270,40 +275,39 @@ impl TermUI {
|
||||||
line.push(c);
|
line.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
Ok(rustbox::Event::ResizeEvent(w, h)) => {
|
Ok(rustbox::Event::ResizeEvent(w, h)) => {
|
||||||
self.width = w as usize;
|
self.width = w as usize;
|
||||||
self.height = h as usize;
|
self.height = h as usize;
|
||||||
self.editor.update_dim(self.height-1, self.width);
|
self.editor.update_dim(self.height - 1, self.width);
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e = self.rb.peek_event(Duration::milliseconds(0), true); // Get next event (if any)
|
e = self.rb.peek_event(Duration::from_millis(0), true); // Get next event (if any)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Cancel if flag is set
|
// Cancel if flag is set
|
||||||
if cancel {
|
if cancel {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump to line!
|
// Jump to line!
|
||||||
if confirm {
|
if confirm {
|
||||||
if let Ok(n) = line.parse() {
|
if let Ok(n) = line.parse() {
|
||||||
let n2: usize = n; // Weird work-around: the type of n wasn't being inferred
|
let n2: usize = n; // Weird work-around: the type of n wasn't being inferred
|
||||||
if n2 > 0 {
|
if n2 > 0 {
|
||||||
self.editor.jump_to_line(n2-1);
|
self.editor.jump_to_line(n2 - 1);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.editor.jump_to_line(0);
|
self.editor.jump_to_line(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,35 +315,52 @@ impl TermUI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn draw_editor(&self, editor: &Editor<ConsoleLineFormatter>, c1: (usize, usize), c2: (usize, usize)) {
|
fn draw_editor(&self,
|
||||||
|
editor: &Editor<ConsoleLineFormatter>,
|
||||||
|
c1: (usize, usize),
|
||||||
|
c2: (usize, usize)) {
|
||||||
let foreground = Color::Black;
|
let foreground = Color::Black;
|
||||||
let background = Color::Cyan;
|
let background = Color::Cyan;
|
||||||
|
|
||||||
// Fill in top row with info line color
|
// Fill in top row with info line color
|
||||||
for i in c1.1..(c2.1 + 1) {
|
for i in c1.1..(c2.1 + 1) {
|
||||||
self.rb.print(i, c1.0, rustbox::RB_NORMAL, foreground, background, " ");
|
self.rb.print(i, c1.0, rustbox::RB_NORMAL, foreground, background, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filename and dirty marker
|
// Filename and dirty marker
|
||||||
let filename = editor.file_path.display();
|
let filename = editor.file_path.display();
|
||||||
let dirty_char = if editor.dirty {"*"} else {""};
|
let dirty_char = if editor.dirty {
|
||||||
|
"*"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
let name = format!("{}{}", filename, dirty_char);
|
let name = format!("{}{}", filename, dirty_char);
|
||||||
self.rb.print(c1.1 + 1, c1.0, rustbox::RB_NORMAL, foreground, background, &name[..]);
|
self.rb.print(c1.1 + 1,
|
||||||
|
c1.0,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
foreground,
|
||||||
|
background,
|
||||||
|
&name[..]);
|
||||||
|
|
||||||
// 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.grapheme_count() > 0 {
|
||||||
(((editor.cursors[0].range.0 as f32) / (editor.buffer.grapheme_count() as f32)) * 100.0) as usize
|
(((editor.cursors[0].range.0 as f32) / (editor.buffer.grapheme_count() as f32)) *
|
||||||
}
|
100.0) as usize
|
||||||
else {
|
} else {
|
||||||
100
|
100
|
||||||
};
|
};
|
||||||
let pstring = format!("{}%", percentage);
|
let pstring = format!("{}%", percentage);
|
||||||
self.rb.print(c2.1 - pstring.len(), c1.0, rustbox::RB_NORMAL, foreground, background, &pstring[..]);
|
self.rb.print(c2.1 - pstring.len(),
|
||||||
|
c1.0,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
foreground,
|
||||||
|
background,
|
||||||
|
&pstring[..]);
|
||||||
|
|
||||||
// Text encoding info and tab style
|
// Text encoding info and tab style
|
||||||
let nl = match editor.line_ending_type {
|
let nl = match editor.line_ending_type {
|
||||||
LineEnding::None => "None",
|
LineEnding::None => "None",
|
||||||
|
@ -352,34 +373,51 @@ impl TermUI {
|
||||||
LineEnding::LS => "LS",
|
LineEnding::LS => "LS",
|
||||||
LineEnding::PS => "PS",
|
LineEnding::PS => "PS",
|
||||||
};
|
};
|
||||||
let soft_tabs_str = if editor.soft_tabs {"spaces"} else {"tabs"};
|
let soft_tabs_str = if editor.soft_tabs {
|
||||||
let info_line = format!("UTF8:{} {}:{}", nl, soft_tabs_str, editor.soft_tab_width as usize);
|
"spaces"
|
||||||
self.rb.print(c2.1 - 30, c1.0, rustbox::RB_NORMAL, foreground, background, &info_line[..]);
|
} else {
|
||||||
|
"tabs"
|
||||||
|
};
|
||||||
|
let info_line = format!("UTF8:{} {}:{}",
|
||||||
|
nl,
|
||||||
|
soft_tabs_str,
|
||||||
|
editor.soft_tab_width as usize);
|
||||||
|
self.rb.print(c2.1 - 30,
|
||||||
|
c1.0,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
foreground,
|
||||||
|
background,
|
||||||
|
&info_line[..]);
|
||||||
|
|
||||||
// Draw main text editing area
|
// Draw main text editing area
|
||||||
self.draw_editor_text(editor, (c1.0 + 1, c1.1), c2);
|
self.draw_editor_text(editor, (c1.0 + 1, c1.1), c2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn draw_editor_text(&self, editor: &Editor<ConsoleLineFormatter>, c1: (usize, usize), c2: (usize, usize)) {
|
fn draw_editor_text(&self,
|
||||||
|
editor: &Editor<ConsoleLineFormatter>,
|
||||||
|
c1: (usize, usize),
|
||||||
|
c2: (usize, usize)) {
|
||||||
// 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 line_block_index, _) = block_index_and_offset(col_i);
|
||||||
let mut grapheme_index = editor.buffer.line_col_to_index((line_index, line_block_index * LINE_BLOCK_LENGTH));
|
let mut grapheme_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 (vis_line_offset, _) = editor.formatter.index_to_v2d(temp_line.grapheme_iter_between_indices(line_block_index*LINE_BLOCK_LENGTH, min(temp_line.grapheme_count(), (line_block_index+1)*LINE_BLOCK_LENGTH)), editor.view_pos.0 - grapheme_index);
|
let (vis_line_offset, _) = editor.formatter.index_to_v2d(temp_line.grapheme_iter_between_indices(line_block_index*LINE_BLOCK_LENGTH, min(temp_line.grapheme_count(), (line_block_index+1)*LINE_BLOCK_LENGTH)), editor.view_pos.0 - grapheme_index);
|
||||||
|
|
||||||
let mut screen_line = c1.0 as isize - vis_line_offset as isize;
|
let mut screen_line = c1.0 as isize - vis_line_offset as isize;
|
||||||
let screen_col = c1.1 as isize + gutter_width as isize;
|
let screen_col = c1.1 as isize + gutter_width as isize;
|
||||||
|
|
||||||
// Fill in the gutter with the appropriate background
|
// Fill in the gutter with the appropriate background
|
||||||
for y in c1.0..(c2.0+1) {
|
for y in c1.0..(c2.0 + 1) {
|
||||||
for x in c1.1..(c1.1+gutter_width-1) {
|
for x in c1.1..(c1.1 + gutter_width - 1) {
|
||||||
self.rb.print(x, y, rustbox::RB_NORMAL, Color::White, Color::Blue, " ");
|
self.rb.print(x, y, rustbox::RB_NORMAL, Color::White, Color::Blue, " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut line_num = line_index + 1;
|
let mut line_num = line_index + 1;
|
||||||
for line in editor.buffer.line_iter_at_index(line_index) {
|
for line in editor.buffer.line_iter_at_index(line_index) {
|
||||||
// Print line number
|
// Print line number
|
||||||
|
@ -387,17 +425,23 @@ impl TermUI {
|
||||||
let lnx = c1.1 + (gutter_width - 1 - digit_count(line_num as u32, 10) as usize);
|
let lnx = c1.1 + (gutter_width - 1 - digit_count(line_num as u32, 10) as usize);
|
||||||
let lny = screen_line as usize;
|
let lny = screen_line as usize;
|
||||||
if lny >= c1.0 && lny <= c2.0 {
|
if lny >= c1.0 && lny <= c2.0 {
|
||||||
self.rb.print(lnx, lny, rustbox::RB_NORMAL, Color::White, Color::Blue, &format!("{}", line_num)[..]);
|
self.rb.print(lnx,
|
||||||
|
lny,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
Color::White,
|
||||||
|
Color::Blue,
|
||||||
|
&format!("{}", line_num)[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 mut line_g_index: usize = 0;
|
||||||
let mut last_pos_y = 0;
|
let mut last_pos_y = 0;
|
||||||
let mut lines_traversed: usize = 0;
|
let mut lines_traversed: usize = 0;
|
||||||
let mut g_iter = editor.formatter.iter(line.grapheme_iter_at_index(line_block_index*LINE_BLOCK_LENGTH));
|
let mut g_iter = editor.formatter.iter(line.grapheme_iter_at_index(line_block_index *
|
||||||
|
LINE_BLOCK_LENGTH));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some((g, (pos_y, pos_x), width)) = g_iter.next() {
|
if let Some((g, (pos_y, pos_x), width)) = g_iter.next() {
|
||||||
if last_pos_y != pos_y {
|
if last_pos_y != pos_y {
|
||||||
|
@ -409,12 +453,12 @@ impl TermUI {
|
||||||
// Calculate the cell coordinates at which to draw the grapheme
|
// Calculate the cell coordinates at which to draw the grapheme
|
||||||
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 = lines_traversed as isize + screen_line;
|
let py = lines_traversed as isize + screen_line;
|
||||||
|
|
||||||
// If we're off the bottom, we're done
|
// If we're off the bottom, we're done
|
||||||
if py > c2.0 as isize {
|
if py > c2.0 as isize {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the grapheme to the screen if it's in bounds
|
// Draw the grapheme to the screen if it's in bounds
|
||||||
if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) {
|
if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) {
|
||||||
// Check if the character is within a cursor
|
// Check if the character is within a cursor
|
||||||
|
@ -424,60 +468,82 @@ impl TermUI {
|
||||||
at_cursor = true;
|
at_cursor = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually print the character
|
// Actually print the character
|
||||||
if is_line_ending(g) {
|
if is_line_ending(g) {
|
||||||
if at_cursor {
|
if at_cursor {
|
||||||
self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
|
self.rb.print(px as usize,
|
||||||
|
py as usize,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
Color::Black,
|
||||||
|
Color::White,
|
||||||
|
" ");
|
||||||
}
|
}
|
||||||
}
|
} else if g == "\t" {
|
||||||
else if g == "\t" {
|
|
||||||
for i in 0..width {
|
for i in 0..width {
|
||||||
let tpx = px as usize + i;
|
let tpx = px as usize + i;
|
||||||
if tpx <= c2.1 {
|
if tpx <= c2.1 {
|
||||||
self.rb.print(tpx as usize, py as usize, rustbox::RB_NORMAL, Color::White, Color::Black, " ");
|
self.rb.print(tpx as usize,
|
||||||
|
py as usize,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
Color::White,
|
||||||
|
Color::Black,
|
||||||
|
" ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if at_cursor {
|
if at_cursor {
|
||||||
self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
|
self.rb.print(px as usize,
|
||||||
|
py as usize,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
Color::Black,
|
||||||
|
Color::White,
|
||||||
|
" ");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if at_cursor {
|
if at_cursor {
|
||||||
self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::Black, Color::White, g);
|
self.rb.print(px as usize,
|
||||||
}
|
py as usize,
|
||||||
else {
|
rustbox::RB_NORMAL,
|
||||||
self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::White, Color::Black, g);
|
Color::Black,
|
||||||
|
Color::White,
|
||||||
|
g);
|
||||||
|
} else {
|
||||||
|
self.rb.print(px as usize,
|
||||||
|
py as usize,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
Color::White,
|
||||||
|
Color::Black,
|
||||||
|
g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
grapheme_index += 1;
|
grapheme_index += 1;
|
||||||
line_g_index += 1;
|
line_g_index += 1;
|
||||||
|
|
||||||
if line_g_index >= LINE_BLOCK_LENGTH {
|
if line_g_index >= LINE_BLOCK_LENGTH {
|
||||||
line_block_index += 1;
|
line_block_index += 1;
|
||||||
line_g_index = 0;
|
line_g_index = 0;
|
||||||
g_iter = editor.formatter.iter(line.grapheme_iter_at_index(line_block_index * LINE_BLOCK_LENGTH));
|
g_iter = editor.formatter.iter(line.grapheme_iter_at_index(line_block_index *
|
||||||
|
LINE_BLOCK_LENGTH));
|
||||||
lines_traversed += 1;
|
lines_traversed += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line_block_index = 0;
|
line_block_index = 0;
|
||||||
screen_line += lines_traversed as isize + 1;
|
screen_line += lines_traversed as isize + 1;
|
||||||
line_num += 1;
|
line_num += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If we get here, it means we reached the end of the text buffer
|
// If we get here, it means we reached the end of the text buffer
|
||||||
// without going off the bottom of the screen. So draw the cursor
|
// without going off the bottom of the screen. So draw the cursor
|
||||||
// at the end if needed.
|
// at the end if needed.
|
||||||
|
|
||||||
// Check if the character is within a cursor
|
// Check if the character is within a cursor
|
||||||
let mut at_cursor = false;
|
let mut at_cursor = false;
|
||||||
for c in editor.cursors.iter() {
|
for c in editor.cursors.iter() {
|
||||||
|
@ -485,18 +551,25 @@ impl TermUI {
|
||||||
at_cursor = true;
|
at_cursor = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if at_cursor {
|
if at_cursor {
|
||||||
// Calculate the cell coordinates at which to draw the cursor
|
// Calculate the cell coordinates at which to draw the cursor
|
||||||
let pos_x = editor.formatter.index_to_horizontal_v2d(&self.editor.buffer, self.editor.buffer.grapheme_count());
|
let pos_x = editor.formatter.index_to_horizontal_v2d(&self.editor.buffer,
|
||||||
|
self.editor
|
||||||
|
.buffer
|
||||||
|
.grapheme_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;
|
||||||
|
|
||||||
if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) && (py <= c2.0 as isize) {
|
if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) &&
|
||||||
self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
|
(py <= c2.0 as isize) {
|
||||||
|
self.rb.print(px as usize,
|
||||||
|
py as usize,
|
||||||
|
rustbox::RB_NORMAL,
|
||||||
|
Color::Black,
|
||||||
|
Color::White,
|
||||||
|
" ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub fn digit_count(mut n: u32, b: u32) -> u32 {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn digit_count_base_10() {
|
fn digit_count_base_10() {
|
||||||
assert_eq!(digit_count(0, 10), 1);
|
assert_eq!(digit_count(0, 10), 1);
|
||||||
|
@ -31,4 +31,4 @@ mod tests {
|
||||||
assert_eq!(digit_count(1000000, 10), 7);
|
assert_eq!(digit_count(1000000, 10), 7);
|
||||||
assert_eq!(digit_count(9999999, 10), 7);
|
assert_eq!(digit_count(9999999, 10), 7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user