hecto/src/editor.rs

222 lines
6.0 KiB
Rust
Raw Normal View History

2021-03-08 10:21:06 -05:00
use crate::Document;
2021-03-08 10:43:40 -05:00
use crate::Row;
2021-03-05 16:36:39 -05:00
use crate::Terminal;
2021-03-08 13:34:25 -05:00
use std::env;
2021-01-28 16:47:40 -05:00
use termion::event::Key;
2021-03-05 16:36:39 -05:00
const VERSION: &str = env!("CARGO_PKG_VERSION");
2021-01-28 16:47:40 -05:00
2021-03-08 10:21:06 -05:00
#[derive(Default)]
2021-03-08 09:50:15 -05:00
pub struct Position {
pub x: usize,
pub y: usize,
}
2021-01-28 16:47:40 -05:00
pub struct Editor {
should_quit: bool,
2021-03-05 16:36:39 -05:00
terminal: Terminal,
2021-03-08 09:50:15 -05:00
cursor_position: Position,
2021-03-08 14:21:24 -05:00
offset: Position,
2021-03-08 10:21:06 -05:00
document: Document,
2021-01-28 16:47:40 -05:00
}
impl Editor {
pub fn run(&mut self) {
loop {
2021-03-05 16:36:39 -05:00
if let Err(error) = self.refresh_screen() {
2021-01-28 16:47:40 -05:00
die(error);
}
if self.should_quit {
break;
}
2021-03-05 16:36:39 -05:00
if let Err(error) = self.process_keypress() {
die(error);
}
2021-01-28 16:47:40 -05:00
}
}
pub fn default() -> Self {
2021-03-08 13:34:25 -05:00
let args: Vec<String> = env::args().collect();
let document = if args.len() > 1 {
let file_name = &args[1];
Document::open(&file_name).unwrap_or_default()
} else {
Document::default()
};
2021-03-05 16:36:39 -05:00
Self {
should_quit: false,
terminal: Terminal::default().expect("Failed to initialize terminal"),
2021-03-08 13:34:25 -05:00
document,
2021-03-08 10:21:06 -05:00
cursor_position: Position::default(),
2021-03-08 14:21:24 -05:00
offset: Position::default(),
2021-03-05 16:36:39 -05:00
}
}
fn refresh_screen(&self) -> Result<(), std::io::Error> {
Terminal::cursor_hide();
2021-03-08 10:21:06 -05:00
Terminal::cursor_position(&Position::default());
2021-03-05 16:36:39 -05:00
if self.should_quit {
Terminal::clear_screen();
println!("Goodbye.\r");
} else {
self.draw_rows();
2021-03-08 14:21:24 -05:00
Terminal::cursor_position(&Position {
x: self.cursor_position.x.saturating_sub(self.offset.x),
y: self.cursor_position.y.saturating_sub(self.offset.y),
});
2021-03-05 16:36:39 -05:00
}
Terminal::cursor_show();
Terminal::flush()
2021-01-28 16:47:40 -05:00
}
fn process_keypress(&mut self) -> Result<(), std::io::Error> {
2021-03-05 16:36:39 -05:00
let pressed_key = Terminal::read_key()?;
2021-01-28 16:47:40 -05:00
match pressed_key {
Key::Ctrl('q') => self.should_quit = true,
2021-03-08 09:51:20 -05:00
Key::Up
| Key::Down
| Key::Left
| Key::Right
| Key::PageUp
| Key::PageDown
| Key::End
| Key::Home => self.move_cursor(pressed_key),
2021-01-28 16:47:40 -05:00
_ => (),
}
2021-03-08 14:21:24 -05:00
self.scroll();
2021-01-28 16:47:40 -05:00
Ok(())
}
2021-03-08 14:21:24 -05:00
fn scroll(&mut self) {
let Position { x, y } = self.cursor_position;
let width = self.terminal.size().width as usize;
let height = self.terminal.size().height as usize;
let mut offset = &mut self.offset;
if y < offset.y {
offset.y = y;
} else if y >= offset.y.saturating_add(height) {
offset.y = y.saturating_sub(height).saturating_add(1);
}
if x < offset.x {
offset.x = x;
} else if x >= offset.x.saturating_add(width) {
offset.x = x.saturating_sub(width).saturating_add(1);
}
}
2021-03-08 09:50:15 -05:00
fn move_cursor(&mut self, key: Key) {
2021-03-08 14:21:24 -05:00
let terminal_height = self.terminal.size().height as usize;
2021-03-08 09:50:15 -05:00
let Position { mut y, mut x } = self.cursor_position;
2021-03-08 14:21:24 -05:00
let height = self.document.len();
let mut width = if let Some(row) = self.document.row(y) {
row.len()
} else {
0
};
2021-03-08 09:50:15 -05:00
match key {
Key::Up => y = y.saturating_sub(1),
Key::Down => {
if y < height {
y = y.saturating_add(1);
}
},
2021-03-08 14:21:24 -05:00
Key::Left => {
if x > 0 {
x -= 1;
} else if y > 0 {
y -= 1;
if let Some(row) = self.document.row(y) {
x = row.len()
} else {
x = 0
}
}
},
2021-03-08 09:50:15 -05:00
Key::Right => {
if x < width {
2021-03-08 14:21:24 -05:00
x += 1;
} else if y < height {
y += 1;
x = 0;
}
},
Key::PageUp => {
y = if y > terminal_height {
y - terminal_height
} else {
0
}
},
Key::PageDown => {
y = if y.saturating_add(terminal_height) < height {
y + terminal_height as usize
} else {
height
2021-03-08 09:50:15 -05:00
}
},
Key::Home => x = 0,
Key::End => x = width,
_ => (),
}
2021-03-08 14:21:24 -05:00
width = if let Some(row) = self.document.row(y) {
row.len()
} else {
0
};
if x > width {
x = width;
}
2021-03-08 09:50:15 -05:00
self.cursor_position = Position { x, y }
}
fn draw_welcome_message(&self) {
let mut welcome_message = format!("Hecto editor -- version {}", VERSION);
let width = self.terminal.size().width as usize;
let len = welcome_message.len();
let padding = width.saturating_sub(len) / 2;
let spaces = " ".repeat(padding.saturating_sub(1));
welcome_message = format!("~{}{}", spaces, welcome_message);
welcome_message.truncate(width);
println!("{}\r", welcome_message);
}
2021-03-08 10:43:40 -05:00
pub fn draw_row(&self, row: &Row) {
2021-03-08 14:21:24 -05:00
let width = self.terminal.size().width as usize;
let start = self.offset.x;
let end = self.offset.x + width;
2021-03-08 10:43:40 -05:00
let row = row.render(start, end);
println!("{}\r", row);
}
2021-03-05 16:36:39 -05:00
fn draw_rows(&self) {
let height = self.terminal.size().height;
2021-03-08 14:21:24 -05:00
for terminal_row in 0..height {
2021-03-05 16:36:39 -05:00
Terminal::clear_current_line();
2021-03-08 14:21:24 -05:00
if let Some(row) = self.document.row(terminal_row as usize + self.offset.y) {
2021-03-08 10:43:40 -05:00
self.draw_row(row);
} else if self.document.is_empty() && terminal_row == height / 3 {
2021-03-08 09:50:15 -05:00
self.draw_welcome_message();
2021-03-05 16:36:39 -05:00
} else {
println!("~\r");
}
2021-01-28 16:47:40 -05:00
}
}
}
fn die(e: std::io::Error) {
2021-03-05 16:36:39 -05:00
Terminal::clear_screen();
2021-01-28 16:47:40 -05:00
panic!(e);
}