Skip to content

Epic: Cross-compilation symbol mapping (SymbolKey, incremental compilation, workspace) #279

@amis92

Description

@amis92

Overview

When an editor operation modifies a roster, wham creates a brand-new WhamCompilation. All old ISymbol instances become invalid — they are bound to the old compilation. Higher layers (editor, UI) that held references to symbols or nodes from the previous compilation have no way to reconnect to their equivalents in the new compilation.

This epic introduces Roslyn-inspired mechanisms for stable symbol identity, incremental compilation, and workspace state management.

Background: Roslyn Patterns

This design is informed by analysis of Roslyn's approach to the same problem:

Roslyn Mechanism Wham Equivalent Notes
SymbolKey SymbolKey (Phase 1) Simpler: BattleScribe IDs provide natural keys
CompilationTracker Catalogue/Roster split (Phase 2) Natural split: catalogues stable, roster volatile
Solution forking RosterState exists Structural sharing via immutable source tree arrays
TranslationAction IRosterOperation exists Already have typed operation objects
Project deps Roster→Catalogue deps (Phase 2) Roster depends on subset of catalogues
SemanticModel EffectiveEntryCache Per-roster cached effective entries
SyntaxAnnotation Not needed All roster nodes have unique generated IDs

Key Insight: All Symbols Have Unique IDs

All wham symbols — both catalogue-defined entries AND roster elements — have stable, unique IDs via IIdentifiableNode.Id:

  • Catalogue entries (SelectionEntry, ForceEntry, CategoryEntry, etc.): IDs authored in BattleScribe data files. Unique within a catalogue.
  • Roster elements (Force, Selection, Category in roster): IDs generated at creation time via Guid.NewGuid() (see NodeFactory.NewId()). Each roster force/selection gets its own unique ID — even multiple selections from the same entry.

This means a single SymbolKey mechanism can handle ALL symbol types uniformly, unlike Roslyn which needs both SymbolKey (for symbols) and SyntaxAnnotation (for syntax nodes without inherent IDs).

Key Differences from Roslyn

  1. Natural stable IDs everywhere: BattleScribe authored IDs on catalogue entries, plus generated GUIDs on roster elements. Roslyn must synthesize identity from names/signatures.
  2. Natural project structure: Catalogues = referenced projects (stable), Rosters = current project (volatile). Multiple rosters share catalogues.
  3. No incremental parsing needed: BattleScribe XML loaded in bulk. Roster tree replaced atomically.
  4. Smaller scale: ~1 gamesystem + N catalogues (typically <20) + 1-few rosters.

Phases

Phase 1: SymbolKey — Cross-compilation symbol identity

Unified SymbolKey struct for all symbol types. Replaces ad-hoc ID-based lookups in editor operations.

Phase 2: Incremental Compilation — Catalogue/roster split with dependency graph

Split compilation into stable catalogue compilation + per-roster compilation. Multiple rosters share catalogue symbols.

Phase 3: Workspace Layer — Multi-roster management

WhamWorkspace managing current state, CompilationTracker per roster, document identity, change notifications.

Current Pain Points

  • AddRootEntryFromSymbol (RosterOperations.cs:195): "hack for referencing symbols from previous compilations, until SymbolReference is done"
  • AddSelectionFromLinkOp (line 179): force lookup by ID because ForceNode invalidated by prior operations
  • All catalogue symbols rebuilt from scratch even when only the roster changes
  • No support for multiple simultaneous rosters sharing catalogue data

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions