The default still reads a string with typed input, which is en-US: "05.04.2026"
from a dd.mm.yyyy export became 4 May, "2.500" became 2.5, and the command said
nothing (#2255). `parse: false` (PR #2267) lets a caller keep the text, but a
script that does not pass it still cannot notice the change.
The result now names them: `converted` lists the strings typed input read as a
number (dates and times included), a boolean or an error, with the cell, the
input and what the cell shows, and `convertedCount` is the total. It is capped
at 20 entries and 32 characters per string, so a big write does not put a second
copy of its data in the result. A string written as a formula (`=1+1`) and a
leading apostrophe are not conversions. `cells` and the default reading of the
values are unchanged.
Two `parse: false` fixes from the review of #2267:
- An empty string clears the cell, as `null` and the default path do. Storing an
empty text cell left a cell that is not blank: `values: [["", ""]]` gave
COUNTA 2 and ISBLANK FALSE.
- MCP `write_range` takes `parse`, so an agent can opt out too; the tool's
description and docs/mcp.md say so.
Tests: the issue's examples under both settings, the report and its cap, the
empty string, and an XLSX save/reopen that keeps the text as text and the
default's date as a date.
`broadcast_len`, the MAX_CELLS guard shared by MAP, the binary operators,
TEXT and array IF, multiplied `rows` and `cols` as `usize`. On wasm32 a
broadcast such as 65536 x 65536 (2^32 cells) wraps that product to 0 and
slips past the cap, after which the allocation and loop run far past
MAX_CELLS. The product is now formed in u64 and converted back only after
the cap check, so the guard means the same on every target.
`single`, the per-cell scalar check in MAP and MAKEARRAY, compared
`rows * cols` with 1. A constructed array stores exactly `rows * cols`
values, so it now tests `data.len()` directly: the same answer without an
unchecked product.
Tests cover MAX_CELLS exactly, one cell over, the 2^32 pair that wraps to
zero on a 32-bit usize, a full column x row (2^34), and MAP itself just
over the cap and at 16384 x 16384.
Closes#2165
egui clicks a focused control on Enter as on Space, so Enter on the
focused category list opened it, and the dialog's OK handling took the
same key press and inserted the selected function. Enter in the open
list did the same while picking a category.
The dialog now leaves Enter to the category list while the list has the
focus or its menu is open (or was open as the frame started, since Enter
on a row closes it in that frame). Enter in the search box or on the
function list still confirms.
Test: function_library::enter_on_the_category_list_does_not_confirm_insert_function
tabs to the list, presses Enter (the list opens, the dialog stays open,
nothing is inserted), presses Enter again in the open list (still open,
nothing inserted), then Shift+Tab back to the search box, where Enter
confirms.
The search box asked for the keyboard focus every frame, so Tab moved to the
category list for one frame and the focus went back to the search box. The
dialog now gives the search box the focus once, when it opens, through the
module's take_first_focus, as the other dialogs do.
Test: insert_function_lets_tab_reach_the_category_list (the search box has
the focus and takes typing; after Tab the category list keeps it over several
frames; Space opens the list).
formulas.recentlyUsed scanned the session journal for cell.set inputs.
It missed formulas entered by range.* commands, lost them when the
journal dropped its oldest entries (a large file open or a long script
is enough), and started empty in every session. The Function Library's
Recently Used menu and the Insert Function dialog list what it returns.
- Prefs::recent_functions (a new engine module, recent.rs) holds the
ten functions most recently entered in a formula, newest first.
Session::execute updates it for every cell.* and range.* command it
journals, from any source (the interface, the control channel, MCP or
a script). It reads at most 2,000 of the formulas a command enters,
starting from the last one; formulas longer than Excel's limit are
skipped. formulas.recentlyUsed
returns it. It never reads the journal back, so the journal's caps do
not affect it.
- The list is kept with the other engine preferences (prefs.json, the
browser's storage on the web), so it survives a restart as Excel's
does. It is read leniently: a malformed value loses only this list. A
fresh install starts from common functions.
The Insert Function dialog is titled "Insert Function", as in Excel (it
said "Formula Builder"), and gains a working category list: Most
Recently Used, All, then each category in Excel's order. A category may
be given by label or by engine id, so a Function Library menu or the
control channel (ui.dialog.set) opens it on one, and a new search
switches to All. The dialog keeps its translated labels and its search
in the interface language. Each row shows the function's name and its
category in two columns; the category column starts after the widest
name shown, so a long name in any font or formula language stays clear
of it. A function counts as recently used once its formula is entered,
not when OK is pressed. The unused "Formula Builder" translations are
removed.
Tests: engine unit tests for reading functions from formulas (strings,
unknown names, over-long and malformed formulas), the formulas a
command enters (newest first, within the budget), the ten-name list
and the lenient read of prefs.json; tests/it/recently_used.rs for
cell.set and range.setValues from the session, a failed command, a
cleared journal and a save and reload of the preferences. UI tests for
the menu's order and the dialog's title, categories, engine-id
category, search and OK, and for the category column with a wide font.
The Function Library group was a big Insert Function button followed by
AutoSum and seven big category buttons that all drew the same icon, the
widest group on the Formulas tab. It is now laid out as Excel lays it
out when the window is not wide enough for a big button per category:
big Insert Function and AutoSum buttons, then the categories as
labelled small buttons in columns of three:
Recently Used Text Math & Trig
Financial Date & Time More Functions
Logical Lookup & Reference
- The category buttons draw a new code-drawn icon, Icon::FnBook, a book
whose spine and glyph (star, coins, question mark, letter, clock,
magnifier, theta, dots) take the category's colour from the theme's
category palette. icons::paint_accent draws an icon with an accent
colour, and widgets::small_button_accent is a small button that uses
it; small_button is unchanged for its callers.
- AutoSum stays the big split button: the icon inserts SUM, the label
and arrow open the AutoSum menu, which now reads Sum, Average, Count
Numbers, Max, Min and More Functions… and is shared with Home ›
Editing. Its tooltip names the control only, so the shortcut comes
from the platform lookup (Alt+= on Windows and Linux).
- Each category menu lists its functions with their descriptions on
hover and ends with Insert Function…, which opens the dialog with
that category. More Functions cascades into Statistical, Engineering,
Cube, Information, Compatibility and Web. Recently Used lists what
the engine's formulas.recentlyUsed returns.
- The function categories, the function list (built once) and the menus
live in a new function_library module; the dialog's category labels
come from it.
- keytips.json gains MR for Recently Used, Excel's sequence. Labelled
small and big buttons report their label to accessibility.
- The one-line labels (Date & Time, Lookup & Reference, Math & Trig,
More Functions) replace the two-line keys in every interface
language, with the same words. Recently Used is translated in Czech,
German, Spanish, French, Japanese, Korean, Portuguese, Russian and
Chinese; the other languages show it in English, their fallback.
Tests: tests/it/function_library.rs covers the layout (columns of three
level with the tab's other columns), each category menu and its Insert
Function… row, the More Functions cascade, the AutoSum split button and
its menu, and the MR key tip. A ribbon unit test keeps Command-key
glyphs out of the ribbon's text. chart_resize picks the chart menu's
Delete now that Home › Cells' Delete has an accessible name.
The view commands are not undo steps and don't mark the workbook changed (as in
Excel), so undoing everything leaves a sheet's view mode as it is. The undo
check now resets the view mode with the other view state before comparing.
Seed 177 (`view.pageBreakPreview` on the sales sample) failed the [undo]
invariant without this.
A sheet's view is not an undo step: Excel doesn't undo a switch between
Normal, Page Layout and Page Break Preview, and view.normal,
view.pageLayout and view.pageBreakPreview are noundo here. But undo and
redo swap in a whole snapshot of the workbook, so undoing an earlier edit
put back the view the sheet had then: set A1, switch to Page Break
Preview, undo, and the sheet was in Normal again (redo switched it back).
Undo and redo now keep each sheet's current view (extra::keep_view_modes),
as they already keep the Watch Window list, carrying it through each step
of a multi-step undo or redo. Sheets are matched by name; while a rename
is undone or redone, by position under the name the restored snapshot
doesn't have. A view kept this way doesn't make a saved
workbook dirty, as set_view doesn't.
Tests: undo and redo with each view, undoing a rename and a new sheet, a
rename and a new sheet undone and redone with steps: 2, and redo back to
the saved state staying clean. Each fails without the change (the view
reverted to Normal).