Repository navigation
refactor(config): move grid-style Save off the UI thread - #176
Merged
Merged
Conversation
The style editor's Load was async but Save was synchronous, so `SaveCommand = ReactiveCommand.Create(Save, …)` blocked the dispatcher on the file write. Make the write path async end to end, matching Load (slice 2 of the #118 grid-style debloat). - IGridStyleEditorFacade.Save returns Task<Result>; the facade awaits GridStyleWriter.SaveAsync. - The writer's temp-file write and header read use WriteAllTextAsync / ReadAllLinesAsync; File.Move stays synchronous inside the atomic-write try/catch (no async move exists, and the rename is a fast metadata op). The Rule-B GridStyleSaveFailedError.CausedBy(ex) envelope is preserved. - The view model's Save becomes SaveAsync, wired via ReactiveCommand.CreateFromTask; SaveCommand stays ReactiveCommand<Unit, bool>, so the window's save->restart-prompt->close flow and the ThrownExceptions -> ReportSaveException routing are unchanged. Behavior-preserving: the bytes written are identical, only the write's threading changes. Validate stays synchronous (it runs per keystroke in RecomputeCanSave), and no CancellationToken is threaded (Save is a button-click). Slice 1's round-trip guards still hold. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The grid-style editor's
Loadisasync, butSavewas synchronous —SaveCommand = ReactiveCommand.Create(Save, …)did file I/O (File.WriteAllText+File.Move) on the UI thread, blocking the dispatcher on every save. Slice 2 of the #118 grid-style debloat.What changed
Make the write path async end to end, matching
Load:IGridStyleEditorFacade.Save→Task<Result>;GridStyleEditorFacade.SaveawaitsGridStyleWriter.SaveAsync.GridStyleWriter— the temp-file write and header read useWriteAllTextAsync/ReadAllLinesAsync;File.Movestays synchronous inside the atomic-write try/catch (there is no async move, and it's a fast metadata rename). The slice-7 Rule-BGridStyleSaveFailedError.CausedBy(ex)envelope is preserved; the private helpers gained theAsyncsuffix (WriteAtomicAsync/ReadLeadingCommentBlockAsync).GridStyleEditorViewModel—Save→SaveAsync, wired viaReactiveCommand.CreateFromTask.SaveCommandstaysReactiveCommand<Unit, bool>, so the window's save→restart-prompt→close flow and theThrownExceptions → ReportSaveExceptionrouting are unchanged (GridStyleEditorWindow.axaml.csis untouched).Behavior-preserving: the bytes written are byte-identical — only the write's threading changes.
Validatestays synchronous (it runs per-keystroke inRecomputeCanSave), noCancellationTokenis threaded (Save is a button-click), and slice 1's round-trip guards still hold. Nothing from slices 3–5 (record nesting,StyleColor, the VM split) leaks in.Verification
dotnet build SemiStep.slnx— 0 warnings;dotnet test— 1687 passed, 0 failed;dotnet formatclean.SaveThenLoad_DistinctFixture…(now awaiting the asyncSave) is green — proving identical bytes. The atomic-write /File.Move-failure test and the save-fault tests (ThrowingFacade's synchronous throw →ReportSaveException;CausedByFailingFacade→LogCausedByExceptions) still fire after the conversion.Savelacking theAsyncsuffix — was correctly kept: the facade'sLoadis also suffix-less-async, soSavematching it is the consistent choice.Slice 2 of #118 (stays open). Next: slice 3 nests
GridStyleOptionsto mirror the DTO groups — the big line-count win.Per-task commits before collapse
🤖 Generated with Claude Code