make config (de)serializable

This commit is contained in:
alice pellerin
2026-04-05 18:55:23 -05:00
parent a042e6ae34
commit d45e0a5032
6 changed files with 683 additions and 172 deletions
Generated
+60
View File
@@ -70,6 +70,9 @@ name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
dependencies = [
"serde_core",
]
[[package]]
name = "block-buffer"
@@ -158,6 +161,7 @@ dependencies = [
"mio",
"parking_lot",
"rustix",
"serde",
"signal-hook",
"signal-hook-mio",
"winapi",
@@ -438,6 +442,8 @@ dependencies = [
"crossterm",
"itertools",
"ratatui",
"serde",
"toml",
]
[[package]]
@@ -1101,6 +1107,15 @@ dependencies = [
"zmij",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "sha2"
version = "0.10.9"
@@ -1333,6 +1348,45 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "toml"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "typenum"
version = "1.19.0"
@@ -1625,6 +1679,12 @@ dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
[[package]]
name = "wit-bindgen"
version = "0.51.0"
+3 -1
View File
@@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2024"
[dependencies]
crossterm = "0.29.0"
crossterm = { version = "0.29.0", features = ["serde"] }
itertools = "0.14.0"
ratatui = "0.30.0"
serde = { version = "1.0.228", features = ["derive"] }
toml = "1.1.2"
+286 -4
View File
@@ -1,16 +1,41 @@
use std::{cmp::min, collections::hash_set::Entry, convert::identity, fs::File, io::Write, iter, mem::{replace, swap}};
use ratatui::{style::{Color, Stylize}, text::Span};
use serde::{Deserialize, Serialize};
use crate::{BYTES_OF_PADDING, BYTES_PER_LINE, LINES_OF_PADDING, app::WindowSize, buffer::{Buffer, Mode, PartialAction, Popup}, cursor::Cursor, edit_action::EditAction};
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Serialize, Deserialize)]
#[derive(Debug)]
#[serde(into = "&str")]
#[serde(try_from = "&str")]
pub enum Action {
App(AppAction),
Buffer(BufferAction),
Cursor(CursorAction),
}
impl From<Action> for &str {
fn from(action: Action) -> Self {
match action {
Action::App(app_action) => app_action.into(),
Action::Buffer(buffer_action) => buffer_action.into(),
Action::Cursor(cursor_action) => cursor_action.into(),
}
}
}
impl TryFrom<&str> for Action {
type Error = String;
fn try_from(string: &str) -> Result<Self, String> {
AppAction::try_from(string).map(Action::from)
.or_else(|_| BufferAction::try_from(string).map(Action::from))
.or_else(|_| CursorAction::try_from(string).map(Action::from))
.map_err(|_| format!("invalid action: {}", string))
}
}
// actions that act on the app as a whole, not just one buffer
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Deserialize)]
pub enum AppAction {
QuitIfSaved,
Quit,
@@ -21,13 +46,50 @@ pub enum AppAction {
Yank,
}
impl From<AppAction> for &str {
fn from(app_action: AppAction) -> Self {
use AppAction::*;
match app_action {
QuitIfSaved => "quit_if_saved",
Quit => "quit",
PreviousBuffer => "previous_buffer",
NextBuffer => "next_buffer",
Yank => "yank",
}
}
}
impl From<AppAction> for Action {
fn from(app_action: AppAction) -> Self {
Self::App(app_action)
}
}
#[derive(Clone, Copy)]
impl TryFrom<&str> for AppAction {
type Error = ();
fn try_from(string: &str) -> Result<Self, ()> {
use AppAction::*;
match string {
"quit_if_saved" => Ok(QuitIfSaved),
"quit" => Ok(Quit),
"previous_buffer" => Ok(PreviousBuffer),
"next_buffer" => Ok(NextBuffer),
"yank" => Ok(Yank),
_ => Err(()),
}
}
}
#[derive(Clone, Copy, Deserialize)]
#[derive(Debug)]
pub enum BufferAction {
NormalMode,
SelectMode,
@@ -93,13 +155,161 @@ pub enum BufferAction {
InspectSelectionColor,
}
impl From<BufferAction> for &str {
fn from(buffer_action: BufferAction) -> Self {
use BufferAction::*;
match buffer_action {
NormalMode => "normal_mode",
SelectMode => "select_mode",
Goto => "goto",
View => "view",
Replace => "replace",
Space => "space",
Repeat => "repeat",
To => "to",
ScrollDown => "scroll_down",
ScrollUp => "scroll_up",
PageCursorHalfDown => "page_cursor_half_down",
PageCursorHalfUp => "page_cursor_half_up",
PageDown => "page_down",
PageUp => "page_up",
CollapseSelection => "collapse_selection",
FlipSelections => "flip_selections",
Delete => "delete",
Undo => "undo",
Redo => "redo",
Save => "save",
CopySelectionOnNextLine => "copy_selection_on_next_line",
RotateSelectionsBackward => "rotate_selections_backward",
RotateSelectionsForward => "rotate_selections_forward",
KeepPrimarySelection => "keep_primary_selection",
RemovePrimarySelection => "remove_primary_selection",
SplitSelectionsInto1s => "split_selections_into_1_s",
SplitSelectionsInto2s => "split_selections_into_2_s",
SplitSelectionsInto3s => "split_selections_into_3_s",
SplitSelectionsInto4s => "split_selections_into_4_s",
SplitSelectionsInto5s => "split_selections_into_5_s",
SplitSelectionsInto6s => "split_selections_into_6_s",
SplitSelectionsInto7s => "split_selections_into_7_s",
SplitSelectionsInto8s => "split_selections_into_8_s",
SplitSelectionsInto9s => "split_selections_into_9_s",
JumpToSelectedOffset => "jump_to_selected_offset",
JumpToSelectedOffsetRelativeToMark => "jump_to_selected_offset_relative_to_mark",
ToggleMark => "toggle_mark",
AlignViewCenter => "align_view_center",
AlignViewBottom => "align_view_bottom",
AlignViewTop => "align_view_top",
ExtendToMark => "extend_to_mark",
ExtendToNull => "extend_to_null",
ExtendToFF => "extend_to_ff",
InspectSelection => "inspect_selection",
InspectSelectionColor => "inspect_selection_color",
}
}
}
impl From<BufferAction> for Action {
fn from(buffer_action: BufferAction) -> Self {
Self::Buffer(buffer_action)
}
}
#[derive(Clone, Copy)]
impl TryFrom<&str> for BufferAction {
type Error = ();
fn try_from(string: &str) -> Result<Self, ()> {
use BufferAction::*;
match string {
"normal_mode" => Ok(NormalMode),
"select_mode" => Ok(SelectMode),
"goto" => Ok(Goto),
"view" => Ok(View),
"replace" => Ok(Replace),
"space" => Ok(Space),
"repeat" => Ok(Repeat),
"to" => Ok(To),
"scroll_down" => Ok(ScrollDown),
"scroll_up" => Ok(ScrollUp),
"page_cursor_half_down" => Ok(PageCursorHalfDown),
"page_cursor_half_up" => Ok(PageCursorHalfUp),
"page_down" => Ok(PageDown),
"page_up" => Ok(PageUp),
"collapse_selection" => Ok(CollapseSelection),
"flip_selections" => Ok(FlipSelections),
"delete" => Ok(Delete),
"undo" => Ok(Undo),
"redo" => Ok(Redo),
"save" => Ok(Save),
"copy_selection_on_next_line" => Ok(CopySelectionOnNextLine),
"rotate_selections_backward" => Ok(RotateSelectionsBackward),
"rotate_selections_forward" => Ok(RotateSelectionsForward),
"keep_primary_selection" => Ok(KeepPrimarySelection),
"remove_primary_selection" => Ok(RemovePrimarySelection),
"split_selections_into_1_s" => Ok(SplitSelectionsInto1s),
"split_selections_into_2_s" => Ok(SplitSelectionsInto2s),
"split_selections_into_3_s" => Ok(SplitSelectionsInto3s),
"split_selections_into_4_s" => Ok(SplitSelectionsInto4s),
"split_selections_into_5_s" => Ok(SplitSelectionsInto5s),
"split_selections_into_6_s" => Ok(SplitSelectionsInto6s),
"split_selections_into_7_s" => Ok(SplitSelectionsInto7s),
"split_selections_into_8_s" => Ok(SplitSelectionsInto8s),
"split_selections_into_9_s" => Ok(SplitSelectionsInto9s),
"jump_to_selected_offset" => Ok(JumpToSelectedOffset),
"jump_to_selected_offset_relative_to_mark" => Ok(JumpToSelectedOffsetRelativeToMark),
"toggle_mark" => Ok(ToggleMark),
"align_view_center" => Ok(AlignViewCenter),
"align_view_bottom" => Ok(AlignViewBottom),
"align_view_top" => Ok(AlignViewTop),
"extend_to_mark" => Ok(ExtendToMark),
"extend_to_null" => Ok(ExtendToNull),
"extend_to_ff" => Ok(ExtendToFF),
"inspect_selection" => Ok(InspectSelection),
"inspect_selection_color" => Ok(InspectSelectionColor),
_ => Err(()),
}
}
}
#[derive(Clone, Copy, Serialize, Deserialize)]
#[derive(Debug)]
#[serde(rename_all = "snake_case")]
pub enum CursorAction {
MoveByteUp,
MoveByteDown,
@@ -128,12 +338,84 @@ pub enum CursorAction {
ExtendLineAbove,
}
impl From<CursorAction> for &str {
fn from(cursor_action: CursorAction) -> Self {
use CursorAction::*;
match cursor_action {
MoveByteUp => "move_byte_up",
MoveByteDown => "move_byte_down",
MoveByteLeft => "move_byte_left",
MoveByteRight => "move_byte_right",
ExtendByteUp => "extend_byte_up",
ExtendByteDown => "extend_byte_down",
ExtendByteLeft => "extend_byte_left",
ExtendByteRight => "extend_byte_right",
GotoLineStart => "goto_line_start",
GotoLineEnd => "goto_line_end",
GotoFileStart => "goto_file_start",
GotoFileEnd => "goto_file_end",
MoveNextWordStart => "move_next_word_start",
MoveNextWordEnd => "move_next_word_end",
MovePreviousWordStart => "move_previous_word_start",
ExtendNextWordStart => "extend_next_word_start",
ExtendNextWordEnd => "extend_next_word_end",
ExtendPreviousWordStart => "extend_previous_word_start",
ExtendLineBelow => "extend_line_below",
ExtendLineAbove => "extend_line_above",
}
}
}
impl From<CursorAction> for Action {
fn from(cursor_action: CursorAction) -> Self {
Self::Cursor(cursor_action)
}
}
impl TryFrom<&str> for CursorAction {
type Error = ();
fn try_from(string: &str) -> Result<Self, ()> {
use CursorAction::*;
match string {
"move_byte_up" => Ok(MoveByteUp),
"move_byte_down" => Ok(MoveByteDown),
"move_byte_left" => Ok(MoveByteLeft),
"move_byte_right" => Ok(MoveByteRight),
"extend_byte_up" => Ok(ExtendByteUp),
"extend_byte_down" => Ok(ExtendByteDown),
"extend_byte_left" => Ok(ExtendByteLeft),
"extend_byte_right" => Ok(ExtendByteRight),
"goto_line_start" => Ok(GotoLineStart),
"goto_line_end" => Ok(GotoLineEnd),
"goto_file_start" => Ok(GotoFileStart),
"goto_file_end" => Ok(GotoFileEnd),
"move_next_word_start" => Ok(MoveNextWordStart),
"move_next_word_end" => Ok(MoveNextWordEnd),
"move_previous_word_start" => Ok(MovePreviousWordStart),
"extend_next_word_start" => Ok(ExtendNextWordStart),
"extend_next_word_end" => Ok(ExtendNextWordEnd),
"extend_previous_word_start" => Ok(ExtendPreviousWordStart),
"extend_line_below" => Ok(ExtendLineBelow),
"extend_line_above" => Ok(ExtendLineAbove),
_ => Err(()),
}
}
}
impl Buffer {
pub fn execute(&mut self, action: BufferAction, window_size: WindowSize) {
match action {
+33 -8
View File
@@ -2,6 +2,7 @@ use core::slice::GetDisjointMutIndex;
use std::{collections::HashSet, fs::File, io::Read, path::PathBuf};
use crossterm::event::KeyEvent;
use ratatui::{layout::{Constraint, Rect}, style::{Color, Stylize}, text::Span, widgets::{Block, Clear, Widget}};
use serde::{Deserialize, Serialize};
use crate::{BYTES_PER_LINE, action::{Action, AppAction, bytes_to_nat}, app::WindowSize, config::Config, cursor::Cursor, edit_action::EditAction};
mod widget;
@@ -36,12 +37,16 @@ pub struct Buffer {
pub logs: Vec<String>,
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug)]
#[serde(rename_all = "snake_case")]
pub enum Mode {
Normal, Select, Insert
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug)]
#[serde(rename_all = "snake_case")]
pub enum PartialAction {
Goto, View, Replace, Space, Repeat, To
}
@@ -73,13 +78,33 @@ impl Mode {
impl PartialAction {
pub const fn label(self) -> &'static str {
use PartialAction::*;
match self {
Self::Goto => "g",
Self::View => "z",
Self::Replace => "r",
Self::Space => "",
Self::Repeat => "×",
Self::To => "t",
Goto => "g",
View => "z",
Replace => "r",
Space => "",
Repeat => "×",
To => "t",
}
}
}
impl TryFrom<&str> for PartialAction {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
use PartialAction::*;
match value {
"goto" => Ok(Goto),
"view" => Ok(View),
"replace" => Ok(Replace),
"space" => Ok(Space),
"repeat" => Ok(Repeat),
"to" => Ok(To),
_ => Err(()),
}
}
}
+288 -160
View File
@@ -1,25 +1,114 @@
use std::collections::HashMap;
use std::{collections::{HashMap, hash_map::Entry}, fmt::{self, Formatter}};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crate::{action::{Action, AppAction, BufferAction, CursorAction}, buffer::{Mode, PartialAction}};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::{Error, MapAccess, Unexpected, Visitor}, ser::SerializeMap};
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Config(
pub HashMap<Mode, ModeConfig>
);
#[derive(Debug)]
pub struct ModeConfig(
pub HashMap<Option<PartialAction>, Keybinds>
);
#[derive(Serialize, Deserialize)]
#[derive(Debug)]
#[serde(transparent)]
pub struct Keybinds(
pub HashMap<Keypress, Action>
);
#[derive(PartialEq, Eq, Hash)]
#[derive(PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
#[derive(Debug)]
#[serde(into = "String")]
#[serde(try_from = "&str")]
pub struct Keypress {
code: KeyCode,
modifiers: KeyModifiers
}
impl Serialize for ModeConfig {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
if let Some(keybinds) = self.0.get(&None) {
for (keypress, action) in &keybinds.0 {
map.serialize_entry(
&String::from(keypress),
&action
)?;
}
}
for (partial_action, keybinds) in &self.0 {
let Some(partial_action) = partial_action else { continue };
map.serialize_entry(
partial_action.into(),
keybinds
)?;
}
map.end()
}
}
struct ModeConfigVisitor;
impl<'de> Visitor<'de> for ModeConfigVisitor {
type Value = ModeConfig;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a config for keypresses in various partial action states")
}
fn visit_map<Map: MapAccess<'de>>(
self,
mut map: Map
) -> Result<Self::Value, Map::Error> {
let mut result = ModeConfig(HashMap::new());
while let Some(key) = map.next_key::<&str>()? {
if let Ok(partial_action) = PartialAction::try_from(key) {
let keybinds: Keybinds = map.next_value()?;
match result.0.entry(Some(partial_action)) {
Entry::Occupied(mut occupied_entry) => {
occupied_entry.get_mut().0.extend(keybinds.0);
},
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(keybinds);
}
}
} else {
let Ok(keypress) = key.try_into() else {
return Err(Error::invalid_value(
Unexpected::Str(key),
&"a valid keypress, with an optional modifier"
));
};
result.0.entry(None)
.or_insert_with(|| Keybinds(HashMap::new()))
.0.insert(keypress, map.next_value()?);
}
}
Ok(result)
}
}
impl<'de> Deserialize<'de> for ModeConfig {
fn deserialize<D: Deserializer<'de>>(
deserializer: D
) -> Result<Self, D::Error> {
deserializer.deserialize_map(ModeConfigVisitor)
}
}
impl<const N: usize> From<[(Mode, ModeConfig); N]> for Config {
fn from(array: [(Mode, ModeConfig); N]) -> Self {
Self(array.into())
@@ -55,11 +144,30 @@ const fn modifier_from_character(character: char) -> Option<KeyModifiers> {
}
}
impl TryFrom<&str> for Keypress {
type Error = ();
const fn str_from_modifiers(modifier: KeyModifiers) -> &'static str {
match modifier {
KeyModifiers::ALT => "A-",
KeyModifiers::CONTROL => "C-",
_ => ""
}
}
fn try_from(string: &str) -> Result<Self, ()> {
match string.len() {
fn string_from_code(code: KeyCode) -> String {
match code {
KeyCode::Char(character) => character.to_string(),
KeyCode::Up => "Up".to_string(),
KeyCode::Down => "Down".to_string(),
KeyCode::Left => "Left".to_string(),
KeyCode::Right => "Right".to_string(),
_ => todo!()
}
}
impl TryFrom<&str> for Keypress {
type Error = String;
fn try_from(string: &str) -> Result<Self, Self::Error> {
match string.chars().count() {
3 => {
Ok(Self {
code: KeyCode::Char(
@@ -67,7 +175,7 @@ impl TryFrom<&str> for Keypress {
),
modifiers: modifier_from_character(
string.chars().nth(0).unwrap()
).ok_or(())?,
).ok_or(format!("unknown modifier: {}", string.chars().nth(0).unwrap()))?,
})
}
1 => {
@@ -77,11 +185,27 @@ impl TryFrom<&str> for Keypress {
).into()
)
}
_ => Err(())
_ => Err(format!("invalid keypress: {}. only one modifier is allowed", string))
}
}
}
impl From<&Keypress> for String {
fn from(value: &Keypress) -> Self {
format!(
"{}{}",
str_from_modifiers(value.modifiers),
string_from_code(value.code)
)
}
}
impl From<Keypress> for String {
fn from(value: Keypress) -> Self {
String::from(&value)
}
}
impl From<KeyEvent> for Keypress {
fn from(event: KeyEvent) -> Self {
Self {
@@ -94,238 +218,242 @@ impl From<KeyEvent> for Keypress {
impl Default for Config {
#[allow(clippy::too_many_lines)]
fn default() -> Self {
use AppAction::*;
use BufferAction::*;
use CursorAction::*;
[
(Mode::Normal, [
(None, [
("q".try_into().unwrap(), AppAction::QuitIfSaved.into()),
("Q".try_into().unwrap(), AppAction::Quit.into()),
("q".try_into().unwrap(), QuitIfSaved.into()),
("Q".try_into().unwrap(), Quit.into()),
("v".try_into().unwrap(), BufferAction::SelectMode.into()),
("v".try_into().unwrap(), SelectMode.into()),
("g".try_into().unwrap(), BufferAction::Goto.into()),
("z".try_into().unwrap(), BufferAction::View.into()),
("r".try_into().unwrap(), BufferAction::Replace.into()),
(" ".try_into().unwrap(), BufferAction::Space.into()),
("*".try_into().unwrap(), BufferAction::Repeat.into()),
("t".try_into().unwrap(), BufferAction::To.into()),
("g".try_into().unwrap(), Goto.into()),
("z".try_into().unwrap(), View.into()),
("r".try_into().unwrap(), Replace.into()),
(" ".try_into().unwrap(), Space.into()),
("*".try_into().unwrap(), Repeat.into()),
("t".try_into().unwrap(), To.into()),
("i".try_into().unwrap(), CursorAction::MoveByteUp.into()),
("k".try_into().unwrap(), CursorAction::MoveByteDown.into()),
("j".try_into().unwrap(), CursorAction::MoveByteLeft.into()),
("l".try_into().unwrap(), CursorAction::MoveByteRight.into()),
("i".try_into().unwrap(), MoveByteUp.into()),
("k".try_into().unwrap(), MoveByteDown.into()),
("j".try_into().unwrap(), MoveByteLeft.into()),
("l".try_into().unwrap(), MoveByteRight.into()),
("G".try_into().unwrap(), CursorAction::GotoFileEnd.into()),
("G".try_into().unwrap(), GotoFileEnd.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::MoveNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::MoveNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::MovePreviousWordStart.into()),
("w".try_into().unwrap(), MoveNextWordStart.into()),
("e".try_into().unwrap(), MoveNextWordEnd.into()),
("b".try_into().unwrap(), MovePreviousWordStart.into()),
(";".try_into().unwrap(), BufferAction::CollapseSelection.into()),
("A-;".try_into().unwrap(), BufferAction::FlipSelections.into()),
(";".try_into().unwrap(), CollapseSelection.into()),
("A-;".try_into().unwrap(), FlipSelections.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("u".try_into().unwrap(), BufferAction::Undo.into()),
("U".try_into().unwrap(), BufferAction::Redo.into()),
("u".try_into().unwrap(), Undo.into()),
("U".try_into().unwrap(), Redo.into()),
("C-j".try_into().unwrap(), AppAction::PreviousBuffer.into()),
("C-l".try_into().unwrap(), AppAction::NextBuffer.into()),
("C-j".try_into().unwrap(), PreviousBuffer.into()),
("C-l".try_into().unwrap(), NextBuffer.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), BufferAction::RotateSelectionsBackward.into()),
(")".try_into().unwrap(), BufferAction::RotateSelectionsForward.into()),
("(".try_into().unwrap(), RotateSelectionsBackward.into()),
(")".try_into().unwrap(), RotateSelectionsForward.into()),
(",".try_into().unwrap(), BufferAction::KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), BufferAction::RemovePrimarySelection.into()),
(",".try_into().unwrap(), KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), RemovePrimarySelection.into()),
("1".try_into().unwrap(), BufferAction::SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), BufferAction::SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), BufferAction::SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), BufferAction::SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), BufferAction::SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), BufferAction::SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), BufferAction::SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), BufferAction::SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), BufferAction::SplitSelectionsInto9s.into()),
("1".try_into().unwrap(), SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), BufferAction::JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), BufferAction::JumpToSelectedOffset.into()),
("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), JumpToSelectedOffset.into()),
("m".try_into().unwrap(), BufferAction::ToggleMark.into()),
("m".try_into().unwrap(), ToggleMark.into()),
("y".try_into().unwrap(), AppAction::Yank.into()),
("y".try_into().unwrap(), Yank.into()),
("C- ".try_into().unwrap(), BufferAction::InspectSelection.into()),
("A- ".try_into().unwrap(), BufferAction::InspectSelectionColor.into()),
("C- ".try_into().unwrap(), InspectSelection.into()),
("A- ".try_into().unwrap(), InspectSelectionColor.into()),
].into()),
(Some(PartialAction::Goto), [
("j".try_into().unwrap(), CursorAction::GotoLineStart.into()),
("l".try_into().unwrap(), CursorAction::GotoLineEnd.into()),
("j".try_into().unwrap(), GotoLineStart.into()),
("l".try_into().unwrap(), GotoLineEnd.into()),
("g".try_into().unwrap(), CursorAction::GotoFileStart.into()),
("g".try_into().unwrap(), GotoFileStart.into()),
].into()),
(Some(PartialAction::View), [
("z".try_into().unwrap(), BufferAction::AlignViewCenter.into()),
("b".try_into().unwrap(), BufferAction::AlignViewBottom.into()),
("t".try_into().unwrap(), BufferAction::AlignViewTop.into()),
("z".try_into().unwrap(), AlignViewCenter.into()),
("b".try_into().unwrap(), AlignViewBottom.into()),
("t".try_into().unwrap(), AlignViewTop.into()),
].into()),
(Some(PartialAction::Space), [
("w".try_into().unwrap(), BufferAction::Save.into()),
("w".try_into().unwrap(), Save.into()),
].into()),
(Some(PartialAction::Repeat), [
("i".try_into().unwrap(), CursorAction::MoveByteUp.into()),
("k".try_into().unwrap(), CursorAction::MoveByteDown.into()),
("j".try_into().unwrap(), CursorAction::MoveByteLeft.into()),
("l".try_into().unwrap(), CursorAction::MoveByteRight.into()),
("i".try_into().unwrap(), MoveByteUp.into()),
("k".try_into().unwrap(), MoveByteDown.into()),
("j".try_into().unwrap(), MoveByteLeft.into()),
("l".try_into().unwrap(), MoveByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::MoveNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::MoveNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::MovePreviousWordStart.into()),
("w".try_into().unwrap(), MoveNextWordStart.into()),
("e".try_into().unwrap(), MoveNextWordEnd.into()),
("b".try_into().unwrap(), MovePreviousWordStart.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
].into()),
(Some(PartialAction::To), [
("m".try_into().unwrap(), BufferAction::ExtendToMark.into()),
("0".try_into().unwrap(), BufferAction::ExtendToNull.into()),
("f".try_into().unwrap(), BufferAction::ExtendToFF.into()),
("m".try_into().unwrap(), ExtendToMark.into()),
("0".try_into().unwrap(), ExtendToNull.into()),
("f".try_into().unwrap(), ExtendToFF.into()),
].into()),
].into()),
(Mode::Select, [
(None, [
("q".try_into().unwrap(), AppAction::QuitIfSaved.into()),
("Q".try_into().unwrap(), AppAction::Quit.into()),
("q".try_into().unwrap(), QuitIfSaved.into()),
("Q".try_into().unwrap(), Quit.into()),
("v".try_into().unwrap(), BufferAction::NormalMode.into()),
("v".try_into().unwrap(), NormalMode.into()),
("g".try_into().unwrap(), BufferAction::Goto.into()),
("z".try_into().unwrap(), BufferAction::View.into()),
("r".try_into().unwrap(), BufferAction::Replace.into()),
(" ".try_into().unwrap(), BufferAction::Space.into()),
("*".try_into().unwrap(), BufferAction::Repeat.into()),
("t".try_into().unwrap(), BufferAction::To.into()),
("g".try_into().unwrap(), Goto.into()),
("z".try_into().unwrap(), View.into()),
("r".try_into().unwrap(), Replace.into()),
(" ".try_into().unwrap(), Space.into()),
("*".try_into().unwrap(), Repeat.into()),
("t".try_into().unwrap(), To.into()),
("i".try_into().unwrap(), CursorAction::ExtendByteUp.into()),
("k".try_into().unwrap(), CursorAction::ExtendByteDown.into()),
("j".try_into().unwrap(), CursorAction::ExtendByteLeft.into()),
("l".try_into().unwrap(), CursorAction::ExtendByteRight.into()),
("i".try_into().unwrap(), ExtendByteUp.into()),
("k".try_into().unwrap(), ExtendByteDown.into()),
("j".try_into().unwrap(), ExtendByteLeft.into()),
("l".try_into().unwrap(), ExtendByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::ExtendNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::ExtendNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::ExtendPreviousWordStart.into()),
("w".try_into().unwrap(), ExtendNextWordStart.into()),
("e".try_into().unwrap(), ExtendNextWordEnd.into()),
("b".try_into().unwrap(), ExtendPreviousWordStart.into()),
(";".try_into().unwrap(), BufferAction::CollapseSelection.into()),
("A-;".try_into().unwrap(), BufferAction::FlipSelections.into()),
(";".try_into().unwrap(), CollapseSelection.into()),
("A-;".try_into().unwrap(), FlipSelections.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("u".try_into().unwrap(), BufferAction::Undo.into()),
("U".try_into().unwrap(), BufferAction::Redo.into()),
("u".try_into().unwrap(), Undo.into()),
("U".try_into().unwrap(), Redo.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), BufferAction::RotateSelectionsBackward.into()),
(")".try_into().unwrap(), BufferAction::RotateSelectionsForward.into()),
("(".try_into().unwrap(), RotateSelectionsBackward.into()),
(")".try_into().unwrap(), RotateSelectionsForward.into()),
(",".try_into().unwrap(), BufferAction::KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), BufferAction::RemovePrimarySelection.into()),
(",".try_into().unwrap(), KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), RemovePrimarySelection.into()),
("1".try_into().unwrap(), BufferAction::SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), BufferAction::SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), BufferAction::SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), BufferAction::SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), BufferAction::SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), BufferAction::SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), BufferAction::SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), BufferAction::SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), BufferAction::SplitSelectionsInto9s.into()),
("1".try_into().unwrap(), SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), BufferAction::JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), BufferAction::JumpToSelectedOffset.into()),
("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), JumpToSelectedOffset.into()),
("m".try_into().unwrap(), BufferAction::ToggleMark.into()),
("m".try_into().unwrap(), ToggleMark.into()),
("y".try_into().unwrap(), AppAction::Yank.into()),
("y".try_into().unwrap(), Yank.into()),
("C- ".try_into().unwrap(), BufferAction::InspectSelection.into()),
("A- ".try_into().unwrap(), BufferAction::InspectSelectionColor.into()),
("C- ".try_into().unwrap(), InspectSelection.into()),
("A- ".try_into().unwrap(), InspectSelectionColor.into()),
].into()),
(Some(PartialAction::View), [
("z".try_into().unwrap(), BufferAction::AlignViewCenter.into()),
("b".try_into().unwrap(), BufferAction::AlignViewBottom.into()),
("t".try_into().unwrap(), BufferAction::AlignViewTop.into()),
("z".try_into().unwrap(), AlignViewCenter.into()),
("b".try_into().unwrap(), AlignViewBottom.into()),
("t".try_into().unwrap(), AlignViewTop.into()),
].into()),
(Some(PartialAction::Space), [
("w".try_into().unwrap(), BufferAction::Save.into()),
("w".try_into().unwrap(), Save.into()),
].into()),
(Some(PartialAction::Repeat), [
("i".try_into().unwrap(), CursorAction::ExtendByteUp.into()),
("k".try_into().unwrap(), CursorAction::ExtendByteDown.into()),
("j".try_into().unwrap(), CursorAction::ExtendByteLeft.into()),
("l".try_into().unwrap(), CursorAction::ExtendByteRight.into()),
("i".try_into().unwrap(), ExtendByteUp.into()),
("k".try_into().unwrap(), ExtendByteDown.into()),
("j".try_into().unwrap(), ExtendByteLeft.into()),
("l".try_into().unwrap(), ExtendByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::ExtendNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::ExtendNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::ExtendPreviousWordStart.into()),
("w".try_into().unwrap(), ExtendNextWordStart.into()),
("e".try_into().unwrap(), ExtendNextWordEnd.into()),
("b".try_into().unwrap(), ExtendPreviousWordStart.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
].into()),
(Some(PartialAction::To), [
("m".try_into().unwrap(), BufferAction::ExtendToMark.into()),
("0".try_into().unwrap(), BufferAction::ExtendToNull.into()),
("f".try_into().unwrap(), BufferAction::ExtendToFF.into()),
("m".try_into().unwrap(), ExtendToMark.into()),
("0".try_into().unwrap(), ExtendToNull.into()),
("f".try_into().unwrap(), ExtendToFF.into()),
].into()),
].into())
].into()
+14
View File
@@ -5,9 +5,13 @@
#![feature(hash_set_entry)]
#![feature(trim_prefix_suffix)]
use std::fs::read_to_string;
use app::App;
use crossterm::{QueueableCommand, event::{DisableMouseCapture, EnableMouseCapture}};
use crate::config::Config;
mod app;
mod buffer;
mod config;
@@ -55,6 +59,16 @@ const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE;
// - how to fit??! `-128` longer than `80`
fn main() {
// let config = Config::default();
// let toml_string = toml::to_string_pretty(&config);
// println!("{}", toml_string.unwrap());
// let string = read_to_string("/Users/simonomi/Desktop/hexapoda_config.toml").unwrap();
// let config: Config = toml::from_str(&string).unwrap();
// dbg!(config.0);
// return;
let mut app = App::new();
let mut terminal = ratatui::init();
crossterm::terminal::enable_raw_mode().unwrap();