Bound the all-level walk to the sheet sparse cell store instead of
iterating every position of each precedent area: formulas late in a
wide area are no longer skipped, blank positions are never selected,
and only formula cells extend the frontier. Adds a regression test
(=SUM(A1:Z1) with a formula at Z1) and silences two clippy lints in
the same function.
Co-Authored-By: Claude <noreply@anthropic.com>
With Frame::NONE the text field takes no text margin off its width, so it
wraps at the editor's width less the 2 px inset a side. Measuring 8 px
narrower counted rows the field doesn't have and made the editor reach
over the cells below. A test pins the field's wrap width. Review feedback
on #1974.
Adds formulas.selectPrecedents / formulas.selectDependents engine commands
and binds them to Ctrl+[ and Ctrl+] (Ctrl+Shift walks every level), matching
Excel's shortcut parity. Direct-level traces keep whole precedent ranges;
cross-sheet precedents jump to the owning sheet; an empty trace leaves the
selection untouched with an actionable message.
A request that waited more than 60 s was answered with a timeout and left in
the queue, so `control::poll` ran it on the next UI pass and dropped the reply.
The client was told its command had failed while the command was applied: an
agent that retried after the error ran it twice, and one that gave up left an
unexpected change in the workbook.
`ControlRequest` now carries who has it -- pending, taken by the app, or
abandoned by its sender -- as the report suggests. `poll` claims each request
and skips one it cannot claim; the connection thread abandons the request when
its reply times out, and says which happened:
still queued timed out waiting for the UI (is the window minimised?)
already started timed out waiting for the UI, which had already started the
request, so it may still finish; its outcome is unknown
The second case cannot be called a failure without guessing. This is the shape
DeckCraft settled on for the same bug (its #719), so the two apps now agree.
Fix: #1914
`C1#` gave `#REF!` while the dynamic-array formula in C1 happened to return a
single value -- a FILTER matching one row, a SORT or UNIQUE over one item -- so
`ROWS(C1#)` and `SUM(C1#)` failed and `COUNT(C1#)` gave 0. Every formula that
counts or sums a filtered list therefore broke the moment the list had exactly
one entry. In Excel the formula is the anchor of a 1 x 1 range and the three
give 1, the value, and 1.
A pass recorded the spill only for an array larger than one cell:
if a.rows > 1 || a.cols > 1 {
self.spills.insert(k, a.clone());
}
so a 1 x 1 result left no range for `spill_range` to answer with, and `eval`
turned the missing range into `#REF!`. It is recorded unconditionally now.
The report suggested keying this on the cell carrying `cm`, which turned out not
to be expressible: nothing in the model says "dynamic array" positively -- the
reader tells one from a legacy CSE formula only by leaving `Formula::array`
unset. The arm itself is the better test, because a formula whose result is a
scalar never reaches it: `=1+1` does not become an anchor, which a test pins.
It also matches Excel 365, where `=A1:A1` is a 1 x 1 anchor of its own.
gaps.md §6 still listed four things as missing and function-parity.md still
marked 15-significant-digit comparison as missing, citing #57 — all five
reports are closed and their PRs merged. The work list reads as the place to
pick work from, so a closed entry sends someone after work that is done.
Checked by behaviour, not by issue state, where that was possible:
=0.1+0.2=0.3 -> TRUE
=(0.1+0.2)-0.3 -> 5.55E-17 (full precision kept,
as in Excel: the 15-digit rule
is for comparison)
=MATCH(0.3,{0.30000000000000004},0) -> 1
=COUNTIF({0.30000000000000004},0.3) -> 1
a spill blocked and then unblocked -> #SPILL! then the spill again,
with its dependent back to 6
and by their regression tests for the other two:
`deleting_a_3d_span_end_sheet_keeps_the_rest` (#140) and
`sheet_operations_keep_names_on_their_sheets` (#134).
§6 is left open rather than deleted: it says wrong numbers are the worst kind of
bug, and five closed reports are not the same as the category being clear —
that needs §15's oracle suite. The function counts are untouched; this is
formula-language semantics, not the inventory.
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.
The in-cell editor ignored Wrap Text: it widened to the text's one-line
width, up to the grid's right edge, so a long wrapped cell became one
unmanageable line while edited. With Wrap Text on it now keeps the
column's width, wraps, and grows down to show every line, as Excel's does.
The multiline field is one row tall at least, not four, so a short edit
doesn't reach over the rows below.
### Summary
This change centralizes the translation of user-facing error messages in the UI layer while preserving the original error payloads for machine consumption and AI-agent integrations.
### What changed
- Added a centralized error-translation path in `i18n.rs`.
- The UI now translates only the final display text, instead of altering the underlying engine or protocol error payload.
- If a translated string is not available, the original message is kept as a fallback, avoiding broken or overly-simplified output.
- The translation logic is applied at the presentation layer, not at the source of the error.
### Updated call sites
- Error and alert rendering in `widgets.rs`
- Dialog error presentation in `dialogs.rs`
- Print and feedback failures in `printing.rs`
- Top-level UI error handling in `lib.rs`
### Design intent
- Keep the raw engine errors intact for scripts, control channels, and AI/agent integrations.
- Present a clearer, localized message only to end users.
- Avoid modifying dictionary files or adding new translation entries to the global catalog unnecessarily.
- Keep the behavior consistent with the project’s accessibility and internationalization goals.
### Validation
- The focused i18n test passed successfully.
- The relevant UI test suite passed successfully.
- The repository-required CI gate was run and completed without failures according to the latest verification output.
The symbol was interpolated with Rust's Debug formatting, which escapes a
control character into literal `\n`-style text. It is now written into the
format's quoted literal directly, and a symbol holding a control character is
refused along with a quote or a backslash, so nothing is escaped behind the
user's back.
Test: `accounting_format_takes_the_symbol_it_is_given` covers `\n` and a tab.
`cargo xtask ci`: 8/8 steps, 0 failures.