Formatters are now primarily factories for iterators.
This is a big shift in the formatter API's. It simplifies the responsibilities of the implementers so that pretty much all they have to do it implement an iterator. Everything else will be automatically derived from that. (Or, at least, that's the hope.)
This commit is contained in:
parent
8319033ae5
commit
3ae51f7f7e
|
@ -5,7 +5,7 @@ use std::ops::{Index, IndexMut};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
use super::formatter::LineFormatter;
|
use formatter::LineFormatter;
|
||||||
|
|
||||||
/// A text cursor. Also represents selections when range.0 != range.1.
|
/// A text cursor. Also represents selections when range.0 != range.1.
|
||||||
///
|
///
|
||||||
|
@ -27,7 +27,7 @@ impl Cursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_vis_start<T: LineFormatter>(&mut self, buf: &Buffer, f: &T) {
|
pub fn update_vis_start<'a, T: LineFormatter<'a>>(&mut self, buf: &Buffer, f: &T) {
|
||||||
// TODO
|
// TODO
|
||||||
//let (_, h) = buf.index_to_v2d(self.range.0);
|
//let (_, h) = buf.index_to_v2d(self.range.0);
|
||||||
//self.vis_start = h;
|
//self.vis_start = h;
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
use buffer::line::Line;
|
|
||||||
use std::cmp::min;
|
|
||||||
|
|
||||||
#[derive(Copy, PartialEq)]
|
|
||||||
pub enum RoundingBehavior {
|
|
||||||
Round,
|
|
||||||
Floor,
|
|
||||||
Ceiling,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub trait LineFormatter {
|
|
||||||
fn single_line_height(&self) -> usize;
|
|
||||||
|
|
||||||
fn dimensions(&self, line: &Line) -> (usize, usize);
|
|
||||||
|
|
||||||
fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize);
|
|
||||||
|
|
||||||
fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================================================================
|
|
||||||
// A simple implementation of LineFormatter, for testing purposes
|
|
||||||
//================================================================
|
|
||||||
|
|
||||||
pub struct TestLineFormatter {
|
|
||||||
tab_width: u8
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestLineFormatter {
|
|
||||||
pub fn new() -> TestLineFormatter {
|
|
||||||
TestLineFormatter {
|
|
||||||
tab_width: 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LineFormatter for TestLineFormatter {
|
|
||||||
fn single_line_height(&self) -> usize {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dimensions(&self, line: &Line) -> (usize, usize) {
|
|
||||||
(1, line.grapheme_count())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize) {
|
|
||||||
(0, min(line.grapheme_count(), index))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
|
|
||||||
if v2d.0 > 0 {
|
|
||||||
line.grapheme_count()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
min(line.grapheme_count(), v2d.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,19 +2,18 @@
|
||||||
|
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
use buffer::line::LineEnding;
|
use buffer::line::LineEnding;
|
||||||
use self::formatter::LineFormatter;
|
use formatter::LineFormatter;
|
||||||
use self::formatter::RoundingBehavior::*;
|
use formatter::RoundingBehavior::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::cmp::{min, max};
|
use std::cmp::{min, max};
|
||||||
use files::{save_buffer_to_file};
|
use files::{save_buffer_to_file};
|
||||||
use string_utils::grapheme_count;
|
use string_utils::grapheme_count;
|
||||||
use self::cursor::CursorSet;
|
use self::cursor::CursorSet;
|
||||||
|
|
||||||
pub mod formatter;
|
|
||||||
mod cursor;
|
mod cursor;
|
||||||
|
|
||||||
|
|
||||||
pub struct Editor<T: LineFormatter> {
|
pub struct Editor<'a, T: LineFormatter<'a>> {
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
pub formatter: T,
|
pub formatter: T,
|
||||||
pub file_path: Path,
|
pub file_path: Path,
|
||||||
|
@ -25,16 +24,16 @@ pub struct Editor<T: LineFormatter> {
|
||||||
|
|
||||||
// The dimensions and position of the editor's view within the buffer
|
// The dimensions and position of the editor's view within the buffer
|
||||||
pub view_dim: (usize, usize), // (height, width)
|
pub view_dim: (usize, usize), // (height, width)
|
||||||
pub view_pos: (usize, usize), // (line, col)
|
pub view_pos: (usize, usize), // (grapheme index, visual horizontal offset)
|
||||||
|
|
||||||
// The editing cursor position
|
// The editing cursor position
|
||||||
pub cursors: CursorSet,
|
pub cursors: CursorSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T: LineFormatter> Editor<T> {
|
impl<'a, T: LineFormatter<'a>> Editor<'a, T> {
|
||||||
/// Create a new blank editor
|
/// Create a new blank editor
|
||||||
pub fn new(formatter: T) -> Editor<T> {
|
pub fn new(formatter: T) -> Editor<'a, T> {
|
||||||
Editor {
|
Editor {
|
||||||
buffer: Buffer::new(),
|
buffer: Buffer::new(),
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
|
@ -49,6 +48,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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},
|
||||||
|
@ -82,6 +82,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
return ed;
|
return ed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn save_if_dirty(&mut self) {
|
pub fn save_if_dirty(&mut self) {
|
||||||
if self.dirty && self.file_path != Path::new("") {
|
if self.dirty && self.file_path != Path::new("") {
|
||||||
let _ = save_buffer_to_file(&self.buffer, &self.file_path);
|
let _ = save_buffer_to_file(&self.buffer, &self.file_path);
|
||||||
|
@ -89,6 +90,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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];
|
||||||
|
|
||||||
|
@ -157,6 +159,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -251,6 +254,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn update_dim(&mut self, h: usize, w: usize) {
|
pub fn update_dim(&mut self, h: usize, w: usize) {
|
||||||
self.view_dim = (h, w);
|
self.view_dim = (h, w);
|
||||||
}
|
}
|
||||||
|
@ -296,26 +300,16 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
// there are no cursors currently in view, and should jump to
|
// there are no cursors currently in view, and should jump to
|
||||||
// the closest cursor.
|
// the closest cursor.
|
||||||
|
|
||||||
// TODO: update to new formatting code
|
let gi = self.cursors[0].range.0;
|
||||||
//let (v, h) = self.buffer.index_to_v2d(self.cursors[0].range.0);
|
let vho = self.cursors[0].vis_start;
|
||||||
//
|
|
||||||
//// Horizontal
|
self.view_pos.0 = gi;
|
||||||
//if h < self.view_pos.1 {
|
|
||||||
// self.view_pos.1 = h;
|
// TODO: horizontal offset
|
||||||
//}
|
//self.view_pos.1 = vho;
|
||||||
//else if h >= (self.view_pos.1 + self.view_dim.1) {
|
|
||||||
// self.view_pos.1 = 1 + h - self.view_dim.1;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// Vertical
|
|
||||||
//if v < self.view_pos.0 {
|
|
||||||
// self.view_pos.0 = v;
|
|
||||||
//}
|
|
||||||
//else if v >= (self.view_pos.0 + self.view_dim.0) {
|
|
||||||
// self.view_pos.0 = 1 + v - self.view_dim.0;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
@ -340,6 +334,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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) {
|
||||||
// TODO: update to new formatting code
|
// TODO: update to new formatting code
|
||||||
|
|
||||||
|
@ -382,16 +377,19 @@ impl<T: LineFormatter> Editor<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();
|
||||||
|
|
||||||
|
@ -428,6 +426,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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();
|
||||||
|
|
||||||
|
@ -463,6 +462,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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();
|
||||||
|
|
||||||
|
@ -496,6 +496,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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();
|
||||||
|
|
||||||
|
@ -506,6 +507,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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();
|
||||||
|
|
||||||
|
@ -517,6 +519,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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 {
|
||||||
|
@ -534,6 +537,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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;
|
||||||
|
@ -550,6 +554,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
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) {
|
||||||
// TODO: update to new formatting code
|
// TODO: update to new formatting code
|
||||||
|
|
||||||
|
@ -571,6 +576,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
//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) {
|
||||||
// TODO: update to new formatting code
|
// TODO: update to new formatting code
|
||||||
|
|
||||||
|
@ -594,6 +600,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
//self.move_view_to_cursor();
|
//self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn page_up(&mut self) {
|
pub fn page_up(&mut self) {
|
||||||
// TODO: update to new formatting code
|
// TODO: update to new formatting code
|
||||||
|
|
||||||
|
@ -614,6 +621,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
//self.move_view_to_cursor();
|
//self.move_view_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn page_down(&mut self) {
|
pub fn page_down(&mut self) {
|
||||||
// TODO: update to new formatting code
|
// TODO: update to new formatting code
|
||||||
|
|
||||||
|
@ -638,6 +646,7 @@ impl<T: LineFormatter> Editor<T> {
|
||||||
//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) {
|
||||||
// TODO: update to new formatting code
|
// TODO: update to new formatting code
|
||||||
|
|
||||||
|
|
111
src/formatter.rs
Normal file
111
src/formatter.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
use buffer::line::{Line, LineGraphemeIter};
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
|
#[derive(Copy, PartialEq)]
|
||||||
|
pub enum RoundingBehavior {
|
||||||
|
Round,
|
||||||
|
Floor,
|
||||||
|
Ceiling,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait LineFormatter<'a> {
|
||||||
|
type Iter: Iterator<Item=(&'a str, (usize, usize), (usize, usize))> + 'a;
|
||||||
|
|
||||||
|
fn single_line_height(&self) -> usize;
|
||||||
|
|
||||||
|
fn iter(&'a self, line: &'a Line) -> Self::Iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// A simple implementation of LineFormatter, and LineFormatIter
|
||||||
|
// for testing purposes.
|
||||||
|
//================================================================
|
||||||
|
|
||||||
|
pub struct TestLineFormatIter<'a> {
|
||||||
|
grapheme_iter: LineGraphemeIter<'a>,
|
||||||
|
f: &'a TestLineFormatter,
|
||||||
|
pos: (usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for TestLineFormatIter<'a> {
|
||||||
|
type Item = (&'a str, (usize, usize), (usize, usize));
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<(&'a str, (usize, usize), (usize, usize))> {
|
||||||
|
if let Some(g) = self.grapheme_iter.next() {
|
||||||
|
let pos = self.pos;
|
||||||
|
self.pos = (pos.0, pos.1 + 1);
|
||||||
|
return Some((g, pos, (1, self.f.tab_width as usize)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct TestLineFormatter {
|
||||||
|
tab_width: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestLineFormatter {
|
||||||
|
pub fn new() -> TestLineFormatter {
|
||||||
|
TestLineFormatter {
|
||||||
|
tab_width: 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LineFormatter<'a> for TestLineFormatter {
|
||||||
|
type Iter = TestLineFormatIter<'a>;
|
||||||
|
|
||||||
|
fn single_line_height(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter(&'a self, line: &'a Line) -> TestLineFormatIter<'a> {
|
||||||
|
TestLineFormatIter {
|
||||||
|
grapheme_iter: line.grapheme_iter(),
|
||||||
|
f: self,
|
||||||
|
pos: (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::{LineFormatter, TestLineFormatter, TestLineFormatIter};
|
||||||
|
use buffer::line::Line;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_iterator() {
|
||||||
|
let line = Line::new_from_str("Hello!");
|
||||||
|
let mut f = TestLineFormatter::new();
|
||||||
|
let mut iter = f.iter(&line);
|
||||||
|
|
||||||
|
let (a,_,_) = iter.next().unwrap();
|
||||||
|
assert_eq!(a, "H");
|
||||||
|
|
||||||
|
let (a,_,_) = iter.next().unwrap();
|
||||||
|
assert_eq!(a, "e");
|
||||||
|
|
||||||
|
let (a,_,_) = iter.next().unwrap();
|
||||||
|
assert_eq!(a, "l");
|
||||||
|
|
||||||
|
let (a,_,_) = iter.next().unwrap();
|
||||||
|
assert_eq!(a, "l");
|
||||||
|
|
||||||
|
let (a,_,_) = iter.next().unwrap();
|
||||||
|
assert_eq!(a, "o");
|
||||||
|
|
||||||
|
let (a,_,_) = iter.next().unwrap();
|
||||||
|
assert_eq!(a, "!");
|
||||||
|
|
||||||
|
let a = iter.next();
|
||||||
|
assert_eq!(a, None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ use term_ui::formatter::ConsoleLineFormatter;
|
||||||
|
|
||||||
mod string_utils;
|
mod string_utils;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
mod formatter;
|
||||||
mod files;
|
mod files;
|
||||||
mod editor;
|
mod editor;
|
||||||
mod term_ui;
|
mod term_ui;
|
||||||
|
@ -56,7 +57,7 @@ fn main() {
|
||||||
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 {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::cmp::max;
|
||||||
|
|
||||||
use string_utils::{is_line_ending};
|
use string_utils::{is_line_ending};
|
||||||
use buffer::line::{Line, LineGraphemeIter};
|
use buffer::line::{Line, LineGraphemeIter};
|
||||||
use editor::formatter::{LineFormatter, RoundingBehavior};
|
use formatter::{LineFormatter, RoundingBehavior};
|
||||||
|
|
||||||
//===================================================================
|
//===================================================================
|
||||||
// LineFormatter implementation for terminals/consoles.
|
// LineFormatter implementation for terminals/consoles.
|
||||||
|
@ -21,22 +21,17 @@ impl ConsoleLineFormatter {
|
||||||
wrap_width: 40,
|
wrap_width: 40,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns the visual cell width of a line
|
impl<'a> LineFormatter<'a> for ConsoleLineFormatter {
|
||||||
pub fn vis_width(&self, line: &Line) -> usize {
|
type Iter = ConsoleLineFormatterVisIter<'a>;
|
||||||
let mut width = 0;
|
|
||||||
|
fn single_line_height(&self) -> usize {
|
||||||
for g in line.grapheme_iter() {
|
return 1;
|
||||||
let w = grapheme_vis_width_at_vis_pos(g, width, self.tab_width as usize);
|
|
||||||
width += w;
|
|
||||||
}
|
|
||||||
|
|
||||||
return width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iter(&'a self, line: &'a Line) -> ConsoleLineFormatterVisIter<'a> {
|
||||||
pub fn vis_grapheme_iter<'b>(&'b self, line: &'b Line) -> ConsoleLineFormatterVisIter<'b> {
|
|
||||||
ConsoleLineFormatterVisIter {
|
ConsoleLineFormatterVisIter {
|
||||||
grapheme_iter: line.grapheme_iter(),
|
grapheme_iter: line.grapheme_iter(),
|
||||||
f: self,
|
f: self,
|
||||||
|
@ -46,63 +41,6 @@ impl ConsoleLineFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> LineFormatter for ConsoleLineFormatter {
|
|
||||||
fn single_line_height(&self) -> usize {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dimensions(&self, line: &Line) -> (usize, usize) {
|
|
||||||
let mut dim: (usize, usize) = (0, 0);
|
|
||||||
|
|
||||||
for (_, pos, width) in self.vis_grapheme_iter(line) {
|
|
||||||
dim = (max(dim.0, pos.0), max(dim.1, pos.1 + width));
|
|
||||||
}
|
|
||||||
|
|
||||||
dim.0 += self.single_line_height();
|
|
||||||
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize) {
|
|
||||||
let mut pos = (0, 0);
|
|
||||||
let mut i = 0;
|
|
||||||
let mut last_width = 0;
|
|
||||||
|
|
||||||
for (_, _pos, width) in self.vis_grapheme_iter(line) {
|
|
||||||
pos = _pos;
|
|
||||||
last_width = width;
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
if i > index {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pos.0, pos.1 + last_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
|
|
||||||
// TODO: handle rounding modes
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
for (_, pos, _) in self.vis_grapheme_iter(line) {
|
|
||||||
if pos.0 > v2d.0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if pos.0 == v2d.0 && pos.1 >= v2d.1 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
||||||
|
@ -116,21 +54,21 @@ pub struct ConsoleLineFormatterVisIter<'a> {
|
||||||
|
|
||||||
|
|
||||||
impl<'a> Iterator for ConsoleLineFormatterVisIter<'a> {
|
impl<'a> Iterator for ConsoleLineFormatterVisIter<'a> {
|
||||||
type Item = (&'a str, (usize, usize), usize);
|
type Item = (&'a str, (usize, usize), (usize, usize));
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> {
|
fn next(&mut self) -> Option<(&'a str, (usize, usize), (usize, usize))> {
|
||||||
if let Some(g) = self.grapheme_iter.next() {
|
if let Some(g) = self.grapheme_iter.next() {
|
||||||
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) > self.f.wrap_width {
|
if (self.pos.1 + width) > self.f.wrap_width {
|
||||||
let pos = (self.pos.0 + self.f.single_line_height(), 0);
|
let pos = (self.pos.0 + self.f.single_line_height(), 0);
|
||||||
self.pos = (self.pos.0 + self.f.single_line_height(), width);
|
self.pos = (self.pos.0 + self.f.single_line_height(), width);
|
||||||
return Some((g, pos, width));
|
return Some((g, pos, (1, width)));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
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, (1, width)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use rustbox;
|
use rustbox;
|
||||||
use rustbox::Color;
|
use rustbox::Color;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use editor::formatter::{LineFormatter, RoundingBehavior};
|
use formatter::{LineFormatter, RoundingBehavior};
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::time::duration::Duration;
|
use std::time::duration::Duration;
|
||||||
use string_utils::{is_line_ending};
|
use string_utils::{is_line_ending};
|
||||||
|
@ -33,16 +33,16 @@ const K_CTRL_Y: u16 = 25;
|
||||||
const K_CTRL_Z: u16 = 26;
|
const K_CTRL_Z: u16 = 26;
|
||||||
|
|
||||||
|
|
||||||
pub struct TermUI {
|
pub struct TermUI<'a> {
|
||||||
rb: rustbox::RustBox,
|
rb: rustbox::RustBox,
|
||||||
editor: Editor<ConsoleLineFormatter>,
|
editor: Editor<'a, ConsoleLineFormatter>,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl TermUI {
|
impl<'a> TermUI<'a> {
|
||||||
pub fn new() -> TermUI {
|
pub fn new() -> TermUI<'a> {
|
||||||
let rb = match rustbox::RustBox::init(&[Some(rustbox::InitOption::BufferStderr)]) {
|
let rb = match rustbox::RustBox::init(&[Some(rustbox::InitOption::BufferStderr)]) {
|
||||||
Ok(rbox) => rbox,
|
Ok(rbox) => rbox,
|
||||||
Err(_) => panic!("Could not create Rustbox instance."),
|
Err(_) => panic!("Could not create Rustbox instance."),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user