Forráskód Böngészése

New feature: ability to disable auto-parsing/-rendering

Joeri Exelmans 2 éve
szülő
commit
95e2e6fec9

+ 0 - 86
src/frontend/correspondence.ts

@@ -1,86 +0,0 @@
-import * as React from "react";
-
-import {newVersionedModel} from "./versioned_model";
-import {TrivialParser} from "../parser/trivial_parser";
-import {Version} from "../onion/version";
-import {GraphState} from "../onion/graph_state"; 
-
-// Pure function
-// Replays all deltas in a version to compute the graph state of that version.
-function getGraphState(version: Version): GraphState {
-  const graphState = new GraphState();
-  for (const d of [...version].reverse()) {
-    graphState.exec(d);
-  }
-  return graphState;
-}
-
-export function newCorrespondence({generateUUID, primitiveRegistry, cs, as}) {
-  const {initialState, getCurrentVersion, getReducer: getReducerOrig, getReactComponents} = newVersionedModel({readonly: true, generateUUID, primitiveRegistry});
-
-  const parser = new TrivialParser(primitiveRegistry, generateUUID);
-
-  // Mapping from correspondence model version to CS and AS model version.
-  const corrMap: Map<Version, {csVersion: Version, asVersion: Version}> = new Map([
-    [initialState.version, {csVersion: cs.initialState.version, asVersion: as.initialState.version}]
-  ]);
-
-  function getReducer(setCorrState, csReducer, asReducer) {
-    const {
-      gotoVersion: gotoVersionOrig,
-      createAndGotoNewVersion,
-      undo,
-      redo,
-    } = getReducerOrig(setCorrState);
-
-    const parse = (csDeltas, description: string) => {
-      const csCurrentVersion = cs.getCurrentVersion();
-      const asCurrentVersion = as.getCurrentVersion();
-
-      const [csGS, corrGS, asGS] = [csCurrentVersion, getCurrentVersion(), asCurrentVersion].map(v => getGraphState(v));
-
-      const {corrDeltas, asDeltas} = parser.parse(csDeltas, csGS, corrGS, asGS);
-
-      const csVersion = csReducer.createAndGotoNewVersion(csDeltas, description);
-      const corrVersion = createAndGotoNewVersion(corrDeltas, description);
-      const asVersion = asDeltas.length > 0 ? asReducer.createAndGotoNewVersion(asDeltas, "parse:"+description) : asCurrentVersion;
-
-      corrMap.set(corrVersion, {csVersion, asVersion});        
-    };
-    const render = (asDeltas, description: string) => {
-      const csCurrentVersion = cs.getCurrentVersion();
-      const asCurrentVersion = as.getCurrentVersion();
-
-      const [csGS, corrGS, asGS] = [csCurrentVersion, getCurrentVersion(), asCurrentVersion].map(v => getGraphState(v));
-      
-      const {corrDeltas, csDeltas} = parser.render(asDeltas, csGS, corrGS, asGS);
-
-      const csVersion = csDeltas.length > 0 ? csReducer.createAndGotoNewVersion(csDeltas, "render:"+description) : csCurrentVersion;
-      const corrVersion = createAndGotoNewVersion(corrDeltas, description);
-      const asVersion = asReducer.createAndGotoNewVersion(asDeltas, description);
-
-      corrMap.set(corrVersion, {csVersion, asVersion});
-    };
-    const gotoVersion = (corrVersion: Version) => {
-      const {csVersion, asVersion} = corrMap.get(corrVersion)!;
-      csReducer.gotoVersion(csVersion);
-      gotoVersionOrig(corrVersion);
-      asReducer.gotoVersion(asVersion);
-    };
-
-    return {
-      parse,
-      render,
-      gotoVersion,
-      undo,
-      redo,
-    };
-  }
-
-  return {
-    initialState,
-    getCurrentVersion,
-    getReducer,
-    getReactComponents,
-  };
-}

+ 179 - 0
src/frontend/correspondence.tsx

