better error handling in editor, fix crash in navigator
This commit is contained in:
parent
b9468ec7da
commit
ec65d9ef61
2 changed files with 49 additions and 31 deletions
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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}")),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue