Commit Graph
292 Commits
Author SHA1 Message Date
prxnvt bf456c9ff8 Engine: a macro's footprint dry runs keep to the run's timeLimit
footprint always ran its dry run with the default 60 second limit, so
macro.run with timeLimit 5 could run for over two minutes (a dry run,
then another after a shift) and fail with "ran longer than 60 seconds".

footprint now takes the run's clock and limit. make_way starts the
run's clock before the dry runs, so the checks and the run share one
limit, and macro.check takes timeLimit too.
2026-10-22 02:26:32 -04:00
prxnvt 54c08645b9 Engine: macro.run and macro.check: input checks, footprint dry run, policy
macro.run runs a macro as one undo step ("Run Macro: <name>"); an error
at any step rolls everything back and names the step and its path (3,
2.1 inside a Repeat, 4.then.1 inside an If). It works in four parts:

- Policy (macro_policy). File, app, window, automate and macro commands
  and Undo and Redo never run in a macro. Reading or writing files
  (data.getData, data.fromTextCsv, insert.picture, Page Layout
  Background, or an export with a path) needs the command in the
  macro's permissions and the person's approval. A macro in a workbook
  opened from a file asks before it runs. Approval holds for the
  session until the macro's content changes; it is asked for with the
  new EngineError::Approve and given with approve: true. A step that
  would open a dialog or a web page fails instead.
- Inputs. Each declared input is bound (given, its default, or the
  selection for the first one), whole columns shrink to the used range,
  and the shape and column types are checked with messages that name
  the cells: "Input column 2 (Amount) must hold numbers; C4 holds text".
- Footprint. Unless the macro may overwrite, it first runs in dry-run
  mode on the real workbook inside an undo group that is rolled back,
  with the session's clipboard, journal, windows and the document's
  trust put back. CellStore::diff on every sheet, plus the cells
  commands wrote (DocState::touched), sheets and objects, gives what it
  would write outside its inputs; cells that only recalculated do not
  count. Writing over data, deleting a sheet or deleting an object is a
  conflict: ask (the new EngineError::Confirm, so programmatic runs fail
  unless they pass onConflict or overwrite), fail, overwrite, or shift
  the data down or right with Insert Cells and check again. A step that
  would fail, for example past the edge of the sheet, fails the run
  before anything changes. File steps and Solver stop the dry run early
  and are reported.
- Steps run on a machine with an explicit frame stack: Repeat over the
  rows or columns of a range with Row or Column bound, If on one cell
  with Excel's comparisons, and Stop, which keeps the changes or rolls
  them back. A run executes at most 100,000 steps within a time limit
  (60 s by default, timeLimit to change; the web build has no clock, so
  only the step limit applies there).

macro.check reports the bound inputs, the output anchor, the approval
needed, the footprint and the conflicts without changing anything;
nothing from an unapproved macro runs, not even as a dry run.
2026-10-22 02:26:32 -04:00
prxnvt 471114e296 Engine: a visible name of the item shape is not workbook data
The item store read, listed and removed every workbook-scope name of the `_gc_<key>_<n>` shape,
visible or not, so saving an item removed a person's own visible name that happened to look like
one. Items are the hidden names the store writes; a legacy `_sc_<key>` name still reads. A visible
`_gc_` name is left alone by every operation, and writing an item that would need that name is
refused before anything changes: a hidden name beside it with the same name is the one an xlsx
file drops, so the item would be lost on save.
2026-10-22 02:24:55 -04:00
prxnvt 0457f102a0 Engine: pasting only formats from a file leaves the destination's trust alone
Paste Special › Formats copies cell styles and merged areas. It copies no conditional formats,
formulas or validation, so it carries nothing that runs. It still marked the destination
FromFile: copying a formula cell from an opened file and pasting its formats into a new workbook
made that workbook ask before running its own active content.

