WIP improvise-6p8q: yank of a coordinate/creating column

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-22 21:20:22 -07:00
parent 9d04b98136
commit 8df9431199
@@ -1,7 +1,7 @@
use crate::model::Aggregation;
use crate::model::cell::{CellKey, CellValue};
use crate::view::synthetic_record_info;
use crate::ui::effect::{self, Effect, ViewTarget};
use crate::view::synthetic_record_info;
use super::core::{Cmd, CmdContext};
@@ -151,7 +151,10 @@ mod tests {
fn yy_then_p_in_records_mode_copies_between_real_records() {
let mut app = records_app();
let (east, west) = (0, 1);
assert_eq!(app.layout.record_row(west).unwrap().coords.get("Region"), Some("West"));
assert_eq!(
app.layout.record_row(west).unwrap().coords.get("Region"),
Some("West")
);
app.cursor_mut().selected = (east, col_of(&app, "Cost"));
press(&mut app, 'y');
@@ -190,6 +193,23 @@ mod tests {
assert_eq!(synthetic_cells(&reloaded), Vec::<CellKey>::new());
}
#[test]
fn yy_on_a_records_coordinate_or_creating_column_yanks_what_is_drawn() {
let mut app = records_app();
app.view_state.yanked = Some(CellValue::Number(5.5));
app.cursor_mut().selected = (1, col_of(&app, "Region"));
press(&mut app, 'y');
press(&mut app, 'y');
assert_eq!(app.view_state.yanked, Some(CellValue::Text("West".into())));
app.cursor_mut().selected = (1, col_of(&app, "+"));
press(&mut app, 'y');
press(&mut app, 'y');
assert_eq!(
app.view_state.yanked, None,
"a blank creating column yanks nothing, as an empty pivot cell does"
);
}
#[test]
fn p_on_a_records_column_that_stores_no_value_changes_nothing() {
for label in ["Region", "+cat", "+"] {
@@ -214,7 +234,10 @@ mod tests {
app.cursor_mut().selected = (1, col_of(&app, "Revenue"));
press(&mut app, 'p');
assert_eq!(
app.model_state.workbook.model.get_cell(&record("West", "Revenue")),
app.model_state
.workbook
.model
.get_cell(&record("West", "Revenue")),
Some(&CellValue::Number(5.5)),
"the control never wrote, so the declines above prove nothing"
);
@@ -238,12 +261,18 @@ mod tests {
app.view_state.yanked = Some(CellValue::Number(5.5));
app.cursor_mut().selected = (0, col_of(&app, "Cost"));
press(&mut app, 'p');
assert_eq!(crate::persistence::format_md(&app.model_state.workbook), before);
assert_eq!(
crate::persistence::format_md(&app.model_state.workbook),
before
);
assert!(
app.drill_state.as_ref().unwrap().pending_edits.is_empty(),
"a paste was staged as text, which loses the value's type"
);
assert_eq!(synthetic_cells(&app.model_state.workbook), Vec::<CellKey>::new());
assert_eq!(
synthetic_cells(&app.model_state.workbook),
Vec::<CellKey>::new()
);
assert!(!app.view_state.status_msg.is_empty(), "declined silently");
}
@@ -257,7 +286,8 @@ mod tests {
wb.add_item("Product", "Widget").unwrap();
wb.add_item("Product", "Gadget").unwrap();
wb.add_item("_Measure", "Revenue").unwrap();
wb.active_view_mut().set_axis("Region", crate::view::Axis::Row);
wb.active_view_mut()
.set_axis("Region", crate::view::Axis::Row);
wb.active_view_mut()
.set_axis("_Measure", crate::view::Axis::Column);
wb.active_view_mut().set_axis("Product", product_axis);
@@ -287,9 +317,10 @@ mod tests {
let mut app = aggregated_app(crate::view::Axis::Page);
press(&mut app, 'p');
assert_eq!(
app.model_state.workbook.model.get_cell(
&record("West", "Revenue").with("Product", "Widget")
),
app.model_state
.workbook
.model
.get_cell(&record("West", "Revenue").with("Product", "Widget")),
Some(&CellValue::Number(5.5))
);
}
@@ -411,7 +442,12 @@ fn paste_into(ctx: &CmdContext, key: &CellKey, value: CellValue) -> Box<dyn Effe
}
}
fn paste_into_record(ctx: &CmdContext, row: usize, col: usize, value: CellValue) -> Box<dyn Effect> {
fn paste_into_record(
ctx: &CmdContext,
row: usize,
col: usize,
value: CellValue,
) -> Box<dyn Effect> {
match (ctx.layout.record_cell_key(row, col), ctx.has_drill_state) {
(Some(_), true) => effect::set_status(PASTE_INSIDE_DRILL),
(Some(real), false) => Box::new(effect::SetCell(real, value)),
@@ -422,11 +458,7 @@ fn paste_into_record(ctx: &CmdContext, row: usize, col: usize, value: CellValue)
/// Whether `key` leaves out a regular category the layout aggregates over.
fn aggregates(ctx: &CmdContext, key: &CellKey) -> bool {
ctx.none_cats().iter().any(|cat| {
key.get(cat).is_none()
&& ctx
.model
.category(cat)
.is_some_and(|c| c.kind.is_regular())
key.get(cat).is_none() && ctx.model.category(cat).is_some_and(|c| c.kind.is_regular())
})
}
@@ -434,7 +466,8 @@ fn is_blank(value: &CellValue) -> bool {
matches!(value, CellValue::Text(text) if text.is_empty())
}
const PASTE_ONTO_AGGREGATE: &str = "Cannot paste onto an aggregate — drill in to paste into a record";
const PASTE_ONTO_AGGREGATE: &str =
"Cannot paste onto an aggregate — drill in to paste into a record";
const PASTE_ONTO_NO_VALUE: &str = "Paste lands only on a measure column";
const PASTE_INSIDE_DRILL: &str = "Paste is not staged inside a drill — edit the cell instead";