Add Memento pattern source generator for classes, structs, and records#88
Conversation
- Created MementoAttribute with configuration options - Implemented MementoGenerator for class/struct/record class/record struct - Generated immutable memento structs with Capture and RestoreNew methods - Generated optional caretaker classes for undo/redo history - Added diagnostic support (PKMEM001-PKMEM006) - All 13 generator tests passing Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
- Created EditorStateDemo showing text editor with undo/redo - Created GameStateDemo showing game state save/load - Added comprehensive README with usage examples - Moved MementoAttribute to Abstractions project for accessibility - Fixed generator to skip computed/read-only properties - All 13 generator tests passing Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
- Added null checks for FirstOrDefault in test assertions - Fixed potential NullReferenceException in generated source access - All 13 Memento tests passing Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Test Results123 tests 123 ✅ 19s ⏱️ Results for commit 8641aca. ♻️ This comment has been updated with latest results. |
🔍 PR Validation ResultsVersion: `` ✅ Validation Steps
📊 ArtifactsDry-run artifacts have been uploaded and will be available for 7 days. This comment was automatically generated by the PR validation workflow. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #88 +/- ##
==========================================
+ Coverage 86.81% 89.77% +2.95%
==========================================
Files 139 143 +4
Lines 11295 11890 +595
Branches 1548 1615 +67
==========================================
+ Hits 9806 10674 +868
- Misses 1101 1216 +115
+ Partials 388 0 -388
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive source generator for the Memento pattern, enabling compile-time generation of immutable snapshot structures and optional undo/redo caretakers for classes, structs, record classes, and record structs.
Changes:
- Adds MementoGenerator with support for four type kinds (class, struct, record class, record struct) with customizable member selection, capture strategies, and six diagnostic codes (PKMEM001-006)
- Generates immutable memento structs with versioning, Capture methods, and RestoreNew/Restore methods based on type mutability
- Generates optional caretaker classes with configurable capacity, FIFO eviction, duplicate suppression, and standard undo/redo semantics
- Provides EditorStateDemo and GameStateDemo showcasing text editor and game state scenarios with generated mementos
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/PatternKit.Generators/MementoGenerator.cs | Core source generator implementing memento struct and caretaker generation with member analysis and restore logic |
| src/PatternKit.Generators.Abstractions/MementoAttribute.cs | Attribute definitions for [Memento], [MementoIgnore], [MementoInclude], and [MementoStrategy] with enums for inclusion mode and capture strategies |
| src/PatternKit.Generators/AnalyzerReleases.Unshipped.md | Adds six diagnostic IDs (PKMEM001-006) for memento-specific errors and warnings |
| test/PatternKit.Generators.Tests/MementoGeneratorTests.cs | Comprehensive test suite covering basic generation, caretaker, member selection, diagnostics, and capacity handling |
| src/PatternKit.Examples/Generators/Memento/README.md | Detailed documentation with usage examples, configuration options, diagnostics reference, and best practices |
| src/PatternKit.Examples/Generators/Memento/EditorStateDemo.cs | Text editor demonstration using record class with undo/redo functionality |
| src/PatternKit.Examples/Generators/Memento/GameStateDemo.cs | Game state demonstration using mutable class with save/load and checkpoint functionality |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot apply changes based on the comments in this thread |
- Fixed namespace mismatch in demo files (MementoDemo -> Memento) - Updated GameStateDemo to use generated memento methods instead of manual copying - Improved computed property detection to exclude init-only properties on non-record types - Enhanced positional constructor detection with parameter name and type matching - Added verification for settable members in RestoreNew for non-record types - Refactored foreach loops to use explicit .Where() filtering - Updated test assertions to use regex for flexible matching All 13 Memento generator tests passing. Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot apply changes based on the comments in this thread Ignore the note about the gitversion bump. That was required, but we can make a comment about it. Fix the others. |
- Removed unused PatternKit.Creational.Builder import from MementoGeneratorTests - Simplified GameStateDemo undo/redo methods to avoid inefficient intermediate memento - Now directly copies state from history instead of creating unnecessary memento instance All 13 Memento generator tests passing. Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot apply changes based on the comments in this thread |
- Removed unused System.Collections.Immutable import - Fixed global namespace handling to use empty string instead of "GlobalNamespace" - Added length check in ToCamelCase to prevent exception with single-char names - Conditionally emit namespace declaration only when not in global namespace All 13 Memento generator tests passing. Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code Coverage |
Memento Pattern Source Generator - Code Quality Improvements ✅
Summary of Latest Changes
Addressed 3 code quality issues from the latest review, improving correctness and robustness.
📝 Changes Made
1. Removed Unused Import (Comment #2711024069)
System.Collections.Immutableimport fromMementoGenerator.cs2. Fixed Global Namespace Handling (Comment #2711024075)
"GlobalNamespace"string for types in global namespacenamespace GlobalNamespace;creating a namespace instead of using globalstring.Emptyfor global namespace types3. Fixed Single-Character Name Handling (Comment #2711024084)
name.Substring(1)would throwArgumentOutOfRangeExceptionfor single-character names|| name.Length == 1to early return✅ Validation
🎯 Impact
These changes improve:
Previous Changes Summary
Previously addressed:
Original prompt
This section details on the original issue you should resolve
<issue_title>Generator: Create Memento Pattern (supports class/struct/record + optional undo/redo caretaker)</issue_title>
<issue_description>## Summary
Add a source generator that produces a complete implementation of the Memento pattern for consumer types — including classes, structs, record classes, and record structs — focused on:
Motivation / Problem
Memento implementations are repetitive and correctness-sensitive:
This is ideal for source generation: emit correct, optimized code once, consistently.
Supported Originator Kinds (must-have)
The generator must support:
classstructrecord classrecord structEach kind has different restore semantics:
Mutable originators (class/struct with settable members)
Immutable originators (records with
initor positional parameters)restore should support returning a new instance:
RestoreNew(...)orApply(...)optionally support
with-expression restore when feasible:with { ... }for the included membersWe should not force mutation on records that are designed to be immutable.
Proposed User Experience
Minimal: snapshot only
Generated (shape):
Snapshot + caretaker (undo/redo)
Generated:
Notes
Member Selection
Support both modes:
Include-all (default, configurable)
Include-explicit
[MementoInclude]Attributes:
[Memento][MementoIgnore][MementoInclude]Capture Strategies for Mutable References (footgun prevention)
Default strategy should be explicit and safe:
stringtreated as safe immutableByReferencewith a warning unless configured otherwisePer-member strategy attribute:
Strategies:
ByReferenceClone(requires a known mechanism)DeepCopy(only when generator can safely emit it)Serialize(opt-in, future)Custom(partial hook)Restore Semantics (by originator type)
For class / struct (mutable)
Generate both:
void Restore(TOriginator originator)(in-place)TOriginator RestoreNew()(convenience)For record class / record struct
Generate:
TOriginator RestoreNew()(always)Apply(TOriginator originator)only if the record has writable members (rare / discouraged)Additionally:
if feasible, prefer
with-expression:originator with { Prop = value, ... }otherwise, use the best available constructor/positional parameters:
The generator must emit diagnostics if it cannot safely construct a new record instance.
Caretaker Generation (Undo/Redo)
Caretaker should be optional and support dete...
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.