release/0.4.2 #11
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -853,7 +853,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mould"
|
name = "mould"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ mould config.template.json -o config.json
|
|||||||
- `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
|
||||||
|
- `u`: Undo the last change
|
||||||
- `a`: Add missing value from template to active config
|
- `a`: Add missing value from template to active config
|
||||||
- `/`: Search for configuration keys (Jump to matches)
|
- `/`: Search for configuration keys (Jump to matches)
|
||||||
- `n`: Jump to the next search match
|
- `n`: Jump to the next search match
|
||||||
|
|||||||
@@ -44,3 +44,4 @@ jump_bottom = "G"
|
|||||||
append_item = "o"
|
append_item = "o"
|
||||||
prepend_item = "O"
|
prepend_item = "O"
|
||||||
delete_item = "dd"
|
delete_item = "dd"
|
||||||
|
undo = "u"
|
||||||
|
|||||||
29
src/app.rs
29
src/app.rs
@@ -33,6 +33,8 @@ pub struct App {
|
|||||||
pub input: Input,
|
pub input: Input,
|
||||||
/// The current search query for filtering keys.
|
/// The current search query for filtering keys.
|
||||||
pub search_query: String,
|
pub search_query: String,
|
||||||
|
/// Stack of previous variable states for undo functionality.
|
||||||
|
pub undo_stack: Vec<Vec<ConfigItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
@@ -47,6 +49,7 @@ impl App {
|
|||||||
status_message: None,
|
status_message: None,
|
||||||
input: Input::new(initial_input),
|
input: Input::new(initial_input),
|
||||||
search_query: String::new(),
|
search_query: String::new(),
|
||||||
|
undo_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +162,8 @@ impl App {
|
|||||||
pub fn enter_insert(&mut self, variant: InsertVariant) {
|
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.save_undo_state();
|
||||||
self.mode = Mode::Insert;
|
self.mode = Mode::Insert;
|
||||||
self.status_message = None;
|
|
||||||
match variant {
|
match variant {
|
||||||
InsertVariant::Start => {
|
InsertVariant::Start => {
|
||||||
use tui_input::InputRequest;
|
use tui_input::InputRequest;
|
||||||
@@ -190,6 +193,7 @@ impl App {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.save_undo_state();
|
||||||
let selected_path = self.vars[self.selected].path.clone();
|
let selected_path = self.vars[self.selected].path.clone();
|
||||||
let is_group = self.vars[self.selected].is_group;
|
let is_group = self.vars[self.selected].is_group;
|
||||||
|
|
||||||
@@ -252,6 +256,7 @@ impl App {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.save_undo_state();
|
||||||
let (base, idx, depth) = {
|
let (base, idx, depth) = {
|
||||||
let selected_item = &self.vars[self.selected];
|
let selected_item = &self.vars[self.selected];
|
||||||
if selected_item.is_group {
|
if selected_item.is_group {
|
||||||
@@ -322,6 +327,28 @@ impl App {
|
|||||||
.map(|v| v.status == crate::format::ItemStatus::MissingFromActive)
|
.map(|v| v.status == crate::format::ItemStatus::MissingFromActive)
|
||||||
.unwrap_or(false)
|
.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)> {
|
fn parse_index(path: &str) -> Option<(&str, usize)> {
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ pub struct KeybindsConfig {
|
|||||||
pub append_item: String,
|
pub append_item: String,
|
||||||
pub prepend_item: String,
|
pub prepend_item: String,
|
||||||
pub delete_item: String,
|
pub delete_item: String,
|
||||||
|
pub undo: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for KeybindsConfig {
|
impl Default for KeybindsConfig {
|
||||||
@@ -140,6 +141,7 @@ impl Default for KeybindsConfig {
|
|||||||
append_item: "o".to_string(),
|
append_item: "o".to_string(),
|
||||||
prepend_item: "O".to_string(),
|
prepend_item: "O".to_string(),
|
||||||
delete_item: "dd".to_string(),
|
delete_item: "dd".to_string(),
|
||||||
|
undo: "u".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ where
|
|||||||
(&self.config.keybinds.append_item, "append_item"),
|
(&self.config.keybinds.append_item, "append_item"),
|
||||||
(&self.config.keybinds.prepend_item, "prepend_item"),
|
(&self.config.keybinds.prepend_item, "prepend_item"),
|
||||||
(&self.config.keybinds.delete_item, "delete_item"),
|
(&self.config.keybinds.delete_item, "delete_item"),
|
||||||
|
(&self.config.keybinds.undo, "undo"),
|
||||||
(&"a".to_string(), "add_missing"),
|
(&"a".to_string(), "add_missing"),
|
||||||
(&":".to_string(), "command"),
|
(&":".to_string(), "command"),
|
||||||
(&"q".to_string(), "quit"),
|
(&"q".to_string(), "quit"),
|
||||||
@@ -164,7 +165,11 @@ where
|
|||||||
"append_item" => self.app.add_array_item(true),
|
"append_item" => self.app.add_array_item(true),
|
||||||
"prepend_item" => self.app.add_array_item(false),
|
"prepend_item" => self.app.add_array_item(false),
|
||||||
"delete_item" => self.app.delete_selected(),
|
"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" => {
|
"command" => {
|
||||||
self.command_buffer.push(':');
|
self.command_buffer.push(':');
|
||||||
self.sync_command_status();
|
self.sync_command_status();
|
||||||
|
|||||||
20
src/ui.rs
20
src/ui.rs
@@ -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 {
|
let status_msg = if let Some(msg) = &app.status_message {
|
||||||
msg.clone()
|
msg.clone()
|
||||||
} else {
|
} else {
|
||||||
|
let kb = &config.keybinds;
|
||||||
match app.mode {
|
match app.mode {
|
||||||
Mode::Normal => {
|
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() {
|
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() {
|
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() {
|
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(format!("{} del", kb.delete_item));
|
||||||
parts.push(":w save");
|
parts.push(format!("{} undo", kb.undo));
|
||||||
parts.push(":q quit");
|
parts.push(format!("{} save", kb.save));
|
||||||
|
parts.push(format!("{} quit", kb.quit));
|
||||||
parts.join(" · ")
|
parts.join(" · ")
|
||||||
}
|
}
|
||||||
Mode::Insert => "Esc normal · Enter commit".to_string(),
|
Mode::Insert => "Esc normal · Enter commit".to_string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user