rs-kilo/src/editor.rs

282 lines
7.6 KiB
Rust
Raw Normal View History

2019-08-22 14:25:18 -04:00
//! Editor functionality
2019-08-22 16:44:47 -04:00
use crate::helpers::*;
2019-08-22 14:25:18 -04:00
use std::cmp::PartialEq;
2019-08-22 16:44:47 -04:00
use std::io;
use std::io::prelude::*;
2019-08-23 16:46:04 -04:00
use std::io::BufReader;
2019-08-22 16:44:47 -04:00
/// Main structure for the editor
///
/// impl blocks are split similarly to the original C implementation
2019-08-23 16:46:04 -04:00
#[derive(Debug, Default)]
pub struct Editor {
cursor_x: usize,
cursor_y: usize,
2019-08-26 10:04:12 -04:00
screen_cols: usize,
screen_rows: usize,
output_buffer: String,
2019-08-23 16:46:04 -04:00
}
2019-08-22 14:25:18 -04:00
#[derive(Copy, Clone, Debug, PartialEq)]
enum EditorKey<T> {
Escape,
ArrowLeft,
ArrowRight,
ArrowUp,
ArrowDown,
DeleteKey,
HomeKey,
EndKey,
PageUp,
PageDown,
OtherKey(T),
}
impl EditorKey<char> {
pub fn unwrap(self) -> char {
match self {
self::OtherKey(val) => val,
_ => panic!("called `EditorKey::unwrap()` on a `None` value"),
}
}
}
use self::EditorKey::*;
// init
2019-08-22 14:25:18 -04:00
impl Editor {
pub fn new() -> Self {
2019-08-23 16:46:04 -04:00
let mut instance = Self::default();
let size = instance.get_window_size();
instance.cursor_x = 0;
instance.cursor_y = 0;
2019-08-26 10:04:12 -04:00
instance.screen_cols = size.cols as usize;
instance.screen_rows = size.rows as usize;
2019-08-23 16:46:04 -04:00
instance
2019-08-22 16:44:47 -04:00
}
}
2019-08-22 16:44:47 -04:00
// Terminal
impl Editor {
fn read_key(&mut self) -> Option<Vec<EditorKey<char>>> {
2019-08-22 16:44:47 -04:00
let stdin = io::stdin();
let stdin = stdin.lock();
2019-08-22 16:44:47 -04:00
let mut in_str = String::new();
let mut input = BufReader::with_capacity(3, stdin);
2019-08-22 16:44:47 -04:00
input.read_to_string(&mut in_str).unwrap();
let mut output: Vec<EditorKey<char>> = vec![];
for char in in_str.chars() {
output.push(match char {
'\x1b' => Escape,
_ => OtherKey(char),
});
}
if output.len() == 0 {
return None;
2019-08-22 16:44:47 -04:00
}
if output[0].eq(&Escape) {
if output.len() == 4 {
if output[3].eq(&OtherKey('~')) {
let action = match output[2].unwrap() {
'1' => HomeKey,
'3' => DeleteKey,
'4' => EndKey,
'5' => PageUp,
'6' => PageDown,
'7' => HomeKey,
'8' => EndKey,
_ => Escape,
};
2019-08-23 16:46:04 -04:00
return Some(vec![action]);
}
}
if output[1].eq(&OtherKey('[')) {
let action = match output[2] {
OtherKey('A') => ArrowUp,
OtherKey('B') => ArrowDown,
OtherKey('C') => ArrowRight,
OtherKey('D') => ArrowLeft,
OtherKey('H') => HomeKey,
OtherKey('F') => EndKey,
// Eh, just return escape otherwise
_ => return Some(vec![Escape]),
};
return Some(vec![action]);
}
if output[1].eq(&OtherKey('O')) {
let action = match output[2] {
OtherKey('H') => HomeKey,
OtherKey('F') => EndKey,
_ => Escape,
};
return Some(vec![action]);
}
}
2019-08-23 16:46:04 -04:00
return Some(output);
2019-08-23 16:46:04 -04:00
}
fn get_window_size(&mut self) -> TermSize {
match get_term_size() {
Some(size) => size,
None => unimplemented!("The easy way usually works")
2019-08-23 16:46:04 -04:00
}
}
}
2019-08-22 16:44:47 -04:00
// Input
impl Editor {
fn move_cursor(&mut self, key: &EditorKey<char>) {
match key {
ArrowLeft => {
if self.cursor_x != 0 {
self.cursor_x -= 1;
}
},
ArrowRight => {
if self.cursor_x != self.screen_cols - 1 {
self.cursor_x += 1;
}
},
ArrowUp => {
if self.cursor_y != 0 {
self.cursor_y -= 1;
}
},
ArrowDown => {
if self.cursor_y != self.screen_rows - 1 {
self.cursor_y += 1;
}
},
_ => (),
};
}
2019-08-23 16:46:04 -04:00
/// Route user input to the appropriate handler method
pub fn process_keypress(&mut self) -> Option<()> {
let chars = self.read_key();
if chars.is_none() {
Some(()) // Continue input loop on empty input
} else {
let chars = chars.unwrap();
// print!("{:?}\r\n", &chars);
let first = &chars[0];
2019-08-22 16:44:47 -04:00
match first {
OtherKey(c) => {
if c == &ctrl_key('q') {
print!("\x1b[2J");
print!("\x1b[H");
// Break out of the input loop
return None;
}
},
HomeKey => {
self.cursor_x = 0;
},
EndKey => {
self.cursor_x = self.screen_cols - 1;
},
PageUp => self.page_up_or_down(PageUp),
PageDown => self.page_up_or_down(PageDown),
ArrowUp => self.move_cursor(&ArrowUp),
ArrowDown => self.move_cursor(&ArrowDown),
ArrowLeft => self.move_cursor(&ArrowLeft),
ArrowRight => self.move_cursor(&ArrowRight),
_ => (),
};
2019-08-23 16:46:04 -04:00
// Continue the main input loop
Some(())
}
}
fn page_up_or_down(&mut self, key: EditorKey<char>) {
let mut times = self.screen_rows;
while times > 1 {
times -= 1;
match key {
PageUp => self.move_cursor(&ArrowUp),
PageDown => self.move_cursor(&ArrowDown),
_ => (),
}
}
2019-08-22 14:25:18 -04:00
}
}
// Output
impl Editor {
2019-08-26 10:04:12 -04:00
/// Equivalent of the abAppend function
/// in the original tutorial, just appends
/// to the `output_buffer` String in the
/// editor struct.
fn append_out(&mut self, str: &str) {
self.output_buffer.push_str(str);
}
2019-08-23 16:46:04 -04:00
fn draw_rows(&mut self) {
2019-08-26 10:04:12 -04:00
for y in 0..self.screen_rows {
if y == (self.screen_rows / 3) {
let mut welcome = format!("Kilo editor -- version {}", env!("CARGO_PKG_VERSION"));
if welcome.len() > self.screen_cols {
welcome.truncate(self.screen_cols)
}
// Center welcome message
let mut padding = (self.screen_cols - welcome.len()) / 2;
if padding > 0 {
self.append_out("~");
padding -= 1;
}
while padding > 0 {
self.append_out(" ");
padding -=1;
}
self.append_out(&welcome);
} else {
self.append_out("~");
}
self.append_out("\x1b[K");
if y < (self.screen_rows - 1) {
2019-08-26 10:04:12 -04:00
self.append_out("\r\n");
}
2019-08-23 16:46:04 -04:00
}
}
pub fn refresh_screen(&mut self) -> io::Result<()> {
2019-08-26 10:04:12 -04:00
self.output_buffer.clear();
2019-08-23 16:46:04 -04:00
2019-08-26 10:04:12 -04:00
// Hide cursor, reposition cursor
//self.append_out("\x1b[?25l");
//self.append_out("\x1b[H");
2019-08-23 16:46:04 -04:00
self.draw_rows();
// Move cursor to state position
let cursor_code = format!("\x1b[{};{}H", self.cursor_y + 1, self.cursor_x + 1);
self.append_out(&cursor_code);
// Show cursor
2019-08-26 10:04:12 -04:00
self.append_out("\x1b[?25h");
let stdout = io::stdout();
let mut handle = stdout.lock();
handle.write_all(&self.output_buffer.as_bytes())
}
}