split selections by size
jump to offset under cursor
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
use std::{cmp::min, convert::identity, fs::File, io::Write, iter, mem::{replace, swap}};
|
use std::{cmp::min, convert::identity, fs::File, io::Write, iter, mem::{replace, swap}};
|
||||||
|
use ratatui::{style::Stylize, text::Span};
|
||||||
|
|
||||||
use crate::{BYTES_PER_LINE, app::WindowSize, buffer::{Buffer, Mode, PartialAction}, cursor::Cursor, edit_action::EditAction};
|
use crate::{BYTES_PER_LINE, app::WindowSize, buffer::{Buffer, Mode, PartialAction}, cursor::Cursor, edit_action::EditAction};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -69,6 +71,16 @@ pub enum Action {
|
|||||||
|
|
||||||
KeepPrimarySelection,
|
KeepPrimarySelection,
|
||||||
RemovePrimarySelection,
|
RemovePrimarySelection,
|
||||||
|
|
||||||
|
SplitSelectionsInto1s,
|
||||||
|
SplitSelectionsInto2s,
|
||||||
|
SplitSelectionsInto3s,
|
||||||
|
SplitSelectionsInto4s,
|
||||||
|
SplitSelectionsInto5s,
|
||||||
|
SplitSelectionsInto6s,
|
||||||
|
SplitSelectionsInto7s,
|
||||||
|
SplitSelectionsInto8s,
|
||||||
|
SplitSelectionsInto9s,
|
||||||
}
|
}
|
||||||
|
|
||||||
// actions that act on the app as a whole, not just one buffer
|
// actions that act on the app as a whole, not just one buffer
|
||||||
@@ -149,6 +161,16 @@ impl Buffer {
|
|||||||
|
|
||||||
Action::KeepPrimarySelection => self.keep_primary_selection(),
|
Action::KeepPrimarySelection => self.keep_primary_selection(),
|
||||||
Action::RemovePrimarySelection => self.remove_primary_selection(),
|
Action::RemovePrimarySelection => self.remove_primary_selection(),
|
||||||
|
|
||||||
|
Action::SplitSelectionsInto1s => self.split_selections_into_size(1),
|
||||||
|
Action::SplitSelectionsInto2s => self.split_selections_into_size(2),
|
||||||
|
Action::SplitSelectionsInto3s => self.split_selections_into_size(3),
|
||||||
|
Action::SplitSelectionsInto4s => self.split_selections_into_size(4),
|
||||||
|
Action::SplitSelectionsInto5s => self.split_selections_into_size(5),
|
||||||
|
Action::SplitSelectionsInto6s => self.split_selections_into_size(6),
|
||||||
|
Action::SplitSelectionsInto7s => self.split_selections_into_size(7),
|
||||||
|
Action::SplitSelectionsInto8s => self.split_selections_into_size(8),
|
||||||
|
Action::SplitSelectionsInto9s => self.split_selections_into_size(9),
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@@ -560,6 +582,30 @@ impl Buffer {
|
|||||||
self.primary_cursor = self.cursors.remove(next_cursor_index);
|
self.primary_cursor = self.cursors.remove(next_cursor_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn split_selections_into_size(&mut self, size: usize) {
|
||||||
|
if !iter::once(&self.primary_cursor)
|
||||||
|
.chain(&self.cursors)
|
||||||
|
.all(|cursor| cursor.len().is_multiple_of(size))
|
||||||
|
{
|
||||||
|
self.alert_message = Span::from(
|
||||||
|
format!("not all selections are a multiple of {size} long")
|
||||||
|
).red();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_cursors = iter::once(self.primary_cursor)
|
||||||
|
.chain(self.cursors.iter().copied())
|
||||||
|
.flat_map(|cursor| {
|
||||||
|
cursor
|
||||||
|
.range()
|
||||||
|
.step_by(size)
|
||||||
|
.map(|head| Cursor { head, tail: head + size - 1 })
|
||||||
|
});
|
||||||
|
|
||||||
|
self.primary_cursor = new_cursors.next().unwrap();
|
||||||
|
self.cursors = new_cursors.collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ pub struct App {
|
|||||||
pub window_size: WindowSize,
|
pub window_size: WindowSize,
|
||||||
|
|
||||||
pub should_quit: bool,
|
pub should_quit: bool,
|
||||||
|
|
||||||
pub logs: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -50,8 +48,6 @@ impl App {
|
|||||||
},
|
},
|
||||||
|
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
|
|
||||||
logs: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -27,6 +27,8 @@ pub struct Buffer {
|
|||||||
pub time_traveling: Option<usize>,
|
pub time_traveling: Option<usize>,
|
||||||
// the index *after* the last saved edit action
|
// the index *after* the last saved edit action
|
||||||
pub last_saved_at: Option<usize>,
|
pub last_saved_at: Option<usize>,
|
||||||
|
|
||||||
|
pub logs: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
@@ -93,6 +95,8 @@ impl Buffer {
|
|||||||
edit_history: Vec::new(),
|
edit_history: Vec::new(),
|
||||||
time_traveling: None,
|
time_traveling: None,
|
||||||
last_saved_at: Some(0),
|
last_saved_at: Some(0),
|
||||||
|
|
||||||
|
logs: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +177,7 @@ impl Buffer {
|
|||||||
pub fn combine_cursors_if_overlapping(&mut self) {
|
pub fn combine_cursors_if_overlapping(&mut self) {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
|
|
||||||
while !self.cursors.is_empty() && index < self.cursors.len() - 1 {
|
while !self.cursors.is_empty() && index < self.cursors.len() {
|
||||||
while index < self.cursors.len() - 1 &&
|
while index < self.cursors.len() - 1 &&
|
||||||
self.cursors[index].range().is_overlapping(
|
self.cursors[index].range().is_overlapping(
|
||||||
&self.cursors[index + 1].range())
|
&self.cursors[index + 1].range())
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ impl From<KeyEvent> for Keypress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
[
|
[
|
||||||
(Mode::Normal, [
|
(Mode::Normal, [
|
||||||
@@ -150,6 +151,16 @@ impl Default for Config {
|
|||||||
|
|
||||||
(",".try_into().unwrap(), Action::KeepPrimarySelection),
|
(",".try_into().unwrap(), Action::KeepPrimarySelection),
|
||||||
("A-,".try_into().unwrap(), Action::RemovePrimarySelection),
|
("A-,".try_into().unwrap(), Action::RemovePrimarySelection),
|
||||||
|
|
||||||
|
("1".try_into().unwrap(), Action::SplitSelectionsInto1s),
|
||||||
|
("2".try_into().unwrap(), Action::SplitSelectionsInto2s),
|
||||||
|
("3".try_into().unwrap(), Action::SplitSelectionsInto3s),
|
||||||
|
("4".try_into().unwrap(), Action::SplitSelectionsInto4s),
|
||||||
|
("5".try_into().unwrap(), Action::SplitSelectionsInto5s),
|
||||||
|
("6".try_into().unwrap(), Action::SplitSelectionsInto6s),
|
||||||
|
("7".try_into().unwrap(), Action::SplitSelectionsInto7s),
|
||||||
|
("8".try_into().unwrap(), Action::SplitSelectionsInto8s),
|
||||||
|
("9".try_into().unwrap(), Action::SplitSelectionsInto9s),
|
||||||
].into()),
|
].into()),
|
||||||
(Some(PartialAction::Goto), [
|
(Some(PartialAction::Goto), [
|
||||||
("j".try_into().unwrap(), Action::GotoLineStart),
|
("j".try_into().unwrap(), Action::GotoLineStart),
|
||||||
@@ -209,6 +220,16 @@ impl Default for Config {
|
|||||||
|
|
||||||
(",".try_into().unwrap(), Action::KeepPrimarySelection),
|
(",".try_into().unwrap(), Action::KeepPrimarySelection),
|
||||||
("A-,".try_into().unwrap(), Action::RemovePrimarySelection),
|
("A-,".try_into().unwrap(), Action::RemovePrimarySelection),
|
||||||
|
|
||||||
|
("1".try_into().unwrap(), Action::SplitSelectionsInto1s),
|
||||||
|
("2".try_into().unwrap(), Action::SplitSelectionsInto2s),
|
||||||
|
("3".try_into().unwrap(), Action::SplitSelectionsInto3s),
|
||||||
|
("4".try_into().unwrap(), Action::SplitSelectionsInto4s),
|
||||||
|
("5".try_into().unwrap(), Action::SplitSelectionsInto5s),
|
||||||
|
("6".try_into().unwrap(), Action::SplitSelectionsInto6s),
|
||||||
|
("7".try_into().unwrap(), Action::SplitSelectionsInto7s),
|
||||||
|
("8".try_into().unwrap(), Action::SplitSelectionsInto8s),
|
||||||
|
("9".try_into().unwrap(), Action::SplitSelectionsInto9s),
|
||||||
].into()),
|
].into()),
|
||||||
(Some(PartialAction::Space), [
|
(Some(PartialAction::Space), [
|
||||||
("w".try_into().unwrap(), Action::Save),
|
("w".try_into().unwrap(), Action::Save),
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ impl Cursor {
|
|||||||
self.lower_bound()..=self.upper_bound()
|
self.lower_bound()..=self.upper_bound()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.upper_bound() - self.lower_bound() + 1
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn contains(&self, index: usize) -> Option<InCursor> {
|
pub const fn contains(&self, index: usize) -> Option<InCursor> {
|
||||||
if index == self.head {
|
if index == self.head {
|
||||||
Some(InCursor::Head)
|
Some(InCursor::Head)
|
||||||
|
|||||||
+5
-9
@@ -20,7 +20,10 @@ const BYTES_PER_CHUNK: usize = 4;
|
|||||||
const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
// - J jump to offset under cursor
|
||||||
|
// - m mark offset
|
||||||
// - search
|
// - search
|
||||||
|
// - s/A-k/A-K
|
||||||
// - modifications
|
// - modifications
|
||||||
// - insert/append
|
// - insert/append
|
||||||
// - mode
|
// - mode
|
||||||
@@ -36,18 +39,11 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
|||||||
// - jumplist
|
// - jumplist
|
||||||
// - y/p
|
// - y/p
|
||||||
// - [/] to cycle view offset?
|
// - [/] to cycle view offset?
|
||||||
// - J jump to offset
|
// - gj jump to entered offset
|
||||||
// - under cursor?
|
|
||||||
|
|
||||||
// future directions
|
// future directions
|
||||||
// - switch between cursor size u8s/u16s/u32s/u64s?
|
|
||||||
// - +/-
|
|
||||||
// - multi-cursor
|
|
||||||
// - s/C
|
|
||||||
// - split selection by u8/16/32/etc
|
|
||||||
// - 'views' for bytes (i8/16/etc u8/16/etc 20.12/8.4/etc)
|
// - 'views' for bytes (i8/16/etc u8/16/etc 20.12/8.4/etc)
|
||||||
// - how to fit??! `-128` longer than `80`
|
// - how to fit??! `-128` longer than `80`
|
||||||
// - mark offsets?
|
|
||||||
// - utf8?
|
// - utf8?
|
||||||
// - diffing
|
// - diffing
|
||||||
|
|
||||||
@@ -71,7 +67,7 @@ fn main() {
|
|||||||
|
|
||||||
// dbg!(app.edit_history);
|
// dbg!(app.edit_history);
|
||||||
|
|
||||||
for log in app.logs {
|
for log in app.buffers.iter().flat_map(|buffer| &buffer.logs) {
|
||||||
println!("{log}");
|
println!("{log}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user