* E37: migrate GridCraft to egui 0.37
Pin the reviewed fork and shared dependencies, adapt input and rendering to the
new frame lifecycle, and preserve bounded capture handling and font notices.
Add representative accessibility checks and meaningful control labels.
Validation: full repository CI passed all ten stages, with 2,249 tests passed,
zero failed and one existing timing test ignored. Native workflows and paired
performance qualification remain documented separately.
* egui037: record final published pins and local qualification
* M0.37: fix RTL clipping and preserve screenshot budgets
* test: share migrated GridCraft UI harness setup
* egui037: document migrated UI and regression contracts
* egui-037: align popup test fonts and avoid redundant tooltip probes
AutoSum over any selection of more than one cell wrote a total under each column, so a row
selected (B2:E2) got four one-cell totals in row 3 instead of =SUM(B2:E2) in F2.
As Excel does, totals now go in the selection's empty last row or column, and otherwise past
it:
- a single row: its total in its last cell when that is empty, else just to its right;
- a block whose last column is empty: each row's total there; whose last row is empty: each
column's; both: both, and the block's total in the corner;
- otherwise totals under each column, as before.
A whole-row selection still stops at the data's last column, and all totals are one undo step.
Tests: autosum_over_a_row_totals_it_to_the_right,
autosum_over_a_whole_row_totals_it_after_the_data and
autosum_fills_a_selections_empty_last_column_and_row; each fails without the change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`macro.check` is a query, so the debugger's lock let it through, but its dry run runs the macro's
steps, which the lock refuses: the check reported the lock as the failure of a step. It now waits
with "Finish or stop the macro first", as other commands that change the workbook do.
- Test: `between_steps_only_looking_around_runs_and_the_run_continues_where_it_was`.
The merge of #2145 took this branch's macro page whole instead of merging it, which left out the
page's newer text from below: the debugging section as #2145 rewrote it (the lock, Stop Action,
parameters, the time limit for each action), the time limit for each debugger action in the
limits, the check after a shift, and moves that rewrite formula references. The page is now the
merge of both, with this branch's own scope line.
Under `shiftDown` or `shiftRight` with conflicts, `macro.check` looked only for the refusals made
before a shift, so it said `ok` for a run the second footprint stops (an anchor that moves with
the shifted data). It now shifts the data inside an undo group, takes the footprint again, rolls
both back and puts the session back, and reports what still stands in the way.
- Test: the `Named` case and a shift that makes room in `shifting_makes_room_and_is_checked_again`.
- The check for writes over data compared the inputs' sheet with the sheet's index when the run
started, while the inputs follow their cells: after a step inserted a sheet in front, a step
writing into its own input on the moved sheet was stopped as writing over data. It now uses the
sheet's index now. Test: the input case in
`inputs_and_the_output_follow_their_cells_when_a_step_moves_them`.
- Docs: the page says `macro.run` checks the macro, its inputs and parameters and the policy
before it runs, and that a formula whose references a move rewrites counts as changed too.
- target-app-parity.md: the summary table and the method say 276 / 292 and 292 unique ids, as
`cargo xtask parity` measures; Last updated and revision history.
- architecture.md: macros are kept as `_gc_macro_<n>_<k>` (CodeRabbit 4366060044).
- macros.md: the intro says the page covers the engine and its commands, without implying
later work.
- A script step that only opened a dialog (a menu-style call that left the workbook unchanged)
is left out of the script run's steps, by the recorder hook's rule; a script run whose steps
the recording couldn't keep is left out and listed in `skipped`, not read from the workbook
as the script stands when recording stops (a_script_step_that_only_opened_a_dialog_is_left_out_of_the_recording).
- A script's toggles keep the state they set (`"on": true`), as a top-level Bold does
(a_script_run_records_the_state_its_toggles_set).
- A recorded macro.run keeps the steps it ran, as a script run does, so saving or deleting the
macro before the recording stops changes nothing (a_recorded_macro_run_keeps_the_steps_that_ran).
- The example input's shape and column types are inferred when recording starts, not from the
data the recording changed (the_recorded_input_is_inferred_as_it_was_when_recording_started).
- Recorded text that playback would read as a token stays text: a reference parameter that reads
as one becomes {"$text": …}, and `{{` becomes `{{{{`
(recorded_text_that_reads_as_a_token_plays_back_as_text). The resolver (below #2152) gains
`{"$text": …}`, text passed as it is (text_marked_as_text_is_never_a_token).
- A recording over the macro size limit is cut at the limit and the steps left out are counted
in `dropped` (a_recording_past_the_macro_size_is_cut_and_counted).
- A macro recorded in a workbook opened from a file is approved for the session, since the
person recorded it (a_macro_recorded_in_a_workbook_from_a_file_runs_without_asking).
- macro.record refuses parameters it would ignore: name or description when starting, relative
when stopping, stop: false while recording, others beside cancel: true, and wrong kinds
(record_refuses_parameters_it_would_ignore).
- Undo while recording keeps the undone step, as in Excel; documented
(undo_while_recording_keeps_the_undone_step_as_excel_does).
- Docs: macros.md Recording and Tokens; module comments no longer say scripts are read.
- macro.save and a stopped recording count the macros kept in a newer format with the readable
ones, so a save never holds more than the next open keeps; the message names the kept count
(kept_macros_count_toward_the_limit_and_keep_their_names, which saves and reopens as xlsx and
JSON).
- The names of kept macros are taken: macro.save and macro.rename refuse them, unique_name and
next_name step around them (same test).
- macro.save no longer replaces by default: `replace` defaults to false, the error names the
existing macro and says to pass `replace: true`, and a `replace` that is not a boolean is
refused (saving_over_a_macro_needs_replace).
- macro.rename runs the whole macro check, size included, so a longer name can't take a macro
past what an xlsx file holds (renaming_checks_the_macro_size).
- Docs: macros.md, the management commands and kept macros.
- The start options are those of `macro.run`, refused as it refuses them.
- The time limit applies to each debugger action; the step and parameter limits to the whole run.
- The lock lists what runs between steps, and says the sheet and selection are put back.
- Stop Action ends the run; the debugger is never a step and never recorded.
- The answers' fields, including `changedCells`, `shifted`, `wroteFiles` and `message`.
`automate.stopAction` rolls back the undo groups open on the workbook. While a macro was being
debugged it rolled back the debugger's own group and left the debugger set, pointing at a group
that was gone. It is now refused under the debugger's lock with "Finish or stop the macro first",
as edits are; `macro.debug {action: "stop"}` ends the run.
- Test: `stop_action_waits_while_a_macro_is_being_debugged`.
- `ok` in `macro.check` meant only that the dry run hit no error. It now says whether
`macro.run` with the same parameters would go ahead without a question, and `errors` says
why not: a failing step, a Stop step that rolls back, data written over under `ask` or `fail`,
a shift that can't make room (the refusals `make_room` makes, now in `shift_refusal`), or a
run past the step or parameter limit once the dry runs before it count. `macro.check` takes
`onConflict` and `overwrite` as `macro.run` does, reports the rule it used, and refuses the
parameters `macro.run` refuses.
- After a partial footprint, the question under `ask` says where the check stopped and that
overwriting also lets the later steps write over data unchecked.
- The dry runs and the run share the parameter budget as they share the step budget.
- A dry run puts back the Go To list (`edit.goTo` with `record: true`).
- The footprint groups the touch log by sheet once, instead of filtering a million entries once
per sheet.
- Docs: what `ok` means; RAND's sequence moves on after a check, as after any recalculation.
- Tests: `the_check_says_whether_the_run_would_go_ahead`,
`the_question_after_a_partial_footprint_says_what_the_answer_covers`,
`a_dry_run_puts_back_the_go_to_list`, the shared parameter budget, and `ok` under `ask` in
`conflicts_ask_fail_or_overwrite`.
- A run that rolls back after writing a file no longer says "Nothing was changed.": the message
says the workbook was put back and names the step whose file stays (`StepError::wrote_files`,
also after a Stop with rollback).
- `macro.run` refuses an input the macro doesn't have (a misspelt input bound the selection),
an input that isn't reference text, and `onConflict`, `overwrite`, `approve` or `timeLimit` of
the wrong kind, which it used to ignore; `null` still counts as not given.
- A step no longer copies itself, and Repeat and If no longer copy their bodies, on every pass:
a body is copied once per run. A run passes its commands at most 256 MB of parameters, all
steps together, which bounds a run's work on the web build, where there is no clock.
- Every step except an approved file step runs inside the scripts' file sandbox, so a command
that touches a file under a parameter other than `path` can't do so from a macro.
- Docs: moving cells that hold data counts as writing over them.
- Tests: `run_parameters_that_would_be_ignored_are_refused`,
`a_run_passes_a_bounded_amount_of_parameters`, and the rollback message in
`each_step_is_checked_for_writes_over_data`.
Inputs, the output and a Repeat's range were bound once, by sheet index and coordinates. A step
that inserted a sheet in front of an input's sheet sent every later token to the new sheet, and
rows inserted above an input left the tokens on the cells that moved into its place; the run
wrote into the wrong cells and reported success.
- Commands record the structural edits they make as they rewrite references (`Ctx::edits`; while
a macro step runs, `DocState::edits` collects them).
- After each step the machine matches the sheets as the run's guard does and moves the inputs,
the output and the ranges of the Repeats in progress by those edits, with the formula engine's
own adjustment.
- A step that deletes an input's or the output's cells, or their sheet, or changes the size of a
range a Repeat is going over, stops the run, which rolls back.
- Test: `inputs_and_the_output_follow_their_cells_when_a_step_moves_them` (a sheet inserted in
front, rows inserted above, an input deleted, a Repeat's range grown).
A footprint is not proof of what the run will do. RAND gives the run other values than the dry
run before it (the recalculation's random sequence goes on), and NOW moves on, so an If can take
another branch in the run and write over data the dry run never reached; under `fail` the run
went ahead and wrote over it.
- Unless the rule is `overwrite`, every run checks each step as it goes, as a run after a
partial footprint already did; a write the footprint didn't see stops the run, which asks under
`ask` and fails otherwise, and rolls back.
- Test: `a_run_that_takes_another_path_than_its_check_is_still_checked` (an If on `=RAND()` over
40 runs under `fail`; before, one of them wrote over B1).
Split from the pull request below at the review's request; the code is the runner's footprint
as reviewed there, unchanged.
- `macro.run` first runs the macro in dry-run mode on the real workbook (unless the rule is
`overwrite`) and puts everything back: the workbook, its selection and history through the
dry run's undo group, and the session state a dry run may touch (`Saved`: the active
workbook, open documents, the saved mark, selections, unlocked ranges, trust, clipboard,
preferences, mode, journal and recording, queued requests, format painter, find, ruler,
hidden windows, drawing tool and the comment panes).
- Cells it would write outside its inputs that hold data, and sheets and objects it would
delete, are conflicts: `ask` asks before anything runs, `fail` fails, and `shiftDown` and
`shiftRight` insert cells over the block of every written cell and check again. A shift is
refused when it would move an input, when the macro deletes sheets or objects, or when the
touch log is full.
- A dry run stops before a file step and before Solver; the footprint is then partial, and the
run checks each step as it goes.
- The dry runs and the run share one clock, one time limit and one step budget.
- `macro.check` reports the inputs, the output anchor, approval needed and the footprint.
- Tests: the footprint tests that moved with this code.
The review asked for the runner to be split. This pull request keeps the policy, the input
checks, the step machine and `macro.run`; working out before a run where a macro writes (the
footprint dry run, shifting data to make room, `macro.check`) moves to the pull request above it.
- Unless the conflict rule is `overwrite`, every run now checks each step as it runs, which the
run did before only after a partial footprint: a step that writes over cells outside the inputs
that held data when the run started, or deletes sheets or objects that were there, stops the
run, which asks (`ask`) or fails, and the run rolls back.
- `shiftDown` and `shiftRight` need the footprint, so here they stop the run as `fail` does.
- Moved: `Footprint`, `Saved`, `footprint`, `diff`, `make_room`, `macro.check`, the dry-run
branches of the machine, `policy::previewable`, `DocState::touched` and the touch log,
`Session::journal_dropped` and `truncate_journal`, and the tests of those.
- Test: `each_step_is_checked_for_writes_over_data` (the review's counterexample: a permitted
file step or Solver, then a write over B1, under `fail` and `ask`).
Main adds UiRequest::InsertName (Paste Name, Use in Formula), which puts a name in the cell
editor; a macro step drops it, as it drops a request to start editing a cell.
The recorder expanded an automate.runScript step from the workbook as it
stood when the recording stopped, so a script edited or deleted during the
recording changed the saved macro, or dropped the step. The run now leaves
the steps it ran with the recorder (Recorder::ran, by step index, within
the byte budget), and the conversion reads those, falling back to the
workbook only when they could not be kept.
* Go To: names match in any case and script, as formulas match them
* Paste Name: only the names in view are laid out
* Docs: LAMBDA recursion limit and called error names; one Page Layout view row with the side margin marked unverified
* Go To and Paste Name: cargo fmt