saving
This commit is contained in:
+25
-6
@@ -1,4 +1,4 @@
|
||||
use std::{cmp::min, mem::{replace, swap}};
|
||||
use std::{cmp::min, fs::File, io::Write, mem::{replace, swap}};
|
||||
|
||||
use crate::{BYTES_PER_LINE, app::{App, Mode, PartialAction}, edit_action::EditAction};
|
||||
|
||||
@@ -10,8 +10,9 @@ pub enum Action {
|
||||
SelectMode,
|
||||
|
||||
Goto,
|
||||
Zview,
|
||||
View,
|
||||
Replace,
|
||||
Space,
|
||||
|
||||
MoveByteUp,
|
||||
MoveByteDown,
|
||||
@@ -54,6 +55,8 @@ pub enum Action {
|
||||
|
||||
Undo,
|
||||
Redo,
|
||||
|
||||
Save,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -65,8 +68,9 @@ impl App {
|
||||
Action::SelectMode => self.select_mode(),
|
||||
|
||||
Action::Goto => self.goto(),
|
||||
Action::Zview => self.zview(),
|
||||
Action::View => self.view(),
|
||||
Action::Replace => self.replace(),
|
||||
Action::Space => self.space(),
|
||||
|
||||
Action::MoveByteUp => self.move_byte_up(),
|
||||
Action::MoveByteDown => self.move_byte_down(),
|
||||
@@ -109,6 +113,8 @@ impl App {
|
||||
|
||||
Action::Undo => self.undo(),
|
||||
Action::Redo => self.redo(),
|
||||
|
||||
Action::Save => self.save(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,14 +134,18 @@ impl App {
|
||||
self.partial_action = Some(PartialAction::Goto);
|
||||
}
|
||||
|
||||
const fn zview(&mut self) {
|
||||
self.partial_action = Some(PartialAction::Zview);
|
||||
const fn view(&mut self) {
|
||||
self.partial_action = Some(PartialAction::View);
|
||||
}
|
||||
|
||||
const fn replace(&mut self) {
|
||||
self.partial_action = Some(PartialAction::Replace);
|
||||
}
|
||||
|
||||
const fn space(&mut self) {
|
||||
self.partial_action = Some(PartialAction::Space);
|
||||
}
|
||||
|
||||
const fn move_byte_up(&mut self) {
|
||||
if self.cursor.head >= BYTES_PER_LINE {
|
||||
self.cursor.head -= BYTES_PER_LINE;
|
||||
@@ -359,7 +369,7 @@ impl App {
|
||||
}
|
||||
|
||||
fn undo(&mut self) {
|
||||
if self.time_traveling == Some(0) { return }
|
||||
if self.time_traveling == Some(0) || self.edit_history.is_empty() { return }
|
||||
|
||||
let current_date = self.time_traveling
|
||||
.map_or(self.edit_history.len() - 1, |date| date - 1);
|
||||
@@ -396,6 +406,15 @@ impl App {
|
||||
|
||||
self.edit_history[previous_date] = edit_action;
|
||||
}
|
||||
|
||||
fn save(&mut self) {
|
||||
let mut file = File::create(&self.file_path).unwrap();
|
||||
file.write_all(&self.contents).unwrap();
|
||||
|
||||
self.last_saved_at = Some(
|
||||
self.time_traveling.unwrap_or(self.edit_history.len())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
+24
-2
@@ -8,6 +8,7 @@ mod widget;
|
||||
pub struct App {
|
||||
pub config: Config,
|
||||
pub file_name: String,
|
||||
pub file_path: PathBuf,
|
||||
|
||||
pub contents: Vec<u8>,
|
||||
|
||||
@@ -25,6 +26,8 @@ pub struct App {
|
||||
pub edit_history: Vec<EditAction>,
|
||||
// the index *after* the latest edit action
|
||||
pub time_traveling: Option<usize>,
|
||||
// the index *after* the last saved edit action
|
||||
pub last_saved_at: Option<usize>,
|
||||
|
||||
pub logs: Vec<String>,
|
||||
}
|
||||
@@ -36,7 +39,7 @@ pub enum Mode {
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub enum PartialAction {
|
||||
Goto, Zview, Replace
|
||||
Goto, View, Replace, Space
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
@@ -61,8 +64,9 @@ impl PartialAction {
|
||||
pub const fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::Goto => "g",
|
||||
Self::Zview => "z",
|
||||
Self::View => "z",
|
||||
Self::Replace => "r",
|
||||
Self::Space => "␠",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +91,7 @@ impl App {
|
||||
Self {
|
||||
config: Config::default(),
|
||||
file_name: file_path.file_name().unwrap().to_str().unwrap().to_owned(),
|
||||
file_path,
|
||||
|
||||
contents,
|
||||
|
||||
@@ -104,6 +109,7 @@ impl App {
|
||||
|
||||
edit_history: Vec::new(),
|
||||
time_traveling: None,
|
||||
last_saved_at: Some(0),
|
||||
|
||||
logs: Vec::new(),
|
||||
}
|
||||
@@ -161,6 +167,22 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn has_unsaved_changes(&self) -> bool {
|
||||
!self.all_changes_saved()
|
||||
}
|
||||
|
||||
const fn all_changes_saved(&self) -> bool {
|
||||
if let Some(last_saved_at) = self.last_saved_at {
|
||||
if let Some(time_traveling) = self.time_traveling {
|
||||
last_saved_at == time_traveling
|
||||
} else {
|
||||
last_saved_at == self.edit_history.len()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nybble_from_hex(hex: char) -> Option<u8> {
|
||||
|
||||
+3
-3
@@ -401,10 +401,10 @@ mod status_line {
|
||||
}
|
||||
|
||||
fn modified_indicator(&self) -> Span<'static> {
|
||||
if self.edit_history.is_empty() {
|
||||
"".into()
|
||||
} else {
|
||||
if self.has_unsaved_changes() {
|
||||
" [+]".into()
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -104,8 +104,9 @@ impl Default for Config {
|
||||
("v".try_into().unwrap(), Action::SelectMode),
|
||||
|
||||
("g".try_into().unwrap(), Action::Goto),
|
||||
("z".try_into().unwrap(), Action::Zview),
|
||||
("z".try_into().unwrap(), Action::View),
|
||||
("r".try_into().unwrap(), Action::Replace),
|
||||
(" ".try_into().unwrap(), Action::Space),
|
||||
|
||||
("i".try_into().unwrap(), Action::MoveByteUp),
|
||||
("k".try_into().unwrap(), Action::MoveByteDown),
|
||||
@@ -142,7 +143,10 @@ impl Default for Config {
|
||||
("l".try_into().unwrap(), Action::GotoLineEnd),
|
||||
|
||||
("g".try_into().unwrap(), Action::GotoFileStart),
|
||||
].into())
|
||||
].into()),
|
||||
(Some(PartialAction::Space), [
|
||||
("w".try_into().unwrap(), Action::Save),
|
||||
].into()),
|
||||
].into()),
|
||||
(Mode::Select, [
|
||||
(None, [
|
||||
@@ -151,8 +155,9 @@ impl Default for Config {
|
||||
("v".try_into().unwrap(), Action::NormalMode),
|
||||
|
||||
("g".try_into().unwrap(), Action::Goto),
|
||||
("z".try_into().unwrap(), Action::Zview),
|
||||
("z".try_into().unwrap(), Action::View),
|
||||
("r".try_into().unwrap(), Action::Replace),
|
||||
(" ".try_into().unwrap(), Action::Space),
|
||||
|
||||
("i".try_into().unwrap(), Action::ExtendByteUp),
|
||||
("k".try_into().unwrap(), Action::ExtendByteDown),
|
||||
@@ -181,7 +186,10 @@ impl Default for Config {
|
||||
|
||||
("u".try_into().unwrap(), Action::Undo),
|
||||
("U".try_into().unwrap(), Action::Redo),
|
||||
].into())
|
||||
].into()),
|
||||
(Some(PartialAction::Space), [
|
||||
("w".try_into().unwrap(), Action::Save),
|
||||
].into()),
|
||||
].into())
|
||||
].into()
|
||||
}
|
||||
|
||||
+5
-1
@@ -34,6 +34,10 @@ impl App {
|
||||
if let Some(date) = self.time_traveling {
|
||||
self.edit_history.truncate(date);
|
||||
self.time_traveling = None;
|
||||
|
||||
if self.last_saved_at.is_some_and(|it| it > date) {
|
||||
self.last_saved_at = None;
|
||||
}
|
||||
}
|
||||
|
||||
self.edit_history.push(edit_action);
|
||||
@@ -62,7 +66,7 @@ impl App {
|
||||
fn delete_at(&mut self, cursor: Cursor) {
|
||||
self.contents.drain(cursor.range());
|
||||
|
||||
self.cursor.head = min(cursor.head, cursor.tail);
|
||||
self.cursor.head = min(min(cursor.head, cursor.tail), self.contents.len() - 1);
|
||||
self.cursor.collapse();
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -17,7 +17,8 @@ const BYTES_PER_CHUNK: usize = 4;
|
||||
const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
||||
|
||||
// TODO:
|
||||
// - undo/redo
|
||||
// - multiple buffers (tabs)
|
||||
// - search
|
||||
// - modifications
|
||||
// - insert/append
|
||||
// - mode
|
||||
@@ -26,8 +27,6 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
||||
// - replace-and-keep-going
|
||||
// - mode
|
||||
// - change
|
||||
// - saving
|
||||
// - search
|
||||
// - edit character panel
|
||||
// - modifier on existing keys like teehee? or jump to panel?
|
||||
// - if jump to panel, space?
|
||||
@@ -51,6 +50,9 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
||||
// - utf8?
|
||||
// - diffing
|
||||
|
||||
// TODO: opening empty file crashes (or deleting entire file)
|
||||
// - cursor is NOT guaranteed to be in-bounds..?
|
||||
|
||||
// when AsciiChar is stabilized, use it instead of char everywhere
|
||||
|
||||
fn main() {
|
||||
|
||||
Reference in New Issue
Block a user