updated insert modes + fixed status bar help
All checks were successful
Version Check / check-version (pull_request) Successful in 3s

This commit is contained in:
2026-03-17 14:29:51 +01:00
parent 01a7bd44b7
commit f6a84416e6
7 changed files with 85 additions and 17 deletions

View File

@@ -30,4 +30,4 @@ tempfile = "3.27.0"
authors = ["Nils Pukropp <nils@narl.io>"]
edition = "2024"
name = "mould"
version = "0.4.1"
version = "0.4.2"

View File

@@ -58,7 +58,9 @@ mould config.template.json -o config.json
- `k` / `Up`: Move selection up
- `gg`: Jump to the top
- `G`: Jump to the bottom
- `i`: Edit the value of the currently selected key (Enter Insert Mode)
- `i`: Edit value (cursor at start)
- `A`: Edit value (cursor at end)
- `S`: Substitute value (clear and edit)
- `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

View File

@@ -31,6 +31,8 @@ tree_depth_4 = "#fab387"
down = "j"
up = "k"
edit = "i"
edit_append = "A"
edit_substitute = "S"
save = ":w"
quit = ":q"
normal_mode = "Esc"

View File

@@ -11,6 +11,12 @@ pub enum Mode {
Search,
}
pub enum InsertVariant {
Start,
End,
Substitute,
}
/// The core application state, holding all configuration variables and UI status.
pub struct App {
/// The list of configuration variables being edited.
@@ -149,12 +155,25 @@ impl App {
}
}
/// Transitions the application into Insert Mode.
pub fn enter_insert(&mut self) {
/// Transitions the application into Insert Mode with a specific variant.
pub fn enter_insert(&mut self, variant: InsertVariant) {
if let Some(var) = self.vars.get(self.selected) {
if !var.is_group {
self.mode = Mode::Insert;
self.status_message = None;
match variant {
InsertVariant::Start => {
use tui_input::InputRequest;
self.input.handle(InputRequest::GoToStart);
}
InsertVariant::End => {
use tui_input::InputRequest;
self.input.handle(InputRequest::GoToEnd);
}
InsertVariant::Substitute => {
self.input = Input::new(String::new());
}
}
}
}
}
@@ -182,9 +201,13 @@ impl App {
to_remove.push(self.selected);
if is_group {
let prefix = format!("{}.", selected_path);
let prefix_dot = format!("{}.", selected_path);
let prefix_bracket = format!("{}[", selected_path);
for (i, var) in self.vars.iter().enumerate() {
if var.path.starts_with(&prefix) {
if i == self.selected {
continue;
}
if var.path.starts_with(&prefix_dot) || var.path.starts_with(&prefix_bracket) {
to_remove.push(i);
}
}
@@ -279,9 +302,26 @@ impl App {
self.vars.insert(insert_pos, new_item);
self.selected = insert_pos;
self.sync_input_with_selected();
self.mode = Mode::Insert;
self.enter_insert(InsertVariant::Start);
self.status_message = None;
}
/// Status bar helpers
pub fn selected_is_group(&self) -> bool {
self.vars.get(self.selected).map(|v| v.is_group).unwrap_or(false)
}
pub fn selected_is_array(&self) -> bool {
self.vars.get(self.selected)
.map(|v| !v.is_group && v.path.contains('['))
.unwrap_or(false)
}
pub fn selected_is_missing(&self) -> bool {
self.vars.get(self.selected)
.map(|v| v.status == crate::format::ItemStatus::MissingFromActive)
.unwrap_or(false)
}
}
fn parse_index(path: &str) -> Option<(&str, usize)> {

View File

@@ -106,6 +106,8 @@ pub struct KeybindsConfig {
pub down: String,
pub up: String,
pub edit: String,
pub edit_append: String,
pub edit_substitute: String,
pub save: String,
pub quit: String,
pub normal_mode: String,
@@ -125,6 +127,8 @@ impl Default for KeybindsConfig {
down: "j".to_string(),
up: "k".to_string(),
edit: "i".to_string(),
edit_append: "A".to_string(),
edit_substitute: "S".to_string(),
save: ":w".to_string(),
quit: ":q".to_string(),
normal_mode: "Esc".to_string(),

View File

@@ -1,4 +1,4 @@
use crate::app::{App, Mode};
use crate::app::{App, InsertVariant, Mode};
use crate::config::Config;
use crate::format::FormatHandler;
use crossterm::event::{self, Event, KeyCode, KeyEvent};
@@ -117,6 +117,8 @@ where
(&self.config.keybinds.down, "down"),
(&self.config.keybinds.up, "up"),
(&self.config.keybinds.edit, "edit"),
(&self.config.keybinds.edit_append, "edit_append"),
(&self.config.keybinds.edit_substitute, "edit_substitute"),
(&self.config.keybinds.search, "search"),
(&self.config.keybinds.next_match, "next_match"),
(&self.config.keybinds.previous_match, "previous_match"),
@@ -147,7 +149,9 @@ where
match action {
"down" => self.app.next(),
"up" => self.app.previous(),
"edit" => self.app.enter_insert(),
"edit" => self.app.enter_insert(InsertVariant::Start),
"edit_append" => self.app.enter_insert(InsertVariant::End),
"edit_substitute" => self.app.enter_insert(InsertVariant::Substitute),
"search" => {
self.app.mode = Mode::Search;
self.app.search_query.clear();

View File

@@ -269,14 +269,30 @@ pub fn draw(f: &mut Frame, app: &mut App, config: &Config) {
),
};
let status_msg = app
.status_message
.as_deref()
.unwrap_or_else(|| match app.mode {
Mode::Normal => " navigation | i: edit | /: search | :w: save | :q: quit ",
Mode::Insert => " Esc: back to normal | Enter: commit ",
Mode::Search => " Esc: back to normal | type to filter ",
});
let status_msg = if let Some(msg) = &app.status_message {
msg.clone()
} else {
match app.mode {
Mode::Normal => {
let mut parts = vec!["j/k move", "gg/G jump", "/ search"];
if !app.selected_is_group() {
parts.push("i/A/S edit");
}
if app.selected_is_missing() {
parts.push("a add");
}
if app.selected_is_array() {
parts.push("o/O array");
}
parts.push("dd del");
parts.push(":w save");
parts.push(":q quit");
parts.join(" · ")
}
Mode::Insert => "Esc normal · Enter commit".to_string(),
Mode::Search => "Esc normal · type to filter".to_string(),
}
};
let status_line = Line::from(vec![
Span::styled(mode_str, mode_style),