Formats pastes now count as passive, like values, column widths and notes. The comment and
docs/architecture.md no longer say that pasted formats carry trust.
2026-10-21 17:27:20 -04:00
prxnvt f4e05534a3 Engine: a command that runs its own undo groups makes one undo step for itself
execute treated any change in the group epoch as 'the group owns the step'. A command that edited outside its own group, or whose group rolled back before it edited, left that edit with no undo step or only part of it. Now only a group that was open before the command (or is still open after) owns the step; a command that opened and closed its own groups replaces their steps with one entry for the command.
2026-10-21 15:23:08 -04:00
prxnvt 83ae63f793 Engine: a stash item with only later chunks reads as missing and falls back to the legacy name
With chunk _gc_k_2 but no _gc_k_0, get returned Some("") and never looked at _sc_k. It now takes the legacy path unless chunk 0 exists.
2026-10-21 15:18:15 -04:00
prxnvt e165eaf3a6 Engine: a linked paste of values from a file carries the file's trust
A linked paste writes formulas even when it pastes values, but the values exception skipped the trust update, so a Local workbook stayed Local after linking to a FromFile one. The exception now applies only to unlinked pastes.
2026-10-21 15:18:15 -04:00
prxnvt 7d1fee8c78 Engine: workbook data in chunked hidden names (cmd/stash.rs)
GridCraft keeps workbook data in hidden defined names so it travels
inside the xlsx file. The existing items write one name holding one
string literal, which past Excel's 255-character literal and
8,192-character formula limits gives a file Excel does not accept.

cmd/stash.rs stores an item `key` as hidden workbook-scope names
_gc_<key>_0, _gc_<key>_1, ..., each a formula of string literals of at
most 255 characters (UTF-16 units, as Excel counts them) joined with &,
at most 8,192 characters per name. Reading concatenates the chunks in
order and stops at a gap; it also accepts the older single _sc_<key>
name. Names are matched case-insensitively and only at workbook scope.
Keys are limited to letters, digits, _ and ., items to 4,194,304
characters, and junk around or inside the literals, duplicate or
out-of-order chunks, non-JSON and over-deep JSON all read safely.
keys(prefix) lists the stored items that start with a prefix.

Custom views, scripts, the watch list and scenarios keep their current
storage; nothing reads or writes the new names yet.

docs/architecture.md describes the engine building blocks added in this
series: undo groups, the recording buffer, document trust and this
storage.
2026-10-21 14:05:48 -04:00
prxnvt b7bce3bc09 Engine: document trust for workbooks opened from files
Active content such as macros must not run only because a workbook was
opened. DocState gets a trust state: Local for workbooks made in the
session (new workbooks and samples) and FromFile for anything opened
with file.open. Active content in a FromFile workbook asks before it
runs. A second window on a workbook shares its trust.

The clipboard carries the trust of its source document. Pasting
formulas, formats (conditional formats hold formulas) or validation
from a FromFile workbook into another workbook makes that workbook
FromFile too; pasting values, column widths or notes carries nothing
active and changes nothing. Sheet moves and copies stay inside one
workbook, so its trust is unchanged. document.inspect reports the trust.
2026-10-21 14:05:48 -04:00
prxnvt acfd38f309 Engine: undo groups make several commands one undo step
Session::grouped(label, f) runs f as one undo step on the active
document. On success the group leaves exactly one undo entry holding the
workbook from before the group; on error it restores the workbook,
selection, undo and redo stacks as they were and returns the error.
group_begin, group_end and group_rollback do the same across separate
calls, for an action that runs one step per call.

While a group is open on a document, undoable commands on it add no
undo steps of their own, so a long group neither grows the history nor
pushes older entries past the undo limit, and Undo and Redo are
disabled there. Groups nest: the outermost one makes the step, and a
failing inner group rolls back only its own changes. Closing a document
drops its groups, and a command that panics drops the groups it opened.
history.list reports whether a group is open.

