Add [TypeSelector] dropdown for [SerializeReference] and type repair#49
Open
VPDPersonal wants to merge 168 commits into
Open
Add [TypeSelector] dropdown for [SerializeReference] and type repair#49VPDPersonal wants to merge 168 commits into
VPDPersonal wants to merge 168 commits into
Conversation
… drawer Add a hierarchical type-selector dropdown for [SerializeReference] fields (single, array and List<T>), reusing TypeSelectorWindow. Picking a concrete type instantiates it, <None> clears the reference, the assigned instance's nested properties are drawn inline under a foldout, and an unresolved stored type is surfaced as a missing-type warning. Implemented for both IMGUI and UIToolkit inspectors. TypeSelectorWindow.Show gains an optional candidate-type filter (backward compatible) used to exclude UnityEngine.Object, open generics, strings and delegates.
Add a loadout-system sample exercising [SerializeReferenceSelector]: single IWeapon field, List<IWeapon>, abstract StatusEffect base, and a nested [SerializeReference] inside Railgun. Includes an IMGUILoadout + forcing editor to demonstrate the IMGUI path, EN/RU README, and a package.json sample entry.
Add Loadout.prefab (UIToolkit) and IMGUILoadout.prefab (IMGUI) with pre-filled managed references — single (Railgun + nested BurnEffect), List<IWeapon> ([Pistol, Shotgun]) and abstract-base (FreezeEffect / BurnEffect) — so the sample can be inspected without building it by hand. Update the EN/RU sample README "How to run" to drive the prefabs.
- Offer open generic definitions (e.g. Modifier<T>) as candidates: infer arguments from a closed-generic field, or resolve them in a new recursive, constraint-aware GenericArgumentSelectorWindow, validating the closed type against the field before assignment. Works in IMGUI and UIToolkit. - Add an optional additionalTypes pass-through to TypeSelectorWindow / HierarchyBuilder / TypeInfo, and render generic names as Modifier<T>.
- Add a non-abstract Modifier<T> hierarchy (IModifier) with closed-generic subclasses, plus IModifier / Modifier<float> / List<IModifier> fields on Loadout and IMGUILoadout to exercise open-generic selection and the T picker. - Document the generic flow in the sample README (EN/RU).
- Note open-generic support and the new additionalTypes parameter in the CHANGELOG, and add a generic bullet to the feature section of all four READMEs.
…he dropdown - Resolve open generics inside TypeSelectorWindow as in-window argument pages (hierarchy, search, breadcrumb, live preview), removing the separate GenericArgumentSelectorWindow and its focus issues - Extract generic resolution into GenericTypeResolver and add an argumentFilter parameter to TypeSelectorWindow.Show; the flow stays dormant unless open generics are present - Format generic type names recursively so nested closed generics render fully (Modifier<Modifier<Int32>>) - Resolve the generic type definition when locating a script so Open Script works for closed generics
…eric argument selector Open generic definitions reach the selector through the additionalTypes path, which bypasses the name and CompilerGeneratedAttribute checks applied to ordinary candidates. As a result anonymous types and closure display classes (e.g. <>c__11<T>, <>f__AnonymousType0<...>) showed up as argument candidates and bloated the unconstrained (object) list. Exclude compiler-generated types in IsAssignableGenericDefinition so the single gate for generic definitions filters them out. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Carry the foldout caption on the toggle's aligned BaseField label so the type dropdown starts at the inspector value column, matching SerializableType - Centre the header row so the open-script button lines up with the dropdown - Split the dropdown into field/input nodes and cancel the EnumField caption's -2px left margin so the text indents like SerializableType
…/paste - CreateInstancePreservingData carries matching [SerializeField] data across a type switch via JsonUtility ToJson/FromJsonOverwrite, instead of resetting - SerializeReferenceClipboard backs a header right-click Copy/Paste menu scoped to the field (IMGUI ContextClick + UIToolkit ContextualMenuManipulator); paste builds an independent instance and is disabled when the type is not assignable
… references - Add an Edit Type action to the missing-type warning: it opens an Assembly/Namespace/Class editor and rewrites the stored managed-reference type straight into the asset YAML (parser-free, no external deps), since Unity cannot reassign a missing reference through the serialization API. Applies to ScriptableObjects — the only assets where Unity preserves missing references (UUM-129100). - Detect an aliased managed reference (two fields sharing one instance) and offer a Make Unique Reference action that clones it into an independent copy. - Sample: add a WeaponPreset ScriptableObject with a pre-broken asset, plus shared-reference and missing-type demo prefabs, and document how to test both.
…YAML - Replace the manual Edit-Type window with a Fix button that opens the existing hierarchical type picker; the chosen type is rewritten into the asset YAML. - Read the missing type straight from the asset file (stored id + RefIds type) instead of Unity's serialization API, which reports nothing per-property and drops the reference on prefabs/GameObjects (UUM-129100). Repair now works on prefab assets too, not just ScriptableObjects. - Detect strictly per-field by resolving the recorded type, removing the single-missing fallback that falsely flagged legitimately-empty fields.
- Scan a prefab/ScriptableObject's YAML for every orphaned managed reference — at any nesting depth and on any child object — by walking each document's flat RefIds block and flagging entries whose stored type no longer resolves. - List each with its own Fix picker that rewrites the type in the file and reimports, reaching references the per-field drawer cannot (nested values, child-object components, anything Unity dropped to <None>) without Prefab Mode. - Open via Tools → Aspid FastTools → Repair Missing References (auto-targets the current selection).
… Mode repair Replace the bulky missing-type and shared-reference help boxes with a compact yellow inline notice: a small warning icon, terse text and an underlined, clickable action word (Fix / Make unique) with the full detail on hover. Covers both the IMGUI and UIToolkit drawers. Extend missing-type repair to objects open in Prefab Mode: detection resolves the backing document through the prefab stage (matching the asset's file id), and the fix is applied in memory — reassigning the reference and recovering the orphaned field data — because rewriting the open stage's file would be discarded on save. Saved assets keep the YAML-rewrite path. Reselect the inspected objects after a repair so Unity's cached object-level missing-types banner clears, and guard the UIToolkit field against the live SerializedObject being invalidated by a reimport.
…n containers Generalise the YAML reference resolver behind the inline missing-type Fix to walk a property path of any shape: through managed-reference chains and through plain [Serializable] containers (struct/class fields and List<T> of them), so a missing type buried in a slot or list element is detected and repaired inline like a top-level field. When an in-memory (Prefab Mode) repair replaces a missing reference that itself carried missing nested references, clear those now-orphaned entries too so the object's missing-types banner clears. Add the SlottedLoadout sample demonstrating [SerializeReferenceSelector] references inside a container field and a List<T> of containers.
- Redesign the Repair References window in the Welcome style: boxed asset card with an Aspid header, centred info/success hero states, warning-accented results header and amber gradient rows. - Extract the hierarchical type selector from TypeSelectorWindow into a reusable TypeSelectorView; the window stays a thin dropdown host with an unchanged public Show API. - Expand the selector inline as an accordion under the clicked Fix row (dark Aspid panel, one at a time, Escape collapses) instead of the floating grey dropdown. - Migrate selector USS classes to BEM under the aspid-fasttools-type-selector block and rename the stylesheet to Aspid-FastTools-TypeSelector.uss; sync the asset field when Open() retargets an already-open window.
- The repair window moved to Tools → Aspid 🐍 → Repair Missing References FastTools; mirror the new path in the CHANGELOG, both root/Documentation README pairs and the sample READMEs.
- Remove `[SerializeReferenceSelector]`; `[TypeSelector]` now also drives `[SerializeReference]` managed-reference fields, dispatching by property type in `TypeSelectorPropertyDrawer`. - The attribute's base types narrow the candidate list below the declared field type — applied to both concrete types and open generic definitions, in the dropdown and the missing-type Fix picker. - Update samples, CHANGELOG and READMEs (EN/RU) to the merged attribute.
- Add the `Aspid.FastTools.Analyzers` git submodule (VPDPersonal/Aspid.FastTools.Analyzers) carrying the `AFT*` [TypeSelector] usage rules. - Ship the prebuilt `Aspid.FastTools.Analyzers.dll` with a `RoslynAnalyzer`-labelled meta (all platforms excluded), mirroring the generator DLL. - Document the submodule and its manual rebuild/deploy in CLAUDE.md.
- Bump the analyzer submodule: AFT0003 now also flags a sealed class paired with an interface it does not implement; redeploy the prebuilt DLL. - Switch the submodule URL from SSH to HTTPS so public clones resolve it without keys. - Add the rebuild-analyzers-on-change PostToolUse hook script and the build-analyzer skill mirroring the generator pipeline; document both in CLAUDE.md.
…attribute Unify SerializeReference selector into [TypeSelector] with usage analyzer
## Summary - Add `DisplayName` (`Namespace.Class`) and `FullName` (`Namespace.Class, Assembly`) members to `ManagedTypeName`, making it the single source of truth for the missing-type display string. - Route the four hand-formatted copies through them: `ProjectGroup.DisplayName`, `SerializeReferenceHelpers.GetMissingTypeDisplayName`, `ReferenceGraphNode.FullName`, and `BreakageEntry.TypeName`. - Future nested (`Outer/Inner`) or generic class-name display fixes now land in one place instead of four. ## Notes for review - `BreakageEntry.TypeName` previously returned `StoredType.Class` (class only); it now returns `DisplayName` (`Namespace.Class`). The property has no current consumers, so this only aligns it with the centralized display string — no observable UI change. - `DisplayName`/`FullName` preserve the prior per-site behavior: empty string for the empty type, class-only when there is no namespace, and the `, Assembly` suffix only on `FullName` (graph tooltip). ## Linked issues Refs #49 - addresses review finding #49 (comment)
## Summary - Extract the duplicated YAML-scan primitives shared by the graph scanner and the repair flow into one new `internal static SerializeReferenceYaml` helper: the `--- !u!N &id` document-header regex, the `RefIds:` block lookup, the inline `class/ns/asm` type grammar plus its `TryParseInlineType`, `IndentOf` and `FindEntryEnd`. - `SerializeReferenceGraphScanner` and `SerializeReferenceYamlEditor` now both call the shared primitives, so the graph window and the missing-reference repair flow can no longer silently disagree about Unity's RefIds shape (quoting, indentation, sentinel handling). ## Notes for review - The shared `IndentOf` counts tabs as well as spaces (matching the editor's existing implementation and both readers' `\s*` indent regexes). The scanner's old copy counted spaces only; since Unity always emits space-indented YAML this is behavior-neutral on real assets and removes a latent inconsistency with the scanner's own regexes. - The shared `DocumentHeader` regex uses named groups (`class`/`id`); the editor's `FindMissingReferences` / `FindDocumentRange` were updated from `Groups[1]` to `Groups["id"]` to keep reading the file-id anchor (not the class id). - `FindEntryEnd` is unified on the `headerIndex` signature; the scanner's single call site was adjusted from passing `i + 1` to `i` accordingly (same scanned range). - The editor keeps thin private forwarders (`IndentOf`/`FindEntryEnd`/`FindRefIdsStart`/`TryParseInlineType`) delegating to the shared helper, so its many internal call sites stay untouched while the implementation is single-sourced. ## Linked issues Refs #49 - addresses review finding #49 (comment)
## Summary
- Anchor the `rid:` pointer matcher in `SerializeReferenceYamlEditor.TryNullReference` / `CountPointersTo` so it only matches Unity's real pointer shapes — a line-anchored `- rid: N` sequence item, a line-anchored `rid: N` scalar child, or an inline `{rid: N}` mapping — instead of any bare `rid: N` substring anywhere on a line.
- Prevents data loss where a string field value containing `rid: <orphan>` (e.g. `_note: 'see rid: 5'`) was rewritten to `rid: -2` on the non-undoable Clear write, and stops that same substring from inflating the Clear dialog's "aliased across N fields" count.
- Replacement now captures and preserves each pointer's structural prefix/suffix (indent + dash, or surrounding braces) and rewrites only the id.
## Notes for review
- The new inline branch matches `{rid: N}` (the only inline shape Unity writes for a managed-reference pointer); a multi-key inline mapping is intentionally not matched, which is strictly safer than the previous loose token.
- This mirrors the reader's intent: `SerializeReferenceGraphScanner` stays safe by validating every `rid:` match against its `knownRids` set; the writer has no such set, so anchoring to genuine pointer positions is the equivalent guard.
- Header-skip logic and the null-sentinel insertion are unchanged.
## Linked issues
Refs #49 - addresses review finding #49 (comment)
## Summary - Group just-broke managed-reference usages by owning asset path in `SerializeReferenceBreakageDetector.BuildReport`, so the expensive `BuildConstraintMap` (`LoadAllAssetsAtPath` + full `SerializedObject` walk) runs once per asset instead of once per broken reference. - A single deleted shared script that breaks many refs in one asset now loads/walks that asset once rather than N times, removing the editor stall on a routine save. ## Notes for review - `BuildEntry` now takes the pre-resolved `path`, `repairable` flag, and shared constraint map; per-entry `GetReferenceFieldNames` and suggestion ranking are unchanged (field-name reads are already cached by `SerializeReferenceYamlProbeCache`). - Best-effort error semantics preserved: a `BuildConstraintMap` parse failure now skips the map for the whole asset (constraints fall back to `typeof(object)`), matching the prior per-entry fallback. ## Linked issues Refs #49 - addresses review finding #49 (comment)
## Summary - Validate the chosen file name as a C# class identifier in `SerializeReferenceScriptCreator.TryCreateSubclassStub`; an invalid name (digits-first, spaces, keyword, …) now shows an explanatory dialog and aborts instead of writing an uncompilable stub. - Rewrite `TypeName` as a recursive, generic-aware C# name builder so generic / array / by-ref base types and member signatures emit valid C# (`Ns.IMelee<int>`) instead of mangled reflection strings (`Ns.IMelee`1[[System.Int32,…]]`). ## Notes for review - Identifier validation is a self-contained check (first char letter/underscore, remaining chars letter/digit/underscore or permitted Unicode marks, plus a C# keyword reject-list). It deliberately avoids `System.CodeDom` / `Microsoft.CSharp.CSharpCodeProvider`, which Unity's `.NET Standard` API profile (`apiCompatibilityLevel: 6`) does not ship — using it would not compile. This mirrors the existing regex-based `IdRegistryValidator`. - The generic builder strips the `` `N `` arity suffix and recurses into type arguments, array ranks, and by-ref/pointer element types; open generic parameters fall back to their bare name. ## Linked issues Refs #49 - addresses review finding #49 (comment)
Clamp AspidHoverGradientOverlay Steps to ushort mesh limit
…aint-nested-generics Filter nested generic definitions by all generic constraints
…firm Confirm before overwriting an existing template by name
…ject Fan out Create New Script assignment across all selected objects
…dentity Match generic types by open-generic identity in delete guard
…ll-type-guard Guard against null types in TypeSelectorAttribute constructor
…e-rename Detect in-place class rename breakage across the detector chain
…rministic-tiebreak Add deterministic tie-break to repair suggestion ranking
…ector-filter Apply TypeSelector base-type filter to paste and templates
Make List + add a single undo step without rid alias
…etach-symmetry Re-subscribe theme updates on attach for symmetric lifecycle
Distinguish absent vs empty required field in YAML gate
…e-state Share per-frame state between GetHeight and Draw
Preserve dominant line terminator in WritePreservingNewlines
…-cache Cache per-asset constraint map in graph view
…property # Conflicts: # Aspid.FastTools/Packages/tech.aspid.fasttools/Unity/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIPropertyDrawer.cs
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.
Summary
Extends
[TypeSelector]to[SerializeReference]and adds a full authoring + integrity toolkit for managed references. Every row works in both IMGUI and UIToolkit.[SerializeReference]List<T>fields. Pick → instantiate,<None>→ clear, the instance's nested props draw inline. Candidates default to the declared type, narrowed by[TypeSelector(typeof(IMelee))].Modifier<T>; arguments are inferred from a closed-generic field or picked in a follow-up window honouring constraints.[TypeSelectorItem](display name /Category/Name/ tooltip / order / icon); per-project Favorites ★ + Recent; collapsible groups with in-header search.Tools → Aspid 🐍 → FastTools(entries: Welcome / Asset References / Project References / Settings, also rebindable shortcuts). Tabs: Welcome (overview + installable samples), Asset References (whole graph from YAML —MISSING/SHAREDbadges, orphans, inline Fix, Open Source Prefab), Project References (project sweep grouped by broken type,Fix allwith YAML diff preview, per-fix receipts with Undo, deep-links into Asset References) and Settings.AspidThemeSettings+ Project Settings page) that re-skins every Aspid FastTools editor view; a reusableAspidWindowFooter(version + GitHub link) is pinned to the bottom of the window.sr:Quick Search; delete-script guard; proactive breakage notification; build/CI gate (SerializeReferenceCiGate.RunCheck); required-field gate via[TypeSelector(Required = true)]; Project Settings page.+; Create New Script.SerializeReferencessample (weapons/effects, melee/ranged types, genericModifier<T>, broken/shared demo prefabs) with a step-by-step tutorial (TypeSelectorTutorialscene +TUTORIAL.mdEN/RU); README EN/RU + Documentation + CHANGELOG; first EditMode test assembly (Aspid.FastTools.Unity.Editor.Tests).Notes for review
New code lives under
Unity/Editor/Scripts/SerializeReferences/. The selector was extracted into a reusable view;TypeSelectorWindowis now a thin dropdown host (publicShowAPI unchanged, gained optionalfilter/additionalTypes).Trickiest spots: generic-argument resolution, YAML detect/rewrite (
SerializeReferenceYamlEditor), at-any-depth repair through nested refs and[Serializable]containers, and the bulk rewrite (edits asset files directly — irreversible, hence the diff preview + confirmation).Window merge: the old separate Repair Missing References and Managed References graph windows are now one workbench, and the Welcome view's overview is embedded as the Welcome tab (the main structural change since earlier revisions).
Shared editor UI: a new project-level theme override (
AspidThemeSettings/AspidThemeSettingsProvider, applied across ~11 editor views) and a reusableAspidWindowFootercomponent pinned to the window bottom.USS: migrated to BEM (
aspid-fasttools-type-selector); stylesheet renamed toAspid-FastTools-TypeSelector.uss(meta GUID preserved).Also on this branch: the
[TypeSelector]unification (Unify SerializeReference selector into [TypeSelector] with usage analyzer #50) and theAspid.FastTools.Analyzerssubmodule —AFT0001fix (Unity declaresSerializeReferencewithout theAttributesuffix), a widenedAFT0003, plus newAFT0004/AFT0005diagnostics. The submodule pointer now tracks the analyzer'smain.Post-audit hardening (merged into this branch): a multi-agent review surfaced edge cases that were fixed and covered by EditMode tests, tracked as ASP-21…31:
-srGateFailoverride honoured[TypeSelector]type array no longer crash the pickerLinked issues
Closes #10