Basic backspacing
This commit is contained in:
parent
19c724d18f
commit
8e125b8c7c
@ -11,6 +11,7 @@ use std::time::{Duration, Instant};
|
|||||||
use self::EditorKey::*;
|
use self::EditorKey::*;
|
||||||
|
|
||||||
const KILO_TAB_STOP: usize = 4;
|
const KILO_TAB_STOP: usize = 4;
|
||||||
|
const KILO_QUIT_TIMES: u8 = 3;
|
||||||
|
|
||||||
/// A representation of a line in the editor
|
/// A representation of a line in the editor
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -40,10 +41,14 @@ pub struct Editor {
|
|||||||
screen_cols: usize,
|
screen_cols: usize,
|
||||||
screen_rows: usize,
|
screen_rows: usize,
|
||||||
rows: Vec<EditorRow>,
|
rows: Vec<EditorRow>,
|
||||||
|
dirty: u64,
|
||||||
filename: String,
|
filename: String,
|
||||||
status_message: String,
|
status_message: String,
|
||||||
status_message_time: Instant,
|
status_message_time: Instant,
|
||||||
|
|
||||||
|
// Properties not present in C version
|
||||||
output_buffer: String,
|
output_buffer: String,
|
||||||
|
quit_times: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keycode mapping enum
|
/// Keycode mapping enum
|
||||||
@ -87,10 +92,12 @@ impl Default for Editor {
|
|||||||
screen_cols: 0,
|
screen_cols: 0,
|
||||||
screen_rows: 0,
|
screen_rows: 0,
|
||||||
rows: vec![],
|
rows: vec![],
|
||||||
|
dirty: 0,
|
||||||
filename: String::new(),
|
filename: String::new(),
|
||||||
status_message: String::new(),
|
status_message: String::new(),
|
||||||
status_message_time: Instant::now(),
|
status_message_time: Instant::now(),
|
||||||
output_buffer: String::new(),
|
output_buffer: String::new(),
|
||||||
|
quit_times: KILO_QUIT_TIMES,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,6 +133,7 @@ impl Editor {
|
|||||||
|
|
||||||
for char in in_str.chars() {
|
for char in in_str.chars() {
|
||||||
input.push(match char {
|
input.push(match char {
|
||||||
|
'\x08' => Backspace,
|
||||||
'\x7f' => Backspace,
|
'\x7f' => Backspace,
|
||||||
'\x1b' => Escape,
|
'\x1b' => Escape,
|
||||||
'\r' => Enter,
|
'\r' => Enter,
|
||||||
@ -285,6 +293,7 @@ impl Editor {
|
|||||||
|
|
||||||
match char {
|
match char {
|
||||||
Backspace => self._del_or_backspace(Backspace),
|
Backspace => self._del_or_backspace(Backspace),
|
||||||
|
DeleteKey => self._del_or_backspace(DeleteKey),
|
||||||
Enter => {
|
Enter => {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
@ -306,6 +315,11 @@ impl Editor {
|
|||||||
OtherKey(c) => {
|
OtherKey(c) => {
|
||||||
if c.is_ascii_control() {
|
if c.is_ascii_control() {
|
||||||
if c == ctrl_key('q') {
|
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[2J");
|
||||||
print!("\x1b[H");
|
print!("\x1b[H");
|
||||||
// Break out of the input loop
|
// Break out of the input loop
|
||||||
@ -313,10 +327,10 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c == ctrl_key('s') {
|
if c == ctrl_key('s') {
|
||||||
// @TODO show save success/error
|
// Save success/error message handled by save method
|
||||||
match self.save() {
|
match self.save() {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => (),
|
Err(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,6 +344,8 @@ impl Editor {
|
|||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.quit_times = KILO_QUIT_TIMES;
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +354,10 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn _del_or_backspace(&mut self, key: EditorKey<char>) {
|
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>) {
|
fn _page_up_or_down(&mut self, key: EditorKey<char>) {
|
||||||
@ -457,7 +476,9 @@ impl Editor {
|
|||||||
&self.filename
|
&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 right_message = format!("{}/{}", self.cursor_y + 1, self.rows.len());
|
||||||
let mut len = left_message.len();
|
let mut len = left_message.len();
|
||||||
if len > self.screen_cols {
|
if len > self.screen_cols {
|
||||||
@ -567,6 +588,8 @@ impl Editor {
|
|||||||
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);
|
self.update_row(self.rows.len() - 1);
|
||||||
|
|
||||||
|
self.dirty += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn row_insert_char(&mut self, row_index: usize, char_index: usize, ch: char) {
|
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);
|
row.chars.insert(at, ch);
|
||||||
|
|
||||||
self.update_row(row_index);
|
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;
|
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
|
// File I/O
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -624,18 +672,30 @@ impl Editor {
|
|||||||
self.append_row(&line);
|
self.append_row(&line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.dirty = 0;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&mut self) -> io::Result<()> {
|
fn save(&mut self) -> io::Result<()> {
|
||||||
if self.filename.len() == 0 {
|
if self.filename.len() == 0 {
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file = File::create(&self.filename)?;
|
let mut file = File::create(&self.filename)?;
|
||||||
let data = &mut self.rows_to_string();
|
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()?;
|
file.sync_all()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -25,7 +25,7 @@ fn main() -> Result<(), Error> {
|
|||||||
editor.open(&args[1])?;
|
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.
|
// 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,
|
// `None` is returned on a quit action, in other cases, `Some(())` is returned,
|
||||||
|
Loading…
Reference in New Issue
Block a user