Joeri Exelmans 2 лет назад
Родитель
Сommit
acc222fa5a

+ 168 - 151
src/frontend/app.tsx

@@ -6,7 +6,7 @@ import {IconPlayerTrackPrev, IconPlayerTrackNext, IconInfoCircle} from "@tabler/
 
 import {GraphDeltaExecutor} from "../onion/delta_executor"; 
 import {CompositeDelta, CompositeLevel} from "../onion/composite_delta";
-import {embed, Version, initialVersion, VersionRegistry} from "../onion/version";
+import {embed, Version, VersionRegistry} from "../onion/version";
 // import {NodeCreation, NodeDeletion, EdgeCreation, EdgeUpdate} from "../onion/primitive_delta";
 import {PrimitiveDelta} from "../onion/primitive_delta";
 import {PrimitiveValue, UUID} from "../onion/types";
@@ -45,12 +45,12 @@ const emptyGraph: d3Types.d3Graph<any,any> = {
 const historyGraphForces = {charge: -600, center: 0.1, link: 2};
 const depGraphForces = {charge: -200, center: 0.1, link: 0.2};
 
-const initialHistoryGraph = {
+const initialHistoryGraph = initialVersion => ({
   nodes: [
     versionToNode(initialVersion, true),
   ],
   links: [],
-};
+});
 
 interface VersionedModelState {
   version: Version; // the 'current version'
@@ -73,6 +73,9 @@ export interface VersionedModelProps {
 
   onUserEdit?: UserEditCallback;
   setNextNodePosition: SetNodePositionCallback;
+  onUndoClicked: (parentVersion: Version, deltaToUndo: Delta) => void;
+  onRedoClicked: (childVersion: Version, deltaToRedo: Delta) => void;
+  onVersionClicked: (Version) => void;
 
   state: VersionedModelState;
   setState: (callback: (VersionedModelState) => VersionedModelState) => void;
@@ -88,77 +91,40 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
 
   render() {
     const historyMouseUpHandler = (event, {x,y}, node: d3Types.d3Node<Version|null> | undefined) => {
-      if (node !== undefined) {
-        // the user clicked on a version -> the clicked version becomes the "current version"
-        const versionClicked = node.obj;
-        if (versionClicked !== null) {
-          if (this.textarearef.current) {
-            this.textarearef.current.value += node.id + '\n';
-          }
-
-          const path = this.props.state.version.findPathTo(versionClicked);
-          if (path === undefined) {
-            throw new Error("Could not find path to clicked version!");
-          }
-
-          // this.manipulator.x = 0;
-          // this.manipulator.y = 0;
-          this.props.setNextNodePosition(0,0);
-
-          for (const [linkType, delta] of path) {
-            if (linkType === 'p') {
-              undo(delta);
-            }
-            else if (linkType === 'c') {
-              redo(delta);
-            }
-          }
-
-          this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
-            version: versionClicked,
-            historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, versionClicked),
-            ...rest,
-          }));
-        }
-      }
-    }
+      // if (node !== undefined) {
+      //   // the user clicked on a version -> the clicked version becomes the "current version"
+      //   const versionClicked = node.obj;
+      //   if (versionClicked !== null) {
+      //     if (this.textarearef.current) {
+      //       this.textarearef.current.value += node.id + '\n';
+      //     }
 
-    const undo = deltaToUndo => {
-      // this.props.setNextNodePosition(0,0);
-      // this.graphDeltaExecutor.unexec(deltaToUndo);
-      // this.props.setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
-      //   dependencyGraphL1: setDeltaInactive(prevDepGraphL1, deltaToUndo),
-      //   dependencyGraphL0: deltaToUndo.deltas.reduce((prevDepGraphL0, delta) => setDeltaInactive(prevDepGraphL0, delta), prevDepGraphL0),
-      //   ...rest,
-      // }));
-    };
-    const redo = deltaToRedo => {
-      // this.props.setNextNodePosition(0,0);
-      // this.graphDeltaExecutor.exec(deltaToRedo);
-      // this.props.setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
-      //   dependencyGraphL1: setDeltaActive(prevDepGraphL1, deltaToRedo),
-      //   dependencyGraphL0: deltaToRedo.deltas.reduce((prevDepGraphL0, delta) => setDeltaActive(prevDepGraphL0, delta), prevDepGraphL0),
-      //   ...rest,
-      // }));
-    };
+      //     const path = this.props.state.version.findPathTo(versionClicked);
+      //     if (path === undefined) {
+      //       throw new Error("Could not find path to clicked version!");
+      //     }
 
