release/0.4.2 #11
@@ -63,6 +63,7 @@ mould config.template.json -o config.json
|
|||||||
- `i`: Edit the value of the currently selected key (Enter Insert Mode)
|
- `i`: Edit the value of the currently selected key (Enter Insert Mode)
|
||||||
- `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
|
||||||
- `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
|
||||||
|
|||||||
@@ -41,3 +41,4 @@ jump_top = "gg"
|
|||||||
jump_bottom = "G"
|
jump_bottom = "G"
|
||||||
append_item = "o"
|
append_item = "o"
|
||||||
prepend_item = "O"
|
prepend_item = "O"
|
||||||
|
delete_item = "dd"
|
||||||
|
|||||||
82
src/app.rs
82
src/app.rs
@@ -165,6 +165,64 @@ impl App {
|
|||||||
self.mode = Mode::Normal;
|
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.
|
/// Adds a new item to an array if the selected item is part of one.
|
||||||
pub fn add_array_item(&mut self, after: bool) {
|
pub fn add_array_item(&mut self, after: bool) {
|
||||||
if self.vars.is_empty() {
|
if self.vars.is_empty() {
|
||||||
@@ -227,12 +285,30 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_index(path: &str) -> Option<(&str, usize)> {
|
fn parse_index(path: &str) -> Option<(&str, usize)> {
|
||||||
if path.ends_with(']') {
|
if let Some(end) = path.rfind(']') {
|
||||||
if let Some(start) = path.rfind('[') {
|
let segment = &path[..=end];
|
||||||
if let Ok(idx) = path[start + 1..path.len() - 1].parse::<usize>() {
|
if let Some(start) = segment.rfind('[') {
|
||||||
|
if let Ok(idx) = segment[start + 1..end].parse::<usize>() {
|
||||||
|
// Return the base and index
|
||||||
return Some((&path[..start], idx));
|
return Some((&path[..start], idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
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::<usize>() {
|
||||||
|
return Some((&path[..base.len()], idx, &remaining[end + 1..]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ pub struct KeybindsConfig {
|
|||||||
pub jump_bottom: String,
|
pub jump_bottom: String,
|
||||||
pub append_item: String,
|
pub append_item: String,
|
||||||
pub prepend_item: String,
|
pub prepend_item: String,
|
||||||
|
pub delete_item: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for KeybindsConfig {
|
impl Default for KeybindsConfig {
|
||||||
@@ -134,6 +135,7 @@ impl Default for KeybindsConfig {
|
|||||||
jump_bottom: "G".to_string(),
|
jump_bottom: "G".to_string(),
|
||||||
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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ where
|
|||||||
(&self.config.keybinds.jump_bottom, "jump_bottom"),
|
(&self.config.keybinds.jump_bottom, "jump_bottom"),
|
||||||
(&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"),
|
||||||
(&"a".to_string(), "add_missing"),
|
(&"a".to_string(), "add_missing"),
|
||||||
(&":".to_string(), "command"),
|
(&":".to_string(), "command"),
|
||||||
(&"q".to_string(), "quit"),
|
(&"q".to_string(), "quit"),
|
||||||
@@ -158,6 +159,7 @@ where
|
|||||||
"jump_bottom" => self.app.jump_bottom(),
|
"jump_bottom" => self.app.jump_bottom(),
|
||||||
"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(),
|
||||||
"add_missing" => self.add_missing_item(),
|
"add_missing" => self.add_missing_item(),
|
||||||
"command" => {
|
"command" => {
|
||||||
self.command_buffer.push(':');
|
self.command_buffer.push(':');
|
||||||
|
|||||||
Reference in New Issue
Block a user