feat(command): add HandleWizardKey and ExecuteCommand handlers
Introduce HandleWizardKey command to dispatch keys to the import wizard. Add ExecuteCommand implementation that parses and executes various commands like :quit, :write, :import, :add-item, and :formula. Handles argument parsing, validation, and mode transitions. Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
This commit is contained in:
@ -1071,6 +1071,211 @@ impl Cmd for SetAxisForTile {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Wizard command ──────────────────────────────────────────────────────────
|
||||
|
||||
/// Dispatch the current key to the import wizard effect.
|
||||
#[derive(Debug)]
|
||||
pub struct HandleWizardKey;
|
||||
impl Cmd for HandleWizardKey {
|
||||
fn name(&self) -> &str {
|
||||
"handle-wizard-key"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![Box::new(effect::WizardKey {
|
||||
key_code: ctx.key_code,
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
||||
// ── Command mode execution ──────────────────────────────────────────────────
|
||||
|
||||
/// Execute the command in the "command" buffer (the `:` command line).
|
||||
#[derive(Debug)]
|
||||
pub struct ExecuteCommand;
|
||||
impl Cmd for ExecuteCommand {
|
||||
fn name(&self) -> &str {
|
||||
"execute-command"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
let raw = ctx.buffers.get("command").cloned().unwrap_or_default();
|
||||
let raw = raw.trim();
|
||||
let (cmd_name, rest) = raw
|
||||
.split_once(char::is_whitespace)
|
||||
.map(|(c, r)| (c, r.trim()))
|
||||
.unwrap_or((raw, ""));
|
||||
|
||||
// Default: return to Normal
|
||||
let mut effects: Vec<Box<dyn Effect>> = Vec::new();
|
||||
|
||||
match cmd_name {
|
||||
"q" | "quit" => {
|
||||
if ctx.dirty {
|
||||
effects.push(effect::set_status(
|
||||
"Unsaved changes. Use :q! to force quit or :wq to save+quit.",
|
||||
));
|
||||
} else {
|
||||
effects.push(effect::change_mode(AppMode::Quit));
|
||||
}
|
||||
}
|
||||
"q!" => {
|
||||
effects.push(effect::change_mode(AppMode::Quit));
|
||||
}
|
||||
"w" | "write" => {
|
||||
if rest.is_empty() {
|
||||
effects.push(Box::new(effect::Save));
|
||||
} else {
|
||||
effects.push(Box::new(effect::SaveAs(std::path::PathBuf::from(rest))));
|
||||
}
|
||||
}
|
||||
"wq" | "x" => {
|
||||
effects.push(Box::new(effect::Save));
|
||||
effects.push(effect::change_mode(AppMode::Quit));
|
||||
}
|
||||
"import" => {
|
||||
if rest.is_empty() {
|
||||
effects.push(effect::set_status("Usage: :import <path.json>"));
|
||||
} else {
|
||||
effects.push(Box::new(effect::StartImportWizard(rest.to_string())));
|
||||
}
|
||||
}
|
||||
"export" => {
|
||||
let path = if rest.is_empty() { "export.csv" } else { rest };
|
||||
effects.push(Box::new(effect::ExportCsv(std::path::PathBuf::from(path))));
|
||||
}
|
||||
"add-cat" | "add-category" | "cat" => {
|
||||
if rest.is_empty() {
|
||||
effects.push(effect::set_status("Usage: :add-cat <name>"));
|
||||
} else {
|
||||
effects.push(Box::new(effect::AddCategory(rest.to_string())));
|
||||
effects.push(effect::mark_dirty());
|
||||
}
|
||||
}
|
||||
"add-item" | "item" => {
|
||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||
let cat = parts.next().unwrap_or("").trim();
|
||||
let item = parts.next().unwrap_or("").trim();
|
||||
if cat.is_empty() || item.is_empty() {
|
||||
effects.push(effect::set_status("Usage: :add-item <category> <item>"));
|
||||
} else {
|
||||
effects.push(Box::new(effect::AddItem {
|
||||
category: cat.to_string(),
|
||||
item: item.to_string(),
|
||||
}));
|
||||
effects.push(effect::mark_dirty());
|
||||
effects.push(effect::set_status("Item added"));
|
||||
}
|
||||
}
|
||||
"add-items" | "items" => {
|
||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||
let cat = parts.next().unwrap_or("").trim().to_string();
|
||||
let items_str = parts.next().unwrap_or("").trim().to_string();
|
||||
if cat.is_empty() || items_str.is_empty() {
|
||||
effects.push(effect::set_status(
|
||||
"Usage: :add-items <category> item1 item2 ...",
|
||||
));
|
||||
} else {
|
||||
let items: Vec<&str> = items_str.split_whitespace().collect();
|
||||
let count = items.len();
|
||||
for item in &items {
|
||||
effects.push(Box::new(effect::AddItem {
|
||||
category: cat.clone(),
|
||||
item: item.to_string(),
|
||||
}));
|
||||
}
|
||||
effects.push(effect::mark_dirty());
|
||||
effects.push(effect::set_status(format!(
|
||||
"Added {count} items to \"{cat}\".",
|
||||
)));
|
||||
}
|
||||
}
|
||||
"formula" | "add-formula" => {
|
||||
if rest.is_empty() {
|
||||
effects.push(Box::new(effect::SetPanelOpen {
|
||||
panel: Panel::Formula,
|
||||
open: true,
|
||||
}));
|
||||
effects.push(effect::change_mode(AppMode::FormulaPanel));
|
||||
return effects; // Don't set mode to Normal
|
||||
} else {
|
||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||
let cat = parts.next().unwrap_or("").trim();
|
||||
let formula = parts.next().unwrap_or("").trim();
|
||||
if cat.is_empty() || formula.is_empty() {
|
||||
effects.push(effect::set_status(
|
||||
"Usage: :formula <category> <Name = expr>",
|
||||
));
|
||||
} else {
|
||||
effects.push(Box::new(effect::AddFormula {
|
||||
raw: formula.to_string(),
|
||||
target_category: cat.to_string(),
|
||||
}));
|
||||
effects.push(effect::mark_dirty());
|
||||
effects.push(effect::set_status("Formula added"));
|
||||
}
|
||||
}
|
||||
}
|
||||
"add-view" | "view" => {
|
||||
let name = if rest.is_empty() {
|
||||
format!("View {}", ctx.model.views.len() + 1)
|
||||
} else {
|
||||
rest.to_string()
|
||||
};
|
||||
effects.push(Box::new(effect::CreateView(name.clone())));
|
||||
effects.push(Box::new(effect::SwitchView(name)));
|
||||
effects.push(effect::mark_dirty());
|
||||
}
|
||||
"set-format" | "fmt" => {
|
||||
if rest.is_empty() {
|
||||
effects.push(effect::set_status(
|
||||
"Usage: :set-format <fmt> e.g. ,.0 ,.2 .4",
|
||||
));
|
||||
} else {
|
||||
effects.push(Box::new(effect::SetNumberFormat(rest.to_string())));
|
||||
effects.push(effect::mark_dirty());
|
||||
effects.push(effect::set_status(format!("Number format set to '{rest}'")));
|
||||
}
|
||||
}
|
||||
"show-item" | "show" => {
|
||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||
let cat = parts.next().unwrap_or("").trim();
|
||||
let item = parts.next().unwrap_or("").trim();
|
||||
if cat.is_empty() || item.is_empty() {
|
||||
effects.push(effect::set_status("Usage: :show-item <category> <item>"));
|
||||
} else {
|
||||
effects.push(Box::new(effect::ShowItem {
|
||||
category: cat.to_string(),
|
||||
item: item.to_string(),
|
||||
}));
|
||||
effects.push(effect::mark_dirty());
|
||||
effects.push(effect::set_status(format!(
|
||||
"Showed \"{item}\" in \"{cat}\""
|
||||
)));
|
||||
}
|
||||
}
|
||||
"help" | "h" => {
|
||||
effects.push(effect::change_mode(AppMode::Help));
|
||||
return effects; // Don't also set Normal
|
||||
}
|
||||
"" => {} // just pressed Enter with empty buffer
|
||||
other => {
|
||||
effects.push(effect::set_status(format!(
|
||||
"Unknown command: :{other} (try :help)"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Default: return to Normal (unless a command already set a different mode)
|
||||
if !effects
|
||||
.iter()
|
||||
.any(|e| format!("{e:?}").contains("ChangeMode"))
|
||||
{
|
||||
effects.push(effect::change_mode(AppMode::Normal));
|
||||
}
|
||||
|
||||
effects
|
||||
}
|
||||
}
|
||||
|
||||
// ── Text buffer commands ─────────────────────────────────────────────────────
|
||||
|
||||
/// Append the pressed character to a named buffer.
|
||||
|
||||
Reference in New Issue
Block a user