-    const onUndoClicked = (parentVersion, deltaToUndo) => {
-      // undo(deltaToUndo);
-      // this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
-      //   version: parentVersion,
-      //   historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, parentVersion),
-      //   ...rest,
-      // }));
-    };
-    const onRedoClicked = (childVersion, deltaToRedo) => {
-      // redo(deltaToRedo);
-      // this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
-      //   version: childVersion,
-      //   historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, childVersion),
-      //   ...rest,
-      // }));
-    };
+      //     // this.manipulator.x = 0;
+      //     // this.manipulator.y = 0;
+      //     this.props.setNextNodePosition(0,0);
+
+      //     for (const [linkType, delta] of path) {
+      //       if (linkType === 'p') {
+      //         undo(delta);
+      //       }
+      //       else if (linkType === 'c') {
+      //         redo(delta);
+      //       }
+      //     }
 
+      //     this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
+      //       version: versionClicked,
+      //       historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, versionClicked),
+      //       ...rest,
+      //     }));
+      //   }
+      // }
+    }
 
     const onMergeClicked = () => {
       // const text = this.textarearef.current?.value;
@@ -294,11 +260,33 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
             <Text>All links are parent links.</Text>
             <Text>Right or middle mouse button: Load version.</Text>
           </>)}
-          <Graph graph={this.props.state.historyGraph} forces={historyGraphForces} />
+          <Graph graph={this.props.state.historyGraph} forces={historyGraphForces}
+            mouseUpHandler={(e, {x, y}, node) => node ? this.props.onVersionClicked(node.obj) : undefined} />
         </Tabs.Panel>
       </Tabs>
     );
 
