fix handling empty files
This commit is contained in:
+33
-23
@@ -139,7 +139,9 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn replace(&mut self) {
|
const fn replace(&mut self) {
|
||||||
self.partial_action = Some(PartialAction::Replace);
|
if !self.contents.is_empty() {
|
||||||
|
self.partial_action = Some(PartialAction::Replace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn space(&mut self) {
|
const fn space(&mut self) {
|
||||||
@@ -156,7 +158,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn move_byte_down(&mut self) {
|
const fn move_byte_down(&mut self) {
|
||||||
if self.contents.len() - 1 - self.cursor.head >= BYTES_PER_LINE {
|
if self.max_contents_index() - self.cursor.head >= BYTES_PER_LINE {
|
||||||
self.cursor.head += BYTES_PER_LINE;
|
self.cursor.head += BYTES_PER_LINE;
|
||||||
self.cursor.collapse();
|
self.cursor.collapse();
|
||||||
|
|
||||||
@@ -174,7 +176,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn move_byte_right(&mut self) {
|
const fn move_byte_right(&mut self) {
|
||||||
if self.contents.len() - 1 - self.cursor.head >= 1 {
|
if self.max_contents_index() - self.cursor.head >= 1 {
|
||||||
self.cursor.head += 1;
|
self.cursor.head += 1;
|
||||||
self.cursor.collapse();
|
self.cursor.collapse();
|
||||||
|
|
||||||
@@ -190,7 +192,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn extend_byte_down(&mut self) {
|
const fn extend_byte_down(&mut self) {
|
||||||
if self.contents.len() - 1 - self.cursor.head >= BYTES_PER_LINE {
|
if self.max_contents_index() - self.cursor.head >= BYTES_PER_LINE {
|
||||||
self.cursor.head += BYTES_PER_LINE;
|
self.cursor.head += BYTES_PER_LINE;
|
||||||
self.clamp_screen_to_cursor();
|
self.clamp_screen_to_cursor();
|
||||||
}
|
}
|
||||||
@@ -204,7 +206,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn extend_byte_right(&mut self) {
|
const fn extend_byte_right(&mut self) {
|
||||||
if self.contents.len() - 1 - self.cursor.head >= 1 {
|
if self.max_contents_index() - self.cursor.head >= 1 {
|
||||||
self.cursor.head += 1;
|
self.cursor.head += 1;
|
||||||
self.clamp_screen_to_cursor();
|
self.clamp_screen_to_cursor();
|
||||||
}
|
}
|
||||||
@@ -235,6 +237,8 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_down(&mut self) {
|
fn scroll_down(&mut self) {
|
||||||
|
if self.contents.len() <= 5 * BYTES_PER_LINE { return; }
|
||||||
|
|
||||||
self.scroll_position = min(
|
self.scroll_position = min(
|
||||||
self.scroll_position + BYTES_PER_LINE,
|
self.scroll_position + BYTES_PER_LINE,
|
||||||
self.contents.len() - (5 * BYTES_PER_LINE)
|
self.contents.len() - (5 * BYTES_PER_LINE)
|
||||||
@@ -248,6 +252,8 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn page_cursor_half_down(&mut self) {
|
fn page_cursor_half_down(&mut self) {
|
||||||
|
if self.contents.len() <= 5 * BYTES_PER_LINE { return; }
|
||||||
|
|
||||||
let head_offset = self.cursor.head - self.scroll_position;
|
let head_offset = self.cursor.head - self.scroll_position;
|
||||||
let tail_offset = self.cursor.tail - self.scroll_position;
|
let tail_offset = self.cursor.tail - self.scroll_position;
|
||||||
|
|
||||||
@@ -256,8 +262,8 @@ impl App {
|
|||||||
self.contents.len() - (5 * BYTES_PER_LINE)
|
self.contents.len() - (5 * BYTES_PER_LINE)
|
||||||
);
|
);
|
||||||
|
|
||||||
self.cursor.head = (self.scroll_position + head_offset).min(self.contents.len() - 1);
|
self.cursor.head = (self.scroll_position + head_offset).min(self.max_contents_index());
|
||||||
self.cursor.tail = (self.scroll_position + tail_offset).min(self.contents.len() - 1);
|
self.cursor.tail = (self.scroll_position + tail_offset).min(self.max_contents_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn page_cursor_half_up(&mut self) {
|
fn page_cursor_half_up(&mut self) {
|
||||||
@@ -268,11 +274,13 @@ impl App {
|
|||||||
(self.screen_size() / 2).next_multiple_of(BYTES_PER_LINE)
|
(self.screen_size() / 2).next_multiple_of(BYTES_PER_LINE)
|
||||||
);
|
);
|
||||||
|
|
||||||
self.cursor.head = (self.scroll_position + head_offset).min(self.contents.len() - 1);
|
self.cursor.head = (self.scroll_position + head_offset).min(self.max_contents_index());
|
||||||
self.cursor.tail = (self.scroll_position + tail_offset).min(self.contents.len() - 1);
|
self.cursor.tail = (self.scroll_position + tail_offset).min(self.max_contents_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn page_down(&mut self) {
|
fn page_down(&mut self) {
|
||||||
|
if self.contents.len() <= 5 * BYTES_PER_LINE { return; }
|
||||||
|
|
||||||
self.scroll_position = min(
|
self.scroll_position = min(
|
||||||
self.scroll_position + self.screen_size(),
|
self.scroll_position + self.screen_size(),
|
||||||
self.contents.len() - (5 * BYTES_PER_LINE)
|
self.contents.len() - (5 * BYTES_PER_LINE)
|
||||||
@@ -288,12 +296,12 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn move_next_word_start(&mut self) {
|
fn move_next_word_start(&mut self) {
|
||||||
self.cursor.move_to_next_word(self.contents.len() - 1);
|
self.cursor.move_to_next_word(self.max_contents_index());
|
||||||
self.clamp_screen_to_cursor();
|
self.clamp_screen_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_next_word_end(&mut self) {
|
fn move_next_word_end(&mut self) {
|
||||||
self.cursor.move_to_next_end(self.contents.len() - 1);
|
self.cursor.move_to_next_end(self.max_contents_index());
|
||||||
self.clamp_screen_to_cursor();
|
self.clamp_screen_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,12 +311,12 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extend_next_word_start(&mut self) {
|
fn extend_next_word_start(&mut self) {
|
||||||
self.cursor.extend_to_next_word(self.contents.len() - 1);
|
self.cursor.extend_to_next_word(self.max_contents_index());
|
||||||
self.clamp_screen_to_cursor();
|
self.clamp_screen_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_next_word_end(&mut self) {
|
fn extend_next_word_end(&mut self) {
|
||||||
self.cursor.extend_to_next_end(self.contents.len() - 1);
|
self.cursor.extend_to_next_end(self.max_contents_index());
|
||||||
self.clamp_screen_to_cursor();
|
self.clamp_screen_to_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +339,7 @@ impl App {
|
|||||||
{
|
{
|
||||||
self.cursor.head = min(
|
self.cursor.head = min(
|
||||||
self.cursor.head + BYTES_PER_LINE,
|
self.cursor.head + BYTES_PER_LINE,
|
||||||
self.contents.len() - 1
|
self.max_contents_index()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.cursor.tail -= self.cursor.tail % BYTES_PER_LINE;
|
self.cursor.tail -= self.cursor.tail % BYTES_PER_LINE;
|
||||||
@@ -346,7 +354,7 @@ impl App {
|
|||||||
|
|
||||||
if self.cursor.head.is_multiple_of(BYTES_PER_LINE) &&
|
if self.cursor.head.is_multiple_of(BYTES_PER_LINE) &&
|
||||||
(self.cursor.tail % BYTES_PER_LINE == BYTES_PER_LINE - 1 ||
|
(self.cursor.tail % BYTES_PER_LINE == BYTES_PER_LINE - 1 ||
|
||||||
self.cursor.tail == self.contents.len() - 1)
|
self.cursor.tail == self.max_contents_index())
|
||||||
{
|
{
|
||||||
self.cursor.head = self.cursor.head.saturating_sub(BYTES_PER_LINE);
|
self.cursor.head = self.cursor.head.saturating_sub(BYTES_PER_LINE);
|
||||||
} else {
|
} else {
|
||||||
@@ -356,12 +364,14 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn delete(&mut self) {
|
fn delete(&mut self) {
|
||||||
self.execute_and_add(
|
if !self.contents.is_empty() {
|
||||||
EditAction::Delete {
|
self.execute_and_add(
|
||||||
cursor: self.cursor,
|
EditAction::Delete {
|
||||||
old_data: self.contents[self.cursor.range()].into()
|
cursor: self.cursor,
|
||||||
}
|
old_data: self.contents[self.cursor.range()].into()
|
||||||
);
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if self.mode == Mode::Select {
|
if self.mode == Mode::Select {
|
||||||
self.mode = Mode::Normal;
|
self.mode = Mode::Normal;
|
||||||
@@ -369,7 +379,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn undo(&mut self) {
|
fn undo(&mut self) {
|
||||||
if self.time_traveling == Some(0) || self.edit_history.is_empty() { return }
|
if self.time_traveling == Some(0) || self.edit_history.is_empty() { return; }
|
||||||
|
|
||||||
let current_date = self.time_traveling
|
let current_date = self.time_traveling
|
||||||
.map_or(self.edit_history.len() - 1, |date| date - 1);
|
.map_or(self.edit_history.len() - 1, |date| date - 1);
|
||||||
@@ -387,7 +397,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn redo(&mut self) {
|
fn redo(&mut self) {
|
||||||
let Some(previous_date) = self.time_traveling else { return };
|
let Some(previous_date) = self.time_traveling else { return; };
|
||||||
|
|
||||||
let current_date = previous_date + 1;
|
let current_date = previous_date + 1;
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -183,10 +183,15 @@ impl App {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns 0 if empty
|
||||||
|
pub const fn max_contents_index(&self) -> usize {
|
||||||
|
self.contents.len().saturating_sub(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nybble_from_hex(hex: char) -> Option<u8> {
|
fn nybble_from_hex(hex: char) -> Option<u8> {
|
||||||
if !hex.is_ascii() { return None }
|
if !hex.is_ascii() { return None; }
|
||||||
|
|
||||||
match hex {
|
match hex {
|
||||||
'0'..='9' => Some(u8::try_from(hex).unwrap() - u8::try_from('0').unwrap()),
|
'0'..='9' => Some(u8::try_from(hex).unwrap() - u8::try_from('0').unwrap()),
|
||||||
|
|||||||
+12
-4
@@ -33,6 +33,10 @@ impl Widget for &App {
|
|||||||
let hex_area = Rect::new(area.x, area.y, area.width, area.height - 1);
|
let hex_area = Rect::new(area.x, area.y, area.width, area.height - 1);
|
||||||
hex_text.render(hex_area, buf);
|
hex_text.render(hex_area, buf);
|
||||||
|
|
||||||
|
if self.contents.is_empty() {
|
||||||
|
Line::from("empty file").render(area, buf);
|
||||||
|
}
|
||||||
|
|
||||||
let status_line_area = Rect::new(area.x, area.bottom() - 1, area.width, 1);
|
let status_line_area = Rect::new(area.x, area.bottom() - 1, area.width, 1);
|
||||||
self.render_status_line().render(status_line_area, buf);
|
self.render_status_line().render(status_line_area, buf);
|
||||||
|
|
||||||
@@ -416,14 +420,18 @@ mod extra_statuses {
|
|||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn render_extra_statuses(&self) -> Line<'_> {
|
pub fn render_extra_statuses(&self) -> Line<'_> {
|
||||||
#[allow(clippy::cast_precision_loss)]
|
|
||||||
let percentage = self.cursor.head as f64 / (self.contents.len() - 1) as f64 * 100.0;
|
|
||||||
|
|
||||||
let partial_action = self.partial_action
|
let partial_action = self.partial_action
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or("", |partial_action| partial_action.label());
|
.map_or("", |partial_action| partial_action.label());
|
||||||
|
|
||||||
format!("{partial_action} {percentage:.0}% ").into()
|
if self.contents.is_empty() {
|
||||||
|
format!("{partial_action} ").into()
|
||||||
|
} else {
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
let percentage = self.cursor.head as f64 / self.max_contents_index() as f64 * 100.0;
|
||||||
|
|
||||||
|
format!("{partial_action} {percentage:.0}% ").into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -50,7 +50,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_to_next_word(&mut self, max: usize) {
|
pub fn move_to_next_word(&mut self, max: usize) {
|
||||||
if self.head == max { return }
|
if self.head == max { return; }
|
||||||
|
|
||||||
if self.head.is_multiple_of(4) { // at the beginning of a word
|
if self.head.is_multiple_of(4) { // at the beginning of a word
|
||||||
self.head = (self.head + 4).min(max);
|
self.head = (self.head + 4).min(max);
|
||||||
@@ -61,7 +61,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_to_next_end(&mut self, max: usize) {
|
pub fn move_to_next_end(&mut self, max: usize) {
|
||||||
if self.head == max { return }
|
if self.head == max { return; }
|
||||||
|
|
||||||
self.collapse();
|
self.collapse();
|
||||||
if self.head % 4 == 3 { // at the end of a word
|
if self.head % 4 == 3 { // at the end of a word
|
||||||
@@ -73,7 +73,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const fn move_to_previous_beginning(&mut self) {
|
pub const fn move_to_previous_beginning(&mut self) {
|
||||||
if self.head == 0 { return }
|
if self.head == 0 { return; }
|
||||||
|
|
||||||
self.collapse();
|
self.collapse();
|
||||||
if self.head.is_multiple_of(4) { // at the beginning of a word
|
if self.head.is_multiple_of(4) { // at the beginning of a word
|
||||||
@@ -85,7 +85,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn extend_to_next_word(&mut self, max: usize) {
|
pub fn extend_to_next_word(&mut self, max: usize) {
|
||||||
if self.head == max { return }
|
if self.head == max { return; }
|
||||||
|
|
||||||
if self.head.is_multiple_of(4) { // at the beginning of a word
|
if self.head.is_multiple_of(4) { // at the beginning of a word
|
||||||
self.head = (self.head + 4).min(max);
|
self.head = (self.head + 4).min(max);
|
||||||
@@ -95,7 +95,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn extend_to_next_end(&mut self, max: usize) {
|
pub fn extend_to_next_end(&mut self, max: usize) {
|
||||||
if self.head == max { return }
|
if self.head == max { return; }
|
||||||
|
|
||||||
if self.head % 4 == 3 { // at the end of a word
|
if self.head % 4 == 3 { // at the end of a word
|
||||||
self.head = (self.head + 4).min(max);
|
self.head = (self.head + 4).min(max);
|
||||||
@@ -105,7 +105,7 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const fn extend_to_previous_beginning(&mut self) {
|
pub const fn extend_to_previous_beginning(&mut self) {
|
||||||
if self.head == 0 { return }
|
if self.head == 0 { return; }
|
||||||
|
|
||||||
if self.head.is_multiple_of(4) { // at the beginning of a word
|
if self.head.is_multiple_of(4) { // at the beginning of a word
|
||||||
self.head -= 4;
|
self.head -= 4;
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ impl App {
|
|||||||
fn delete_at(&mut self, cursor: Cursor) {
|
fn delete_at(&mut self, cursor: Cursor) {
|
||||||
self.contents.drain(cursor.range());
|
self.contents.drain(cursor.range());
|
||||||
|
|
||||||
self.cursor.head = min(min(cursor.head, cursor.tail), self.contents.len() - 1);
|
self.cursor.head = min(min(cursor.head, cursor.tail), self.max_contents_index());
|
||||||
self.cursor.collapse();
|
self.cursor.collapse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -50,8 +50,7 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
|||||||
// - utf8?
|
// - utf8?
|
||||||
// - diffing
|
// - diffing
|
||||||
|
|
||||||
// TODO: opening empty file crashes (or deleting entire file)
|
// TODO: quit should confirm if unsaved changes
|
||||||
// - cursor is NOT guaranteed to be in-bounds..?
|
|
||||||
|
|
||||||
// when AsciiChar is stabilized, use it instead of char everywhere
|
// when AsciiChar is stabilized, use it instead of char everywhere
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user