@@ -0,0 +1,179 @@
+import * as React from "react";
+import * as Mantine from "@mantine/core";
+
+import {newVersionedModel, VersionedModelState} from "./versioned_model";
+import {TrivialParser} from "../parser/trivial_parser";
+import {Version} from "../onion/version";
+import {GraphState} from "../onion/graph_state"; 
+import {CompositeDelta} from "../onion/composite_delta";
+
+// Pure function
+// Replays all deltas in a version to compute the graph state of that version.
+function getGraphState(version: Version): GraphState {
+  const graphState = new GraphState();
+  for (const d of [...version].reverse()) {
+    graphState.exec(d);
+  }
+  return graphState;
+}
+
+export function newCorrespondence({generateUUID, primitiveRegistry, cs, as}) {
+  const {initialState, getCurrentVersion, getReducer: getReducerOrig, getReactComponents} = newVersionedModel({readonly: true, generateUUID, primitiveRegistry});
+
+  const parser = new TrivialParser(primitiveRegistry, generateUUID);
+
+  // Mapping from correspondence model version to CS and AS model version.
+  const corrMap: Map<Version, {csVersion: Version, asVersion: Version}> = new Map([
+    [initialState.version, {csVersion: cs.initialState.version, asVersion: as.initialState.version}]
+  ]);
+
+  // Mapping from CS to correspondence
+  const parseMap: Map<Version, Version> = new Map([[cs.initialState.version, initialState.version]]);
+
+  // Mapping from AS to correspondence
+  const renderMap: Map<Version, Version> = new Map([[as.initialState.version, initialState.version]]);
+
+  function getReducer(setCorrState, csReducer, asReducer) {
+    const {
+      gotoVersion: gotoVersionOrig,
+      createAndGotoNewVersion,
+      undo,
+      redo,
+    } = getReducerOrig(setCorrState);
+
+    const parse = (csDeltas, description: string) => {
+      const csParentVersion = cs.getCurrentVersion();
+      const corrParentVersion = parseMap.get(csParentVersion)!;
+      const asParentVersion = corrMap.get(corrParentVersion)!.asVersion;
+
+      const [csGS, corrGS, asGS] = [csParentVersion, corrParentVersion, asParentVersion].map(getGraphState);
+
+      const {corrDeltas, asDeltas} = parser.parse(csDeltas, csGS, corrGS, asGS);
+
+      const csVersion = csReducer.createAndGotoNewVersion(csDeltas, description);
+      const corrVersion = createAndGotoNewVersion(corrDeltas, "cs:"+description, corrParentVersion);
+      const asVersion = asDeltas.length > 0 ? asReducer.createAndGotoNewVersion(asDeltas, "parse:"+description, asParentVersion) : asParentVersion;
+
+      corrMap.set(corrVersion, {csVersion, asVersion});
+      parseMap.set(csVersion, corrVersion);
+      renderMap.set(asVersion, corrVersion);
+    };
+    const render = (asDeltas, description: string) => {
+      const asParentVersion = as.getCurrentVersion();
+      const corrParentVersion = renderMap.get(asParentVersion)!;
+      const csParentVersion = corrMap.get(corrParentVersion)!.csVersion;
+
+      const [csGS, corrGS, asGS] = [csParentVersion, corrParentVersion, asParentVersion].map(getGraphState);
+      
+      const {corrDeltas, csDeltas} = parser.render(asDeltas, csGS, corrGS, asGS);
+
+      const csVersion = csDeltas.length > 0 ? csReducer.createAndGotoNewVersion(csDeltas, "render:"+description) : csParentVersion;
+      const corrVersion = createAndGotoNewVersion(corrDeltas, "as:"+description);
+      const asVersion = asReducer.createAndGotoNewVersion(asDeltas, description);
+
+      corrMap.set(corrVersion, {csVersion, asVersion});
+      parseMap.set(csVersion, corrVersion);
+      renderMap.set(asVersion, corrVersion);
+    };
+    const parseExistingVersion = (csVersion: Version) => {
+      for (const [csParentVersion, compositeDelta] of csVersion.parents) {
+
+        const csDeltas = (compositeDelta as CompositeDelta).deltas;
+        const description = compositeDelta.getDescription();
+
+        if (!parseMap.has(csParentVersion)) {
+          parseExistingVersion(csParentVersion);
+        }
+
+        const corrParentVersion = parseMap.get(csParentVersion);
+        if (corrParentVersion === undefined) {
+          console.log("Cannot parse - no parent CORR version");
+          continue;
+        }
+        const asParentVersion = corrMap.get(corrParentVersion)?.asVersion;
+        if (asParentVersion === undefined) {
+          console.log("Cannot parse - no parent AS version");
+          continue;
+        }
+
+        const [csGS, corrGS, asGS] = [csParentVersion, corrParentVersion, asParentVersion].map(getGraphState);
+
+        const {corrDeltas, asDeltas} = parser.parse(csDeltas, csGS, corrGS, asGS);
+        const corrVersion = createAndGotoNewVersion(corrDeltas, "cs:"+description, corrParentVersion);
+        const asVersion = asDeltas.length > 0 ? asReducer.createAndGotoNewVersion(asDeltas, "parse:"+description, asParentVersion) : asParentVersion;
+
+        corrMap.set(corrVersion, {csVersion, asVersion});
+        parseMap.set(csVersion, corrVersion);
+        renderMap.set(asVersion, corrVersion);
+      }
+    };
+    const renderExistingVersion = (asVersion: Version) => {
+      for (const [asParentVersion, compositeDelta] of asVersion.parents) {
+
+        const asDeltas = (compositeDelta as CompositeDelta).deltas;
+        const description = compositeDelta.getDescription();
+
+        if (!renderMap.has(asParentVersion)) {
+          parseExistingVersion(asParentVersion);
+        }
+
+        const corrParentVersion = renderMap.get(asParentVersion);
+        if (corrParentVersion === undefined) {
+          console.log("Cannot parse - no parent CORR version");
+          continue;
+        }
+        const csParentVersion = corrMap.get(corrParentVersion)?.csVersion;
+        if (csParentVersion === undefined) {
+          console.log("Cannot parse - no parent CS version");
+          continue;
+        }
+
+        const [csGS, corrGS, asGS] = [csParentVersion, corrParentVersion, asParentVersion].map(getGraphState);
+
+        const {corrDeltas, csDeltas} = parser.render(asDeltas, csGS, corrGS, asGS);
+        const corrVersion = createAndGotoNewVersion(corrDeltas, "as:"+description, corrParentVersion);
+        const csVersion = csDeltas.length > 0 ? csReducer.createAndGotoNewVersion(csDeltas, "parse:"+description, csParentVersion) : asParentVersion;
+
+        corrMap.set(corrVersion, {csVersion, asVersion});
+        parseMap.set(csVersion, corrVersion);
+        renderMap.set(asVersion, corrVersion);
+      }
+    };
+    const gotoVersion = (corrVersion: Version) => {
+      const {csVersion, asVersion} = corrMap.get(corrVersion)!;
+      csReducer.gotoVersion(csVersion);
+      gotoVersionOrig(corrVersion);
+      asReducer.gotoVersion(asVersion);
+    };
+    const getParseRenderButtons = (csState: VersionedModelState, asState: VersionedModelState) => {
+      const parseButton =
+        <Mantine.Button compact
+          disabled={parseMap.has(csState.version)}
+          onClick={() => parseExistingVersion(csState.version)}
+        >Parse</Mantine.Button>;
+      const renderButton =
+        <Mantine.Button compact
+          disabled={renderMap.has(asState.version)}
+          onClick={() => renderExistingVersion(asState.version)}
+        >Render</Mantine.Button>;
+      return {parseButton, renderButton};
+    };
+
+    return {
+      parse,
+      render,
+      gotoVersion,
+      undo,
+      redo,
+      getParseRenderButtons,
+    };
+  }
+
+
+  return {
+    initialState,
+    getCurrentVersion,
+    getReducer,
+    getReactComponents,
+  };
+}

