release/0.4.2 #11
@@ -30,4 +30,4 @@ tempfile = "3.27.0"
|
|||||||
authors = ["Nils Pukropp <nils@narl.io>"]
|
authors = ["Nils Pukropp <nils@narl.io>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
name = "mould"
|
name = "mould"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ mould config.template.json -o config.json
|
|||||||
- `k` / `Up`: Move selection up
|
- `k` / `Up`: Move selection up
|
||||||
- `gg`: Jump to the top
|
- `gg`: Jump to the top
|
||||||
- `G`: Jump to the bottom
|
- `G`: Jump to the bottom
|
||||||
- `i`: Edit the value of the currently selected key (Enter Insert Mode)
|
- `i`: Edit value (cursor at start)
|
||||||
|
- `A`: Edit value (cursor at end)
|
||||||
|
- `S`: Substitute value (clear and edit)
|
||||||
- `o`: Append a new item to the current array
|
- `o`: Append a new item to the current array
|
||||||
- `O`: Prepend a new item to the current array
|
- `O`: Prepend a new item to the current array
|
||||||
- `dd`: Delete the currently selected variable or group
|
- `dd`: Delete the currently selected variable or group
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ tree_depth_4 = "#fab387"
|
|||||||
down = "j"
|
down = "j"
|
||||||
up = "k"
|
up = "k"
|
||||||
edit = "i"
|
edit = "i"
|
||||||
|
edit_append = "A"
|
||||||
|
edit_substitute = "S"
|
||||||
save = ":w"
|
save = ":w"
|
||||||
quit = ":q"
|
quit = ":q"
|
||||||
normal_mode = "Esc"
|
normal_mode = "Esc"
|
||||||
|
|||||||
50
src/app.rs
50
src/app.rs
@@ -11,6 +11,12 @@ pub enum Mode {
|
|||||||
Search,
|
Search,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum InsertVariant {
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
Substitute,
|
||||||
|
}
|
||||||
|
|
||||||
/// The core application state, holding all configuration variables and UI status.
|
/// The core application state, holding all configuration variables and UI status.
|
||||||
pub struct App {
|
pub struct App {
|
||||||
/// The list of configuration variables being edited.
|
/// The list of configuration variables being edited.
|
||||||
@@ -149,12 +155,25 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transitions the application into Insert Mode.
|
/// Transitions the application into Insert Mode with a specific variant.
|
||||||
pub fn enter_insert(&mut self) {
|
pub fn enter_insert(&mut self, variant: InsertVariant) {
|
||||||
if let Some(var) = self.vars.get(self.selected) {
|
if let Some(var) = self.vars.get(self.selected) {
|
||||||
if !var.is_group {
|
if !var.is_group {
|
||||||
self.mode = Mode::Insert;
|
self.mode = Mode::Insert;
|
||||||
self.status_message = None;
|
self.status_message = None;
|
||||||
|
match variant {
|
||||||
|
InsertVariant::Start => {
|
||||||
|
use tui_input::InputRequest;
|
||||||
|
self.input.handle(InputRequest::GoToStart);
|
||||||
|
}
|
||||||
|
InsertVariant::End => {
|
||||||
|
use tui_input::InputRequest;
|
||||||
|
self.input.handle(InputRequest::GoToEnd);
|
||||||
|
}
|
||||||
|
InsertVariant::Substitute => {
|
||||||
|
self.input = Input::new(String::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,9 +201,13 @@ impl App {
|
|||||||
to_remove.push(self.selected);
|
to_remove.push(self.selected);
|
||||||
|
|
||||||
if is_group {
|
if is_group {
|
||||||
let prefix = format!("{}.", selected_path);
|
let prefix_dot = format!("{}.", selected_path);
|
||||||
|
let prefix_bracket = format!("{}[", selected_path);
|
||||||
for (i, var) in self.vars.iter().enumerate() {
|
for (i, var) in self.vars.iter().enumerate() {
|
||||||
if var.path.starts_with(&prefix) {
|
if i == self.selected {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if var.path.starts_with(&prefix_dot) || var.path.starts_with(&prefix_bracket) {
|
||||||
to_remove.push(i);
|
to_remove.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,9 +302,26 @@ impl App {
|
|||||||
self.vars.insert(insert_pos, new_item);
|
self.vars.insert(insert_pos, new_item);
|
||||||
self.selected = insert_pos;
|
self.selected = insert_pos;
|
||||||
self.sync_input_with_selected();
|
self.sync_input_with_selected();
|
||||||
self.mode = Mode::Insert;
|
self.enter_insert(InsertVariant::Start);
|
||||||
self.status_message = None;
|
self.status_message = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Status bar helpers
|
||||||
|
pub fn selected_is_group(&self) -> bool {
|
||||||
|
self.vars.get(self.selected).map(|v| v.is_group).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_is_array(&self) -> bool {
|
||||||
|
self.vars.get(self.selected)
|
||||||
|
.map(|v| !v.is_group && v.path.contains('['))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_is_missing(&self) -> bool {
|
||||||
|
self.vars.get(self.selected)
|
||||||
|
.map(|v| v.status == crate::format::ItemStatus::MissingFromActive)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_index(path: &str) -> Option<(&str, usize)> {
|
fn parse_index(path: &str) -> Option<(&str, usize)> {
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ pub struct KeybindsConfig {
|
|||||||
pub down: String,
|
pub down: String,
|
||||||
pub up: String,
|
pub up: String,
|
||||||
pub edit: String,
|
pub edit: String,
|
||||||
|
pub edit_append: String,
|
||||||
|
pub edit_substitute: String,
|
||||||
pub save: String,
|
pub save: String,
|
||||||
pub quit: String,
|
pub quit: String,
|
||||||
pub normal_mode: String,
|
pub normal_mode: String,
|
||||||
@@ -125,6 +127,8 @@ impl Default for KeybindsConfig {
|
|||||||
down: "j".to_string(),
|
down: "j".to_string(),
|
||||||
up: "k".to_string(),
|
up: "k".to_string(),
|
||||||
edit: "i".to_string(),
|
edit: "i".to_string(),
|
||||||
|
edit_append: "A".to_string(),
|
||||||
|
edit_substitute: "S".to_string(),
|
||||||
save: ":w".to_string(),
|
save: ":w".to_string(),
|
||||||
quit: ":q".to_string(),
|
quit: ":q".to_string(),
|
||||||
normal_mode: "Esc".to_string(),
|
normal_mode: "Esc".to_string(),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::app::{App, Mode};
|
use crate::app::{App, InsertVariant, Mode};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::format::FormatHandler;
|
use crate::format::FormatHandler;
|
||||||
use crossterm::event::{self, Event, KeyCode, KeyEvent};
|
use crossterm::event::{self, Event, KeyCode, KeyEvent};
|
||||||
@@ -117,6 +117,8 @@ where
|
|||||||
(&self.config.keybinds.down, "down"),
|
(&self.config.keybinds.down, "down"),
|
||||||
(&self.config.keybinds.up, "up"),
|
(&self.config.keybinds.up, "up"),
|
||||||
(&self.config.keybinds.edit, "edit"),
|
(&self.config.keybinds.edit, "edit"),
|
||||||
|
(&self.config.keybinds.edit_append, "edit_append"),
|
||||||
|
(&self.config.keybinds.edit_substitute, "edit_substitute"),
|
||||||
(&self.config.keybinds.search, "search"),
|
(&self.config.keybinds.search, "search"),
|
||||||
(&self.config.keybinds.next_match, "next_match"),
|
(&self.config.keybinds.next_match, "next_match"),
|
||||||
(&self.config.keybinds.previous_match, "previous_match"),
|
(&self.config.keybinds.previous_match, "previous_match"),
|
||||||
@@ -147,7 +149,9 @@ where
|
|||||||
match action {
|
match action {
|
||||||
"down" => self.app.next(),
|
"down" => self.app.next(),
|
||||||
"up" => self.app.previous(),
|
"up" => self.app.previous(),
|
||||||
"edit" => self.app.enter_insert(),
|
"edit" => self.app.enter_insert(InsertVariant::Start),
|
||||||
|
"edit_append" => self.app.enter_insert(InsertVariant::End),
|
||||||
|
"edit_substitute" => self.app.enter_insert(InsertVariant::Substitute),
|
||||||
"search" => {
|
"search" => {
|
||||||
self.app.mode = Mode::Search;
|
self.app.mode = Mode::Search;
|
||||||
self.app.search_query.clear();
|
self.app.search_query.clear();
|
||||||
|
|||||||
32
src/ui.rs
32
src/ui.rs
@@ -269,14 +269,30 @@ pub fn draw(f: &mut Frame, app: &mut App, config: &Config) {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_msg = app
|
let status_msg = if let Some(msg) = &app.status_message {
|
||||||
.status_message
|
msg.clone()
|
||||||
.as_deref()
|
} else {
|
||||||
.unwrap_or_else(|| match app.mode {
|
match app.mode {
|
||||||
Mode::Normal => " navigation | i: edit | /: search | :w: save | :q: quit ",
|
Mode::Normal => {
|
||||||
Mode::Insert => " Esc: back to normal | Enter: commit ",
|
let mut parts = vec!["j/k move", "gg/G jump", "/ search"];
|
||||||
Mode::Search => " Esc: back to normal | type to filter ",
|
if !app.selected_is_group() {
|
||||||
});
|
parts.push("i/A/S edit");
|
||||||
|
}
|
||||||
|
if app.selected_is_missing() {
|
||||||
|
parts.push("a add");
|
||||||
|
}
|
||||||
|
if app.selected_is_array() {
|
||||||
|
parts.push("o/O array");
|
||||||
|
}
|
||||||
|
parts.push("dd del");
|
||||||
|
parts.push(":w save");
|
||||||
|
parts.push(":q quit");
|
||||||
|
parts.join(" · ")
|
||||||
|
}
|
||||||
|
Mode::Insert => "Esc normal · Enter commit".to_string(),
|
||||||
|
Mode::Search => "Esc normal · type to filter".to_string(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let status_line = Line::from(vec![
|
let status_line = Line::from(vec![
|
||||||
Span::styled(mode_str, mode_style),
|
Span::styled(mode_str, mode_style),
|
||||||
|
|||||||
Reference in New Issue
Block a user