WOrking on getting cursor movement working again.
This commit is contained in:
parent
e9c06615f9
commit
26965417f3
|
@ -1,7 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::Path;
|
use std::old_path::Path;
|
||||||
use std::old_io::fs::File;
|
use std::old_io::fs::File;
|
||||||
use std::old_io::{IoResult, BufferedReader};
|
use std::old_io::{IoResult, BufferedReader};
|
||||||
|
|
||||||
|
@ -485,22 +485,22 @@ impl<'a> BufferGraphemeIter<'a> {
|
||||||
self.gi.skip_graphemes(n)
|
self.gi.skip_graphemes(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_non_newline_graphemes(&mut self, n: usize) -> bool {
|
//pub fn skip_non_newline_graphemes(&mut self, n: usize) -> bool {
|
||||||
let mut i: usize = 0;
|
// let mut i: usize = 0;
|
||||||
|
//
|
||||||
for g in self.gi {
|
// for g in self.gi {
|
||||||
if is_line_ending(g) {
|
// if is_line_ending(g) {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
i += 1;
|
// i += 1;
|
||||||
if i >= n {
|
// if i >= n {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,8 @@ impl Cursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_vis_start<'a, T: LineFormatter<'a>>(&mut self, buf: &Buffer, f: &T) {
|
pub fn update_vis_start<T: LineFormatter>(&mut self, buf: &Buffer, f: &T) {
|
||||||
// TODO
|
self.vis_start = f.index_to_horizontal_v2d(buf, self.range.0);
|
||||||
//let (_, h) = buf.index_to_v2d(self.range.0);
|
|
||||||
//self.vis_start = h;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use buffer::Buffer;
|
||||||
use buffer::line::LineEnding;
|
use buffer::line::LineEnding;
|
||||||
use formatter::LineFormatter;
|
use formatter::LineFormatter;
|
||||||
use formatter::RoundingBehavior::*;
|
use formatter::RoundingBehavior::*;
|
||||||
use std::path::Path;
|
use std::old_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;
|
||||||
|
@ -13,7 +13,7 @@ use self::cursor::CursorSet;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
|
|
||||||
|
|
||||||
pub struct Editor<'a, T: LineFormatter<'a>> {
|
pub struct Editor<T: LineFormatter> {
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
pub formatter: T,
|
pub formatter: T,
|
||||||
pub file_path: Path,
|
pub file_path: Path,
|
||||||
|
@ -31,9 +31,9 @@ pub struct Editor<'a, T: LineFormatter<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, T: LineFormatter<'a>> Editor<'a, T> {
|
impl<T: LineFormatter> Editor<T> {
|
||||||
/// Create a new blank editor
|
/// Create a new blank editor
|
||||||
pub fn new(formatter: T) -> Editor<'a, T> {
|
pub fn new(formatter: T) -> Editor<T> {
|
||||||
Editor {
|
Editor {
|
||||||
buffer: Buffer::new(),
|
buffer: Buffer::new(),
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
|
@ -300,10 +300,10 @@ impl<'a, T: LineFormatter<'a>> Editor<'a, 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.
|
||||||
|
|
||||||
let gi = self.cursors[0].range.0;
|
//let gi = self.cursors[0].range.0;
|
||||||
//let vho = self.cursors[0].vis_start;
|
//let vho = self.cursors[0].vis_start;
|
||||||
|
|
||||||
self.view_pos.0 = gi;
|
//self.view_pos.0 = gi;
|
||||||
|
|
||||||
// TODO: horizontal offset
|
// TODO: horizontal offset
|
||||||
//self.view_pos.1 = vho;
|
//self.view_pos.1 = vho;
|
||||||
|
@ -336,45 +336,43 @@ impl<'a, T: LineFormatter<'a>> Editor<'a, T> {
|
||||||
|
|
||||||
|
|
||||||
pub fn insert_tab_at_cursor(&mut self) {
|
pub fn insert_tab_at_cursor(&mut self) {
|
||||||
// TODO: update to new formatting code
|
self.cursors.make_consistent();
|
||||||
|
|
||||||
//self.cursors.make_consistent();
|
if self.soft_tabs {
|
||||||
//
|
let mut offset = 0;
|
||||||
//if self.soft_tabs {
|
|
||||||
// let mut offset = 0;
|
for c in self.cursors.iter_mut() {
|
||||||
//
|
// Update cursor with offset
|
||||||
// for c in self.cursors.iter_mut() {
|
c.range.0 += offset;
|
||||||
// // Update cursor with offset
|
c.range.1 += offset;
|
||||||
// c.range.0 += offset;
|
|
||||||
// c.range.1 += offset;
|
// Figure out how many spaces to insert
|
||||||
//
|
let vis_pos = self.formatter.index_to_horizontal_v2d(&self.buffer, c.range.0);
|
||||||
// // Figure out how many spaces to insert
|
// TODO: handle tab settings
|
||||||
// let (_, vis_pos) = self.buffer.index_to_v2d(c.range.0);
|
let next_tab_stop = ((vis_pos / self.soft_tab_width as usize) + 1) * self.soft_tab_width as usize;
|
||||||
// // TODO: handle tab settings
|
let space_count = min(next_tab_stop - vis_pos, 8);
|
||||||
// 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);
|
|
||||||
//
|
// Insert spaces
|
||||||
//
|
let space_strs = ["", " ", " ", " ", " ", " ", " ", " ", " "];
|
||||||
// // Insert spaces
|
self.buffer.insert_text(space_strs[space_count], c.range.0);
|
||||||
// let space_strs = ["", " ", " ", " ", " ", " ", " ", " ", " "];
|
self.dirty = true;
|
||||||
// self.buffer.insert_text(space_strs[space_count], c.range.0);
|
|
||||||
// self.dirty = true;
|
// Move cursor
|
||||||
//
|
c.range.0 += space_count;
|
||||||
// // Move cursor
|
c.range.1 += space_count;
|
||||||
// c.range.0 += space_count;
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
// c.range.1 += space_count;
|
|
||||||
// c.update_vis_start(&(self.buffer), &(self.formatter));
|
// Update offset
|
||||||
//
|
offset += space_count;
|
||||||
// // Update offset
|
}
|
||||||
// offset += space_count;
|
|
||||||
// }
|
// Adjust view
|
||||||
//
|
self.move_view_to_cursor();
|
||||||
// // Adjust view
|
}
|
||||||
// self.move_view_to_cursor();
|
else {
|
||||||
//}
|
self.insert_text_at_cursor("\t");
|
||||||
//else {
|
}
|
||||||
// self.insert_text_at_cursor("\t");
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -556,48 +554,44 @@ impl<'a, T: LineFormatter<'a>> Editor<'a, T> {
|
||||||
|
|
||||||
|
|
||||||
pub fn cursor_up(&mut self, n: usize) {
|
pub fn cursor_up(&mut self, n: usize) {
|
||||||
// TODO: update to new formatting code
|
for c in self.cursors.iter_mut() {
|
||||||
|
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));
|
||||||
|
|
||||||
//for c in self.cursors.iter_mut() {
|
if temp_index == 0 {
|
||||||
// let vmove = n * self.buffer.formatter.single_line_height();
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
// let (v, _) = self.buffer.index_to_v2d(c.range.0);
|
}
|
||||||
//
|
else {
|
||||||
// if vmove <= v {
|
temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer, temp_index, c.vis_start, Round);
|
||||||
// c.range.0 = self.buffer.v2d_to_index((v - vmove, c.vis_start), (Floor, Floor));
|
}
|
||||||
// c.range.1 = c.range.0;
|
|
||||||
// }
|
c.range.0 = temp_index;
|
||||||
// else {
|
c.range.1 = temp_index;
|
||||||
// c.range = (0, 0);
|
}
|
||||||
// c.update_vis_start(&(self.buffer), &(self.formatter));
|
|
||||||
// }
|
// Adjust view
|
||||||
//}
|
self.move_view_to_cursor();
|
||||||
//
|
|
||||||
//// Adjust view
|
|
||||||
//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
|
for c in self.cursors.iter_mut() {
|
||||||
|
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));
|
||||||
|
|
||||||
//for c in self.cursors.iter_mut() {
|
if temp_index == self.buffer.grapheme_count() {
|
||||||
// let vmove = n * self.buffer.formatter.single_line_height();
|
c.update_vis_start(&(self.buffer), &(self.formatter));
|
||||||
// let (v, _) = self.buffer.index_to_v2d(c.range.0);
|
}
|
||||||
// let (h, _) = self.buffer.dimensions();
|
else {
|
||||||
//
|
temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer, temp_index, c.vis_start, Round);
|
||||||
// if vmove < (h - v) {
|
}
|
||||||
// c.range.0 = self.buffer.v2d_to_index((v + vmove, c.vis_start), (Floor, Floor));
|
|
||||||
// c.range.1 = c.range.0;
|
c.range.0 = temp_index;
|
||||||
// }
|
c.range.1 = temp_index;
|
||||||
// else {
|
}
|
||||||
// let end = self.buffer.grapheme_count();
|
|
||||||
// c.range = (end, end);
|
// Adjust view
|
||||||
// c.update_vis_start(&(self.buffer), &(self.formatter));
|
self.move_view_to_cursor();
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// Adjust view
|
|
||||||
//self.move_view_to_cursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::old_io::{IoResult, BufferedWriter};
|
use std::old_io::{IoResult, BufferedWriter};
|
||||||
use std::old_io::fs::File;
|
use std::old_io::fs::File;
|
||||||
use std::path::Path;
|
use std::old_path::Path;
|
||||||
|
|
||||||
use buffer::line::{line_ending_to_str};
|
use buffer::line::{line_ending_to_str};
|
||||||
use buffer::Buffer as TextBuffer;
|
use buffer::Buffer as TextBuffer;
|
||||||
|
|
283
src/formatter.rs
283
src/formatter.rs
|
@ -1,6 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use buffer::line::{Line, LineGraphemeIter};
|
use buffer::line::Line;
|
||||||
|
use buffer::Buffer;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
#[derive(Copy, PartialEq)]
|
#[derive(Copy, PartialEq)]
|
||||||
|
@ -11,160 +12,178 @@ pub enum RoundingBehavior {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub trait LineFormatter<'a> {
|
pub trait LineFormatter {
|
||||||
// The iterator yields the grapheme, the 2d position of the grapheme, and the grapheme's width
|
|
||||||
type Iter: Iterator<Item=(&'a str, (usize, usize), usize)> + 'a;
|
|
||||||
|
|
||||||
fn single_line_height(&self) -> usize;
|
fn single_line_height(&self) -> usize;
|
||||||
|
|
||||||
fn iter(&'a self, line: &'a Line) -> Self::Iter;
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns the 2d visual dimensions of the given line when formatted
|
/// Returns the 2d visual dimensions of the given line when formatted
|
||||||
/// by the formatter.
|
/// by the formatter.
|
||||||
fn dimensions(&'a self, line: &'a Line) -> (usize, usize) {
|
fn dimensions(&self, line: &Line) -> (usize, usize);
|
||||||
let mut dim: (usize, usize) = (0, 0);
|
|
||||||
|
|
||||||
for (_, pos, width) in self.iter(line) {
|
|
||||||
dim = (max(dim.0, pos.0), max(dim.1, pos.1 + width));
|
|
||||||
}
|
|
||||||
|
|
||||||
dim.0 += self.single_line_height();
|
|
||||||
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Converts a grapheme index within a line into a visual 2d position.
|
/// Converts a grapheme index within a line into a visual 2d position.
|
||||||
fn index_to_v2d(&'a self, line: &'a Line, index: usize) -> (usize, usize) {
|
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.iter(line) {
|
|
||||||
pos = _pos;
|
|
||||||
last_width = width;
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
if i > index {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pos.0, pos.1 + last_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Converts a visual 2d position into a grapheme index within a line.
|
/// Converts a visual 2d position into a grapheme index within a line.
|
||||||
fn v2d_to_index(&'a self, line: &'a Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
|
fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> 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 = buf.get_line(line_i);
|
||||||
|
return self.index_to_v2d(line, col_i).1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Takes a grapheme index and a visual vertical offset, and returns the grapheme
|
||||||
|
/// index after that visual offset is applied.
|
||||||
|
fn index_offset_vertical_v2d(&self, buf: &Buffer, index: usize, offset: isize, rounding: (RoundingBehavior, RoundingBehavior)) -> usize {
|
||||||
// TODO: handle rounding modes
|
// TODO: handle rounding modes
|
||||||
let mut i = 0;
|
// TODO: do this with bidirectional line iterator
|
||||||
|
let (mut line_i, mut col_i) = buf.index_to_line_col(index);
|
||||||
|
let (mut y, x) = self.index_to_v2d(buf.get_line(line_i), col_i);
|
||||||
|
let mut new_y = y as isize + offset;
|
||||||
|
|
||||||
for (_, pos, _) in self.iter(line) {
|
// First, find the right line while keeping track of the vertical offset
|
||||||
if pos.0 > v2d.0 {
|
let mut line;
|
||||||
|
loop {
|
||||||
|
line = buf.get_line(line_i);
|
||||||
|
let (mut h, _) = self.dimensions(line);
|
||||||
|
|
||||||
|
if new_y >= 0 && new_y < h as isize {
|
||||||
|
y = new_y as usize;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if pos.0 == v2d.0 && pos.1 >= v2d.1 {
|
else {
|
||||||
break;
|
if new_y < 0 {
|
||||||
|
// Check for off-the-end
|
||||||
|
if (line_i + 1) >= buf.line_count() {
|
||||||
|
return buf.grapheme_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
line_i += 1;
|
||||||
|
new_y -= h as isize;
|
||||||
|
}
|
||||||
|
else if new_y > 0 {
|
||||||
|
// Check for off-the-end
|
||||||
|
if line_i == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_i -= 1;
|
||||||
|
new_y -= h as isize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
// Next, convert the resulting coordinates back into buffer-wide
|
||||||
}
|
// coordinates.
|
||||||
}
|
let col_i = self.v2d_to_index(line, (y, x), rounding);
|
||||||
|
return buf.line_col_to_index((line_i, col_i));
|
||||||
|
|
||||||
|
|
||||||
//================================================================
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(&'a str, (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));
|
|
||||||
}
|
|
||||||
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 {
|
fn index_set_horizontal_v2d(&self, buf: &Buffer, index: usize, horizontal: usize, rounding: RoundingBehavior) -> usize {
|
||||||
grapheme_iter: line.grapheme_iter(),
|
let (line_i, col_i) = buf.index_to_line_col(index);
|
||||||
f: self,
|
let line = buf.get_line(line_i);
|
||||||
pos: (0, 0),
|
|
||||||
}
|
let (v, h) = self.index_to_v2d(line, col_i);
|
||||||
|
let new_col_i = self.v2d_to_index(line, (v, horizontal), (RoundingBehavior::Floor, rounding));
|
||||||
|
|
||||||
|
return (index + new_col_i) - col_i;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mod tests {
|
//====================================================================
|
||||||
#![allow(unused_imports)]
|
// UNIT TESTS
|
||||||
use super::{TestLineFormatter, TestLineFormatIter};
|
//====================================================================
|
||||||
use buffer::line::Line;
|
|
||||||
|
|
||||||
#[test]
|
//#[cfg(test)]
|
||||||
fn simple_iterator() {
|
//mod tests {
|
||||||
let line = Line::new_from_str("Hello!");
|
// #![allow(unused_imports)]
|
||||||
let mut f = TestLineFormatter::new();
|
// use buffer::line::{Line, LineGraphemeIter};
|
||||||
let mut iter = f.iter(&line);
|
// use super::LineFormatter;
|
||||||
|
//
|
||||||
let (a,_,_) = iter.next().unwrap();
|
// pub struct TestLineFormatIter<'a> {
|
||||||
assert_eq!(a, "H");
|
// grapheme_iter: LineGraphemeIter<'a>,
|
||||||
|
// f: &'a TestLineFormatter,
|
||||||
let (a,_,_) = iter.next().unwrap();
|
// pos: (usize, usize),
|
||||||
assert_eq!(a, "e");
|
// }
|
||||||
|
//
|
||||||
let (a,_,_) = iter.next().unwrap();
|
// impl<'a> Iterator for TestLineFormatIter<'a> {
|
||||||
assert_eq!(a, "l");
|
// type Item = (&'a str, (usize, usize), usize);
|
||||||
|
//
|
||||||
let (a,_,_) = iter.next().unwrap();
|
// fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> {
|
||||||
assert_eq!(a, "l");
|
// if let Some(g) = self.grapheme_iter.next() {
|
||||||
|
// let pos = self.pos;
|
||||||
let (a,_,_) = iter.next().unwrap();
|
// self.pos = (pos.0, pos.1 + 1);
|
||||||
assert_eq!(a, "o");
|
// return Some((g, pos, 1));
|
||||||
|
// }
|
||||||
let (a,_,_) = iter.next().unwrap();
|
// else {
|
||||||
assert_eq!(a, "!");
|
// return None;
|
||||||
|
// }
|
||||||
let a = iter.next();
|
// }
|
||||||
assert_eq!(a, None);
|
// }
|
||||||
}
|
//
|
||||||
}
|
// pub struct TestLineFormatter {
|
||||||
|
// tab_width: u8
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl TestLineFormatter {
|
||||||
|
// pub fn new() -> TestLineFormatter {
|
||||||
|
// TestLineFormatter {
|
||||||
|
// tab_width: 4,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl<'a> LineFormatter<'a, TestLineFormatIter<'a>> for TestLineFormatter {
|
||||||
|
// 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),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// #[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);
|
||||||
|
// }
|
||||||
|
//}//
|
|
@ -10,7 +10,7 @@ extern crate "rustc-serialize" as rustc_serialize;
|
||||||
//extern crate freetype;
|
//extern crate freetype;
|
||||||
//extern crate sdl2;
|
//extern crate sdl2;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::old_path::Path;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use term_ui::TermUI;
|
use term_ui::TermUI;
|
||||||
|
|
|
@ -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 formatter::LineFormatter;
|
use formatter::{LineFormatter, RoundingBehavior};
|
||||||
|
|
||||||
//===================================================================
|
//===================================================================
|
||||||
// LineFormatter implementation for terminals/consoles.
|
// LineFormatter implementation for terminals/consoles.
|
||||||
|
@ -21,17 +21,8 @@ impl ConsoleLineFormatter {
|
||||||
wrap_width: 40,
|
wrap_width: 40,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self, line: &'a Line) -> ConsoleLineFormatterVisIter<'a> {
|
||||||
impl<'a> LineFormatter<'a> for ConsoleLineFormatter {
|
|
||||||
type Iter = ConsoleLineFormatterVisIter<'a>;
|
|
||||||
|
|
||||||
fn single_line_height(&self) -> usize {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter(&'a self, line: &'a Line) -> ConsoleLineFormatterVisIter<'a> {
|
|
||||||
ConsoleLineFormatterVisIter {
|
ConsoleLineFormatterVisIter {
|
||||||
grapheme_iter: line.grapheme_iter(),
|
grapheme_iter: line.grapheme_iter(),
|
||||||
f: self,
|
f: self,
|
||||||
|
@ -41,6 +32,66 @@ impl<'a> LineFormatter<'a> for ConsoleLineFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl 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.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.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;
|
||||||
|
let mut pos = (0, 0);
|
||||||
|
|
||||||
|
for (_, _pos, _) in self.iter(line) {
|
||||||
|
pos = _pos;
|
||||||
|
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.
|
||||||
|
|
|
@ -8,7 +8,7 @@ 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};
|
||||||
use buffer::line::{line_ending_to_str, LineEnding};
|
use buffer::line::{line_ending_to_str, LineEnding};
|
||||||
use self::formatter::ConsoleLineFormatter;
|
use self::formatter::{ConsoleLineFormatter, ConsoleLineFormatterVisIter};
|
||||||
|
|
||||||
pub mod formatter;
|
pub mod formatter;
|
||||||
|
|
||||||
|
@ -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<'a> {
|
pub struct TermUI {
|
||||||
rb: rustbox::RustBox,
|
rb: rustbox::RustBox,
|
||||||
editor: Editor<'a, ConsoleLineFormatter>,
|
editor: Editor<ConsoleLineFormatter>,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> TermUI<'a> {
|
impl TermUI {
|
||||||
pub fn new() -> TermUI<'a> {
|
pub fn new() -> TermUI {
|
||||||
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."),
|
||||||
|
@ -288,7 +288,7 @@ impl<'a> TermUI<'a> {
|
||||||
|
|
||||||
// Jump to line!
|
// Jump to line!
|
||||||
if confirm {
|
if confirm {
|
||||||
if let Some(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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user