+ 44 - 7
src/frontend/demo_corr.tsx

@@ -1,5 +1,5 @@
 import * as React from "react";
-import {SimpleGrid, Text, Title, Group, Stack, Button, Space, Textarea, Tabs, HoverCard, ActionIcon, Center} from "@mantine/core";
+import {SimpleGrid, Text, Title, Stack, Center, Switch, Group, Space} from "@mantine/core";
 
 import {PrimitiveRegistry} from "../onion/primitive_delta";
 import {mockUuid} from "../onion/test_helpers";
@@ -23,12 +23,21 @@ export function getDemoCorr() {
     const [csState, setCsState] = React.useState<VersionedModelState>(cs.initialState);
     const [corrState, setCorrState] = React.useState<VersionedModelState>(corr.initialState);
 
+    const [autoParse, setAutoParse] = React.useState<boolean>(true);
+    const [autoRender, setAutoRender] = React.useState<boolean>(true);
+
     const asReducer = as.getReducer(setAsState);
     const csReducer = cs.getReducer(setCsState);
     const corrReducer = corr.getReducer(setCorrState, csReducer, asReducer);
 
     const csComponents = cs.getReactComponents(csState, {
-      onUserEdit: corrReducer.parse,
+      onUserEdit: (deltas, description) => {
+        if (autoParse) {
+          corrReducer.parse(deltas, description);
+        } else {
+          csReducer.createAndGotoNewVersion(deltas, description);
+        }
+      },
       onUndoClicked: csReducer.undo,
       onRedoClicked: csReducer.redo,
       onVersionClicked: csReducer.gotoVersion,
@@ -39,7 +48,14 @@ export function getDemoCorr() {
       onVersionClicked: corrReducer.gotoVersion,
     });
     const asComponents = as.getReactComponents(asState, {
-      onUserEdit: corrReducer.render,
+      onUserEdit: (deltas, description) => {
+        if (autoRender) {
+          corrReducer.render(deltas, description);
+        }
+        else {
+          asReducer.createAndGotoNewVersion(deltas, description);
+        }
+      },
       onUndoClicked: asReducer.undo,
       onRedoClicked: asReducer.redo,
       onVersionClicked: asReducer.gotoVersion,
@@ -49,31 +65,52 @@ export function getDemoCorr() {
     const corrTabs = ["state", "history", "dependencyL1", "dependencyL0"];
     const asTabs = ["state", "history", "dependencyL1", "dependencyL0"];
 
+    const {parseButton, renderButton} = corrReducer.getParseRenderButtons(csState, asState);
+
     return (<>
+          <Group position="right">
+          </Group>
       <SimpleGrid cols={3}>
         <div>
-          <Title order={4}>Concrete Syntax</Title>
+          <Group position="center">
+            <Title order={4}>Concrete Syntax</Title>
+            <Switch label="Auto-parse"
+              checked={autoParse} onChange={(event) => setAutoParse(event.currentTarget.checked)}/>
+            {parseButton}
+          </Group>
+          <Space h="md" />
           <Stack>
             {csComponents.makeTabs("editor", csTabs)}
             {csComponents.makeTabs("history", csTabs)}
+            <Center>{csComponents.undoRedoButtons}</Center>
           </Stack>
         </div>
         <div>
-          <Title order={4}>Correspondence</Title>
+          <Group position="center">
+            <Title order={4}>Correspondence</Title>
+          </Group>
+          <Space h="md" />
           <Stack>
             {corrComponents.makeTabs("state", corrTabs)}
             {corrComponents.makeTabs("history", corrTabs)}
+            <Center>{corrComponents.undoRedoButtons}</Center>
           </Stack>
         </div>
         <div>
-          <Title order={4}>Abstract Syntax</Title>
+          <Group position="center">
+            <Title order={4}>Abstract Syntax</Title>
+            <Switch label="Auto-render"
+              checked={autoRender} onChange={(event) => setAutoRender(event.currentTarget.checked)}/>
+            {renderButton}
+          </Group>
+          <Space h="md" />
           <Stack>
             {asComponents.makeTabs("state", asTabs)}
             {asComponents.makeTabs("history", asTabs)}
+            <Center>{asComponents.undoRedoButtons}</Center>
           </Stack>
         </div>
       </SimpleGrid>
-      <Center>{corrComponents.undoRedoButtons}</Center>
     </>);
   }
 }

+ 3 - 2
src/frontend/demo_pd.tsx

@@ -103,12 +103,13 @@ export function getDemoPD() {
               <Space h="md"/>
               <SimpleGrid cols={4} spacing="xs">
                 <Button onClick={unionClicked} compact disabled={i===0} leftIcon={<Icons.IconChevronUp/>}>Union </Button>
-                {components.undoRedoButtons}
+                {components.undoButton}
+                {components.redoButton}
                 <Button onClick={cloneClicked} compact rightIcon={<Icons.IconChevronDown/>}>Clone</Button>
               </SimpleGrid>
             </div>
             <div>
-              <Text>Deltas</Text>
+              <Text>Deltas (L0)</Text>
               {components.depGraphL0Component}
             </div>
           </SimpleGrid>

+ 8 - 28
src/frontend/versioned_model.tsx

@@ -121,35 +121,14 @@ export function newVersionedModel({generateUUID, primitiveRegistry, readonly}) {
               }, dependencyGraphL0),
             ...rest,
           };
-        });        
+        });
+
+        return newVersion;
       }
     };
