feat(sheet): Suchen & Ersetzen - #378
Conversation
Code Review by Qodo
1. Unicode replacement corrupts text
|
PR Summary by QodoAdd Excel-style find and replace to sheets
AI Description
Diagram
High-Level Assessment
Files changed (7)
|
Code Review by Qodo
1. Unicode replacement corrupts text
|
| const hay = raw.toLowerCase(); | ||
| const needle = query.toLowerCase(); | ||
| let out = ''; | ||
| let i = 0; | ||
| for (let at = hay.indexOf(needle); at !== -1; at = hay.indexOf(needle, i)) { | ||
| out += raw.slice(i, at) + replacement; | ||
| i = at + needle.length; |
There was a problem hiding this comment.
1. Unicode replacement corrupts text 🐞 Bug ≡ Correctness
Case-insensitive replacement uses offsets from raw.toLowerCase() to slice the original string, but Unicode lowercasing can change UTF-16 length. Replacing x in İX, for example, produces İXz instead of İz, silently corrupting cell contents or formulas.
Agent Prompt
## Issue description
Case-insensitive replacement derives match offsets from a lowercased string and applies them to the original string. Unicode case conversion can change string length, causing incorrect slicing and corrupted cell contents.
## Issue Context
For example, JavaScript lowercases `İX` to `i\u0307x`; the folded `x` offset is therefore not its offset in the original string. Add regression coverage for this and multiple matches after length-changing folds.
## Fix Focus Areas
- ui/src/js/sheet/findReplace.ts[21-63]
- ui/src/js/sheet/findReplace.test.ts[54-73]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| const cells = cellsOfActive(); | ||
| const hit = findNext(cells, query, selection.focus, opts); | ||
| if (hit) view?.setSelection(selFromSingle(hit.row, hit.col)); |
There was a problem hiding this comment.
2. Hidden merged matches selected 🐞 Bug ≡ Correctness
Find searches covered cells whose contents are retained but whose DOM elements are hidden by a merge, then selects the hidden coordinate and reports success without a visible focus outline. Replace and Replace All can consequently mutate hidden covered-cell content that users cannot inspect until unmerging.
Agent Prompt
## Issue description
Find and replace treat covered coordinates inside merged ranges as independently visible cells. A covered match is selected even though its DOM cell is hidden, and replacement can silently modify retained hidden content.
## Issue Context
Merged-cell application intentionally retains covered cell contents, while the view sets covered table cells to `display: none`. Define merged ranges as one searchable logical cell, normally by excluding non-anchor covered coordinates from Find, Replace, and Replace All.
## Fix Focus Areas
- ui/src/js/sheet/sheetEditor.ts[657-692]
- ui/src/js/sheet/findReplace.test.ts[29-91]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
704d10a to
5efc964
Compare
Ctrl+F / Ctrl+H open an Excel-style dialog with Find Next, Replace and Replace All, plus Match case and Entire cell; the ribbon gets a Find & Select menu in the Editing group. The search runs against the raw cell content, which is Excel default "Look in: Formulas": a formula is found by its text and a replacement rewrites the formula. Find Next walks row-major from the current cell and wraps around. Replace All emits one setCell op per changed cell within a single tick, so the whole sweep is one undo step. Case-insensitive replacement is done by walking the folded string instead of a regex, so untouched parts keep their original casing and no query needs escaping. The view gains setSelection() so a hit can be selected and scrolled into view the same way a click would, notifications included. Not covered: wildcards in the query, searching across all sheets, and Find All as a result list - the dialog reports the match count instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5efc964 to
1b1198c
Compare
Was
Ctrl+F / Ctrl+H öffnen einen Excel-artigen Dialog mit Find Next, Replace, Replace All sowie Match case und Entire cell. Im Ribbon gibt es dazu „Find & Select" in der Editing-Gruppe.
Gesucht wird im Rohinhalt der Zelle — das ist Excels Standard „Look in: Formulas": eine Formel wird über ihren Text gefunden, und ein Ersetzen schreibt die Formel um (
=SUM(A1:A2)→=SUM(B1:B2)). Find Next läuft zeilenweise ab der aktuellen Zelle und springt am Ende wieder an den Anfang.Replace All schreibt eine setCell-Op pro geänderter Zelle innerhalb eines Ticks — damit ist der ganze Durchlauf dank #377 genau ein Undo-Schritt. Ein Playwright-Test hält das fest.
Beim Ersetzen ohne Groß-/Kleinschreibungs-Beachtung läuft die Ersetzung über den gefalteten String statt über eine Regex: die unberührten Teile behalten ihre Schreibweise, und die Suchanfrage muss nirgends escaped werden.
Die View bekommt
setSelection(), damit ein Treffer wie ein Klick ausgewählt und in den sichtbaren Bereich gescrollt wird — inklusive der üblichen Benachrichtigungen an Editor und Formelleiste.Tests
14 Unit-Tests für die reine Logik (Groß-/Kleinschreibung, ganze Zelle, Zeilenreihenfolge, Wrap-Around, mehrfaches Vorkommen pro Zelle, Ersetzung die die Suchanfrage enthält, Formeltext) plus ein Playwright-Test über Tastatur- und Dialogpfad inklusive Undo des Replace-All. 180 Vitest-Tests grün.
Nicht enthalten
Platzhalter (
*,?) in der Suche, Suche über alle Blätter, und „Find All" als Trefferliste — der Dialog nennt stattdessen die Trefferzahl.Hinweis zur Basis
Basiert auf #377 (Undo/Redo), weil der Replace-All-Test dessen Undo braucht. Sobald #377 gemergt ist, stelle ich die Basis auf main um.
🤖 Generated with Claude Code