refactor!: unify records and pivot mode cell handling
Refactor records mode to use synthetic CellKeys (_Index, _Dim) for all columns, allowing uniform handling of display values and edits across both pivot and records modes. - Introduce `synthetic_record_info` to extract metadata from synthetic keys. - Update `GridLayout::cell_key` to return synthetic keys in records mode. - Add `GridLayout::resolve_display` to handle value resolution for synthetic keys. - Replace `records_col` and `records_value` in `CmdContext` with a unified `display_value`. - Update `EditOrDrill` and `AddRecordRow` to use synthetic key detection. - Refactor `CommitCellEdit` to use a shared `commit_cell_value` helper. BREAKING CHANGE: CmdContext fields `records_col` and `records_value` are replaced by `display_value` . Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-31B-it-GGUF:UD-Q5_K_XL)
This commit is contained in:
@ -41,12 +41,8 @@ pub struct CmdContext<'a> {
|
||||
/// View navigation stacks (for drill back/forward)
|
||||
pub view_back_stack: Vec<String>,
|
||||
pub view_forward_stack: Vec<String>,
|
||||
/// Records-mode info (drill view). None for normal pivot views.
|
||||
/// When Some, edits stage to drill_state.pending_edits.
|
||||
pub records_col: Option<String>,
|
||||
/// The display value at the cursor in records mode (including any
|
||||
/// pending edit override). None for normal pivot views.
|
||||
pub records_value: Option<String>,
|
||||
/// Display value at the cursor — works uniformly for pivot and records mode.
|
||||
pub display_value: String,
|
||||
/// How many data rows/cols fit on screen (for viewport scrolling).
|
||||
/// Defaults to generous fallbacks when unknown.
|
||||
pub visible_rows: usize,
|
||||
@ -688,7 +684,11 @@ impl Cmd for EditOrDrill {
|
||||
.map(|cat| cat.kind.is_regular())
|
||||
.unwrap_or(false)
|
||||
});
|
||||
let is_aggregated = ctx.records_col.is_none() && regular_none;
|
||||
// In records mode (synthetic key), always edit directly — no drilling.
|
||||
let is_synthetic = ctx.cell_key.as_ref()
|
||||
.and_then(|k| crate::view::synthetic_record_info(k))
|
||||
.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(
|
||||
@ -697,18 +697,10 @@ impl Cmd for EditOrDrill {
|
||||
};
|
||||
return DrillIntoCell { key }.execute(ctx);
|
||||
}
|
||||
// Edit path: prefer records display value (includes pending edits),
|
||||
// else the underlying cell's stored value.
|
||||
let initial_value = if let Some(v) = &ctx.records_value {
|
||||
v.clone()
|
||||
} else {
|
||||
ctx.cell_key
|
||||
.as_ref()
|
||||
.and_then(|k| ctx.model.get_cell(k).cloned())
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
EnterEditMode { initial_value }.execute(ctx)
|
||||
EnterEditMode {
|
||||
initial_value: ctx.display_value.clone(),
|
||||
}
|
||||
.execute(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,7 +713,10 @@ impl Cmd for AddRecordRow {
|
||||
"add-record-row"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
if ctx.records_col.is_none() {
|
||||
let is_records = ctx.cell_key.as_ref()
|
||||
.and_then(|k| crate::view::synthetic_record_info(k))
|
||||
.is_some();
|
||||
if !is_records {
|
||||
return vec![effect::set_status("add-record-row only works in records mode")];
|
||||
}
|
||||
// Build a CellKey from the current page filters
|
||||
@ -1928,14 +1923,34 @@ impl Cmd for PopChar {
|
||||
|
||||
/// Commit a cell edit: set cell value, advance cursor, return to Normal.
|
||||
/// In records mode, stages the edit in drill_state.pending_edits instead of
|
||||
/// writing directly to the model.
|
||||
/// Commit a cell value: for synthetic records keys, stage in drill pending edits
|
||||
/// or apply directly; for real keys, write to the model.
|
||||
fn commit_cell_value(key: &CellKey, value: &str, effects: &mut Vec<Box<dyn Effect>>) {
|
||||
if let Some((record_idx, col_name)) = crate::view::synthetic_record_info(key) {
|
||||
effects.push(Box::new(effect::SetDrillPendingEdit {
|
||||
record_idx,
|
||||
col_name,
|
||||
new_value: value.to_string(),
|
||||
}));
|
||||
} else if value.is_empty() {
|
||||
effects.push(Box::new(effect::ClearCell(key.clone())));
|
||||
effects.push(effect::mark_dirty());
|
||||
} else if let Ok(n) = value.parse::<f64>() {
|
||||
effects.push(Box::new(effect::SetCell(key.clone(), CellValue::Number(n))));
|
||||
effects.push(effect::mark_dirty());
|
||||
} else {
|
||||
effects.push(Box::new(effect::SetCell(key.clone(), CellValue::Text(value.to_string()))));
|
||||
effects.push(effect::mark_dirty());
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit a cell edit: set cell value, advance cursor, return to editing.
|
||||
/// In records mode with drill, stages the edit in drill_state.pending_edits.
|
||||
/// In records mode without drill or in pivot mode, writes directly to the model.
|
||||
#[derive(Debug)]
|
||||
pub struct CommitCellEdit {
|
||||
pub key: crate::model::cell::CellKey,
|
||||
pub key: CellKey,
|
||||
pub value: String,
|
||||
/// Records-mode edit: (record_idx, column_name). When Some, stage in
|
||||
/// pending_edits; otherwise write to the model directly.
|
||||
pub records_edit: Option<(usize, String)>,
|
||||
}
|
||||
impl Cmd for CommitCellEdit {
|
||||
fn name(&self) -> &'static str {
|
||||
@ -1944,29 +1959,7 @@ impl Cmd for CommitCellEdit {
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
let mut effects: Vec<Box<dyn Effect>> = Vec::new();
|
||||
|
||||
if let Some((record_idx, col_name)) = &self.records_edit {
|
||||
// Stage the edit in drill_state.pending_edits
|
||||
effects.push(Box::new(effect::SetDrillPendingEdit {
|
||||
record_idx: *record_idx,
|
||||
col_name: col_name.clone(),
|
||||
new_value: self.value.clone(),
|
||||
}));
|
||||
} else if self.value.is_empty() {
|
||||
effects.push(Box::new(effect::ClearCell(self.key.clone())));
|
||||
effects.push(effect::mark_dirty());
|
||||
} else if let Ok(n) = self.value.parse::<f64>() {
|
||||
effects.push(Box::new(effect::SetCell(
|
||||
self.key.clone(),
|
||||
CellValue::Number(n),
|
||||
)));
|
||||
effects.push(effect::mark_dirty());
|
||||
} else {
|
||||
effects.push(Box::new(effect::SetCell(
|
||||
self.key.clone(),
|
||||
CellValue::Text(self.value.clone()),
|
||||
)));
|
||||
effects.push(effect::mark_dirty());
|
||||
}
|
||||
commit_cell_value(&self.key, &self.value, &mut effects);
|
||||
// Advance cursor down (typewriter-style) and re-enter edit mode
|
||||
// at the new cell so the user can continue data entry.
|
||||
let adv = EnterAdvance {
|
||||
|
||||
Reference in New Issue
Block a user