add config file
This commit is contained in:
@@ -25,8 +25,7 @@ currently, hexapoda is very unpolished, and missing some major features. if you'
|
||||
|
||||
### notable features that are missing (for now)
|
||||
|
||||
- inserting bytes
|
||||
- only replacing and deleting right now
|
||||
- custom keybinds
|
||||
- search
|
||||
- diffing
|
||||
- inserting bytes
|
||||
- only replacing and deleting right now
|
||||
|
||||
+8
-8
@@ -27,10 +27,10 @@ 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))
|
||||
AppAction::try_from(string).map(Self::from)
|
||||
.or_else(|()| BufferAction::try_from(string).map(Self::from))
|
||||
.or_else(|()| CursorAction::try_from(string).map(Self::from))
|
||||
.map_err(|()| format!("invalid action: {string}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1249,13 +1249,13 @@ const fn nat_to_int_if_different(nat: u64, bytes: usize) -> Option<i64> {
|
||||
fn nat_to_int_tests() {
|
||||
assert_eq!(nat_to_int_if_different(0, 1), None);
|
||||
assert_eq!(nat_to_int_if_different(i8::MAX as u64, 1), None);
|
||||
assert_eq!(nat_to_int_if_different(i8::MAX as u64 + 1, 1), Some(i8::MIN as i64));
|
||||
assert_eq!(nat_to_int_if_different(u8::MAX as u64, 1), Some(-1));
|
||||
assert_eq!(nat_to_int_if_different(i8::MAX as u64 + 1, 1), Some(i8::MIN.into()));
|
||||
assert_eq!(nat_to_int_if_different(u8::MAX.into(), 1), Some(-1));
|
||||
|
||||
assert_eq!(nat_to_int_if_different(0, 2), None);
|
||||
assert_eq!(nat_to_int_if_different(i16::MAX as u64, 2), None);
|
||||
assert_eq!(nat_to_int_if_different(i16::MAX as u64 + 1, 2), Some(i16::MIN as i64));
|
||||
assert_eq!(nat_to_int_if_different(u16::MAX as u64, 2), Some(-1));
|
||||
assert_eq!(nat_to_int_if_different(i16::MAX as u64 + 1, 2), Some(i16::MIN.into()));
|
||||
assert_eq!(nat_to_int_if_different(u16::MAX.into(), 2), Some(-1));
|
||||
}
|
||||
|
||||
// or 0 if no mark is before
|
||||
|
||||
+26
-3
@@ -1,7 +1,7 @@
|
||||
use std::{env, process::exit, time::Duration};
|
||||
use std::{env::{self}, io::{self}, process::exit, time::Duration};
|
||||
use crossterm::{ExecutableCommand, event::{self, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}, terminal::window_size};
|
||||
use ratatui::{DefaultTerminal, style::Stylize, text::Span};
|
||||
use crate::{BYTES_PER_LINE, action::AppAction, buffer::Buffer, config::Config, cursor::Cursor};
|
||||
use crate::{BYTES_PER_LINE, action::AppAction, buffer::Buffer, config::{Config, ConfigInitError}, cursor::Cursor};
|
||||
|
||||
mod widget;
|
||||
|
||||
@@ -29,6 +29,29 @@ pub struct WindowSize {
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
let config = {
|
||||
let config = Config::init();
|
||||
|
||||
match &config {
|
||||
Err(ConfigInitError::IO(io_error)) if io_error.kind() != io::ErrorKind::NotFound => {
|
||||
eprintln!("IO error while reading config, press <ENTER> to continue with default config");
|
||||
|
||||
let mut temp = String::new();
|
||||
let _ = io::stdin().read_line(&mut temp);
|
||||
}
|
||||
Err(ConfigInitError::Deserialization(deserialization_error)) => {
|
||||
eprintln!("bad config: {deserialization_error}");
|
||||
eprintln!("press <ENTER> to continue with default config");
|
||||
|
||||
let mut temp = String::new();
|
||||
let _ = io::stdin().read_line(&mut temp);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
config.unwrap_or_default()
|
||||
};
|
||||
|
||||
let buffers: Vec<Buffer> = env::args()
|
||||
.skip(1)
|
||||
.map(Into::into)
|
||||
@@ -50,7 +73,7 @@ impl App {
|
||||
};
|
||||
|
||||
Self {
|
||||
config: Config::default(),
|
||||
config,
|
||||
|
||||
buffers,
|
||||
current_buffer_index: 0,
|
||||
|
||||
@@ -98,7 +98,7 @@ mod address {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn style_for_address(address: usize) -> Style {
|
||||
pub const fn style_for_address(address: usize) -> Style {
|
||||
if address.is_multiple_of(0x100) {
|
||||
Style::new().fg(Color::Rgb(0x68, 0x99, 0xA0))
|
||||
} else {
|
||||
@@ -108,9 +108,9 @@ mod address {
|
||||
}
|
||||
|
||||
mod hex {
|
||||
use std::{borrow::Cow, iter, mem};
|
||||
use itertools::{Itertools, repeat_n};
|
||||
use ratatui::{style::{Color, Style, Stylize}, text::Span};
|
||||
use std::{borrow::Cow, iter::{self, repeat_n}, mem};
|
||||
use itertools::Itertools;
|
||||
use ratatui::{style::{Color, Style, Stylize}, text::Span};
|
||||
|
||||
use crate::{BYTES_PER_CHUNK, BYTES_PER_LINE, CHUNKS_PER_LINE, buffer::{Buffer, Mode, PartialAction}, cardinality::HasCardinality, cursor::InCursor, custom_greys::CustomGreys, empty_span::empty_span};
|
||||
|
||||
@@ -173,7 +173,6 @@ mod hex {
|
||||
address: usize,
|
||||
bytes: &[u8; BYTES_PER_CHUNK]
|
||||
) -> impl Iterator<Item=Span<'static>> {
|
||||
#[allow(unstable_name_collisions)]
|
||||
iter::once(self.render_large_space_before(address))
|
||||
.chain(
|
||||
bytes
|
||||
@@ -195,7 +194,6 @@ mod hex {
|
||||
address: usize,
|
||||
bytes: &[u8]
|
||||
) -> impl Iterator<Item=Span<'static>> {
|
||||
#[allow(unstable_name_collisions)]
|
||||
iter::once(self.render_large_space_before(address))
|
||||
.chain(
|
||||
bytes
|
||||
|
||||
+45
-5
@@ -1,4 +1,4 @@
|
||||
use std::{collections::{HashMap, hash_map::Entry}, fmt::{self, Formatter}};
|
||||
use std::{collections::{HashMap, hash_map::Entry}, env::{self, home_dir}, fmt::{self, Formatter}, fs::read_to_string, io, path::PathBuf};
|
||||
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};
|
||||
@@ -30,6 +30,46 @@ pub struct Keypress {
|
||||
modifiers: KeyModifiers
|
||||
}
|
||||
|
||||
impl Config {
|
||||
#[cfg(unix)]
|
||||
fn path() -> Option<PathBuf> {
|
||||
env::var_os("XDG_CONFIG_HOME")
|
||||
.map(PathBuf::from)
|
||||
.take_if(|xdg_config_home| xdg_config_home.is_absolute())
|
||||
.or_else(|| home_dir().map(|home| home.join(".config")))
|
||||
.map(|config_path| config_path.join("hexapoda.toml"))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn path() -> Option<PathBuf> {
|
||||
// this isn't technically the right way but it should be good enough
|
||||
home_dir().map(|home| home.join("AppData").join("Roaming"))
|
||||
}
|
||||
|
||||
pub fn init() -> Result<Self, ConfigInitError> {
|
||||
let path = Self::path().ok_or(ConfigInitError::NoConfigPath)?;
|
||||
let raw_config = read_to_string(path)?;
|
||||
|
||||
Ok(toml::from_str(&raw_config)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ConfigInitError {
|
||||
NoConfigPath, IO(io::Error), Deserialization(toml::de::Error)
|
||||
}
|
||||
|
||||
impl From<io::Error> for ConfigInitError {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::IO(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<toml::de::Error> for ConfigInitError {
|
||||
fn from(error: toml::de::Error) -> Self {
|
||||
Self::Deserialization(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ModeConfig {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
@@ -47,7 +87,7 @@ impl Serialize for ModeConfig {
|
||||
let Some(partial_action) = partial_action else { continue };
|
||||
|
||||
map.serialize_entry(
|
||||
partial_action.into(),
|
||||
partial_action,
|
||||
keybinds
|
||||
)?;
|
||||
}
|
||||
@@ -175,7 +215,7 @@ impl TryFrom<&str> for Keypress {
|
||||
),
|
||||
modifiers: modifier_from_character(
|
||||
string.chars().nth(0).unwrap()
|
||||
).ok_or(format!("unknown modifier: {}", string.chars().nth(0).unwrap()))?,
|
||||
).ok_or_else(|| format!("unknown modifier: {}", string.chars().nth(0).unwrap()))?,
|
||||
})
|
||||
}
|
||||
1 => {
|
||||
@@ -185,7 +225,7 @@ impl TryFrom<&str> for Keypress {
|
||||
).into()
|
||||
)
|
||||
}
|
||||
_ => Err(format!("invalid keypress: {}. only one modifier is allowed", string))
|
||||
_ => Err(format!("invalid keypress: {string}. only one modifier is allowed"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,7 +242,7 @@ impl From<&Keypress> for String {
|
||||
|
||||
impl From<Keypress> for String {
|
||||
fn from(value: Keypress) -> Self {
|
||||
String::from(&value)
|
||||
Self::from(&value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-14
@@ -1,17 +1,14 @@
|
||||
#![warn(clippy::pedantic, clippy::nursery)]
|
||||
#![allow(clippy::cast_possible_truncation)]
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
#![feature(get_disjoint_mut_helpers)]
|
||||
#![feature(exact_bitshifts)]
|
||||
#![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;
|
||||
@@ -59,16 +56,6 @@ 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();
|
||||
|
||||
Reference in New Issue
Block a user