Addresses CodeRabbit review on #3231, "Reparse consumed separators when a
field is not quoted": parse_tsv read "a<TAB>b"x as one quoted field,
then fell back to its raw text without the separator it had consumed,
so the cells "a, b"x and c pasted back as two. The new copy (which no
longer quotes a cell holding only a quote) makes that text. A field
whose closing quote doesn't end it, or that never closes, is now read
again as unquoted text, so its tabs and line breaks separate cells.
The text read again is bounded by the input's length, so hostile input
stays linear.
Test: a_field_that_is_not_really_quoted_keeps_its_separators.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
edit.find and edit.replace take within: selection, which searches only
the selected cells; Find Next then moves the active cell through them
and keeps the range selected, as Excel does. The Find and Replace
dialog uses it whenever more than one cell is selected (Excel searches
a selected range; one selected cell still means the whole sheet).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses CodeRabbit review on #3226, "Skip hidden corner destinations"
and "Keep merged destinations inside the selected area": with column D
hidden, Ctrl+. on B2:D3 made D2 active; with B1:C1 merged and column C
selected, it made B1 active, outside the selection. A corner in a hidden
row or column, or one whose merged cell starts outside the selected
area, is now passed over like a locked corner on a protected sheet.
Test: next_corner_skips_hidden_corners_and_stays_inside_the_selected_area.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses CodeRabbit review on #3226:
- "Check the next corner against protected-sheet selection rules": on a
sheet protected without Select Locked Cells, Ctrl+. could make a locked
corner active. It now goes on to the next corner it may select.
- "Keep the active cell at a merged cell's anchor": a corner inside a
merged cell made that cell active instead of the merge's top-left
cell. The corner now lands on the top-left cell, and corners in the
same merged cell are one stop.
Selection::next_corner_by takes where each corner leads (or None to skip
it); next_corner keeps its plain behaviour.
Tests: next_corner_skips_corners_the_sheet_protection_does_not_let_you_select,
next_corner_stops_once_on_a_merged_corner_at_its_top_left_cell.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy wrapped any cell that held a double quote in quotes (and doubled
its quotes), so copying `say "hi"` into another app gave
`"say ""hi"""`. Excel quotes only text with a tab or line break; a cell
with just quotes goes on the clipboard as it is.
Pasting text read a field that starts with a quote as quoted even when
its closing quote doesn't end the field, so `"hi" there` (which Excel
copies as it is) pasted as `hi there`. A field is now quoted only when
its closing quote is followed by a tab, a line end or the end of the
text; otherwise it is the text as written, as Excel pastes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel's Next Corner of Selection (Ctrl+., Control+. on a Mac) moves the
active cell clockwise to the next corner of the selected area (top-left,
top-right, bottom-right, bottom-left) and keeps the area selected; the
new active cell is also the anchor, so Shift+arrows then move the
opposite corner. GridCraft had no such command.
New command selection.nextCorner (Selection::next_corner) bound to
Ctrl+. on every platform. A one-row or one-column area goes end to end;
a single cell stays where it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses CodeRabbit review on #3216, "Keep the selection on the sheet it
describes": moving Two!B1:B2 with Sheet1 active selected A1:A2 on
Sheet1, where nothing moved. The selection belongs to the active sheet,
so range.move now keeps it when the moved cells are on another sheet; a
move on the active sheet still selects the moved cells.
Test: drag_and_drop_on_another_sheet_stays_on_that_sheet (now also
checks the selection).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses CodeRabbit review on #3216:
- "Pass the resolved sheet to the paste step": a sheet-qualified move
(`Two!B1:B2` with Sheet1 active) was checked on Two but pasted on the
active sheet, replacing its data unasked. range.move now calls
paste_special with the range's sheet.
- "Check spilled values in the drop destination": cells showing a dynamic
array's spilled values hold no cell of their own, so the drop replaced
them without the prompt. They now count as data, as in Excel
(ISBLANK of a spill cell is FALSE, COUNTA counts it).
Tests: drag_and_drop_on_another_sheet_stays_on_that_sheet,
drag_and_drop_asks_before_replacing_spilled_values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging the selection's border onto cells that hold data replaced them
without asking, so dragging column B onto column A silently destroyed A.
Excel asks "There's already data here. Do you want to replace it?" first
(its "Alert before overwriting cells" option, on by default).
The drop now runs a new `range.move {range?, to, overwrite?}` command:
a cut and paste that returns a confirm question when the destination
holds data other than the moved cells; the grid shows it with OK and
Cancel, and OK runs it again with `overwrite: true`. A refused move
leaves the sheet and the clipboard as they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.