First status bar output
This commit is contained in:
parent
c1c87429fb
commit
172d5ad699
176
src/editor.rs
176
src/editor.rs
@ -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());
|
||||||
|
Loading…
Reference in New Issue
Block a user