fix(datatypes): wire undo/redo snapshots and dirty tracking for data type edits [DOPE-534] - #989
Conversation
…type edits Datatype editors called the POU-keyed snapshot capture with a data type name, so every capture and undo/redo silently no-oped. Snapshot capture and snapshotActions.undo/redo now branch on data type names, restoring via the previously unwired projectActions.applyDatatypeSnapshot. Content edits (enum values, struct fields, array dimensions/base type/initial value) now mark the file and workspace unsaved via handleFileAndWorkspaceSavedState, matching the variables editor. The datatype header rename now goes through datatypeActions.rename, which validates the new name and rekeys the editor/tab/file entries instead of leaving the file slice orphaned. DOPE-534 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014LCcPrjCcCF29NQbG8xbwA
WalkthroughData type editing now centralizes rename validation, tracks saved state after array, enumerated, and structure updates, and supports data type snapshots in undo and redo history. Tests cover restoration, history movement, saved-depth tracking, and missing snapshot data. ChangesData type state management
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant DataTypeEditor
participant usePouSnapshot
participant sharedSlice
DataTypeEditor->>usePouSnapshot: Capture data type snapshot
usePouSnapshot->>sharedSlice: Store dataTypes snapshot
DataTypeEditor->>sharedSlice: Request undo or redo
sharedSlice->>sharedSlice: Move snapshot between history stacks
sharedSlice->>DataTypeEditor: Restore data type state
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/frontend/hooks/use-pou-snapshot.ts`:
- Around line 34-38: Disambiguate POU and datatype history entries in the
snapshot, undo, and redo flows around the POU resolution branch in
usePouSnapshot: use namespaced keys or propagate an explicit entity kind so a
shared name always restores the correct entity. Ensure datatype edits capture
datatype snapshots rather than matching a same-named POU, while preserving
existing behavior for unique names.
In `@src/frontend/store/__tests__/shared-slice.test.ts`:
- Around line 1483-1484: Remove the non-null assertions from the datatype
lookups in the shared-slice tests, including the occurrences near the Colors,
corresponding later fixtures, and their uses in pushToHistory. Narrow each
Array.find result with an assertion or test helper that throws when the datatype
is missing before passing it onward.
In `@src/frontend/store/slices/shared/slice.ts`:
- Around line 1121-1123: Update the undo/redo saved-state handling around
applyDatatypeSnapshot so the saved flag is derived from whether savedAtDepth
equals history.past.length after both operations, setting saved false when
depths differ. Add a regression test covering redo after the saved-depth undo
case and verify the workspace becomes unsaved.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 61da3794-29fc-4577-8bdb-ac1399fc0d36
📒 Files selected for processing (11)
src/frontend/components/_features/[workspace]/data-type/index.tsxsrc/frontend/components/_molecules/data-types/array/index.tsxsrc/frontend/components/_molecules/data-types/array/table/index.tsxsrc/frontend/components/_molecules/data-types/enumerated/index.tsxsrc/frontend/components/_molecules/data-types/enumerated/table/index.tsxsrc/frontend/components/_molecules/data-types/structure/index.tsxsrc/frontend/components/_molecules/data-types/structure/table/index.tsxsrc/frontend/hooks/use-pou-snapshot.tssrc/frontend/store/__tests__/shared-slice.test.tssrc/frontend/store/slices/shared/slice.tssrc/frontend/store/slices/shared/types.ts
…ue history Undo/redo now marks the file and workspace unsaved whenever history does not land on the saved depth — previously an undo away from the saved state kept the file flagged saved while the store diverged from disk, so the next save-all silently skipped the revert. The array initial-value input re-syncs from the store on external changes (undo/redo) instead of keeping its mount-time value, and captures one history entry per typing burst (rearmed on blur or external change) rather than one per keystroke. Own writes are tracked in a ref because the data prop lags one render behind the store. The enumerated initial value gets the same store re-sync. DOPE-534 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014LCcPrjCcCF29NQbG8xbwA
Replace find(...)! lookups with a throwing helper per the coding guideline banning non-null assertions. DOPE-534 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014LCcPrjCcCF29NQbG8xbwA
Review — approve after one test, plus a few small adjustmentsReviewed together with the web counterpart Autonomy-Logic/openplc-web#649 — all 11 files are byte-identical between the repos, so everything below applies to both. No logic defects found. The diagnosis is the kind that hides in plain sight: Three details show real care: the How I verified: 11/11 files byte-identical across the repos; 🟡 1 — The fallback that makes the feature work has no test (the one thing I'd like before merge)
if (!pou) {
const dataType = project.data.dataTypes.find((d) => d.name === pouName)
if (!dataType) return
pushToHistory(pouName, { variables: [], body: null, dataTypes: [dataType] })
return
}What I found. I deleted that whole block (back to Why it needs fixing. The capture side is the side that was broken — the original bug was precisely "no snapshot is taken". As it stands, if someone simplifies this hook later, datatype undo silently reverts to a no-op and nothing in the suite complains. And the symptom (a shortcut that quietly does nothing) is exactly the kind nobody notices until a user reports it. There's no test file for Suggested fix. A 🟡 2 — The dirty-tracking half is also untested (fine as follow-up)Every new With eight call sites across seven files, one component test for the most representative case (edit a struct table cell → 🟡 3 — Capture and dirty use different name sourcesIn the molecules, capture uses Why it needs fixing. Your own comment states that Impact. Narrow window, but rename-then-edit is now a first-class flow because of this PR's header-rename fix, and the failure is silent: the user's edit isn't flagged unsaved. Using 🟡 4 — This PR and its sibling collide in three filesBoth this and the Why it matters. Whichever merges second needs a rebase, and since all three are mirrored-surface files, byte-identity has to be re-verified after conflict resolution — that's exactly where a mirror breaks. This PR looks like the natural one to land first (smaller, no flag). Worth agreeing the order explicitly and warning whoever rebases. 🟡 5 — Two claims in the description don't match the diff
🟢 Smaller things
What's good
One thread that connects three PRsLooking at the series together, DOPE-538 is the root cause behind three symptoms that have now surfaced in three separate reviews:
Until DOPE-538 lands, each PR in the series will trip over one facet of it. Worth prioritising before the DOPE-385 release PR — and note the case-folding blocker I raised on the Review assisted by Claude Code. |
…n, rename history Review follow-ups from PR #649/#989 (Gustavo): - Add capture-side tests for usePouSnapshot: the datatype fallback (the core of the original bug) was only covered on the restore side, so deleting it left the suite green. - Unify capture and dirty-marking on editor.meta.name in the datatype molecules: data.name lags one render behind the store, so right after a rename the dirty call hit the orphaned old file key and silently marked nothing. - Rekey the undoRedo bucket in renameElement (new snapshotActions.renameHistory): history was orphaned under the old name after rename, turning undo into a silent no-op. Restored datatype snapshots pin name to the current history key so pre-rename snapshots can't desync tabs/files/editors. - Document the single-element dataTypes shape and the null body of datatype snapshots in PouHistorySnapshot. DOPE-534 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014LCcPrjCcCF29NQbG8xbwA
|
Thanks — all actionable items are addressed in the latest commit (web #1 (capture-side test) — new #3 (name-source mismatch) — all dirty-marking calls in the datatype molecules now use 🟢 rename discards history — folded in: 🟢 doc nits — one-liners added on #5 (description drift) — both PR bodies corrected: #2 (dirty-tracking component test) — deferring as you suggested; proposal is to fold it into the DOPE-385 form/code-toggle subtask, which already has to touch these molecules. #4 (merge order) — agreed this one lands first: it's smaller and flag-free. Whoever rebases the Root-cause thread — DOPE-538 updated with your case-folding point: IEC identifiers are case-insensitive and |
Summary
Fixes two pre-existing datatype editor bugs (DOPE-534, sub-task of DOPE-385):
Undo/redo was silently broken for data types.
usePouSnapshot().captureAndPushandsnapshotActions.undo/redoare POU-keyed and early-returned for datatype names, so every capture call in the datatype molecules was a no-op. Now:captureAndPushfalls back to a data type lookup and pushes adataTypessnapshot. The field is added toPouHistorySnapshot(shared/types.ts, a mirrored type) by this PR, mirroring the shape of the pre-existing-but-unusedHistorySnapshot.dataTypes; what already existed unused and is now wired isprojectActions.applyDatatypeSnapshot.snapshotActions.undo/redobranch on data type names and restore via the previously unwiredprojectActions.applyDatatypeSnapshot.addNewRowcaptures on every add instead of only the first.Undo/redo divergence now flags dirty — POUs included. When undo/redo moves history away from the saved depth, the file + workspace are marked unsaved (previously only the "back at saved depth → mark saved" direction existed). This changes POU undo/redo behavior too, not just data types — without it, undoing away from a saved state left the file flagged saved while the store diverged from disk, and the next save-all silently skipped the revert.
Content edits didn't mark the project dirty. Only create/rename/delete/duplicate flagged unsaved. Now every content edit (enum values, struct fields via table cells/add/remove/reorder, array dimensions/base type/initial value) calls
handleFileAndWorkspaceSavedState, matching the variables editor pattern.Review follow-ups (Gustavo): capture-side tests for the hook fallback, capture/dirty unified on
editor.meta.name(thedataprop lags one render behind the store),renameHistoryrekey so undo survives a rename (restored snapshots pinnameto the current key).Bonus fix folded in: the datatype header rename bypassed
datatypeActions.rename(no name validation, no duplicate check, file-slice entry left orphaned under the old name). It now goes through the shared action and shows a toast on rejection.Tests
shared-slice.test.tscovering datatype undo/redo, saved-depth restore, and snapshots withoutdataTypesentries.jestshared-slice suite: 163 passed; full suite 5433 passed. Typecheck, prettier, eslint clean.Mirror of https://github.com/Autonomy-Logic/openplc-web/pull/649
🤖 Generated with Claude Code
https://claude.ai/code/session_014LCcPrjCcCF29NQbG8xbwA
Summary by CodeRabbit
New Features
Bug Fixes