Compare commits
4 Commits
50278821ca
...
v0.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| c773fdf472 | |||
| 029b4f9da8 | |||
| ee85be4e6b | |||
| 31dc062ce5 |
60
src/app.rs
60
src/app.rs
@@ -242,8 +242,13 @@ impl App {
|
|||||||
|
|
||||||
/// Transitions the application into Insert Mode with a specific variant.
|
/// Transitions the application into Insert Mode with a specific variant.
|
||||||
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) {
|
||||||
&& !var.is_group {
|
if var.is_group {
|
||||||
|
self.enter_insert_key();
|
||||||
|
} else {
|
||||||
|
if !matches!(variant, InsertVariant::Substitute) {
|
||||||
|
self.sync_input_with_selected();
|
||||||
|
}
|
||||||
self.mode = Mode::Insert;
|
self.mode = Mode::Insert;
|
||||||
match variant {
|
match variant {
|
||||||
InsertVariant::Start => {
|
InsertVariant::Start => {
|
||||||
@@ -259,6 +264,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commits the current input and transitions the application into Normal Mode.
|
/// Commits the current input and transitions the application into Normal Mode.
|
||||||
@@ -337,9 +343,9 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new item relative to the selected item.
|
/// Adds a new item relative to the selected item.
|
||||||
pub fn add_item(&mut self, after: bool, is_group: bool) {
|
pub fn add_item(&mut self, after: bool, is_group: bool, as_child: bool) {
|
||||||
if self.vars.is_empty() {
|
if self.vars.is_empty() {
|
||||||
let new_key = "NEW_VAR".to_string();
|
let new_key = if is_group { "NEW_GROUP".to_string() } else { "NEW_VAR".to_string() };
|
||||||
self.vars.push(ConfigItem {
|
self.vars.push(ConfigItem {
|
||||||
key: new_key.clone(),
|
key: new_key.clone(),
|
||||||
path: vec![PathSegment::Key(new_key)],
|
path: vec![PathSegment::Key(new_key)],
|
||||||
@@ -370,8 +376,8 @@ impl App {
|
|||||||
let insert_pos;
|
let insert_pos;
|
||||||
let mut is_array_item = false;
|
let mut is_array_item = false;
|
||||||
|
|
||||||
if let Some(PathSegment::Index(idx)) = selected_item.path.last() {
|
if !as_child && let Some(PathSegment::Index(idx)) = selected_item.path.last() {
|
||||||
// ARRAY ITEM LOGIC
|
// ARRAY ITEM LOGIC (Adding sibling to an existing index)
|
||||||
is_array_item = true;
|
is_array_item = true;
|
||||||
let base_path = selected_item.path[..selected_item.path.len() - 1].to_vec();
|
let base_path = selected_item.path[..selected_item.path.len() - 1].to_vec();
|
||||||
let new_idx = if after { idx + 1 } else { *idx };
|
let new_idx = if after { idx + 1 } else { *idx };
|
||||||
@@ -392,11 +398,29 @@ impl App {
|
|||||||
new_path = base_path;
|
new_path = base_path;
|
||||||
new_path.push(PathSegment::Index(new_idx));
|
new_path.push(PathSegment::Index(new_idx));
|
||||||
new_depth = selected_item.depth;
|
new_depth = selected_item.depth;
|
||||||
} else if after && selected_item.is_group {
|
} else if as_child && selected_item.is_group {
|
||||||
// ADD AS CHILD OF GROUP
|
// ADD AS CHILD OF GROUP
|
||||||
insert_pos = self.selected + 1;
|
insert_pos = self.selected + 1;
|
||||||
new_path = selected_item.path.clone();
|
new_path = selected_item.path.clone();
|
||||||
new_depth = selected_item.depth + 1;
|
new_depth = selected_item.depth + 1;
|
||||||
|
|
||||||
|
// Check if this group already contains array items
|
||||||
|
if self.is_array_group(&selected_item.path) {
|
||||||
|
is_array_item = true;
|
||||||
|
let new_idx = 0; // Prepend to array
|
||||||
|
new_path.push(PathSegment::Index(new_idx));
|
||||||
|
|
||||||
|
// Shift existing children
|
||||||
|
for var in self.vars.iter_mut() {
|
||||||
|
if var.path.starts_with(&selected_item.path) && var.path.len() > selected_item.path.len()
|
||||||
|
&& let PathSegment::Index(i) = var.path[selected_item.path.len()] {
|
||||||
|
var.path[selected_item.path.len()] = PathSegment::Index(i + 1);
|
||||||
|
if var.path.len() == selected_item.path.len() + 1 {
|
||||||
|
var.key = format!("[{}]", i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// ADD AS SIBLING
|
// ADD AS SIBLING
|
||||||
let parent_path = if selected_item.path.len() > 1 {
|
let parent_path = if selected_item.path.len() > 1 {
|
||||||
@@ -417,6 +441,17 @@ impl App {
|
|||||||
|
|
||||||
new_path = parent_path;
|
new_path = parent_path;
|
||||||
new_depth = selected_item.depth;
|
new_depth = selected_item.depth;
|
||||||
|
|
||||||
|
// If the parent is an array group, this is also an array item
|
||||||
|
if !new_path.is_empty() && self.is_array_group(&new_path) {
|
||||||
|
is_array_item = true;
|
||||||
|
if let Some(PathSegment::Index(idx)) = selected_item.path.last() {
|
||||||
|
let new_idx = if after { idx + 1 } else { *idx };
|
||||||
|
new_path.push(PathSegment::Index(new_idx));
|
||||||
|
} else {
|
||||||
|
new_path.push(PathSegment::Index(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Generate a unique key for non-array items
|
// 2. Generate a unique key for non-array items
|
||||||
@@ -458,9 +493,10 @@ 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.save_undo_state();
|
||||||
|
|
||||||
if is_array_item {
|
if is_array_item {
|
||||||
self.sync_input_with_selected();
|
self.sync_input_with_selected();
|
||||||
self.save_undo_state();
|
|
||||||
self.enter_insert(InsertVariant::Start);
|
self.enter_insert(InsertVariant::Start);
|
||||||
} else {
|
} else {
|
||||||
self.enter_insert_key();
|
self.enter_insert_key();
|
||||||
@@ -495,6 +531,14 @@ impl App {
|
|||||||
self.vars.get(self.selected).map(|v| v.is_group).unwrap_or(false)
|
self.vars.get(self.selected).map(|v| v.is_group).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_array_group(&self, group_path: &[PathSegment]) -> bool {
|
||||||
|
self.vars.iter().any(|v|
|
||||||
|
v.path.starts_with(group_path)
|
||||||
|
&& v.path.len() == group_path.len() + 1
|
||||||
|
&& matches!(v.path.last(), Some(PathSegment::Index(_)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn selected_is_array(&self) -> bool {
|
pub fn selected_is_array(&self) -> bool {
|
||||||
self.vars.get(self.selected)
|
self.vars.get(self.selected)
|
||||||
.map(|v| !v.is_group && matches!(v.path.last(), Some(PathSegment::Index(_))))
|
.map(|v| !v.is_group && matches!(v.path.last(), Some(PathSegment::Index(_))))
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ where
|
|||||||
if !key_str.is_empty() {
|
if !key_str.is_empty() {
|
||||||
self.key_sequence.push_str(&key_str);
|
self.key_sequence.push_str(&key_str);
|
||||||
|
|
||||||
|
let mut exact_match = None;
|
||||||
|
let mut prefix_match = false;
|
||||||
|
|
||||||
// Collect all configured keybinds
|
// Collect all configured keybinds
|
||||||
let binds = [
|
let binds = [
|
||||||
(&self.config.keybinds.down, "down"),
|
(&self.config.keybinds.down, "down"),
|
||||||
@@ -131,6 +134,7 @@ where
|
|||||||
(&self.config.keybinds.edit, "edit"),
|
(&self.config.keybinds.edit, "edit"),
|
||||||
(&self.config.keybinds.edit_append, "edit_append"),
|
(&self.config.keybinds.edit_append, "edit_append"),
|
||||||
(&self.config.keybinds.edit_substitute, "edit_substitute"),
|
(&self.config.keybinds.edit_substitute, "edit_substitute"),
|
||||||
|
(&"S".to_string(), "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"),
|
||||||
@@ -150,9 +154,6 @@ where
|
|||||||
(&"q".to_string(), "quit"),
|
(&"q".to_string(), "quit"),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut exact_match = None;
|
|
||||||
let mut prefix_match = false;
|
|
||||||
|
|
||||||
for (bind, action) in binds.iter() {
|
for (bind, action) in binds.iter() {
|
||||||
if bind == &&self.key_sequence {
|
if bind == &&self.key_sequence {
|
||||||
exact_match = Some(*action);
|
exact_match = Some(*action);
|
||||||
@@ -162,6 +163,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if exact_match.is_none() && !prefix_match {
|
||||||
|
// Not a match and not a prefix, restart with current key
|
||||||
|
self.key_sequence.clear();
|
||||||
|
self.key_sequence.push_str(&key_str);
|
||||||
|
|
||||||
|
for (bind, action) in binds.iter() {
|
||||||
|
if bind == &&self.key_sequence {
|
||||||
|
exact_match = Some(*action);
|
||||||
|
break;
|
||||||
|
} else if bind.starts_with(&self.key_sequence) {
|
||||||
|
prefix_match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(action) = exact_match {
|
if let Some(action) = exact_match {
|
||||||
self.key_sequence.clear();
|
self.key_sequence.clear();
|
||||||
match action {
|
match action {
|
||||||
@@ -179,14 +195,14 @@ where
|
|||||||
"previous_match" => self.app.jump_previous_match(),
|
"previous_match" => self.app.jump_previous_match(),
|
||||||
"jump_top" => self.app.jump_top(),
|
"jump_top" => self.app.jump_top(),
|
||||||
"jump_bottom" => self.app.jump_bottom(),
|
"jump_bottom" => self.app.jump_bottom(),
|
||||||
"append_item" => self.app.add_item(true, false),
|
"append_item" => self.app.add_item(true, false, false),
|
||||||
"prepend_item" => self.app.add_item(false, false),
|
"prepend_item" => self.app.add_item(false, false, false),
|
||||||
"delete_item" => self.app.delete_selected(),
|
"delete_item" => self.app.delete_selected(),
|
||||||
"undo" => self.app.undo(),
|
"undo" => self.app.undo(),
|
||||||
"redo" => self.app.redo(),
|
"redo" => self.app.redo(),
|
||||||
"rename" => self.app.enter_insert_key(),
|
"rename" => self.app.enter_insert_key(),
|
||||||
"append_group" => self.app.add_item(true, true),
|
"append_group" => self.app.add_item(true, true, true),
|
||||||
"prepend_group" => self.app.add_item(false, true),
|
"prepend_group" => self.app.add_item(false, true, true),
|
||||||
"toggle_group" => {
|
"toggle_group" => {
|
||||||
self.app.toggle_group_selected();
|
self.app.toggle_group_selected();
|
||||||
self.app.save_undo_state();
|
self.app.save_undo_state();
|
||||||
@@ -203,7 +219,6 @@ where
|
|||||||
}
|
}
|
||||||
} else if !prefix_match {
|
} else if !prefix_match {
|
||||||
self.key_sequence.clear();
|
self.key_sequence.clear();
|
||||||
self.key_sequence.push_str(&key_str);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Non-character keys reset the sequence buffer
|
// Non-character keys reset the sequence buffer
|
||||||
|
|||||||
44
src/ui.rs
44
src/ui.rs
@@ -103,24 +103,30 @@ pub fn draw(f: &mut Frame, app: &mut App, config: &Config) {
|
|||||||
Span::styled(&var.key, key_style),
|
Span::styled(&var.key, key_style),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add status indicator if not present (only for leaf variables)
|
// Add status indicator if not present
|
||||||
if !var.is_group {
|
match var.status {
|
||||||
match var.status {
|
crate::format::ItemStatus::MissingFromActive if !var.is_group => {
|
||||||
crate::format::ItemStatus::MissingFromActive => {
|
let missing_style = if is_selected {
|
||||||
let missing_style = if is_selected {
|
Style::default().fg(theme.fg_highlight()).add_modifier(Modifier::BOLD)
|
||||||
Style::default().fg(theme.fg_highlight()).add_modifier(Modifier::BOLD)
|
} else {
|
||||||
} else {
|
Style::default().fg(theme.fg_warning()).add_modifier(Modifier::BOLD)
|
||||||
Style::default().fg(theme.fg_warning()).add_modifier(Modifier::BOLD)
|
};
|
||||||
};
|
key_spans.push(Span::styled(" (missing)", missing_style));
|
||||||
key_spans.push(Span::styled(" (missing)", missing_style));
|
|
||||||
}
|
|
||||||
crate::format::ItemStatus::Modified => {
|
|
||||||
if !is_selected {
|
|
||||||
key_spans.push(Span::styled(" (*)", Style::default().fg(theme.fg_modified())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
crate::format::ItemStatus::MissingFromActive if var.is_group => {
|
||||||
|
let missing_style = if is_selected {
|
||||||
|
Style::default().fg(theme.fg_highlight()).add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(theme.fg_warning()).add_modifier(Modifier::BOLD)
|
||||||
|
};
|
||||||
|
key_spans.push(Span::styled(" (missing group)", missing_style));
|
||||||
|
}
|
||||||
|
crate::format::ItemStatus::Modified => {
|
||||||
|
if !is_selected {
|
||||||
|
key_spans.push(Span::styled(" (*)", Style::default().fg(theme.fg_modified())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_style = if is_selected {
|
let item_style = if is_selected {
|
||||||
@@ -210,7 +216,9 @@ pub fn draw(f: &mut Frame, app: &mut App, config: &Config) {
|
|||||||
|
|
||||||
// Show template value in normal mode if it differs
|
// Show template value in normal mode if it differs
|
||||||
let display_text = if let Some(var) = current_var {
|
let display_text = if let Some(var) = current_var {
|
||||||
if var.is_group {
|
if matches!(app.mode, Mode::InsertKey) {
|
||||||
|
input_text.to_string()
|
||||||
|
} else if var.is_group {
|
||||||
"<group>".to_string()
|
"<group>".to_string()
|
||||||
} else if matches!(app.mode, Mode::Normal) {
|
} else if matches!(app.mode, Mode::Normal) {
|
||||||
format!("{}{}", input_text, extra_info)
|
format!("{}{}", input_text, extra_info)
|
||||||
|
|||||||
Reference in New Issue
Block a user