refactor: split cmd.rs
This commit is contained in:
189
src/command/cmd/mode.rs
Normal file
189
src/command/cmd/mode.rs
Normal file
@ -0,0 +1,189 @@
|
||||
use crate::ui::app::AppMode;
|
||||
use crate::ui::effect::{self, Effect};
|
||||
|
||||
use super::core::{Cmd, CmdContext};
|
||||
use super::grid::DrillIntoCell;
|
||||
|
||||
// ── Mode change commands ─────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EnterMode(pub AppMode);
|
||||
impl Cmd for EnterMode {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-mode"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
let mut effects: Vec<Box<dyn Effect>> = Vec::new();
|
||||
// Clear the corresponding buffer when entering a text-entry mode
|
||||
if let Some(mb) = self.0.minibuffer() {
|
||||
effects.push(Box::new(effect::SetBuffer {
|
||||
name: mb.buffer_key.to_string(),
|
||||
value: String::new(),
|
||||
}));
|
||||
}
|
||||
effects.push(effect::change_mode(self.0.clone()));
|
||||
effects
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ForceQuit;
|
||||
impl Cmd for ForceQuit {
|
||||
fn name(&self) -> &'static str {
|
||||
"force-quit"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![effect::change_mode(AppMode::Quit)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Quit with dirty check — refuses if unsaved changes exist.
|
||||
#[derive(Debug)]
|
||||
pub struct Quit;
|
||||
impl Cmd for Quit {
|
||||
fn name(&self) -> &'static str {
|
||||
"q"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
if ctx.dirty {
|
||||
vec![effect::set_status(
|
||||
"Unsaved changes. Use :q! to force quit or :wq to save+quit.",
|
||||
)]
|
||||
} else {
|
||||
vec![effect::change_mode(AppMode::Quit)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Save then quit.
|
||||
#[derive(Debug)]
|
||||
pub struct SaveAndQuit;
|
||||
impl Cmd for SaveAndQuit {
|
||||
fn name(&self) -> &'static str {
|
||||
"wq"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![Box::new(effect::Save), effect::change_mode(AppMode::Quit)]
|
||||
}
|
||||
}
|
||||
|
||||
// ── Editing entry ───────────────────────────────────────────────────────
|
||||
|
||||
/// Enter editing mode with an initial buffer value.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterEditMode {
|
||||
pub initial_value: String,
|
||||
}
|
||||
impl Cmd for EnterEditMode {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-edit-mode"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![
|
||||
Box::new(effect::SetBuffer {
|
||||
name: "edit".to_string(),
|
||||
value: self.initial_value.clone(),
|
||||
}),
|
||||
effect::change_mode(AppMode::editing()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Smart dispatch for i/a: if the cursor is on an aggregated pivot cell
|
||||
/// (categories on `Axis::None`, no records mode), drill into it instead of
|
||||
/// editing. Otherwise enter edit mode with the current displayed value.
|
||||
#[derive(Debug)]
|
||||
pub struct EditOrDrill;
|
||||
impl Cmd for EditOrDrill {
|
||||
fn name(&self) -> &'static str {
|
||||
"edit-or-drill"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
// Only consider regular (non-virtual, non-label) categories on None
|
||||
// as true aggregation. Virtuals like _Index/_Dim are always None in
|
||||
// pivot mode and don't imply aggregation.
|
||||
let regular_none = ctx.none_cats().iter().any(|c| {
|
||||
ctx.model
|
||||
.category(c)
|
||||
.map(|cat| cat.kind.is_regular())
|
||||
.unwrap_or(false)
|
||||
});
|
||||
// In records mode (synthetic key), always edit directly — no drilling.
|
||||
let is_synthetic = ctx
|
||||
.cell_key()
|
||||
.as_ref()
|
||||
.and_then(crate::view::synthetic_record_info)
|
||||
.is_some();
|
||||
let is_aggregated = !is_synthetic && regular_none;
|
||||
if is_aggregated {
|
||||
let Some(key) = ctx.cell_key().clone() else {
|
||||
return vec![effect::set_status("cannot drill — no cell at cursor")];
|
||||
};
|
||||
return DrillIntoCell { key }.execute(ctx);
|
||||
}
|
||||
EnterEditMode {
|
||||
initial_value: ctx.display_value.clone(),
|
||||
}
|
||||
.execute(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Thin command wrapper around the `EnterEditAtCursor` effect so it can
|
||||
/// participate in `Binding::Sequence`.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterEditAtCursorCmd;
|
||||
impl Cmd for EnterEditAtCursorCmd {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-edit-at-cursor"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![Box::new(effect::EnterEditAtCursor)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter export prompt mode.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterExportPrompt;
|
||||
impl Cmd for EnterExportPrompt {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-export-prompt"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![effect::change_mode(AppMode::export_prompt())]
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter search mode.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterSearchMode;
|
||||
impl Cmd for EnterSearchMode {
|
||||
fn name(&self) -> &'static str {
|
||||
"search"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![
|
||||
Box::new(effect::SetSearchMode(true)),
|
||||
Box::new(effect::SetSearchQuery(String::new())),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter tile select mode.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterTileSelect;
|
||||
impl Cmd for EnterTileSelect {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-tile-select"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
let count = ctx.model.category_names().len();
|
||||
if count > 0 {
|
||||
vec![
|
||||
Box::new(effect::SetTileCatIdx(0)),
|
||||
effect::change_mode(AppMode::TileSelect),
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user