From f123f2d6df481d2c7cff76430d6b4b7deaec1a63 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Tue, 17 Mar 2026 13:12:22 +0100 Subject: [PATCH] added dd remove --- README.md | 1 + config.example.toml | 1 + src/app.rs | 82 +++++++++++++++++++++++++++++++++++++++++++-- src/config.rs | 2 ++ src/runner.rs | 2 ++ 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 81bf6cb..6677834 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ mould config.template.json -o config.json - `i`: Edit the value of the currently selected key (Enter Insert Mode) - `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 - `a`: Add missing value from template to active config - `/`: Search for configuration keys (Jump to matches) - `n`: Jump to the next search match diff --git a/config.example.toml b/config.example.toml index 94c1d5e..2b70a63 100644 --- a/config.example.toml +++ b/config.example.toml @@ -41,3 +41,4 @@ jump_top = "gg" jump_bottom = "G" append_item = "o" prepend_item = "O" +delete_item = "dd" diff --git a/src/app.rs b/src/app.rs index 3ef8b4d..c8e7195 100644 --- a/src/app.rs +++ b/src/app.rs @@ -165,6 +165,64 @@ impl App { self.mode = Mode::Normal; } + /// Deletes the currently selected item. If it's a group, deletes all children. + pub fn delete_selected(&mut self) { + if self.vars.is_empty() { + return; + } + + let selected_path = self.vars[self.selected].path.clone(); + let is_group = self.vars[self.selected].is_group; + + // Identify if the item being removed is an array item + let array_info = parse_index(&selected_path); + + // 1. Identify all items to remove + let mut to_remove = Vec::new(); + to_remove.push(self.selected); + + if is_group { + let prefix = format!("{}.", selected_path); + for (i, var) in self.vars.iter().enumerate() { + if var.path.starts_with(&prefix) { + to_remove.push(i); + } + } + } + + // 2. Perform removal (reverse order to preserve indices) + to_remove.sort_unstable_by(|a, b| b.cmp(a)); + for i in to_remove { + self.vars.remove(i); + } + + // 3. Re-index subsequent array items if applicable + if let Some((base, removed_idx)) = array_info { + let base = base.to_string(); + for var in self.vars.iter_mut() { + if var.path.starts_with(&base) { + // We need to find the index segment that matches this array + if let Some((b, i, suffix)) = find_array_segment(&var.path, &base) { + if b == base && i > removed_idx { + let new_idx = i - 1; + var.path = format!("{}[{}]{}", base, new_idx, suffix); + // Also update key if it matches the old index exactly + if var.key == format!("[{}]", i) { + var.key = format!("[{}]", new_idx); + } + } + } + } + } + } + + // 4. Adjust selection + if self.selected >= self.vars.len() && !self.vars.is_empty() { + self.selected = self.vars.len() - 1; + } + self.sync_input_with_selected(); + } + /// Adds a new item to an array if the selected item is part of one. pub fn add_array_item(&mut self, after: bool) { if self.vars.is_empty() { @@ -227,12 +285,30 @@ impl App { } fn parse_index(path: &str) -> Option<(&str, usize)> { - if path.ends_with(']') { - if let Some(start) = path.rfind('[') { - if let Ok(idx) = path[start + 1..path.len() - 1].parse::() { + if let Some(end) = path.rfind(']') { + let segment = &path[..=end]; + if let Some(start) = segment.rfind('[') { + if let Ok(idx) = segment[start + 1..end].parse::() { + // Return the base and index return Some((&path[..start], idx)); } } } None } + +/// Helper to find an array segment in a path given a base prefix. +fn find_array_segment<'a>(path: &'a str, base: &str) -> Option<(&'a str, usize, &'a str)> { + if !path.starts_with(base) { + return None; + } + let remaining = &path[base.len()..]; + if remaining.starts_with('[') { + if let Some(end) = remaining.find(']') { + if let Ok(idx) = remaining[1..end].parse::() { + return Some((&path[..base.len()], idx, &remaining[end + 1..])); + } + } + } + None +} diff --git a/src/config.rs b/src/config.rs index 963bbfd..ab3084d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -116,6 +116,7 @@ pub struct KeybindsConfig { pub jump_bottom: String, pub append_item: String, pub prepend_item: String, + pub delete_item: String, } impl Default for KeybindsConfig { @@ -134,6 +135,7 @@ impl Default for KeybindsConfig { jump_bottom: "G".to_string(), append_item: "o".to_string(), prepend_item: "O".to_string(), + delete_item: "dd".to_string(), } } } diff --git a/src/runner.rs b/src/runner.rs index 332130a..ec738d1 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -124,6 +124,7 @@ where (&self.config.keybinds.jump_bottom, "jump_bottom"), (&self.config.keybinds.append_item, "append_item"), (&self.config.keybinds.prepend_item, "prepend_item"), + (&self.config.keybinds.delete_item, "delete_item"), (&"a".to_string(), "add_missing"), (&":".to_string(), "command"), (&"q".to_string(), "quit"), @@ -158,6 +159,7 @@ where "jump_bottom" => self.app.jump_bottom(), "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(), "command" => { self.command_buffer.push(':');