+    const undoButtons = this.props.state.version.parents.map(([parentVersion,deltaToUndo]) => {
+      return (
+        <div key={fullVersionId(parentVersion)}>
+          <Button fullWidth={true} compact={true} leftIcon={<IconPlayerTrackPrev size={18}/>} onClick={this.props.onUndoClicked.bind(null, parentVersion, deltaToUndo)}>
+            UNDO {deltaToUndo.getDescription()}
+          </Button>
+          <Space h="xs"/>
+        </div>
+      );
+    });
+    const redoButtons = this.props.state.version.children.map(([childVersion,deltaToRedo]) => {
+      return (
+        <div key={fullVersionId(childVersion)}>
+          <Button style={{width: "100%"}} compact={true} rightIcon={<IconPlayerTrackNext size={18}/>} onClick={this.props.onRedoClicked.bind(null, childVersion, deltaToRedo)}>
+            REDO {deltaToRedo.getDescription()}
+          </Button>
+          <Space h="xs"/>
+        </div>
+      );
+    });
+
     return (
       <>
         <Title order={4}>{this.props.title}</Title>
@@ -309,6 +297,15 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
           {makeConcreteSyntaxTabs("dependencyL0")}
         </Stack>
 
+        <SimpleGrid cols={2}>
+          <div>
+            {undoButtons}
+          </div>
+          <div>
+            {redoButtons}
+          </div>
+        </SimpleGrid>
+
 {/*        <Textarea size="xs" label="Versions to merge:" ref={this.textarearef}
           autosize placeholder="Right-click on version in History Graph to add it to this textbox"></Textarea>
         <Button onClick={() => {if (this.textarearef.current) this.textarearef.current.value = "";}}>CLEAR</Button>
@@ -325,9 +322,6 @@ interface AppState {
   as: VersionedModelState;
 }
 
-// const initialModel: VersionedModelState = 
-
-
 export class App extends React.Component<{}, AppState> {
   generateUUID = mockUuid();
   // csLvl = new CompositeLevel();
@@ -336,6 +330,8 @@ export class App extends React.Component<{}, AppState> {
   // versionRegistry = new VersionRegistry();
   parser: TrivialParser;
 
+  renderedBy: Map<Version,{corr: Version, as: Version}>;
+
   setCsState(callback) {
     return this.setState(({cs, ...rest}) => ({cs: callback(cs), ...rest}));
   }
@@ -357,15 +353,16 @@ export class App extends React.Component<{}, AppState> {
           ...rest,
         }));
       };
+      const versionRegistry = new VersionRegistry();
       const manipulator = new D3StateManipulator(setGraph);
       return {
-        version: initialVersion,
+        version: versionRegistry.initialVersion,
         graph: emptyGraph,
-        historyGraph: initialHistoryGraph,
+        historyGraph: initialHistoryGraph(versionRegistry.initialVersion),
         dependencyGraphL1: emptyGraph,
         dependencyGraphL0: emptyGraph,
         compositeLevel: new CompositeLevel(),
-        versionRegistry: new VersionRegistry(),
+        versionRegistry,
         manipulator,
         graphDeltaExecutor: new GraphDeltaExecutor(manipulator),
       };
@@ -377,6 +374,8 @@ export class App extends React.Component<{}, AppState> {
       as: makeModelState(this.setAsState.bind(this)),
     };
 
+    this.renderedBy = new Map([[this.state.cs.versionRegistry.initialVersion, {corr:this.state.corr.versionRegistry.initialVersion, as:this.state.as.versionRegistry.initialVersion}]]);
+
     this.parser = new TrivialParser(this.generateUUID,
       {
         csLvl: this.state.cs.compositeLevel,
@@ -389,46 +388,106 @@ export class App extends React.Component<{}, AppState> {
     const addVersionAndDeltas = ({version, historyGraph, dependencyGraphL1, dependencyGraphL0, ...rest}: VersionedModelState, newVersion: Version, composite: CompositeDelta) => {
       return {
         version: newVersion,
-
         // add new version to history graph + highlight the new version as the current version:
         historyGraph: setCurrentVersion(appendToHistoryGraph(historyGraph, newVersion), version, newVersion),
-
         // add the composite delta to the L1-graph + highlight it as 'active':
         dependencyGraphL1: addDeltaAndActivate(dependencyGraphL1, composite),
-
         // add the primitive L0-deltas to the L0-graph + highlight them as 'active':
         dependencyGraphL0: composite.deltas.reduce(
           (graph, delta) => {
             return addDeltaAndActivate(graph, delta);
           }, dependencyGraphL0),
-
         ...rest,
       };
     };
 
+    const undoWithoutUpdatingHistoryGraph = (state, setState, deltaToUndo) => {
+      state.manipulator.x = 0;
+      state.manipulator.y = 0;
+      state.graphDeltaExecutor.unexec(deltaToUndo);
+      setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
+        dependencyGraphL1: setDeltaInactive(prevDepGraphL1, deltaToUndo),
+        dependencyGraphL0: deltaToUndo.deltas.reduce((prevDepGraphL0, delta) => setDeltaInactive(prevDepGraphL0, delta), prevDepGraphL0),
+        ...rest,
+      }));
+    };
+    const redoWithoutUpdatingHistoryGraph = (state, setState, deltaToRedo) => {
+      state.manipulator.x = 0;
+      state.manipulator.y = 0;
+      state.graphDeltaExecutor.exec(deltaToRedo);
+      setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
+        dependencyGraphL1: setDeltaActive(prevDepGraphL1, deltaToRedo),
+        dependencyGraphL0: deltaToRedo.deltas.reduce((prevDepGraphL0, delta) => setDeltaActive(prevDepGraphL0, delta), prevDepGraphL0),
+        ...rest,
+      }));
+    };
+
+    const onUndoClicked = (state, setState, parentVersion, deltaToUndo) => {
+      console.log("onUndoClicked")
+      undoWithoutUpdatingHistoryGraph(state, setState, deltaToUndo);
+      setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
+        version: parentVersion,
+        historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, parentVersion),
+        ...rest,
+      }));
+    };
+    const onRedoClicked = (state, setState, childVersion, deltaToRedo) => {
+      console.log("onRedoClicked")
+      redoWithoutUpdatingHistoryGraph(state, setState, deltaToRedo);
+      setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
+        version: childVersion,
+        historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, childVersion),
+        ...rest,
+      }));
+    };
+
+    const gotoVersion = (state, setState, chosenVersion: Version) => {
+      const path = state.version.findPathTo(chosenVersion);
+      if (path === undefined) {
+        throw new Error("Could not find path to version!");
+      }
+      state.manipulator.x = 0;
+      state.manipulator.y = 0;
+      for (const [linkType, delta] of path) {
+        if (linkType === 'p') {
+          undoWithoutUpdatingHistoryGraph(state, setState, delta);
+        }
+        else if (linkType === 'c') {
+          redoWithoutUpdatingHistoryGraph(state, setState, delta);
+        }
+      }
+      setState(({historyGraph, version, ...rest}) => ({
+        version: chosenVersion,
+        historyGraph: setCurrentVersion(historyGraph, version, chosenVersion),
+        ...rest,
+      }));
+    }
+
+
     const onCsEdit = (deltas: PrimitiveDelta[], description: string) => {
       const csComposite = this.state.cs.compositeLevel.createComposite(deltas, description);
 
-      const {corr: corrComposite, as: asComposite, csOverrides, asOverrides} = this.parser.parse(csComposite,
-        this.state.cs.version, this.state.corr.version, this.state.as.version);
+      const csParent = this.state.cs.version;
+      const {corr: corrParent, as: asParent} = this.renderedBy.get(csParent) || (()=>{throw new Error("X")})();
 
+      const {corr: corrComposite, as: asComposite, csOverrides, asOverrides} = this.parser.parse(
+        csComposite,
+        csParent, corrParent, asParent,
+      );
 
       const newCsVersion = this.state.cs.versionRegistry.createVersion(this.state.cs.version, csComposite);
-      
+
       const newAsVersion =  (asComposite.deltas.length > 0) ?
-        this.state.as.versionRegistry.createVersion(this.state.as.version, asComposite)
+        this.state.as.versionRegistry.createVersion(asParent, asComposite)
         : this.state.as.version; // do not create a new AS version
 
-      const newCorrVersion = this.state.corr.versionRegistry.createVersion(this.state.corr.version, corrComposite,
+      const newCorrVersion = this.state.corr.versionRegistry.createVersion(corrParent, corrComposite,
         embed(
           ["cs", newCsVersion, csOverrides],
           ["as", newAsVersion, asOverrides],
         ));
 
-      // This also trigggers a few state updates:
-      this.state.cs.graphDeltaExecutor.exec(csComposite);
-      this.state.corr.graphDeltaExecutor.exec(corrComposite);
-      this.state.as.graphDeltaExecutor.exec(asComposite);
+      this.renderedBy.set(newCsVersion, {corr: newCorrVersion, as: newAsVersion});
 
       // Update state:
       this.setState(({cs, corr, as}) => {
@@ -439,6 +498,10 @@ export class App extends React.Component<{}, AppState> {
         };
         return result;
       });
+
+      gotoVersion(this.state.cs, this.setCsState.bind(this), newCsVersion);
+      gotoVersion(this.state.corr, this.setCorrState.bind(this), newCorrVersion);
+      gotoVersion(this.state.as, this.setAsState.bind(this), newAsVersion);
     };
 
     return (<>
@@ -449,24 +512,23 @@ export class App extends React.Component<{}, AppState> {
             state={this.state.cs}
             setState={this.setCsState.bind(this)}
             setNextNodePosition={(x,y)=>{this.state.cs.manipulator.x = x; this.state.cs.manipulator.y = y;}}
-            onUserEdit={onCsEdit} />
+            onUserEdit={onCsEdit}
+            onUndoClicked={onUndoClicked.bind(null, this.state.cs, this.setCsState.bind(this))}
+            onRedoClicked={onRedoClicked.bind(null, this.state.cs, this.setCsState.bind(this))}
+            onVersionClicked={version => gotoVersion(this.state.cs, this.setCsState.bind(this), version)}
+          />
         </Grid.Col>
         <Grid.Col span={1}>
           <VersionedModel title="Correspondence" readonly
             generateUUID={this.generateUUID}
             setNextNodePosition={(x,y)=>{this.state.corr.manipulator.x = x; this.state.corr.manipulator.y = y;}}
             state={this.state.corr}
-            setState={this.setCorrState.bind(this)} />
-
-  {/*        <SimpleGrid cols={2}>
-            <div>
-              {undoButtons}
-            </div>
-            <div>
-              {redoButtons}
-            </div>
-          </SimpleGrid>
-  */}
+            setState={this.setCorrState.bind(this)}
+            onUndoClicked={onUndoClicked.bind(null, this.state.corr, this.setCorrState.bind(this))}
+            onRedoClicked={onRedoClicked.bind(null, this.state.corr, this.setCorrState.bind(this))}
+            onVersionClicked={version => gotoVersion(this.state.corr, this.setCorrState.bind(this), version)}
+          />
+
         </Grid.Col>
         <Grid.Col span={1}>
           <VersionedModel title="Abstract Syntax"
@@ -474,6 +536,9 @@ export class App extends React.Component<{}, AppState> {
             setNextNodePosition={(x,y)=>{this.state.as.manipulator.x = x; this.state.as.manipulator.y = y;}}
             state={this.state.as}
             setState={this.setAsState.bind(this)}
+            onUndoClicked={onUndoClicked.bind(null, this.state.as, this.setAsState.bind(this))}
+            onRedoClicked={onRedoClicked.bind(null, this.state.as, this.setAsState.bind(this))}
+            onVersionClicked={version => gotoVersion(this.state.as, this.setAsState.bind(this), version)}
           />
         </Grid.Col>
       </Grid>
@@ -481,51 +546,3 @@ export class App extends React.Component<{}, AppState> {
   }
 }
 
-// export function makeApp() {
-//   const generateUUID = mockUuid();
-
-//   function createModelState() {
-//     const [state, setState] = React.useState
-//     return {
-
-//       lvl: new CompositeLevel(),
-//       registry: new VersionRegistry(),
-//     }
-//   }
-
-
-
-
-//   // functional component:
-//   return  function App() {
-//     const [cs, setCs] = React.useState<VersionedModelState>(initialModel);
-//     const [corr, setCorr] = React.useState<VersionedModelState>(initialModel);
-//     const [as, setAs] = React.useState<VersionedModelState>(initialModel);
-
-//     const [corrVersion, setCorrVersion] = React.useState<Version>(initialVersion);
-
-
-//     // const undoButtons = corrVersion.parents.map(([parentVersion,deltaToUndo]) => {
-//     //   return (
-//     //     <div key={fullVersionId(parentVersion)}>
-//     //       <Button fullWidth={true} compact={true} leftIcon={<IconPlayerTrackPrev size={18}/>} onClick={onUndoClicked.bind(null, parentVersion,deltaToUndo)}>
-//     //         UNDO {deltaToUndo.getDescription()}
-//     //       </Button>
-//     //       <Space h="xs"/>
-//     //     </div>
-//     //   );
-//     // });
-//     // const redoButtons = corrVersion.children.map(([childVersion,deltaToRedo]) => {
-//     //   return (
-//     //     <div key={fullVersionId(childVersion)}>
-//     //       <Button style={{width: "100%"}} compact={true} rightIcon={<IconPlayerTrackNext size={18}/>} onClick={onRedoClicked.bind(null, childVersion,deltaToRedo)}>
-//     //         REDO {deltaToRedo.getDescription()}
-//     //       </Button>
-//     //       <Space h="xs"/>
-//     //     </div>
-//     //   );
-//     // });
-
-//   };
-// }
-

+ 3 - 3
src/frontend/app_state.ts

@@ -1,4 +1,4 @@
-import {initialVersion, Version} from "../onion/version";
+import {Version} from "../onion/version";
 import {Delta} from "../onion/delta";
 import {NodeCreation, NodeDeletion, EdgeCreation, EdgeUpdate} from "../onion/primitive_delta";
 import {CompositeDelta} from "../onion/composite_delta";
@@ -19,8 +19,8 @@ export function fullDeltaId(delta: Delta): string {
 export function versionToNode(version: Version, highlight: boolean, x?: number, y?: number): d3Types.d3Node<Version> {
   return {
     id: fullVersionId(version),
-    label: (version === initialVersion ? "initial" : ""),
-    color: (version === initialVersion ? "grey" : "purple"),
+    label: (version.parents.length === 0 ? "initial" : ""),
+    color: (version.parents.length === 0 ? "grey" : "purple"),
     obj: version,
     highlight,
     x, y,

+ 0 - 1
src/frontend/editable_graph.tsx

@@ -16,7 +16,6 @@ import {PrimitiveValue, UUID} from "../onion/types";
 
 import {
   Version,
-  initialVersion,
   VersionRegistry,
 } from "../onion/version";
 

+ 9 - 10
src/onion/version.test.ts

@@ -2,7 +2,6 @@ import * as _ from "lodash";
 
 import {
   Version,
-  initialVersion,
   VersionRegistry,
   embed,
   overrideDeltas,
@@ -41,10 +40,10 @@ describe("Version", () => {
     const nodeCreation = new NodeCreation(getId());
     const nodeDeletion = new NodeDeletion(nodeCreation, [], []);
 
-    const version1 = registry.createVersion(initialVersion, nodeCreation);
+    const version1 = registry.createVersion(registry.initialVersion, nodeCreation);
     const version2 = registry.createVersion(version1, nodeDeletion);
 
-    assert(_.isEqual([... initialVersion], []), "expected initialVersion to be empty");
+    assert(_.isEqual([... registry.initialVersion], []), "expected initialVersion to be empty");
     assert(_.isEqual([... version1], [nodeCreation]), "expected version1 to contain creation");
     assert(_.isEqual([... version2], [nodeDeletion, nodeCreation]), "expected version2 to contain creation and deletion");
   });
@@ -56,10 +55,10 @@ describe("Version", () => {
     const nodeCreationA = new NodeCreation(getId());
     const nodeCreationB = new NodeCreation(getId());
 
-    const versionA = registry.createVersion(initialVersion, nodeCreationA);
+    const versionA = registry.createVersion(registry.initialVersion, nodeCreationA);
     const versionAB = registry.createVersion(versionA, nodeCreationB);
 
-    const versionB = registry.createVersion(initialVersion, nodeCreationB);
+    const versionB = registry.createVersion(registry.initialVersion, nodeCreationB);
     const versionBA = registry.createVersion(versionB, nodeCreationA);
 
     assert(versionAB === versionBA, "expected versions to be equal");
@@ -87,7 +86,7 @@ describe("Version", () => {
     assert(intersection2 === v1, "expected intersection of v1 with itself to be v1");
 
     const intersection3 = registry.getIntersection([]);
-    assert(intersection3 === initialVersion, "expected intersection of empty set to be initial (empty) version");
+    assert(intersection3 === registry.initialVersion, "expected intersection of empty set to be initial (empty) version");
   });
 
 
@@ -103,7 +102,7 @@ describe("Version", () => {
     it("Merge empty set", () => {
       const registry = new VersionRegistry();
       const merged = registry.merge([], new Map());
-      assert(merged.length === 1 && merged[0] === initialVersion, "expected intial version");
+      assert(merged.length === 1 && merged[0] === registry.initialVersion, "expected intial version");
 
       mergeAgain(registry, merged, new Map());
     })
@@ -115,8 +114,8 @@ describe("Version", () => {
       const nodeCreationA = new NodeCreation(getId());
       const nodeCreationB = new NodeCreation(getId());
 
-      const versionA = registry.createVersion(initialVersion, nodeCreationA);
-      const versionB = registry.createVersion(initialVersion, nodeCreationB);
+      const versionA = registry.createVersion(registry.initialVersion, nodeCreationA);
+      const versionB = registry.createVersion(registry.initialVersion, nodeCreationB);
 
       const nameMap = new Map([[nodeCreationA, "A"], [nodeCreationB, "B"]]);
 
@@ -188,7 +187,7 @@ describe("Version", () => {
         nameMap.set(delta, i.toString());
       }
       // Create a version for each delta, containing only that delta:
-      const versions = deltas.map(d => registry.createVersion(initialVersion, d));
+      const versions = deltas.map(d => registry.createVersion(registry.initialVersion, d));
 
       const merged = registry.merge(versions, nameMap);
       assert(merged.length === 1, "only one merged version should result");

+ 29 - 23
src/onion/version.ts

@@ -135,36 +135,42 @@ export class Version {
 
 const initialHash = Buffer.alloc(32); // all zeros
 
-export class InitialVersion extends Version {
-  private static instance: InitialVersion = new InitialVersion(); // singleton pattern
-  // private static embedded: EmbeddedVersion = {embedded: InitialVersion.instance, overriddenDeltas: new Map()};
+// export class InitialVersion extends Version {
+//   private static instance: InitialVersion = new InitialVersion(); // singleton pattern
+//   // private static embedded: EmbeddedVersion = {embedded: InitialVersion.instance, overriddenDeltas: new Map()};
 
-  private constructor() {
-    super([], initialHash, 0);
-  }
+//   private constructor() {
+//     super([], initialHash, 0);
+//   }
 
-  static getInstance(): InitialVersion {
-    return InitialVersion.instance;
-  }
+//   static getInstance(): InitialVersion {
+//     return InitialVersion.instance;
+//   }
 
-  // // override: we pretend that the initial version has all other empty models (also represented by the initial version) embedded into it.
-  // getEmbedded(id: string): EmbeddedVersion| undefined {
-  //   return InitialVersion.embedded;
-  // }
+//   // // override: we pretend that the initial version has all other empty models (also represented by the initial version) embedded into it.
+//   // getEmbedded(id: string): EmbeddedVersion| undefined {
+//   //   return InitialVersion.embedded;
+//   // }
 
-  // // override: we pretend that the initial version has all other empty models (also represented by the initial version) embedded into it.
-  // getEmbeddedOrThrow(id: string): EmbeddedVersion {
-  //   return InitialVersion.embedded;
-  // }
-}
+//   // // override: we pretend that the initial version has all other empty models (also represented by the initial version) embedded into it.
+//   // getEmbeddedOrThrow(id: string): EmbeddedVersion {
+//   //   return InitialVersion.embedded;
+//   // }
+// }
 
-// The initial, empty version.
-export const initialVersion = InitialVersion.getInstance();
+// // The initial, empty version.
+// export const initialVersion = InitialVersion.getInstance();
+
+// export function makeInitialVersion() {
+//   return new Version([], initialHash, )
+// }
 
 export class VersionRegistry {
+  readonly initialVersion: Version = new Version([], initialHash, 0);
+
   // Maps version ID (as string, because a Buffer cannot be a map key) to Version
   readonly versionMap: Map<string, Version> = new Map([
-    [initialHash.toString('base64'), initialVersion], // the initial version, always already there
+    [initialHash.toString('base64'), this.initialVersion], // the initial version, always already there
   ]);
 
   lookupOptional(hash: Buffer): Version | undefined {
@@ -262,14 +268,14 @@ export class VersionRegistry {
   // Order of deltas should be recent -> early
   // Or put more precisely: a delta's dependencies should occur AFTER the delta in the array.
   quickVersion(deltas: Array<Delta>): Version {
-    return deltas.reduceRight((parentVersion, delta) => this.createVersion(parentVersion, delta), initialVersion);
+    return deltas.reduceRight((parentVersion, delta) => this.createVersion(parentVersion, delta), this.initialVersion);
   }
 
   // Get the version whose deltas are a subset of all given versions. This is typically the 'common parent'.
   getIntersection(versions: Array<Version>): Version {
     // treat special case first:
     if (versions.length === 0) {
-      return initialVersion;
+      return this.initialVersion;
     }
 
     // sort versions (out place) from few deltas to many (FASTEST):

+ 8 - 8
src/parser/trivial_parser.test.ts

@@ -1,7 +1,6 @@
 import {TrivialParser} from "./trivial_parser";
 
 import {
-  initialVersion,
   VersionRegistry,
 } from "../onion/version";
 
@@ -21,22 +20,23 @@ describe("Trivial Parser", () => {
     const csRegistry = new VersionRegistry();
     const corrRegistry = new VersionRegistry();
     const asRegistry = new VersionRegistry();
+
     const csLvl = new CompositeLevel(); // L1
     const asLvl = new CompositeLevel(); // L1
     const corrLvl = new CompositeLevel(); // L1
     const getUuid = mockUuid();
     const parser = new TrivialParser(getUuid, {csLvl, asLvl, corrLvl});
 
-    const csV0 = initialVersion;
-    const corrV0 = initialVersion;
-    const asV0 = initialVersion;
-
     const csCreation = new NodeCreation(getUuid());
     const csDeletion = new NodeDeletion(csCreation, [], []);
 
     const csV1Composite = csLvl.createComposite([csCreation]);
     const csV2Composite = csLvl.createComposite([csDeletion]);
 
+    const csV0 = csRegistry.initialVersion;
+    const corrV0 = corrRegistry.initialVersion;
+    const asV0 = asRegistry.initialVersion;
+    
     const {corr: corrV1Composite, as: asV1Composite} = parser.parse(csV1Composite, csV0, corrV0, asV0);
 
     const csV1 = csRegistry.createVersion(csV0, csV1Composite);
@@ -65,9 +65,9 @@ describe("Trivial Parser", () => {
 
     // Same as before, but this time, we also create an edge in CS:
 
-    const csV0 = initialVersion;
-    const corrV0 = initialVersion;
-    const asV0 = initialVersion;
+    const csV0 = csRegistry.initialVersion;
+    const corrV0 = corrRegistry.initialVersion;
+    const asV0 = asRegistry.initialVersion;
 
     const csCreation = new NodeCreation(getUuid());
     const csCreateEdge = new EdgeCreation(csCreation, "x", csCreation); // self-edge