Commit Graph
589 Commits
Author SHA1 Message Date
prxnvt c801e50e40 Merge #2152 (f8e1f8df, with main at f6b0cec6) into #478
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/io.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/lib.rs
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-26 00:32:55 -04:00
prxnvt f8e1f8dfc0 Merge #2145 (4d26e395, with main at f6b0cec6) into #2152
# Conflicts:
#	crates/engine/src/io.rs
#	crates/engine/src/lib.rs
#	docs/target-app-parity.md
2026-10-26 00:29:13 -04:00
prxnvt 4d26e395fc Merge #2550 (d4096eee, with main at f6b0cec6) into #2145
# Conflicts:
#	crates/engine/src/lib.rs
2026-10-26 00:26:35 -04:00
prxnvt d4096eee5a Merge #2144 (34eee860, with main at f6b0cec6) into #2550 2026-10-26 00:24:57 -04:00
prxnvt 34eee86053 Merge #2151 (6781aa49, with main at f6b0cec6) into #2144
# Conflicts:
#	crates/engine/src/io.rs
#	crates/engine/src/lib.rs
2026-10-26 00:24:11 -04:00
prxnvt 6781aa4927 Merge main (f6b0cec6) into #2151 2026-10-26 00:21:54 -04:00
Pranav 1cd45cc909 Merge branch 'main' into pr/macros-ui 2026-10-25 23:54:09 -04:00
Pranav e85297e26c Merge branch 'main' into pr/macros-engine 2026-10-25 23:49:20 -04:00
Pranav b552b7a929 Merge branch 'main' into pr/macros-record 2026-10-25 23:49:08 -04:00
Pranav 910b50c1dd Merge branch 'main' into pr/macros-run 2026-10-25 23:48:13 -04:00
aiongg f102fe29a1 Merge g3 into g4
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/edit.rs
#	crates/engine/src/cmd/formulas.rs
#	crates/engine/src/cmd/inspect.rs
#	crates/engine/src/cmd/pivot.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/tabs.rs
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-26 02:45:41 +00:00
aiongg 929495e274 Merge g1 into g2
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/extra.rs
#	crates/engine/src/lib.rs
#	crates/engine/src/tests.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-26 02:37:33 +00:00
aiongg 22aec7e667 G1: build on current main
Scripts can't run the View tab's window commands (Switch Windows, New Window, Hide, Unhide):
they change the active workbook, as window.* steps do. The Switch Windows button names its
accessibility role with egui::Role (egui 0.37).
2026-10-26 02:37:27 +00:00
aiongg 903bece86f Merge upstream main into g1
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
#	crates/ui-egui/src/lib.rs
#	docs/ui-parity.md
2026-10-26 02:23:11 +00:00
Dylan M. Taylor 42bbf6baa2 refactor: split complex functions for Sonar S3776 (#2612)
Extract group_bracket from outline layout and move_marks from data
outline_settings. No behaviour change.
2026-10-25 21:39:17 -04:00
Dylan M. Taylor 52cf825395 refactor: split complex functions for Sonar S3776 (#2609)
Extract helpers from auto_sum (selection and single-cell guess), update_cursor
(drag, hover, freeze, and header-edge cursors) and read_styles (dxf reader).
No behaviour change.
2026-10-25 21:37:09 -04:00
3b65b9af58 Sonar maintainability: explicit imports, method paths, smaller functions (#2412)
* Sonar: explicit imports, fn paths for closures, extract helpers for cognitive complexity

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Fix imports

* Fix serde imports

* Fix engine imports

* Fix clippy gate and a type error from the explicit-import change

Remove the unused imports the review flagged (cf.rs, pivot.rs, tables.rs, cmd/sheet.rs) so clippy -D warnings passes, and point the backstage parent-folder lookup at Path::to_str, which the fn-path rewrite had broken.

Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Apply rustfmt to the explicit-import cleanup so the fmt gate passes

* Share the per-directory font path expansion in theme.rs

The candidate font lists repeated the same loop over the Linux font
directories in every role. A single helper expands them in the same order,
so the lookup order is unchanged and the duplicated blocks go away.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Read the Windows fonts folder in one place in theme.rs

Every script-font list repeated the same WINDIR lookup. A single helper
returns the same path, so the duplicated lines go away without changing
which font files are tried.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Build the light and dark UI palettes from one table in theme.rs

Tokens::light() and Tokens::dark() listed the same 40 colour fields in the
same order, so every change had to be made twice. A single palette
constructor takes the mode and picks each colour's value, so each field is
written once and both modes keep their exact colours.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Share the Noto CJK font list between the CJK script lists in theme.rs

The Japanese, Simplified and Korean lists each repeated the same loop over
the Linux CJK directories with a different locale file name. One helper
builds that loop, and each list keeps its own face index, so the lookup
order is unchanged.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Share Linux Noto directory list across Thai and Hebrew font lookups

Removes the duplicated directory arrays flagged by SonarCloud new-code duplication.

* Reuse in_dirs for Thai and Hebrew Noto font candidates

Removes the remaining duplicated per-directory loops flagged by SonarCloud.

* Share one font-list builder for the Thai and Hebrew faces

The two per-script functions repeated the same Windows, macOS, Linux and
variable-font setup. A single script_fonts helper now builds both lists,
keeping the candidate order unchanged, which clears the Sonar duplication gate.

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-25 12:32:02 -04:00
Brandon Thomas 4a44dec500 Merge pull request #2515 from Fubeo:fub/edit-note-keeps-thread 2026-10-25 09:36:36 -04:00
Brandon Thomas 27e96a9a58 Merge pull request #2561 from arcanii:autosum-across-a-row 2026-10-25 09:36:34 -04:00
Bryan MarkandClaude Opus 5.5 802c782360 AutoSum: a selected row gets its total to the right, as in Excel
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>
2026-10-25 20:34:30 +08:00
prxnvt ffacf7405f Docs: Record Macro's shortcut example uses a letter a macro may take
The example was K, which GridCraft uses (Group), so a caller who copied it was refused; it is M
now, in the command's description and on the macro page.
2026-10-25 06:48:57 -04:00
prxnvt c442da9221 Engine: stopping a recording says when it left the chosen shortcut off
Record Macro checks the Ctrl+Shift letter when recording starts, but
another macro can take it before recording stops; the save then leaves
the shortcut off, and nothing said so. The result now names it in
shortcutDropped, the toast after Stop says the macro was saved without
it, and the macro page describes it.
2026-10-25 06:48:57 -04:00
prxnvt 7c66aaa2d3 Engine: Record Macro takes a name, shortcut and description
macro.record checks name, shortcut and description when recording
starts and keeps them for the save, so a name that is taken, too long
or holds line breaks, or a Ctrl+Shift letter that GridCraft or another
macro uses, is refused before anything is recorded. With empty
parameters it records under the next MacroN, as before. A name or
description given when stopping still wins, and a shortcut that another
macro took while recording is dropped.

The shortcut rules (macros::parse_shortcut, shortcut_text,
shortcut_conflict, RESERVED_SHORTCUTS and check_new_name) live in the
engine, so the Record Macro dialog, Macro Options and agents agree.
2026-10-25 06:48:57 -04:00
NetterSilverandClaude Haiku 5.5 80e2041159 Remove sparklines when their row or column is deleted
Found while testing crates/engine/src/cmd/sheet.rs:~199 (shift_sheet_features, sparklines loop)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 09:39:12 +00:00
prxnvt 90a46da496 Engine: macro.run's description lists only what it returns here
`shifted` comes with the footprint in the pull request above; here a shift rule stops the run as
`fail` does, which the description now says.
2026-10-25 04:13:05 -04:00
prxnvt fdd26876fb Merge #2145 (227078f5, with the footprint pull request and #2144) into #2152
# Conflicts:
#	docs/macros.md
2026-10-25 03:21:39 -04:00
prxnvt 460a72d3bb Engine: a recording keeps what ran and plays back as it was recorded
- 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.
2026-10-25 03:19:35 -04:00
prxnvt bc0d3035fb Engine: kept macros count toward the macro limit and keep their names; saving over a macro needs replace: true; renaming checks the size
- 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.
2026-10-25 03:18:26 -04:00
prxnvt 7dad14e77e Engine: the macro debugger locks its workbook to looking around, ends with Stop Action and checks its parameters
- `macro.debug` refuses to run as a step of a script or macro, and is never recorded (the
  recording buffer and Record Actions' journal both leave it out); a debugger whose undo group
  something else closed locks nothing, and its next action says the run had ended instead of
  reporting a keep or a rollback. Tests: `the_debugger_runs_on_its_own_and_is_never_recorded`,
  `a_debugger_whose_group_was_closed_elsewhere_locks_nothing`.
- Stop Action is no longer refused under the debugger: it rolls the run back, ends the debugger
  and names the macro. Test: `stop_action_stops_a_macro_being_debugged` (was
  `stop_action_waits_while_a_macro_is_being_debugged`).
- Between steps the debugged workbook runs only `macro.debug`, queries and an allow-list of
  commands that look around (selection, sheet changes, Go To, Find, Copy, zoom, Switch Window,
  Close, Stop Action); everything else is refused, saving and view modes included. The run's
  active sheet and selection are put back before its next step. Test:
  `between_steps_only_looking_around_runs_and_the_run_continues_where_it_was`.
- Only the top-level `macro.debug` call rolls the run back after a panic; a panic nested in a step
  is that step's error. Test: `a_panic_inside_a_step_is_that_steps_error_and_the_run_goes_on`.
- `keep` and `breakpointsOnly` must be true or false, breakpoints must be step paths of the macro
  (at most 10,000), and the action is checked before breakpoints change. Test:
  `debugger_parameters_are_checked_before_anything_changes`.
- Answers: `executed` counts the run's own steps (not the footprint's), `shifted` and `wroteFiles`
  are reported, a rollback's message names the files that stay, `changed` lists deleted and
  renamed sheets and at most 500 entries with `changedCells`, and a Repeat over whole rows or
  columns shows the part in use. Tests: `the_debuggers_answers_count_its_own_steps_and_say_what_stays`,
  `the_debugger_lists_deleted_and_renamed_sheets_and_caps_the_list`,
  `the_debugger_shows_a_repeat_over_whole_columns_as_the_run_goes_over_them`.
2026-10-25 03:17:42 -04:00
prxnvt 60e0039524 Merge #2145 (ce2ab9a4, with the footprint pull request and #2144) into #2152
# Conflicts:
#	docs/macros.md
2026-10-25 02:50:32 -04:00
prxnvt ce2ab9a473 Merge the footprint pull request (c715c36c, with #2144) into #2145
# Conflicts:
#	docs/macros.md
2026-10-25 02:50:17 -04:00
prxnvt 3708c80168 Engine: macro.check says whether the run would go ahead; the question names what the answer covers
- `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`.
2026-10-25 02:49:35 -04:00
prxnvt 567ed0abc9 Merge #2144 (65cbcdd6) into the footprint pull request
# Conflicts:
#	crates/engine/src/cmd/mod.rs
#	crates/engine/src/lib.rs
#	crates/engine/src/macros/run.rs
#	crates/engine/src/tests/macros.rs
#	docs/macros.md
2026-10-25 02:45:42 -04:00
prxnvt ad79b071ce Engine: a macro's inputs, output and Repeat ranges follow their cells when a step moves them
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).
2026-10-25 02:40:21 -04:00
prxnvt 0719458114 Engine: macro.check and the footprint: where a macro writes, worked out before it runs
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.
2026-10-25 02:33:26 -04:00
prxnvt 5860857937 Engine: a macro run checks each step for writes over data; the check before the run moves to its own pull request
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`).
2026-10-25 02:32:37 -04:00
prxnvt b40fa4e065 Merge #2145 (2fb31f11, with main at 17b9f6ff) into #2152
Main refuses window.* steps in scripts, so the recorder test's script step that a macro may not
run is app.commands.
2026-10-25 02:29:03 -04:00
prxnvt 2fb31f11f2 Merge #2144 (8ea4e279, with main at 17b9f6ff) into #2145 2026-10-25 02:26:03 -04:00
prxnvt 8ea4e27918 Merge #2151 (63a29800, with main at 17b9f6ff) into #2144
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.
2026-10-25 02:26:02 -04:00
prxnvt 63a29800a3 Merge main at 17b9f6ff into #2151 2026-10-25 02:19:57 -04:00
prxnvt b995c7c9a0 Engine: a recorded script run keeps the steps that ran
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.
2026-10-25 02:18:58 -04:00
prxnvt 1e34a76428 Merge #2145 (c225caf3, with #2144, #2151, #465 at 3f8e6c28 and main at 6ead1fb5, where #462 is merged) into #2152
# Conflicts:
#	crates/engine/src/cmd/extra.rs
#	docs/architecture.md
2026-10-25 02:18:55 -04:00
prxnvt c225caf30a Merge #2144 (9331b05f, with #2151, #465 at 3f8e6c28 and main at 6ead1fb5, where #462 is merged) into #2145 2026-10-25 02:18:21 -04:00
prxnvt 9331b05f62 Merge #2151 (9fdf03cf, with #465 at 3f8e6c28 and main at 6ead1fb5, where #462 is merged) into #2144
# Conflicts:
#	crates/engine/src/lib.rs
#	docs/macros.md
2026-10-25 02:17:32 -04:00
aiongg 17b9f6ff01 Paste Name and Go To: follow-ups from the batch review (#2540)
* 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
2026-10-25 01:24:24 -04:00
aiongg 24e49d18ad Batch: Subtotal puts summary rows below their detail in the same undo step (#2482) 2026-10-25 04:27:21 +00:00
aiongg f17179ceeb Batch: Show Details turned off says so in plain words; the option is in the command's docs (#2461) 2026-10-25 04:22:02 +00:00
prxnvt e88760b480 Engine: an item too large to read takes no place in a stash listing
stash::items capped the items at MAX_ITEMS before reading them, so one
over the size limit took a place (a readable item past the cap was then
left out with fewer than MAX_ITEMS listed) and one past the cap was
counted in `more` though it could never be listed. Items are now read
first: the first MAX_ITEMS readable ones are listed and only readable
ones past them are counted.
2026-10-25 00:10:14 -04:00
aiongg 87a649c763 Batch: Paste Name lists the names once, finds local names by hash and refuses a name it doesn't list (#2410) 2026-10-25 03:56:45 +00:00
aiongg 6194f8241d Merge storytold/gridcraft#2522: Charts: 100% Stacked Area draws each value as a share of its category's total
# Conflicts:
#	ROADMAP.md
2026-10-25 02:54:23 +00:00