release/0.4.2 #11

Merged
nvrl merged 5 commits from release/0.4.2 into main 2026-03-17 14:33:35 +01:00
7 changed files with 52 additions and 10 deletions
Showing only changes of commit 1d9342186a - Show all commits

2
Cargo.lock generated
View File

@@ -853,7 +853,7 @@ dependencies = [
[[package]]
name = "mould"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"anyhow",
"clap",

View File

@@ -64,6 +64,7 @@ mould config.template.json -o config.json
- `o`: Append a new item to the current array
- `O`: Prepend a new item to the current array
- `dd`: Delete the currently selected variable or group
- `u`: Undo the last change
- `a`: Add missing value from template to active config
- `/`: Search for configuration keys (Jump to matches)
- `n`: Jump to the next search match

View File

@@ -44,3 +44,4 @@ jump_bottom = "G"
append_item = "o"
prepend_item = "O"
delete_item = "dd"
undo = "u"

View File

@@ -33,6 +33,8 @@ pub struct App {
pub input: Input,
/// The current search query for filtering keys.
pub search_query: String,
/// Stack of previous variable states for undo functionality.
pub undo_stack: Vec<Vec<ConfigItem>>,
}
impl App {
@@ -47,6 +49,7 @@ impl App {
status_message: None,
input: Input::new(initial_input),
search_query: String::new(),
undo_stack: Vec::new(),
}
}
@@ -159,8 +162,8 @@ impl App {
pub fn enter_insert(&mut self, variant: InsertVariant) {
if let Some(var) = self.vars.get(self.selected) {
if !var.is_group {
self.save_undo_state();
self.mode = Mode::Insert;
self.status_message = None;
match variant {
InsertVariant::Start => {
use tui_input::InputRequest;
@@ -190,6 +193,7 @@ impl App {
return;
}
self.save_undo_state();
let selected_path = self.vars[self.selected].path.clone();
let is_group = self.vars[self.selected].is_group;
@@ -252,6 +256,7 @@ impl App {
return;
}
self.save_undo_state();
let (base, idx, depth) = {
let selected_item = &self.vars[self.selected];
if selected_item.is_group {
@@ -322,6 +327,28 @@ impl App {
.map(|v| v.status == crate::format::ItemStatus::MissingFromActive)
.unwrap_or(false)
}
/// Saves the current state of variables to the undo stack.
pub fn save_undo_state(&mut self) {
self.undo_stack.push(self.vars.clone());
if self.undo_stack.len() > 50 {
self.undo_stack.remove(0);
}
}
/// Reverts to the last saved state of variables.
pub fn undo(&mut self) {
if let Some(previous_vars) = self.undo_stack.pop() {
self.vars = previous_vars;
if self.selected >= self.vars.len() && !self.vars.is_empty() {
self.selected = self.vars.len() - 1;
}
self.sync_input_with_selected();
self.status_message = Some("Undo applied".to_string());
} else {
self.status_message = Some("Nothing to undo".to_string());
}
}
}
fn parse_index(path: &str) -> Option<(&str, usize)> {

View File

@@ -119,6 +119,7 @@ pub struct KeybindsConfig {
pub append_item: String,
pub prepend_item: String,
pub delete_item: String,
pub undo: String,
}
impl Default for KeybindsConfig {
@@ -140,6 +141,7 @@ impl Default for KeybindsConfig {
append_item: "o".to_string(),
prepend_item: "O".to_string(),
delete_item: "dd".to_string(),
undo: "u".to_string(),
}
}
}

View File

@@ -127,6 +127,7 @@ where
(&self.config.keybinds.append_item, "append_item"),
(&self.config.keybinds.prepend_item, "prepend_item"),
(&self.config.keybinds.delete_item, "delete_item"),
(&self.config.keybinds.undo, "undo"),
(&"a".to_string(), "add_missing"),
(&":".to_string(), "command"),
(&"q".to_string(), "quit"),
@@ -164,7 +165,11 @@ where
"append_item" => self.app.add_array_item(true),
"prepend_item" => self.app.add_array_item(false),
"delete_item" => self.app.delete_selected(),
"add_missing" => self.add_missing_item(),
"undo" => self.app.undo(),
"add_missing" => {
self.app.save_undo_state();
self.add_missing_item();
}
"command" => {
self.command_buffer.push(':');
self.sync_command_status();

View File

@@ -272,21 +272,27 @@ pub fn draw(f: &mut Frame, app: &mut App, config: &Config) {
let status_msg = if let Some(msg) = &app.status_message {
msg.clone()
} else {
let kb = &config.keybinds;
match app.mode {
Mode::Normal => {
let mut parts = vec!["j/k move", "gg/G jump", "/ search"];
let mut parts = vec![
format!("{}/{} move", kb.down, kb.up),
format!("{}/{} jump", kb.jump_top, kb.jump_bottom),
format!("{} search", kb.search),
];
if !app.selected_is_group() {
parts.push("i/A/S edit");
parts.push(format!("{}/{}/{} edit", kb.edit, kb.edit_append, kb.edit_substitute));
}
if app.selected_is_missing() {
parts.push("a add");
parts.push(format!("{} add", "a")); // 'a' is currently hardcoded in runner
}
if app.selected_is_array() {
parts.push("o/O array");
parts.push(format!("{}/{} array", kb.append_item, kb.prepend_item));
}
parts.push("dd del");
parts.push(":w save");
parts.push(":q quit");
parts.push(format!("{} del", kb.delete_item));
parts.push(format!("{} undo", kb.undo));
parts.push(format!("{} save", kb.save));
parts.push(format!("{} quit", kb.quit));
parts.join(" · ")
}
Mode::Insert => "Esc normal · Enter commit".to_string(),