Insert Copied Cells folds its nested steps into one undo step by
counting revisions; inside a group the nested steps push no entries, so
it now leaves the history alone there instead of removing entries from
before the group.
2026-10-21 14:05:41 -04:00
Brandon Thomas ab6fd48856 Merge pull request #1867 from HyakoV3:fix/1808-csv-as-text
# Conflicts:
#	docs/file-format-parity.md
2026-10-19 12:22:05 -04:00
Brandon Thomas 12ee27a177 Merge pull request #1943 from Mouhand-Kaddo:fix/table-name-length
# Conflicts:
#	docs/localization-parity.md
2026-10-19 12:22:01 -04:00
Brandon Thomas 8d562e9b56 Merge pull request #1879 from Silvertree2010:fix/h2804-custom-autofilter-is-less-than-l 2026-10-19 12:21:34 -04:00
Brandon Thomas 937cfd1e91 Merge pull request #1903 from justinjohn0306:fix/1855-backspace-undo 2026-10-19 12:21:29 -04:00
Brandon Thomas 8e8e2ba73c Merge pull request #1920 from vedran-dobos:range-fill-formats 2026-10-19 12:19:51 -04:00
Brandon Thomas 851583365f Merge pull request #1934 from Mouhand-Kaddo:fix/arabic-name-length 2026-10-19 12:19:23 -04:00
HaSh3003 5eae222fbc Outline: + shows zero-width hidden columns at the default width (fixes #1714)
Templates hide grouped columns as width="0" hidden="1". Show Detail and the level buttons only cleared hidden, so the columns stayed invisible; they now reset a zero size, as Unhide does.
2026-10-19 18:21:54 +03:00
Mouhand-Kaddo eee872e429 M12.AR16: limit table names to 255 characters 2026-10-19 17:04:06 +04:00
Mouhand-Kaddo fb7b23dbad M12.AR8: count characters, not bytes, in name length limits 2026-10-19 15:33:05 +04:00
Vedran DobosandClaude Opus 5.5 6ac8ad9216 fix(edit): Ctrl+Enter gives each cell the number format typing gives it
range.fill parsed the input once at the active cell, then put back the old
style of every target that already held a cell, so 5%, $5, 3/4/2026 or
=DATE(...) lost their automatic format there (0.05, 5, 46085), and empty
targets took the active cell's style. Each target now gets what
input_to_cell gives for it (parsed once per distinct style), as typing does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:18:34 +02:00
justinjohn0306andClaude Opus 5.5 dcd4d1d13f #1855: redo selects the redone cell
Undo stored the selection at the moment of undoing (wherever the user had
clicked) in the redo entry, so redo jumped back there instead of to the
cells it changed. The redo entry now keeps the undone step's selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:16:13 +05:30
NetterSilverandClaude Haiku 5.5 4f3853d4a9 Hide blank and text rows for numeric ordering custom filters
Found while testing crates/engine/src/cmd/data.rs:550 (custom_ok, used from apply_filter at :623)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 20:25:07 +02:00
Eduardo GarciaandClaude Sonnet 5.5 95f8206bb9 #1808: file.open can keep every CSV/TSV field as text
file.open takes {csvAsText: true}: a CSV, TSV or TXT file opens with every field as the text
it holds, with no number, date or boolean conversion, so identifiers such as GTINs, SKUs and
ZIP codes keep their digits and leading zeros and save back unchanged. The default is
unchanged. The CsvOptions.parse_values switch already existed in the reader.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 12:49:32 -03:00
Dante BertuzziandClaude Opus 5.5 19f4387cca Conditional formatting: show a rule's number format (fixes #1163) (#1769)
The grid's look() and print's cell_look() layered a rule's fill, font and
borders onto the cell style but not its num_fmt, so a cell under a rule with a
number format kept its own. Take the rule's code when it isn't General (in a
differential style General means "not set", as layer_under treats it).

Print also re-formatted every non-General cell from its own style through
display::cell_text; format with the look's code instead, through a new
display::value_text_as that value_text now calls (CSV/Text output unchanged).
Show Formulas keeps cell_text.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 18:37:09 -06:00
NetterSilverandClaude Haiku 5.5 217a8907ff Reject unknown value-field func in insert.pivotTable (#1757)
Found while testing crates/engine/src/cmd/pivot.rs:678 (func_param at :248)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-17 18:34:28 -06:00
Harrison O'Connor-Hoover 79e85c1b80 fix(edit): recalculate every cell changed by large clears (#1032) (#1128) 2026-10-15 13:38:13 -06:00
Brandon Thomas ebe0138d30 Merge main into #855
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 01:52:08 -04:00
Brandon Thomas be003d44af Merge main into #863
# Conflicts:
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 01:40:22 -04:00
Brandon Thomas 2d0a3f8a47 Merge main into #780
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/insert.rs
#	crates/engine/src/tests.rs
2026-10-13 01:02:28 -04:00
Brandon Thomas 20d6923cb6 Merge main into #766
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/sheet.rs
2026-10-13 01:00:59 -04:00
Brandon Thomas 9972a75f10 Merge main into #824
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/tests.rs
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 00:53:41 -04:00
Brandon Thomas 0068c8f517 Merge main into #649
# Conflicts:
#	crates/ui-egui/src/grid.rs
2026-10-13 00:48:39 -04:00
Brandon Thomas 2f33d43546 Merge main into #591
# Conflicts:
#	crates/engine/src/cmd/data.rs
2026-10-13 00:41:59 -04:00
Marco Arnulfo c7cb91ea53 Merge remote-tracking branch 'origin/main' into fix/xlsx-xml-maps 2026-10-13 06:33:03 +02:00
Brandon Thomas 6dd8f7abe8 Merge main into #796
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
2026-10-13 00:08:27 -04:00
Brandon Thomas e5f31a02f3 Merge main into #786
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
2026-10-13 00:08:15 -04:00
Brandon Thomas d2bd177b98 Merge main into #753
# Conflicts:
#	crates/calc/src/tests.rs
2026-10-13 00:08:09 -04:00
Brandon ThomasandClaude Opus 5.5 45221f5243 Fix-up: rustfmt extra.rs after merging
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:51:11 -04:00
Brandon Thomas 147db777b1 Merge pull request #879 from marcoarnulfo:fix/unknown-save-extension 2026-10-12 23:50:12 -04:00
Brandon Thomas efed7caa01 Merge pull request #826 from marcoarnulfo:fix/xlsx-external-file-hyperlinks 2026-10-12 23:49:53 -04:00
Brandon Thomas d6bb4bdf7a Merge pull request #763 from msaulc:fix/ctrl-shift-arrow-scroll 2026-10-12 23:49:25 -04:00
Brandon Thomas a435ef6ada Merge pull request #720 from dantebertuzzi:fix/cell-set-numeric-input 2026-10-12 23:49:02 -04:00
Brandon Thomas 98c85c6de4 Merge pull request #718 from neilcawse:fix/pivot-values-on-rows 2026-10-12 23:49:00 -04:00
Brandon Thomas 2f38d0162f Merge pull request #704 from isaac-levine:file-export-pdf-menu 2026-10-12 23:48:55 -04:00
Brandon Thomas 89f9e24aad Merge pull request #664 from richard1912:fix/top10-filter-threshold 2026-10-12 23:48:43 -04:00
Brandon Thomas 50853e3def Merge pull request #647 from MarekSpodBiedry:issue-508 2026-10-12 23:48:36 -04:00
Brandon Thomas 02c973536b Merge pull request #640 from MarekSpodBiedry:issue-515 2026-10-12 23:48:26 -04:00
Brandon Thomas 8b845ebb6e Merge pull request #639 from MarekSpodBiedry:issue-516 2026-10-12 23:48:24 -04:00
Brandon Thomas 62100c2542 Merge pull request #638 from MarekSpodBiedry:issue-517 2026-10-12 23:48:23 -04:00
Brandon Thomas e154960ce3 Merge pull request #602 from MarekSpodBiedry:issue-562 2026-10-12 23:35:51 -04:00