Basic backspacing

This commit is contained in:
Timothy Warren 2019-08-30 11:20:52 -04:00
parent 19c724d18f
commit 8e125b8c7c
2 changed files with 67 additions and 7 deletions

View File

@ -11,6 +11,7 @@ use std::time::{Duration, Instant};
use self::EditorKey::*;
const KILO_TAB_STOP: usize = 4;
const KILO_QUIT_TIMES: u8 = 3;
/// A representation of a line in the editor
#[derive(Debug, Default)]
@ -40,10 +41,14 @@ pub struct Editor {
screen_cols: usize,
screen_rows: usize,
rows: Vec<EditorRow>,
dirty: u64,
filename: String,
status_message: String,
status_message_time: Instant,
// Properties not present in C version
output_buffer: String,
quit_times: u8,
}
/// Keycode mapping enum
@ -87,10 +92,12 @@ impl Default for Editor {
screen_cols: 0,
screen_rows: 0,
rows: vec![],
dirty: 0,
filename: String::new(),
status_message: String::new(),
status_message_time: Instant::now(),
output_buffer: String::new(),
quit_times: KILO_QUIT_TIMES,
}
}
}
@ -126,6 +133,7 @@ impl Editor {
for char in in_str.chars() {
input.push(match char {
'\x08' => Backspace,
'\x7f' => Backspace,
'\x1b' => Escape,
'\r' => Enter,
@ -285,6 +293,7 @@ impl Editor {
match char {
Backspace => self._del_or_backspace(Backspace),
DeleteKey => self._del_or_backspace(DeleteKey),
Enter => {
// TODO
}
@ -306,6 +315,11 @@ impl Editor {
OtherKey(c) => {
if c.is_ascii_control() {
if c == ctrl_key('q') {
if self.dirty > 0 && self.quit_times > 0 {
self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times));
self.quit_times -= 1;
return Some(OtherKey('\0'));
}
print!("\x1b[2J");
print!("\x1b[H");
// Break out of the input loop
@ -313,10 +327,10 @@ impl Editor {
}
if c == ctrl_key('s') {
// @TODO show save success/error
// Save success/error message handled by save method
match self.save() {
Ok(_) => (),
Err(e) => (),
Err(_) => (),
}
}
@ -330,6 +344,8 @@ impl Editor {
_ => (),
};
self.quit_times = KILO_QUIT_TIMES;
return key;
}
@ -338,7 +354,10 @@ impl Editor {
}
fn _del_or_backspace(&mut self, key: EditorKey<char>) {
// TODO
if key == DeleteKey {
self.move_cursor(&ArrowRight);
}
self.delete_char();
}
fn _page_up_or_down(&mut self, key: EditorKey<char>) {
@ -457,7 +476,9 @@ impl Editor {
&self.filename
};
let mut left_message = format!("{:.80} - {} lines", filename, self.rows.len());
let modified = if self.dirty > 0 { "(modified}" } else { "" };
let mut left_message = format!("{:.80} - {} lines {}", filename, self.rows.len(), modified);
let right_message = format!("{}/{}", self.cursor_y + 1, self.rows.len());
let mut len = left_message.len();
if len > self.screen_cols {
@ -567,6 +588,8 @@ impl Editor {
fn append_row(&mut self, row: &str) {
self.rows.push(EditorRow::new(row));
self.update_row(self.rows.len() - 1);
self.dirty += 1;
}
fn row_insert_char(&mut self, row_index: usize, char_index: usize, ch: char) {
@ -580,6 +603,20 @@ impl Editor {
row.chars.insert(at, ch);
self.update_row(row_index);
self.dirty += 1;
}
fn row_delete_char(&mut self, row_index: usize, char_index: usize) {
let row = &mut self.rows[row_index];
if char_index >= row.chars.len() {
return;
}
row.chars.remove(char_index);
self.update_row(row_index);
self.dirty += 1;
}
// ------------------------------------------------------------------------
@ -595,6 +632,17 @@ impl Editor {
self.cursor_x += 1;
}
fn delete_char(&mut self) {
if self.cursor_y == self.rows.len() {
return;
}
if self.cursor_x > 0 {
self.row_delete_char(self.cursor_y, self.cursor_x - 1);
self.cursor_x -= 1;
}
}
// ------------------------------------------------------------------------
// File I/O
// ------------------------------------------------------------------------
@ -624,18 +672,30 @@ impl Editor {
self.append_row(&line);
}
self.dirty = 0;
Ok(())
}
fn save(&mut self) -> io::Result<()> {
if self.filename.len() == 0 {
return Ok(())
return Ok(());
}
let mut file = File::create(&self.filename)?;
let data = &mut self.rows_to_string();
file.write_all(data.as_bytes())?;
let res = file.write_all(data.as_bytes());
match res {
Ok(()) => {
self.dirty = 0;
self.set_status_message(&format!("{} bytes written to disk", data.len()));
}
Err(e) => self.set_status_message(&format!("Failed to save: {:?}", e)),
};
file.sync_all()?;
Ok(())

View File

@ -25,7 +25,7 @@ fn main() -> Result<(), Error> {
editor.open(&args[1])?;
}
editor.set_status_message("HELP: Ctrl-Q = quit");
editor.set_status_message("HELP: Ctrl-S = save | Ctrl-Q = quit");
// Main input loop. Editor::process_keypress uses an Option Enum as a sentinel.
// `None` is returned on a quit action, in other cases, `Some(())` is returned,