-    const createAndGotoNewVersion = (deltas: PrimitiveDelta[], description: string): Version => {
-      const composite = compositeLevel.createComposite(deltas, description);
-      const newVersion = versionRegistry.createVersion(currentVersion, composite);
-      currentVersion = newVersion;
-
-      // update graph state:
-      const d3Updater = new D3GraphStateUpdater(setGraph, x, y);
-      graphState.exec(composite, d3Updater);
-
-      // update rest of state:
-      setState(({version: oldVersion, historyGraph, dependencyGraphL1, dependencyGraphL0, ...rest}) => {
-        return {
-          version: newVersion,
-          // add new version to history graph + highlight the new version as the current version:
-          historyGraph: setCurrentVersion(appendToHistoryGraph(historyGraph, newVersion), oldVersion, newVersion),
-          // add the composite delta to the L1-graph + highlight it as 'active':
-          dependencyGraphL1: composite.deltas.length > 0 ? addDeltaAndActivate(dependencyGraphL1, composite) : dependencyGraphL1, // never add an empty 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 createAndGotoNewVersion = (deltas: PrimitiveDelta[], description: string, parentVersion: Version = currentVersion): Version => {
+      const newVersion = addDeltasAndVersion(deltas, description, parentVersion.hash) as Version;
+      gotoVersion(newVersion);
       return newVersion;
     };
     const undoWithoutUpdatingHistoryGraph = (deltaToUndo) => {
@@ -277,6 +256,7 @@ export function newVersionedModel({generateUUID, primitiveRegistry, readonly}) {
 
     const undoRedoButtons = <>
       {undoButton}
+      <Mantine.Space w="md"/>
       {redoButton}
     </>;
 
@@ -308,7 +288,7 @@ export function newVersionedModel({generateUUID, primitiveRegistry, readonly}) {
     );
     const makeTabs = (defaultTab: string, tabs: string[]) => {
       return <Mantine.Tabs defaultValue={defaultTab} keepMounted={false}>
-        <Mantine.Tabs.List>
+        <Mantine.Tabs.List grow>
           {tabs.map(tab => ({
             editor: <Mantine.Tabs.Tab key={tab} value={tab}>Editor</Mantine.Tabs.Tab>,
             state:  <Mantine.Tabs.Tab key={tab} value={tab}>State</Mantine.Tabs.Tab>,