From d9b92b860cf18fc60f9e9c31cc6f99257e80b7d7 Mon Sep 17 00:00:00 2001 From: Drew Hess Date: Sat, 20 Apr 2024 23:01:05 +0100 Subject: [PATCH] feat: use the interpreter to evaluate top-level definitions Signed-off-by: Drew Hess --- orval.config.ts | 2 +- src/components/Edit/index.tsx | 8 +- .../EvalBoundedInterp.stories.tsx | 33 ++++++ src/components/EvalBoundedInterp/index.tsx | 105 ++++++++++++++++++ src/components/PictureInPicture/index.tsx | 8 +- src/components/index.ts | 1 + src/primer-api/primer-api.ts | 67 ++++++----- 7 files changed, 187 insertions(+), 37 deletions(-) create mode 100644 src/components/EvalBoundedInterp/EvalBoundedInterp.stories.tsx create mode 100644 src/components/EvalBoundedInterp/index.tsx diff --git a/orval.config.ts b/orval.config.ts index 5ec9bac9..e443a338 100644 --- a/orval.config.ts +++ b/orval.config.ts @@ -6,7 +6,7 @@ import { defineConfig } from "orval"; // This lists the operation IDs for such requests. // We configure Orval to generate simple `useQuery`-based code, as it would for a GET request, // rather than its default `useMutation` approach for POSTs. -const getStylePostRequests = ["getAvailableActions", "eval-full"]; +const getStylePostRequests = ["getAvailableActions", "eval-full", "eval-bounded-interp"]; const useQueryPost: { [key: string]: OperationOptions; } = Object.assign( diff --git a/src/components/Edit/index.tsx b/src/components/Edit/index.tsx index aa6c165c..c117753b 100644 --- a/src/components/Edit/index.tsx +++ b/src/components/Edit/index.tsx @@ -39,7 +39,7 @@ import { useGetActionOptions, useCreateDefinition, useCreateTypeDef, - useEvalFull, + useEvalBoundedInterp, Level, useUndo, useRedo, @@ -250,10 +250,10 @@ const AppNoError = ({ const [evalTarget, setEvalTarget] = useState(); const evalResult = useInvalidateOnChange( - useEvalFull( + useEvalBoundedInterp( p.sessionId, { qualifiedModule: p.module.modname, baseName: evalTarget || "" }, - { stepLimit: 300 }, + { timeoutMicroseconds: 100000 }, { query: { enabled: !!evalTarget } } ), [p.module] @@ -344,7 +344,7 @@ const AppNoError = ({ // Note: these offsets are rather arbitrary. initialPosition={{ x: 10, y: 10 }} moduleName={p.module.modname} - evalFull={{ + evalBoundedInterp={{ request: setEvalTarget, ...(evalResult.isSuccess ? { result: evalResult.data } : {}), }} diff --git a/src/components/EvalBoundedInterp/EvalBoundedInterp.stories.tsx b/src/components/EvalBoundedInterp/EvalBoundedInterp.stories.tsx new file mode 100644 index 00000000..9f1049fa --- /dev/null +++ b/src/components/EvalBoundedInterp/EvalBoundedInterp.stories.tsx @@ -0,0 +1,33 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { tree3 } from "../examples/trees"; + +import { EvalBoundedInterp } from "./"; + +const meta: Meta = { + title: "Application/Component Library/EvalBoundedInterp", + component: EvalBoundedInterp, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + moduleName: ["Primer", "Examples"], + evalBoundedInterp: { + request: () => { + return; + }, + result: { tag: "EvalBoundedInterpRespNormal", contents: tree3 }, + }, + level: "Expert", + defs: ["footballGame", "whatsopposite"], + }, +}; diff --git a/src/components/EvalBoundedInterp/index.tsx b/src/components/EvalBoundedInterp/index.tsx new file mode 100644 index 00000000..97b5561d --- /dev/null +++ b/src/components/EvalBoundedInterp/index.tsx @@ -0,0 +1,105 @@ +import { useState } from "react"; +import { NodeChange, ReactFlowProvider, useReactFlow } from "reactflow"; +import { EvalBoundedInterpResp, GlobalName, Level } from "@/primer-api"; +import { SelectMenu, TreeReactFlowOne } from "@/components"; +import { + TreeReactFlowOneProps, + defaultTreeReactFlowProps, +} from "../TreeReactFlow"; + +export type EvalBoundedInterpProps = { + moduleName: string[]; + evalBoundedInterp: { + request: (baseName: string | undefined) => void; + result?: EvalBoundedInterpResp; + }; + level: Level; + defs: string[]; + initialEvalDef: string | undefined; + extraTreeProps: Partial; +}; + +const Evaluated = (p: { + defName: GlobalName; + evaluated?: EvalBoundedInterpResp; + level: Level; + extraTreeProps: Partial; +}) => { + const padding = 1.0; + const { fitView } = useReactFlow(); + const onNodesChange = (_: NodeChange[]) => { + fitView({ padding }); + }; + const resultTree = () => { + switch (p?.evaluated?.tag) { + case "EvalBoundedInterpRespNormal": + return { tree: p.evaluated.contents }; + default: + // This should be some indication of an error having occurred, + // but our UI is a bit too limited for that at the moment. + return {}; + } + }; + + return ( + + ); +}; + +const disableEval = ""; + +// We only offer to evaluate the definitions in the "main" module +export const EvalBoundedInterp = ({ + defs, + evalBoundedInterp, + moduleName, + level, + initialEvalDef, + extraTreeProps, +}: EvalBoundedInterpProps): JSX.Element => { + const [evalDef, setEvalDef0] = useState(initialEvalDef ?? disableEval); + const setEvalDef = (e: string) => { + setEvalDef0(e); + evalBoundedInterp.request(e === disableEval ? undefined : e); + }; + return ( +
+
+ setEvalDef(selection)} + /> +
+ {evalDef !== disableEval && ( + <> +
+ + + +
+ + )} +
+ ); +}; + +export default EvalBoundedInterp; diff --git a/src/components/PictureInPicture/index.tsx b/src/components/PictureInPicture/index.tsx index d400e227..6cb4daff 100644 --- a/src/components/PictureInPicture/index.tsx +++ b/src/components/PictureInPicture/index.tsx @@ -11,16 +11,16 @@ import classNames from "classnames"; import { useRef, useState } from "react"; import { useDraggable, DragOptions } from "@neodrag/react"; import { Tab } from "@headlessui/react"; -import type { EvalFullProps } from "@/components/EvalFull"; +import type { EvalBoundedInterpProps } from "@/components/EvalBoundedInterp"; import type { SelectionInfoProps } from "@/components/SelectionInfo"; -import { EvalFull, SelectionInfo } from "@/components"; +import { EvalBoundedInterp, SelectionInfo } from "@/components"; export type PictureInPictureTab = "Eval" | "Info"; export type PictureInPictureProps = { initialTab: PictureInPictureTab; initialPosition: { x: number; y: number }; -} & EvalFullProps & +} & EvalBoundedInterpProps & SelectionInfoProps; const tabClasses = (selected: boolean, extra?: string) => @@ -96,7 +96,7 @@ export const PictureInPicture = (p: PictureInPictureProps): JSX.Element => {
- +
diff --git a/src/components/index.ts b/src/components/index.ts index 8dc16d0a..ae1fd3fe 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,6 +9,7 @@ export { default as CreateDefModal } from "./CreateDefModal"; export { default as DefsToolbar } from "./DefsToolbar"; export { default as Edit } from "./Edit"; export { default as Error } from "./Error"; +export { default as EvalBoundedInterp } from "./EvalBoundedInterp"; export { default as EvalFull } from "./EvalFull"; export { default as GroupedButtonList } from "./GroupedButtonList"; export { default as NoMatch } from "./NoMatch"; diff --git a/src/primer-api/primer-api.ts b/src/primer-api/primer-api.ts index 6c1d5b85..29704865 100644 --- a/src/primer-api/primer-api.ts +++ b/src/primer-api/primer-api.ts @@ -695,7 +695,7 @@ export const useEvalFull = { +const useEvalBoundedInterpHook = () => { const evalBoundedInterp = useCustomInstance(); return ( @@ -714,47 +714,58 @@ export const useEvalBoundedInterpHook = () => { } +export const getEvalBoundedInterpQueryKey = (sessionId: string, + globalName: GlobalName, + params?: EvalBoundedInterpParams,) => { + return [`/openapi/sessions/${sessionId}/eval-bounded-interp`, ...(params ? [params]: []), globalName] as const; + } -export const useEvalBoundedInterpMutationOptions = , - TContext = unknown>(options?: { mutation?:UseMutationOptions>>, TError,{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, TContext>, } -): UseMutationOptions>>, TError,{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, TContext> => { -const {mutation: mutationOptions} = options ?? {}; + +export const useEvalBoundedInterpQueryOptions = >>, TError = ErrorType>(sessionId: string, + globalName: GlobalName, + params?: EvalBoundedInterpParams, options?: { query?:Partial>>, TError, TData>>, } +) => { - const evalBoundedInterp = useEvalBoundedInterpHook() +const {query: queryOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getEvalBoundedInterpQueryKey(sessionId,globalName,params); - const mutationFn: MutationFunction>>, {sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}> = (props) => { - const {sessionId,data,params} = props ?? {}; + const evalBoundedInterp = useEvalBoundedInterpHook(); - return evalBoundedInterp(sessionId,data,params,) - } + const queryFn: QueryFunction>>> = () => evalBoundedInterp(sessionId,globalName,params, ); - + + - return { mutationFn, ...mutationOptions }} + return { queryKey, queryFn, enabled: !!(sessionId), ...queryOptions} as UseQueryOptions>>, TError, TData> & { queryKey: QueryKey } +} - export type EvalBoundedInterpMutationResult = NonNullable>>> - export type EvalBoundedInterpMutationBody = GlobalName - export type EvalBoundedInterpMutationError = ErrorType +export type EvalBoundedInterpQueryResult = NonNullable>>> +export type EvalBoundedInterpQueryError = ErrorType - /** +/** * @summary Using the interpreter, evaluate the named definition to normal form (or time out) */ -export const useEvalBoundedInterp = , - TContext = unknown>(options?: { mutation?:UseMutationOptions>>, TError,{sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, TContext>, } -): UseMutationResult< - Awaited>>, - TError, - {sessionId: string;data: GlobalName;params?: EvalBoundedInterpParams}, - TContext - > => { +export const useEvalBoundedInterp = >>, TError = ErrorType>( + sessionId: string, + globalName: GlobalName, + params?: EvalBoundedInterpParams, options?: { query?:Partial>>, TError, TData>>, } + + ): UseQueryResult & { queryKey: QueryKey } => { + + const queryOptions = useEvalBoundedInterpQueryOptions(sessionId,globalName,params,options) + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey ; + + return query; +} + + - const mutationOptions = useEvalBoundedInterpMutationOptions(options); - return useMutation(mutationOptions); - } - /** * @summary Get the specified session's name */