diff --git a/src/components/TreeReactFlow/Flavor.ts b/src/components/TreeReactFlow/Flavor.ts index 92040006..c0baa903 100644 --- a/src/components/TreeReactFlow/Flavor.ts +++ b/src/components/TreeReactFlow/Flavor.ts @@ -31,25 +31,25 @@ export const flavorClasses = (flavor: NodeFlavor): string => { ); case "Ann": return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", + "border-black-primary ring-black-primary bg-black-primary", "hover:ring-black-primary", commonHoverClasses ); case "App": return classNames( - "rounded-md border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", + "border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", "hover:ring-blue-tertiary", commonHoverClasses ); case "APP": return classNames( - "rounded-md border-yellow-secondary ring-yellow-secondary bg-yellow-secondary", - "hover:ring-yellow-secondary", + "border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", + "hover:ring-blue-tertiary", commonHoverClasses ); case "Con": return classNames( - "rounded-md border-green-primary ring-green-primary bg-white-primary", + "border-green-primary ring-green-primary bg-white-primary", "hover:ring-green-primary", commonHoverClasses ); @@ -61,7 +61,7 @@ export const flavorClasses = (flavor: NodeFlavor): string => { ); case "KType": return classNames( - "rounded-md border-grey-tertiary ring-grey-tertiary bg-grey-tertiary", + "border-grey-tertiary ring-grey-tertiary bg-grey-tertiary", "hover:ring-grey-tertiary", commonHoverClasses ); @@ -73,109 +73,109 @@ export const flavorClasses = (flavor: NodeFlavor): string => { ); case "Lam": return classNames( - "rounded-md border-blue-primary ring-blue-primary bg-white-primary", + "border-blue-primary ring-blue-primary bg-white-primary", "hover:ring-blue-primary", commonHoverClasses ); case "LAM": return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", + "border-blue-secondary ring-blue-secondary bg-white-primary", + "hover:ring-blue-secondary", commonHoverClasses ); case "GlobalVar": return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-white-primary", + "border-blue-quaternary ring-blue-quaternary bg-white-primary", "hover:ring-blue-quaternary", commonHoverClasses ); case "LocalVar": return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-white-primary", + "border-blue-quaternary ring-blue-quaternary bg-white-primary", "hover:ring-blue-quaternary", commonHoverClasses ); case "Let": return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", "hover:ring-blue-quaternary", commonHoverClasses ); case "LetType": return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", "hover:ring-blue-quaternary", commonHoverClasses ); case "Letrec": return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", "hover:ring-blue-quaternary", commonHoverClasses ); case "Case": return classNames( - "rounded-md border-yellow-primary ring-yellow-primary bg-yellow-primary", + "border-yellow-primary ring-yellow-primary bg-yellow-primary", "hover:ring-yellow-primary", commonHoverClasses ); // Note: not selectable. case "CaseWith": - return "rounded-md border-yellow-primary ring-yellow-primary bg-yellow-primary"; + return "border-yellow-primary ring-yellow-primary bg-yellow-primary"; case "PrimCon": return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", + "border-green-primary ring-green-primary bg-white-primary", + "hover:ring-green-primary", commonHoverClasses ); case "TEmptyHole": return classNames( - "rounded-full border-red-primary ring-red-primary bg-white-primary", - "hover:ring-red-primary", + "border-red-tertiary ring-red-tertiary bg-white-primary", + "hover:ring-red-tertiary", commonHoverClasses ); case "THole": return classNames( - "rounded-full border-red-primary ring-red-primary bg-white-primary", - "hover:ring-red-primary", + "border-red-tertiary ring-red-tertiary bg-white-primary", + "hover:ring-red-tertiary", commonHoverClasses ); case "TCon": return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", + "border-green-primary ring-green-primary bg-white-primary", + "hover:ring-green-primary", commonHoverClasses ); case "TFun": return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", + "border-blue-primary ring-blue-primary bg-blue-primary", + "hover:ring-blue-primary", commonHoverClasses ); case "TVar": return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", + "border-blue-quaternary ring-blue-quaternary bg-white-primary", + "hover:ring-blue-quaternary", commonHoverClasses ); case "TApp": return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", + "border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", + "hover:ring-blue-tertiary", commonHoverClasses ); case "TForall": return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", + "border-blue-secondary ring-blue-secondary bg-white-primary", + "hover:ring-blue-secondary", commonHoverClasses ); case "TLet": return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "hover:ring-blue-quaternary", commonHoverClasses ); @@ -185,17 +185,17 @@ export const flavorClasses = (flavor: NodeFlavor): string => { // edges over it. Otherwise, we'd need to special-case the // z-index of edges when drawn inside a pattern. case "Pattern": - return "rounded-md border-yellow-primary ring-yellow-primary"; + return "border-yellow-primary ring-yellow-primary"; case "PatternCon": - return "rounded-md border-green-primary ring-green-primary bg-white-primary"; + return "border-green-primary ring-green-primary bg-white-primary"; case "PrimPattern": - return "border-black-primary ring-black-primary bg-white-primary"; + return "border-green-primary ring-green-primary bg-white-primary"; case "PatternWildcard": return "border-none bg-transparent"; case "PatternBind": return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-white-primary", + "border-blue-quaternary ring-blue-quaternary bg-white-primary", "hover:ring-blue-quaternary", commonHoverClasses ); @@ -227,7 +227,7 @@ export const flavorContentClasses = ( case "Lam": return "text-blue-primary"; case "LAM": - return "text-blue-primary"; + return "text-blue-secondary"; case "GlobalVar": return "text-blue-primary"; case "LocalVar": @@ -257,7 +257,7 @@ export const flavorContentClasses = ( case "TApp": return "text-white-primary"; case "TForall": - return "text-blue-primary"; + return "text-blue-secondary"; case "TLet": return "text-white-primary"; case "PatternCon": @@ -285,13 +285,13 @@ export const flavorLabelClasses = (flavor: NodeFlavor): string => { case "App": return "font-code bg-blue-tertiary border-blue-tertiary text-white-primary"; case "APP": - return "font-code bg-yellow-secondary border-yellow-secondary text-white-primary"; + return "font-code bg-blue-tertiary border-blue-tertiary text-white-primary"; case "Con": return "bg-green-primary border-green-primary text-white-primary"; case "Lam": return "font-code bg-blue-primary border-blue-primary text-white-primary"; case "LAM": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-secondary border-blue-secondary text-white-primary"; case "GlobalVar": return "bg-blue-quaternary border-blue-quaternary text-white-primary"; case "KHole": @@ -313,29 +313,29 @@ export const flavorLabelClasses = (flavor: NodeFlavor): string => { case "CaseWith": // Special case: we hide this label. return "hidden font-code bg-yellow-primary border-yellow-primary text-white-primary"; case "PrimCon": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-green-primary border-green-primary text-white-primary"; case "TEmptyHole": - return "italic font-code bg-red-primary border-red-primary text-white-primary"; + return "font-code bg-red-tertiary border-red-tertiary text-white-primary"; case "THole": - return "italic font-code bg-red-primary border-red-primary text-white-primary"; + return "font-code bg-red-tertiary border-red-tertiary text-white-primary"; case "TCon": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-green-primary border-green-primary text-white-primary"; case "TFun": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-primary border-blue-primary text-white-primary"; case "TVar": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-blue-quaternary border-blue-quaternary text-white-primary"; case "TApp": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-tertiary border-blue-tertiary text-white-primary"; case "TForall": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-secondary border-blue-secondary text-white-primary"; case "TLet": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-quaternary border-blue-quaternary text-white-primary"; case "Pattern": - return "bg-yellow-primary border-yellow-primary text-white-primary"; + return ""; case "PatternCon": return "bg-green-primary border-green-primary text-white-primary"; case "PrimPattern": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-green-primary border-green-primary text-white-primary"; case "PatternWildcard": return "hidden"; case "PatternBind": @@ -354,7 +354,7 @@ export const flavorEdgeClasses = (flavor: NodeFlavor): string => { case "App": return "stroke-blue-tertiary"; case "APP": - return "stroke-yellow-secondary"; + return "stroke-blue-tertiary"; case "Con": return "stroke-green-primary"; case "KHole": @@ -366,7 +366,7 @@ export const flavorEdgeClasses = (flavor: NodeFlavor): string => { case "Lam": return "stroke-blue-primary"; case "LAM": - return "stroke-black-primary"; + return "stroke-blue-secondary"; case "GlobalVar": return "stroke-blue-quaternary"; case "LocalVar": @@ -382,31 +382,31 @@ export const flavorEdgeClasses = (flavor: NodeFlavor): string => { case "CaseWith": return "stroke-yellow-primary"; case "PrimCon": - return "stroke-black-primary"; + return "stroke-green-primary"; case "TEmptyHole": - return "stroke-red-primary"; + return "stroke-red-tertiary"; case "THole": - return "stroke-red-primary"; + return "stroke-red-tertiaryy"; case "TCon": - return "stroke-black-primary"; + return "stroke-green-primary"; case "TFun": - return "stroke-black-primary"; + return "stroke-blue-primary"; case "TVar": - return "stroke-black-primary"; + return "stroke-blue-quaternary"; case "TApp": - return "stroke-black-primary"; + return "stroke-blue-tertiary"; case "TForall": - return "stroke-black-primary"; + return "stroke-blue-secondary"; case "TLet": - return "stroke-black-primary"; + return "stroke-blue-quaternary"; case "Pattern": return "stroke-yellow-primary"; case "PatternCon": return "stroke-green-primary"; case "PrimPattern": - return "stroke-black-primary"; + return "stroke-green-primary"; case "PatternWildcard": - return "stroke-black-primary"; + return "stroke-grey-secondary"; case "PatternBind": return "stroke-blue-quaternary"; } @@ -421,9 +421,9 @@ export const flavorLabel = (flavor: NodeFlavor): string => { case "Ann": return ":"; case "App": - return "$"; + return "←"; case "APP": - return "@"; + return "←"; case "Con": return "V"; case "Lam": @@ -457,13 +457,13 @@ export const flavorLabel = (flavor: NodeFlavor): string => { case "TVar": return "Var"; case "TApp": - return "@"; + return "←"; case "TForall": return "∀"; case "TLet": return "let"; case "Pattern": - return "P"; + return ""; case "PatternCon": return "V"; case "PrimPattern": @@ -522,3 +522,53 @@ export const boxFlavorBackground = (flavor: NodeFlavorBoxBody): string => { return "bg-yellow-primary"; } }; + +// TODO get from backend? this comes down to whether these are from `Expr` or `Type` +export const isTypeLevel = (flavor: NodeFlavor): "term" | "type" => { + switch (flavor) { + case "Con": + case "Lam": + case "LAM": + case "Let": + case "Letrec": + case "PatternBind": + case "PatternCon": + case "LetType": + case "GlobalVar": + case "LocalVar": + case "PrimCon": + case "Pattern": + case "Hole": + case "EmptyHole": + case "Ann": + case "App": + case "APP": + case "Case": + case "CaseWith": + case "PatternWildcard": + case "PrimPattern": + return "term"; + case "TCon": + case "TVar": + case "TForall": + case "TLet": + case "TEmptyHole": + case "THole": + case "TFun": + case "TApp": + return "type"; + // TODO separate rendering for kinds? + case "KFun": + case "KHole": + case "KType": + return "type"; + } +}; +export const typeOrTermClasses = (x: "term" | "type"): string => { + switch (x) { + case "term": + return "rounded-3xl"; + case "type": + return ""; + } +}; diff --git a/src/components/TreeReactFlow/index.tsx b/src/components/TreeReactFlow/index.tsx index 6f89b306..8dfc13ab 100644 --- a/src/components/TreeReactFlow/index.tsx +++ b/src/components/TreeReactFlow/index.tsx @@ -60,7 +60,9 @@ import { flavorEdgeClasses, flavorLabel, flavorLabelClasses, + isTypeLevel, noBodyFlavorContents, + typeOrTermClasses, } from "./Flavor"; import { ZoomBar, ZoomBarProps } from "./ZoomBar"; import { WasmLayoutType } from "@zxch3n/tidy/wasm_dist"; @@ -152,6 +154,7 @@ const nodeTypes = {
( +const augmentTree = async ( tree: APITree, - f: (tree: APITreeNode) => Promise<[T, (child: T, isRight: boolean) => E]> -): Promise> => { + f: (tree: APITreeNode) => Promise<[N, (child: N, isRight: boolean) => E]> +): Promise> => { const childTrees = await Promise.all( tree.childTrees.map((t) => augmentTree(t, f)) ); @@ -998,78 +1001,106 @@ const typeDefToTree = async ( * It ensures that these are clearly displayed as "one atomic thing", * i.e. to avoid confused readings that group the type of 'foo' with the body of 'bar' (etc). */ -export const TreeReactFlow = (p: PropsWithChildren) => ( - - typeDefToTree(def, { ...p.defParams, ...p }).then((t) => - layoutTree(t, p.layout).then(({ tree, width, height }) => ({ - // All we're doing here is adding `nested: []` to all type def nodes. - // We just have to be very explicit here in order to please the typechecker. - width, - height, - tree: treeMap(tree, ({ position, ...n }) => ({ - position, - ...primerNodeWith(n, { nested: [], ...n.data }), - })), - })) - ) - ), - ...p.defs.map((def) => - defToTree(def, { ...p.defParams, ...p }).then((t) => - layoutTree(t, p.layout) - ) - ), - ]).then( - // Space out the forest. - (sizedTrees) => - sizedTrees.reduce< - [Tree, PrimerEdge>[], number] - >( - ([trees, offset], { tree, width, height }) => { - const { increment, offsetVector } = (() => { - switch (p.forestLayout) { - case "Horizontal": - return { - increment: width, - offsetVector: { x: offset, y: 0 }, - }; - case "Vertical": - return { - increment: height, - offsetVector: { x: 0, y: offset }, - }; - } - })(); - return [ - trees.concat( - treeMap(tree, (n) => ({ - ...n, - position: { - x: n.position.x + p.layout.margins.sibling + offsetVector.x, - y: n.position.y + p.layout.margins.child + offsetVector.y, - }, - })) - ), - offset + increment + p.treePadding, - ]; - }, - [[], 0] - )[0] - )} - onNodeClick={(mouseEvent, node) => - p.onNodeClick(mouseEvent, makeSelectionFromNode(node)) - } - zoomBarProps={p.zoomBarProps} - > - - {p.children} - -); +export const TreeReactFlow = (p: PropsWithChildren) => { + const spaceForest = ( + sizedTrees: { + tree: Tree, PrimerEdge>; + width: number; + height: number; + }[], + extra: { x: number; y: number } = { x: 0, y: 0 } + ) => + sizedTrees.reduce< + [Tree, PrimerEdge>[], number] + >( + ([trees, offset], { tree, width, height }) => { + const { increment, offsetVector } = (() => { + switch (p.forestLayout) { + case "Horizontal": + return { + increment: width, + offsetVector: { x: offset, y: 0 }, + }; + case "Vertical": + return { + increment: height, + offsetVector: { x: 0, y: offset }, + }; + } + })(); + return [ + trees.concat( + treeMap(tree, (n) => ({ + ...n, + position: { + x: + n.position.x + + p.layout.margins.sibling + + offsetVector.x + + extra.x, + y: + n.position.y + + p.layout.margins.child + + offsetVector.y + + extra.y, + }, + })) + ), + offset + increment + p.treePadding, + ]; + }, + [[], 0] + )[0]; + return ( + { + const typeDefTrees = await Promise.all( + p.typeDefs.map((def) => + typeDefToTree(def, { ...p.defParams, ...p }).then((t) => + layoutTree(t, p.layout).then(({ tree, width, height }) => ({ + // All we're doing here is adding `nested: []` to all type def nodes. + // We just have to be very explicit here in order to please the typechecker. + width, + height, + tree: treeMap(tree, ({ position, ...n }) => ({ + position, + ...primerNodeWith(n, { nested: [], ...n.data }), + })), + })) + ) + ) + ); + const termDefTrees = await Promise.all( + p.defs.map((def) => + defToTree(def, { ...p.defParams, ...p }).then((t) => + layoutTree(t, p.layout) + ) + ) + ); + const typeRowHeight = + typeDefTrees.length > 0 + ? Math.max(...typeDefTrees.map((x) => x.height)) + p.treePadding + : 0; + return [ + ...spaceForest(typeDefTrees), + ...spaceForest(termDefTrees, { x: 0, y: typeRowHeight }), + ]; + })()} + onNodeClick={(mouseEvent, node) => + p.onNodeClick(mouseEvent, makeSelectionFromNode(node)) + } + zoomBarProps={p.zoomBarProps} + > + + {p.children} + + ); +}; export default TreeReactFlow; export type TreeReactFlowOneProps = {