First status bar output

This commit is contained in:
Timothy Warren 2019-08-28 16:35:48 -04:00
parent c1c87429fb
commit 172d5ad699

View File

@ -6,20 +6,25 @@ use std::fs::File;
use std::io; use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::BufReader; use std::io::BufReader;
use std::string::ToString;
use self::EditorKey::*; use self::EditorKey::*;
const KILO_TAB_STOP: usize = 4;
/// A representation of a line in the editor /// A representation of a line in the editor
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct EditorRow { pub struct EditorRow {
chars: String, chars: String,
render: String,
} }
impl EditorRow { impl EditorRow {
pub fn new(str: &str) -> Self { pub fn new(chars: &str) -> Self {
EditorRow { let mut instance = EditorRow::default();
chars: str.to_owned(), instance.chars = chars.to_owned();
}
instance
} }
} }
@ -29,10 +34,13 @@ impl EditorRow {
pub struct Editor { pub struct Editor {
cursor_x: usize, cursor_x: usize,
cursor_y: usize, cursor_y: usize,
rows: Vec<EditorRow>, render_x: usize,
col_offset: usize,
row_offset: usize, row_offset: usize,
screen_cols: usize, screen_cols: usize,
screen_rows: usize, screen_rows: usize,
rows: Vec<EditorRow>,
filename: String,
output_buffer: String, output_buffer: String,
} }
@ -76,7 +84,7 @@ impl Editor {
instance.cursor_x = 0; instance.cursor_x = 0;
instance.cursor_y = 0; instance.cursor_y = 0;
instance.screen_cols = size.cols as usize; instance.screen_cols = size.cols as usize;
instance.screen_rows = size.rows as usize; instance.screen_rows = (size.rows - 1) as usize;
instance instance
} }
@ -135,12 +143,12 @@ impl Editor {
'6' => PageDown, '6' => PageDown,
'7' => HomeKey, '7' => HomeKey,
'8' => EndKey, '8' => EndKey,
_ => input[2]// Escape, _ => input[2], // Escape,
}; };
return Some(action); return Some(action);
} }
}, }
3 => { 3 => {
match input[1] { match input[1] {
// Escape code of the form `^[[X` // Escape code of the form `^[[X`
@ -154,11 +162,11 @@ impl Editor {
'F' => EndKey, 'F' => EndKey,
// Eh, just return escape otherwise // Eh, just return escape otherwise
_ => input[2] //Escape, _ => input[2], //Escape,
}; };
return Some(action); return Some(action);
}, }
// Escape code of the form `^[OX` // Escape code of the form `^[OX`
OtherKey('O') => { OtherKey('O') => {
let action = match input[2].unwrap() { let action = match input[2].unwrap() {
@ -168,14 +176,14 @@ impl Editor {
'Q' => Function('2'), 'Q' => Function('2'),
'R' => Function('3'), 'R' => Function('3'),
'S' => Function('4'), 'S' => Function('4'),
_ => input[2] //Escape, _ => input[2], //Escape,
}; };
return Some(action); return Some(action);
},
_ => return Some(input[1])
} }
}, _ => return Some(input[1]),
}
}
_ => return Some(input[0]), _ => return Some(input[0]),
} }
} }
@ -197,15 +205,26 @@ impl Editor {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
fn move_cursor(&mut self, key: &EditorKey<char>) { fn move_cursor(&mut self, key: &EditorKey<char>) {
let row = self.rows.get(self.cursor_y);
match key { match key {
ArrowLeft => { ArrowLeft => {
if self.cursor_x > 0 { if self.cursor_x != 0 {
// Move cursor left
self.cursor_x -= 1; self.cursor_x -= 1;
} else if self.cursor_y > 0 {
// Move to the end of the previous line
self.cursor_y -= 1;
self.cursor_x = self.rows[self.cursor_y].chars.len();
} }
} }
ArrowRight => { ArrowRight => {
if self.cursor_x < self.screen_cols - 1 { if row.is_some() && self.cursor_x < row.unwrap().chars.len() {
// Move cursor right
self.cursor_x += 1; self.cursor_x += 1;
} else if row.is_some() && self.cursor_x == row.unwrap().chars.len() {
// Move to start of next line
self.cursor_y += 1;
self.cursor_x = 0;
} }
} }
ArrowUp => { ArrowUp => {
@ -220,6 +239,18 @@ impl Editor {
} }
_ => (), _ => (),
}; };
let row = self.rows.get(self.cursor_y);
let row_len = if row.is_some() {
row.unwrap().chars.len()
} else {
0
};
// Snap to end of line when scrolling down
if self.cursor_x > row_len {
self.cursor_x = row_len;
}
} }
/// Route user input to the appropriate handler method /// Route user input to the appropriate handler method
@ -248,12 +279,14 @@ impl Editor {
self.cursor_x = 0; self.cursor_x = 0;
} }
EndKey => { EndKey => {
self.cursor_x = self.screen_cols - 1; if self.cursor_y < self.rows.len() {
}, self.cursor_x = self.rows[self.cursor_y].chars.len();
}
}
_ => (), _ => (),
}; };
return key return key;
} }
// Continue the main input loop // Continue the main input loop
@ -263,13 +296,28 @@ impl Editor {
fn page_up_or_down(&mut self, key: EditorKey<char>) { fn page_up_or_down(&mut self, key: EditorKey<char>) {
let mut times = self.screen_rows; let mut times = self.screen_rows;
while times > 1 { // Update the cursor position
times -= 1;
match key { match key {
PageUp => self.move_cursor(&ArrowUp), PageUp => {
PageDown => self.move_cursor(&ArrowDown), self.cursor_y = self.row_offset;
}
PageDown => {
self.cursor_y = self.row_offset + self.screen_rows - 1;
if self.cursor_y > self.rows.len() {
self.cursor_y = self.rows.len();
}
}
_ => (), _ => (),
} }
// Scroll the file up or down
while times > 1 {
times -= 1;
self.move_cursor(match key {
PageUp => &ArrowUp,
PageDown => &ArrowDown,
_ => &OtherKey('\0'),
})
} }
} }
@ -286,13 +334,26 @@ impl Editor {
} }
fn scroll(&mut self) { fn scroll(&mut self) {
self.render_x = 0;
if self.cursor_y < self.rows.len() {
self.render_x = self.row_cx_to_rx(self.cursor_y, self.cursor_x);
}
// Vertical scrolling
if self.cursor_y < self.row_offset { if self.cursor_y < self.row_offset {
self.row_offset = self.cursor_y; self.row_offset = self.cursor_y;
} }
if self.cursor_y >= self.row_offset + self.screen_rows { if self.cursor_y >= self.row_offset + self.screen_rows {
self.row_offset = self.cursor_y - self.screen_rows + 1; self.row_offset = self.cursor_y - self.screen_rows + 1;
} }
// Horizontal scrolling
if self.render_x < self.col_offset {
self.col_offset = self.render_x;
}
if self.render_x >= self.col_offset + self.screen_cols {
self.col_offset = self.render_x - self.screen_cols + 1;
}
} }
fn draw_rows(&mut self) { fn draw_rows(&mut self) {
@ -323,18 +384,43 @@ impl Editor {
self.append_out("~"); self.append_out("~");
} }
} else { } else {
let mut output = self.rows[file_row].chars.clone(); let mut len = self.rows[file_row].render.len() - self.col_offset;
if output.len() > self.screen_cols { if len > self.screen_cols {
output.truncate(self.screen_cols); len = self.screen_cols;
} }
self.append_out(&output);
let output = self.rows[file_row].render.clone();
// let mut output = self.rows[file_row].render.clone();
// output.truncate(len);
self.append_out(&output[self.col_offset..len]);
} }
self.append_out("\x1b[K"); self.append_out("\x1b[K");
if y < (self.screen_rows - 1) {
self.append_out("\r\n"); self.append_out("\r\n");
} }
} }
fn draw_status_bar(&mut self) {
self.append_out("\x1b[7m");
let filename = if self.filename.is_empty() {
"[No Name]"
} else {
&self.filename
};
let mut message = format!("{:.20} - {} lines", filename, self.rows.len());
let mut len = message.len();
if len > self.screen_cols {
len = self.screen_cols;
message.truncate(len);
}
self.append_out(&message);
for _ in len..self.screen_cols {
self.append_out(" ");
}
self.append_out("\x1b[m");
} }
pub fn refresh_screen(&mut self) -> io::Result<()> { pub fn refresh_screen(&mut self) -> io::Result<()> {
@ -346,9 +432,12 @@ impl Editor {
self.append_out("\x1b[H"); self.append_out("\x1b[H");
self.draw_rows(); self.draw_rows();
self.draw_status_bar();
// Move cursor to state position // Move cursor to state position
let cursor_code = format!("\x1b[{y};{x}H", y=self.cursor_y + 1, x=self.cursor_x + 1); let y = (self.cursor_y - self.row_offset) + 1;
let x = (self.render_x - self.col_offset) + 1;
let cursor_code = format!("\x1b[{y};{x}H", y=y, x=x);
self.append_out(&cursor_code); self.append_out(&cursor_code);
// Show cursor // Show cursor
@ -363,8 +452,32 @@ impl Editor {
// Row Operations // Row Operations
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
fn row_cx_to_rx(&mut self, index: usize, cx: usize) -> usize {
let mut rx: usize = 0;
let row = &mut self.rows[index];
for char in row.chars.chars() {
if char == '\t' {
rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP);
}
rx += 1;
}
rx
}
fn update_row(&mut self, index: usize) {
let row = &mut self.rows[index];
let str = row.chars.clone();
// Cheat at rendering tabs as spaces
let str = str.replace('\t', " ");
row.render = str;
}
fn append_row(&mut self, row: &str) { fn append_row(&mut self, row: &str) {
self.rows.push(EditorRow::new(row)); self.rows.push(EditorRow::new(row));
self.update_row(self.rows.len() - 1);
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -373,7 +486,8 @@ impl Editor {
/// Open a file for display /// Open a file for display
pub fn open(&mut self, filename: &str) -> io::Result<()> { pub fn open(&mut self, filename: &str) -> io::Result<()> {
let file = File::open(filename)?; self.filename = filename.to_owned();
let file = File::open(&self.filename)?;
let buf_reader = BufReader::new(file); let buf_reader = BufReader::new(file);
let lines = buf_reader.lines().map(|l| l.unwrap()); let lines = buf_reader.lines().map(|l| l.unwrap());