T10: resolve conflicts with main; new upstream commands go live (slicer, pivot chart, forecast sheet, show comments)

This commit is contained in:
James
2026-10-11 23:54:15 +03:00
parent 9e9f9aec84
commit 7a2361de7f
4 changed files with 57 additions and 74 deletions
@@ -180,11 +180,11 @@ pub const SEQUENCES: &[(&str, Entry)] = &[
("NSA", Entry::Finish(Action::Control("insert.chart.statistic"))), // Excel: Statistical Chart; Statistical chart (kind TBD)
("NSC", Entry::Finish(Action::Pending("insert.screenshot"))), // pending; Excel: Screenshot Menu; Screenshot; reserved until the command lands
("NSD", Entry::Finish(Action::Command("insert.chart"))), // Excel: Combo Chart; Combo (kind TBD)
("NSF", Entry::Finish(Action::Pending("insert.slicer"))), // pending; Excel: Slicer; insert.slicer missing (gap #8); reserved until the command lands
("NSH", Entry::Finish(Action::Command("insert.shape"))), // Excel: Shapes
("NSF", Entry::Finish(Action::Command("insert.slicer"))), // Excel: Slicer; insert.slicer missing (gap #8); reserved until the command lands
("NSH", Entry::Finish(Action::Command("insert.shape"))), // Excel: Shapes
("NSP", Entry::Finish(Action::Command("insert.recommendedPivotTables"))), // Excel: Recommended PivotTables
("NST", Entry::Finish(Action::Pending("insert.timeline"))), // pending; Excel: Timeline; insert.timeline missing (gap #8); reserved until the command lands
("NSZ", Entry::Finish(Action::Pending("insert.pivotChart"))), // pending; Excel: PivotChart; PivotChart; reserved until the command lands
("NSZ", Entry::Finish(Action::Command("insert.pivotChart"))), // Excel: PivotChart; PivotChart; reserved until the command lands
("NT", Entry::Finish(Action::Command("insert.table"))), // Excel: Table
("NU", Entry::Finish(Action::Command("insert.symbol"))), // Excel: Symbols; containment(Symbol)
("NZS", Entry::Finish(Action::Command("insert.symbol"))), // Excel: Excel: Symbol (modern)
@@ -265,7 +265,7 @@ pub const SEQUENCES: &[(&str, Entry)] = &[
("AF1C", Entry::Finish(Action::Command("insert.picture"))), // Excel: Picture from Clipboard (source TBD)
("AF1P", Entry::Finish(Action::Command("insert.picture"))), // Excel: Picture from File (source TBD)
("AFA", Entry::Finish(Action::Pending("data.fromAccess"))), // pending; Excel: Get External Data: From Access; gap #12 Get & Transform; reserved until the command lands
("AFC", Entry::Finish(Action::Pending("data.forecastSheet"))), // pending; Excel: Forecast Sheet; IN FLIGHT: vangel76 PR #325; reserved until the command lands
("AFC", Entry::Finish(Action::Command("data.forecastSheet"))), // Excel: Forecast Sheet; IN FLIGHT: vangel76 PR #325; reserved until the command lands
("AFE", Entry::Finish(Action::Pending("data.analyzeData"))), // pending; Excel: Analyze Data; AI feature GAP; reserved until the command lands
("AFF", Entry::Finish(Action::Command("edit.flashFill"))), // Excel: Flash Fill
("AFO", Entry::Finish(Action::Pending("data.fromOtherSources"))), // pending; Excel: Get External Data: From Other Sources; gap #12; reserved until the command lands
@@ -316,50 +316,50 @@ pub const SEQUENCES: &[(&str, Entry)] = &[
("AX", Entry::Finish(Action::Pending("data.existingConnections"))), // pending; Excel: Existing Connections; gap #12; reserved until the command lands
("AY", Entry::Finish(Action::Command("data.reapply"))), // Excel: Reapply Filter; containment(Reapply)
("RA1", Entry::Finish(Action::Dialog("accessibility"))), // Excel: Check Accessibility
("RA2", Entry::Finish(Action::Pending("review.showComments"))), // pending; Excel: Show All Comments; Show All Comments; reserved until the command lands
("RA2", Entry::Finish(Action::Command("review.showComments"))), // Excel: Show All Comments; Show All Comments; reserved until the command lands
("RC", Entry::Finish(Action::Dialog("comment"))), // Excel: New Comment
("RD", Entry::Finish(Action::Command("review.deleteComment"))), // Excel: Delete Comment
("RG", Entry::Finish(Action::Pending("review.showChanges"))), // pending; Excel: Track Changes; reserved until the command lands
("RL", Entry::Finish(Action::Pending("review.translate"))), // pending; Excel: Translate; feature gap; reserved until the command lands
("RN", Entry::Finish(Action::Pending("review.nextComment"))), // pending; Excel: Next Comment; Next Comment; reserved until the command lands
("RN", Entry::Finish(Action::Command("review.nextComment"))), // Excel: Next Comment
("RO", Entry::Finish(Action::Command("review.protectWorkbook"))), // Excel: Protect and Share Workbook
("RPS", Entry::Finish(Action::Dialog("protectSheet"))), // Excel: Protect Sheet
("RPW", Entry::Finish(Action::Command("review.protectWorkbook"))), // Excel: Protect Workbook
("RRS", Entry::Finish(Action::Pending("review.smartLookup"))), // pending; Excel: Smart Lookup; feature gap; reserved until the command lands
("RS", Entry::Finish(Action::Dialog("spelling"))), // Excel: Spelling
("RTN", Entry::Finish(Action::Command("review.newNote"))), // Excel: New Note
("RV", Entry::Finish(Action::Pending("review.previousComment"))), // pending; Excel: Previous Comment; Previous Comment; reserved until the command lands
("WA", Entry::Finish(Action::Command("view.arrangeAll"))), // Excel: Arrange All
("WC", Entry::Finish(Action::Command("view.customViews"))), // Excel: Custom Views
("WF", Entry::Continue), // Excel: Freeze Panes Menu; group level:
("RV", Entry::Finish(Action::Command("review.previousComment"))), // Excel: Previous Comment
("WA", Entry::Finish(Action::Command("view.arrangeAll"))), // Excel: Arrange All
("WC", Entry::Finish(Action::Command("view.customViews"))), // Excel: Custom Views
("WF", Entry::Continue), // Excel: Freeze Panes Menu; group level:
("WFC", Entry::Finish(Action::Command("view.freezeFirstColumn"))), // Excel: Freeze First Column
("WFF", Entry::Finish(Action::Command("view.freezePanes"))), // Excel: Freeze Panes
("WFR", Entry::Finish(Action::Command("view.freezeTopRow"))), // Excel: Freeze Top Row
("WG", Entry::Finish(Action::Command("view.zoomToSelection"))), // Excel: Zoom To Selection
("WH", Entry::Finish(Action::Command("view.hideWindow"))), // Excel: Hide Window
("WI", Entry::Finish(Action::Command("view.pageBreakPreview"))), // Excel: Page Break Preview
("WJ", Entry::Finish(Action::Control("view.zoom"))), // Excel: Zoom To 100%; runs the 100% zoom handler (percent param)
("WL", Entry::Finish(Action::Command("view.normal"))), // Excel: Normal View; containment(Normal)
("WM", Entry::Continue), // Excel: Macros Menu; group level: containment(Macros)
("WMR", Entry::Finish(Action::Command("view.macros"))), // Excel: Record Macro; Record Macro (macros menu)
("WMV", Entry::Finish(Action::Command("view.macros"))), // Excel: View Macros; containment(Macros)
("WN", Entry::Finish(Action::Command("view.newWindow"))), // Excel: New Window
("WP", Entry::Finish(Action::Command("view.pageLayout"))), // Excel: Page Layout View
("WQ", Entry::Finish(Action::Dialog("zoom"))), // Excel: Zoom; opens the Zoom dialog
("WR", Entry::Finish(Action::Command("view.ruler"))), // Excel: Ruler
("WS", Entry::Finish(Action::Command("view.split"))), // Excel: Split
("WFF", Entry::Finish(Action::Command("view.freezePanes"))), // Excel: Freeze Panes
("WFR", Entry::Finish(Action::Command("view.freezeTopRow"))), // Excel: Freeze Top Row
("WG", Entry::Finish(Action::Command("view.zoomToSelection"))), // Excel: Zoom To Selection
("WH", Entry::Finish(Action::Command("view.hideWindow"))), // Excel: Hide Window
("WI", Entry::Finish(Action::Command("view.pageBreakPreview"))), // Excel: Page Break Preview
("WJ", Entry::Finish(Action::Control("view.zoom"))), // Excel: Zoom To 100%; runs the 100% zoom handler (percent param)
("WL", Entry::Finish(Action::Command("view.normal"))), // Excel: Normal View; containment(Normal)
("WM", Entry::Continue), // Excel: Macros Menu; group level: containment(Macros)
("WMR", Entry::Finish(Action::Command("view.macros"))), // Excel: Record Macro; Record Macro (macros menu)
("WMV", Entry::Finish(Action::Command("view.macros"))), // Excel: View Macros; containment(Macros)
("WN", Entry::Finish(Action::Command("view.newWindow"))), // Excel: New Window
("WP", Entry::Finish(Action::Command("view.pageLayout"))), // Excel: Page Layout View
("WQ", Entry::Finish(Action::Dialog("zoom"))), // Excel: Zoom; opens the Zoom dialog
("WR", Entry::Finish(Action::Command("view.ruler"))), // Excel: Ruler
("WS", Entry::Finish(Action::Command("view.split"))), // Excel: Split
("WT", Entry::Finish(Action::Pending("view.resetWindowPosition"))), // pending; Excel: Reset Window Position; reserved until the command lands
("WU", Entry::Finish(Action::Command("view.unhideWindow"))), // Excel: Unhide Window
("WVC", Entry::Finish(Action::Pending("view.focusCell"))), // pending; Excel: Focus Cell Menu; Focus Cell; reserved until the command lands
("WVF", Entry::Finish(Action::Control("view.formulaBar"))), // Excel: Toggle Formula Bar; toggles the formula bar
("WVH", Entry::Finish(Action::Command("view.headings"))), // Excel: Toggle Headings; containment(Headings)
("WVN", Entry::Finish(Action::Pending("view.navigation"))), // pending; Excel: Navigation; Navigation pane; reserved until the command lands
("WVR", Entry::Finish(Action::Command("view.ruler"))), // Excel: Ruler
("WW", Entry::Finish(Action::Command("view.switchWindows"))), // Excel: Switch Windows
("WU", Entry::Finish(Action::Command("view.unhideWindow"))), // Excel: Unhide Window
("WVC", Entry::Finish(Action::Pending("view.focusCell"))), // pending; Excel: Focus Cell Menu; Focus Cell; reserved until the command lands
("WVF", Entry::Finish(Action::Control("view.formulaBar"))), // Excel: Toggle Formula Bar; toggles the formula bar
("WVH", Entry::Finish(Action::Command("view.headings"))), // Excel: Toggle Headings; containment(Headings)
("WVN", Entry::Finish(Action::Pending("view.navigation"))), // pending; Excel: Navigation; Navigation pane; reserved until the command lands
("WVR", Entry::Finish(Action::Command("view.ruler"))), // Excel: Ruler
("WW", Entry::Finish(Action::Command("view.switchWindows"))), // Excel: Switch Windows
("JAM", Entry::Finish(Action::Command("chart.formatSelection"))), // Excel: Chart format: Format Selection pane; containment(Format Selection)
("JCA", Entry::Finish(Action::Command("chart.addElement"))), // Excel: Chart: Add Chart Element; containment(Add Chart Element)
("JCC", Entry::Finish(Action::Command("chart.changeType"))), // Excel: Chart: Change Chart Type; containment(Change Chart Type)
("JTS", Entry::Finish(Action::Command("table.style"))), // Excel: Table: table style
("JCA", Entry::Finish(Action::Command("chart.addElement"))), // Excel: Chart: Add Chart Element; containment(Add Chart Element)
("JCC", Entry::Finish(Action::Command("chart.changeType"))), // Excel: Chart: Change Chart Type; containment(Change Chart Type)
("JTS", Entry::Finish(Action::Command("table.style"))), // Excel: Table: table style
("JTV", Entry::Finish(Action::Command("table.summarizePivot"))), // Excel: Table: Summarize with PivotTable; containment(Summarize with PivotTable)
("FA", Entry::Finish(Action::Command("file.saveAs"))), // Excel: Save / Save As page; fires immediately in Excel; deeper aliases dropped as unreachable
("FC", Entry::Finish(Action::Command("file.close"))), // Excel: Close workbook
+8 -8
View File
@@ -1188,7 +1188,7 @@
"seq": "NSF",
"kind": "command",
"target": "insert.slicer",
"pending": true,
"pending": false,
"excel": "Slicer",
"note": "insert.slicer missing (gap #8); reserved until the command lands"
},
@@ -1218,7 +1218,7 @@
"seq": "NSZ",
"kind": "command",
"target": "insert.pivotChart",
"pending": true,
"pending": false,
"excel": "PivotChart",
"note": "PivotChart; reserved until the command lands"
},
@@ -1773,7 +1773,7 @@
"seq": "AFC",
"kind": "command",
"target": "data.forecastSheet",
"pending": true,
"pending": false,
"excel": "Forecast Sheet",
"note": "IN FLIGHT: vangel76 PR #325; reserved until the command lands"
},
@@ -2142,7 +2142,7 @@
"seq": "RA2",
"kind": "command",
"target": "review.showComments",
"pending": true,
"pending": false,
"excel": "Show All Comments",
"note": "Show All Comments; reserved until the command lands"
},
@@ -2180,9 +2180,9 @@
"seq": "RN",
"kind": "command",
"target": "review.nextComment",
"pending": true,
"pending": false,
"excel": "Next Comment",
"note": "Next Comment; reserved until the command lands"
"note": ""
},
{
"seq": "RO",
@@ -2230,9 +2230,9 @@
"seq": "RV",
"kind": "command",
"target": "review.previousComment",
"pending": true,
"pending": false,
"excel": "Previous Comment",
"note": "Previous Comment; reserved until the command lands"
"note": ""
},
{
"seq": "WA",
+7 -6
View File
@@ -308,6 +308,7 @@ fn tab_letters_switch_to_their_ribbon_tabs() {
assert_eq!(h.state().keytips.prefix(), None);
}
// A terminal tab unit with no deeper sequences finishes and clears.
key(&mut h, Key::F10); // re-arm
letter(&mut h, Key::J, false);
assert_eq!(h.state().keytips.prefix(), Some("J"), "J waits for its multi-letter unit");
letter(&mut h, Key::I, false);
@@ -406,7 +407,7 @@ fn vertical_alignment_keytips_run_their_commands() {
fn pending_sequences_are_silent_no_ops() {
let mut h = harness(blank());
let journal = h.state().session.journal.len();
sequence(&mut h, &[Key::N, Key::E], false); // NE -> insert.equation (missing)
sequence(&mut h, &[Key::N, Key::W], false); // NW -> insert.wordArt (missing)
assert!(h.state().dialog.is_none(), "no dialog for a pending sequence");
assert!(h.state().message.is_none(), "no message box for a pending sequence");
assert!(h.state().toast.is_none(), "no toast for a pending sequence");
@@ -418,15 +419,15 @@ fn pending_sequences_are_silent_no_ops() {
fn pending_sequences_go_live_when_the_command_lands() {
// The command lands in the catalog (here: a test-registered stand-in).
// The sequence must run it with no table edit.
gridcraft_engine::register_test_command("insert.slicer", |s, _| {
gridcraft_engine::register_test_command("insert.equation", |s, _| {
s.execute("cell.set", json!({"cell": "Z9", "input": "live"}))?;
Ok(json!({}))
});
let mut h = harness(blank());
sequence(&mut h, &[Key::N, Key::S, Key::F], false); // NSF -> insert.slicer
sequence(&mut h, &[Key::N, Key::E], false); // NE -> insert.equation
assert_eq!(value(&h, "Z9"), Value::from("live"), "the pending sequence ran the landed command");
assert_eq!(
h.state().session.journal.iter().filter(|(id, _)| id == "insert.slicer").count(),
h.state().session.journal.iter().filter(|(id, _)| id == "insert.equation").count(),
1,
"the landed command is journaled like any live command"
);
@@ -445,9 +446,9 @@ fn control_keytips_open_dropdowns_and_run_handlers() {
// WVF toggles the formula bar: pure UI state, no engine command exists.
let mut h = harness(blank());
assert!(h.state().ui.formula_bar);
assert!(h.state().session.prefs.formula_bar);
sequence(&mut h, &[Key::W, Key::V, Key::F], false);
assert!(!h.state().ui.formula_bar, "WVF must toggle the formula bar off");
assert!(!h.state().session.prefs.formula_bar, "WVF must toggle the formula bar off");
// WJ runs the 100% handler with its percent param ({} would raise "missing percent").
let mut h = harness(blank());
+7 -25
View File
@@ -1276,7 +1276,7 @@ fn keytip_nq_() {
fn keytip_nr_() {
let mut h = harness(blank());
sequence(&mut h, "NR", false);
assert!(h.state().session.journal.iter().any(|(id, _)| id == "insert.recommendedCharts"), "NR must journal insert.recommendedCharts");
assert!(h.state().message.is_some() || h.state().toast.is_some(), "NR must invoke insert.recommendedCharts and surface its missing-params error");
}
#[test]
@@ -1308,10 +1308,7 @@ fn keytip_nsd_() {
fn keytip_nsf_() {
let mut h = harness(blank());
sequence(&mut h, "NSF", false);
assert!(h.state().dialog.is_none(), "NSF must not open a dialog");
assert!(h.state().message.is_none(), "NSF must not open a message box");
assert!(h.state().toast.is_none(), "NSF must not raise a toast");
assert!(h.state().session.journal.is_empty(), "NSF must not journal");
assert!(h.state().message.is_some() || h.state().toast.is_some(), "NSF must invoke insert.slicer and surface its missing-params error");
}
#[test]
@@ -1345,10 +1342,7 @@ fn keytip_nst_() {
fn keytip_nsz_() {
let mut h = harness(blank());
sequence(&mut h, "NSZ", false);
assert!(h.state().dialog.is_none(), "NSZ must not open a dialog");
assert!(h.state().message.is_none(), "NSZ must not open a message box");
assert!(h.state().toast.is_none(), "NSZ must not raise a toast");
assert!(h.state().session.journal.is_empty(), "NSZ must not journal");
assert!(h.state().message.is_some() || h.state().toast.is_some(), "NSZ must invoke insert.pivotChart and surface its missing-params error");
}
#[test]
@@ -1934,10 +1928,7 @@ fn keytip_afa_() {
fn keytip_afc_() {
let mut h = harness(blank());
sequence(&mut h, "AFC", false);
assert!(h.state().dialog.is_none(), "AFC must not open a dialog");
assert!(h.state().message.is_none(), "AFC must not open a message box");
assert!(h.state().toast.is_none(), "AFC must not raise a toast");
assert!(h.state().session.journal.is_empty(), "AFC must not journal");
assert!(h.state().message.is_some() || h.state().toast.is_some(), "AFC must invoke data.forecastSheet and surface its missing-params error");
}
#[test]
@@ -2351,10 +2342,7 @@ fn keytip_ra1_() {
fn keytip_ra2_() {
let mut h = harness(blank());
sequence(&mut h, "RA2", false);
assert!(h.state().dialog.is_none(), "RA2 must not open a dialog");
assert!(h.state().message.is_none(), "RA2 must not open a message box");
assert!(h.state().toast.is_none(), "RA2 must not raise a toast");
assert!(h.state().session.journal.is_empty(), "RA2 must not journal");
assert!(h.state().message.is_none() && h.state().toast.is_none(), "RA2 must run review.showComments cleanly");
}
#[test]
@@ -2395,10 +2383,7 @@ fn keytip_rl_() {
fn keytip_rn_() {
let mut h = harness(blank());
sequence(&mut h, "RN", false);
assert!(h.state().dialog.is_none(), "RN must not open a dialog");
assert!(h.state().message.is_none(), "RN must not open a message box");
assert!(h.state().toast.is_none(), "RN must not raise a toast");
assert!(h.state().session.journal.is_empty(), "RN must not journal");
assert!(h.state().message.is_some() || h.state().toast.is_some(), "RN must invoke review.nextComment and surface its missing-params error");
}
#[test]
@@ -2450,10 +2435,7 @@ fn keytip_rtn_() {
fn keytip_rv_() {
let mut h = harness(blank());
sequence(&mut h, "RV", false);
assert!(h.state().dialog.is_none(), "RV must not open a dialog");
assert!(h.state().message.is_none(), "RV must not open a message box");
assert!(h.state().toast.is_none(), "RV must not raise a toast");
assert!(h.state().session.journal.is_empty(), "RV must not journal");
assert!(h.state().message.is_some() || h.state().toast.is_some(), "RV must invoke review.previousComment and surface its missing-params error");
}
#[test]