Basic backspacing
This commit is contained in:
parent
19c724d18f
commit
8e125b8c7c
@ -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(())
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user