You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xplr/src/config.rs

493 lines
12 KiB
Rust

use crate::input::Key;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tui::layout::Constraint as TUIConstraint;
use tui::style::Color;
use tui::style::Modifier;
use tui::style::Style;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Mode {
Explore,
ExploreSubmode(String),
Select,
SelectSubmode(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Format {
Line,
Pretty,
Yaml,
YamlPretty,
Template(String),
}
impl Default for Format {
fn default() -> Self {
Self::Line
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GlobalAction {
// Common actions
ToggleShowHidden,
Back,
Enter,
FocusPrevious,
FocusNext,
FocusFirst,
FocusLast,
FocusPath(String),
FocusPathByIndex(usize),
FocusPathByBufferRelativeIndex(usize),
FocusPathByFocusRelativeIndex(isize),
ChangeDirectory(String),
// Quit options
PrintPwdAndQuit,
PrintAppStateAndQuit,
Quit,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ExploreModeAction {
// Common actions
ToggleShowHidden,
Back,
Enter,
FocusPrevious,
FocusNext,
FocusFirst,
FocusLast,
FocusPathByIndex(usize),
FocusPathByBufferRelativeIndex(usize),
FocusPathByFocusRelativeIndex(isize),
FocusPath(String),
ChangeDirectory(String),
// Explore mode exclusive options
EnterSubmode(String),
ExitSubmode,
Select,
// Unselect,
// SelectAll,
// SelectAllRecursive,
// Quit options
PrintFocusedAndQuit,
PrintPwdAndQuit,
PrintAppStateAndQuit,
Quit,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SelectModeAction {
// Common actions
ToggleShowHidden,
Back,
Enter,
FocusPrevious,
FocusNext,
FocusFirst,
FocusLast,
FocusPathByIndex(usize),
FocusPathByBufferRelativeIndex(usize),
FocusPathByFocusRelativeIndex(isize),
FocusPath(String),
ChangeDirectory(String),
// Select mode exclusive options
EnterSubmode(String),
ExitSubmode,
// Select,
// Unselect,
// SelectAll,
// SelectAllRecursive,
// UnselectAll,
// UnSelectAllRecursive,
ToggleSelection,
// ClearSelectedPaths,
// Quit options
PrintSelectedAndQuit,
PrintAppStateAndQuit,
Quit,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
Global(GlobalAction),
ExploreMode(ExploreModeAction),
SelectMode(SelectModeAction),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GlobalActionMenu {
#[serde(default)]
pub help: String,
pub actions: Vec<GlobalAction>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExploreModeActionMenu {
#[serde(default)]
pub help: String,
pub actions: Vec<ExploreModeAction>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SelectModeActionMenu {
#[serde(default)]
pub help: String,
pub actions: Vec<SelectModeAction>,
}
pub type ExploreSubmodeActionMenu = HashMap<Key, ExploreModeActionMenu>;
pub type SelectSubmodeActionMenu = HashMap<Key, SelectModeActionMenu>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyBindings {
pub global: HashMap<Key, GlobalActionMenu>,
#[serde(default)]
pub explore_mode: HashMap<Key, ExploreModeActionMenu>,
#[serde(default)]
pub explore_submodes: HashMap<String, ExploreSubmodeActionMenu>,
#[serde(default)]
pub select_mode: HashMap<Key, SelectModeActionMenu>,
#[serde(default)]
pub select_submodes: HashMap<String, SelectSubmodeActionMenu>,
}
impl Default for KeyBindings {
fn default() -> Self {
let mut global: HashMap<Key, GlobalActionMenu> = Default::default();
let mut explore_mode: HashMap<Key, ExploreModeActionMenu> = Default::default();
let explore_submodes: HashMap<String, ExploreSubmodeActionMenu> = Default::default();
let mut select_mode: HashMap<Key, SelectModeActionMenu> = Default::default();
let select_submodes: HashMap<String, SelectSubmodeActionMenu> = Default::default();
global.insert(
Key::CtrlC,
GlobalActionMenu {
help: "quit".into(),
actions: vec![GlobalAction::Quit],
},
);
global.insert(
Key::Q,
GlobalActionMenu {
help: "quit".into(),
actions: vec![GlobalAction::Quit],
},
);
global.insert(
Key::Escape,
GlobalActionMenu {
help: "quit".into(),
actions: vec![GlobalAction::Quit],
},
);
global.insert(
Key::Left,
GlobalActionMenu {
help: "left".into(),
actions: vec![GlobalAction::Back],
},
);
global.insert(
Key::Dot,
GlobalActionMenu {
help: "show/hide hidden files".into(),
actions: vec![GlobalAction::ToggleShowHidden],
},
);
global.insert(
Key::Right,
GlobalActionMenu {
help: "enter".into(),
actions: vec![GlobalAction::Enter],
},
);
global.insert(
Key::Up,
GlobalActionMenu {
help: "<prev".into(),
actions: vec![GlobalAction::FocusPrevious],
},
);
global.insert(
Key::Down,
GlobalActionMenu {
help: "next>".into(),
actions: vec![GlobalAction::FocusNext],
},
);
global.insert(
Key::G,
GlobalActionMenu {
help: "top".into(),
actions: vec![GlobalAction::FocusFirst],
},
);
global.insert(
Key::ShiftG,
GlobalActionMenu {
help: "bottom".into(),
actions: vec![GlobalAction::FocusLast],
},
);
global.insert(
Key::Tilda,
GlobalActionMenu {
help: "home".into(),
actions: vec![GlobalAction::ChangeDirectory("~".to_string())],
},
);
explore_mode.insert(
Key::Space,
ExploreModeActionMenu {
help: "select".into(),
actions: vec![ExploreModeAction::Select, ExploreModeAction::FocusNext],
},
);
select_mode.insert(
Key::Space,
SelectModeActionMenu {
help: "select/unselect".into(),
actions: vec![
SelectModeAction::ToggleSelection,
SelectModeAction::FocusNext,
],
},
);
Self {
global,
explore_mode,
explore_submodes,
select_mode,
select_submodes,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FileTypeConfig {
#[serde(default)]
pub icon: String,
#[serde(default)]
pub style: Style,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileTypesConfig {
#[serde(default)]
pub directory: FileTypeConfig,
#[serde(default)]
pub file: FileTypeConfig,
#[serde(default)]
pub symlink: FileTypeConfig,
#[serde(default)]
pub extension: HashMap<String, FileTypeConfig>,
#[serde(default)]
pub special: HashMap<String, FileTypeConfig>,
}
impl Default for FileTypesConfig {
fn default() -> Self {
FileTypesConfig {
directory: FileTypeConfig {
icon: "d".into(),
style: Style::default()
.add_modifier(Modifier::BOLD)
.fg(Color::Blue),
},
file: FileTypeConfig {
icon: "f".into(),
style: Default::default(),
},
symlink: FileTypeConfig {
icon: "s".into(),
style: Style::default()
.add_modifier(Modifier::ITALIC)
.fg(Color::Cyan),
},
extension: Default::default(),
special: Default::default(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UIConfig {
#[serde(default)]
pub prefix: String,
#[serde(default)]
pub suffix: String,
#[serde(default)]
pub style: Style,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UIElement {
#[serde(default)]
pub format: String,
#[serde(default)]
pub style: Style,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TableRowConfig {
#[serde(default)]
pub cols: Vec<UIElement>,
#[serde(default)]
pub style: Style,
#[serde(default)]
pub height: u16,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Constraint {
Percentage(u16),
Ratio(u32, u32),
Length(u16),
Max(u16),
Min(u16),
}
impl Default for Constraint {
fn default() -> Self {
Self::Min(1)
}
}
impl Into<TUIConstraint> for Constraint {
fn into(self) -> TUIConstraint {
match self {
Self::Length(n) => TUIConstraint::Length(n),
Self::Percentage(n) => TUIConstraint::Percentage(n),
Self::Ratio(x, y) => TUIConstraint::Ratio(x, y),
Self::Max(n) => TUIConstraint::Max(n),
Self::Min(n) => TUIConstraint::Min(n),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TableConfig {
#[serde(default)]
pub header: Option<TableRowConfig>,
#[serde(default)]
pub row: TableRowConfig,
#[serde(default)]
pub style: Style,
#[serde(default)]
pub tree: Option<(UIElement, UIElement, UIElement)>,
#[serde(default)]
pub col_spacing: u16,
#[serde(default)]
pub col_widths: Vec<Constraint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeneralConfig {
#[serde(default)]
pub show_hidden: bool,
#[serde(default)]
pub table: TableConfig,
#[serde(default)]
pub normal_ui: UIConfig,
#[serde(default)]
pub focused_ui: UIConfig,
#[serde(default)]
pub selected_ui: UIConfig,
}
impl Default for GeneralConfig {
fn default() -> Self {
Self {
show_hidden: false,
table: TableConfig {
header: None,
row: TableRowConfig {
cols: vec![UIElement {
format: "{{tree}}{{prefix}}{{relativePath}}{{#if isDir}}/{{/if}}{{suffix}}"
.into(),
style: Default::default(),
}],
style: Default::default(),
height: 1,
},
style: Default::default(),
tree: Some((
UIElement {
format: "├─".into(),
style: Default::default(),
},
UIElement {
format: "├─".into(),
style: Default::default(),
},
UIElement {
format: "└─".into(),
style: Default::default(),
},
)),
col_spacing: 1,
col_widths: vec![Constraint::Percentage(100)],
},
normal_ui: UIConfig {
prefix: " ".into(),
suffix: " ".into(),
style: Default::default(),
},
focused_ui: UIConfig {
prefix: "▸ [".into(),
suffix: "]".into(),
style: Style::default().add_modifier(Modifier::BOLD),
},
selected_ui: UIConfig {
prefix: " {".into(),
suffix: "}".into(),
style: Style::default().add_modifier(Modifier::BOLD),
},
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub general: GeneralConfig,
#[serde(default)]
pub filetypes: FileTypesConfig,
#[serde(default)]
pub key_bindings: KeyBindings,
}