better error handling in editor, fix crash in navigator

This commit is contained in:
Crispy 2023-03-16 22:18:30 +01:00
parent b9468ec7da
commit ec65d9ef61
2 changed files with 49 additions and 31 deletions

View file

@ -6,8 +6,9 @@ use crossterm::{
}; };
use std::{ use std::{
env, env,
fmt::Display,
fs::{self, File}, fs::{self, File},
io::{stdout, Write}, io::{self, stdout, Write},
ops::Range, ops::Range,
path::PathBuf, path::PathBuf,
}; };
@ -37,10 +38,18 @@ struct Cursor {
// target_column: usize, // target_column: usize,
} }
#[derive(Debug)]
enum Error {
WritingToFile(io::Error),
CreatingFile(PathBuf, io::Error),
CurrentDir,
}
type EditorResult = Result<(), Error>;
type Line = Range<usize>; type Line = Range<usize>;
impl Editor { impl Editor {
pub fn open_file(path: PathBuf) -> std::io::Result<Self> { pub fn open_file(path: PathBuf) -> io::Result<Self> {
let text = fs::read_to_string(&path)?; let text = fs::read_to_string(&path)?;
Ok(Editor { Ok(Editor {
text, text,
@ -80,15 +89,15 @@ impl Editor {
while self.active { while self.active {
self.draw(config); self.draw(config);
self.message = None; self.input(config)
self.input(config); .unwrap_or_else(|e| self.message = Some(format!("Error: {e}")));
} }
} }
fn input(&mut self, config: &mut Config) { fn input(&mut self, config: &mut Config) -> EditorResult {
if let Ok(Event::Key(event)) = event::read() { if let Ok(Event::Key(event)) = event::read() {
if self.input_movement(&event) { if self.input_movement(&event) {
return; return Ok(());
} }
match event.modifiers { match event.modifiers {
KeyModifiers::NONE => match event.code { KeyModifiers::NONE => match event.code {
@ -105,7 +114,7 @@ impl Editor {
_ => (), _ => (),
}, },
KeyModifiers::CONTROL => match event.code { KeyModifiers::CONTROL => match event.code {
KeyCode::Char('s') => self.save(), KeyCode::Char('s') => self.save()?,
KeyCode::Char('c') => self.copy(config), KeyCode::Char('c') => self.copy(config),
KeyCode::Char('x') => self.cut(config), KeyCode::Char('x') => self.cut(config),
KeyCode::Char('v') => self.paste(config), KeyCode::Char('v') => self.paste(config),
@ -116,6 +125,7 @@ impl Editor {
_ => (), _ => (),
} }
} }
Ok(())
} }
/// Cursor movement logic, returns true if cursor moved (so consider the event consumed in that case) /// Cursor movement logic, returns true if cursor moved (so consider the event consumed in that case)
@ -143,7 +153,7 @@ impl Editor {
true true
} }
fn draw(&self, config: &Config) { fn draw(&mut self, config: &Config) {
queue!(stdout(), Clear(ClearType::All)).unwrap(); queue!(stdout(), Clear(ClearType::All)).unwrap();
let max_rows = terminal::size().unwrap().1 as usize - 1; let max_rows = terminal::size().unwrap().1 as usize - 1;
@ -203,11 +213,12 @@ impl Editor {
stdout().flush().unwrap(); stdout().flush().unwrap();
} }
fn status_line(&self) { fn status_line(&mut self) {
queue!(stdout(), MoveTo(0, terminal::size().unwrap().1)).unwrap(); queue!(stdout(), MoveTo(0, terminal::size().unwrap().1)).unwrap();
if let Some(message) = &self.message { if let Some(message) = &self.message {
print!("{message}"); print!("{message}");
self.message = None;
} else { } else {
print!( print!(
"[{}, {}] {}", "[{}, {}] {}",
@ -300,7 +311,7 @@ impl Editor {
} }
fn current_line(&self) -> &Line { fn current_line(&self) -> &Line {
self.lines.get(self.cursor.line).unwrap() &self.lines[self.cursor.line]
} }
fn find_lines(&mut self) { fn find_lines(&mut self) {
@ -419,27 +430,22 @@ impl Editor {
preceding_chars + preceding_tabs * (TAB_SIZE - 1) preceding_chars + preceding_tabs * (TAB_SIZE - 1)
} }
fn save(&mut self) { fn save(&mut self) -> EditorResult {
let mut filename_new = false; let cwd = env::current_dir().map_err(|_| Error::CurrentDir)?;
if self.path.is_none() { let Some(path) = self.path
self.path = read_line("Enter path: ").map(|s| env::current_dir().unwrap().join(s)); .clone()
filename_new = true; .or_else(|| read_line("Enter path: ")
} .map(|s| cwd.join(s))
if let Some(path) = &self.path { ) else { return Ok(()); };
match File::create(path) {
Ok(mut file) => { let mut file = File::create(&path).map_err(|e| Error::CreatingFile(path.to_owned(), e))?;
self.set_message(format!("Saved file as '{}'", path.display())); file.write_all(self.text.as_bytes())
file.write_all(self.text.as_bytes()).unwrap(); .map_err(Error::WritingToFile)?;
self.unsaved_changes = false;
} self.set_message(format!("Saved file as '{}'", path.display()));
Err(e) => { self.path = Some(path);
self.set_message(format!("Could not save file as '{}': {e}", path.display())); self.unsaved_changes = false;
if filename_new { Ok(())
self.path = None;
}
}
}
}
} }
fn go_to_line(&mut self) { fn go_to_line(&mut self) {
@ -456,3 +462,14 @@ impl Editor {
} }
} }
} }
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let text = match self {
Error::CreatingFile(name, err) => format!("Could not create {name:?}: {err}"),
Error::CurrentDir => "Could not get current directory".into(),
Error::WritingToFile(err) => format!("{err}"),
};
f.write_str(&text)
}
}

View file

@ -229,6 +229,7 @@ impl Navigator {
Ok(()) => { Ok(()) => {
self.path = new_path; self.path = new_path;
self.selected = self.editors.len(); self.selected = self.editors.len();
self.scroll = 0;
} }
Err(err) => self.message(format!("Could not navigate to directory: {err}")), Err(err) => self.message(format!("Could not navigate to directory: {err}")),
} }