dope2-webapp/src/component/expr/CallBlock.tsx

112 lines
3.8 KiB
TypeScript

import { useContext } from "react";
import { CallContext } from "../../context/CallContext";
import { GlobalContext } from "../../context/GlobalContext";
import { type TypeInfoCall } from "../../eval/infer_type";
import { getActions } from "../app/actions";
import { TypeInfoBlock } from "../other/Type";
import "./CallBlock.css";
import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock";
import type { DeepEvalResultCall } from "../../eval/deep_eval";
export interface CallBlockState {
kind: "call";
fn: ExprBlockState;
input: ExprBlockState;
}
export interface CallBlockProps<
FnState=ExprBlockState,
InputState=ExprBlockState,
> extends State2Props<CallBlockState,ExprBlockState> {
typeInfo: TypeInfoCall;
evalResult: DeepEvalResultCall;
}
function nestedFnProperties({state, setState, score, typeInfo, evalResult}: CallBlockProps) {
const setFn = (callback: SetStateFn) => {
setState(state => ({...state, fn: callback(state.fn)}));
};
const onFnCancel = () => {
setState(state => state.input); // we become our input
};
const scoreFn = (fnSuggestion: ExprBlockState) => {
return score({ ...state, fn: fnSuggestion });
};
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn, typeInfo: typeInfo.fn, evalResult: evalResult.fn};
}
function nestedInputProperties({state, setState, score, typeInfo, evalResult}: CallBlockProps) {
const setInput = (callback: SetStateFn) => {
setState(state => ({...state, input: callback(state.input)}));
};
const onInputCancel = () => {
setState(state => /*addFocusRightMost*/(state.fn)); // we become our function
};
const scoreInput = (inputSuggestion: ExprBlockState) => {
return score({ ...state, input: inputSuggestion });
};
return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput, typeInfo: typeInfo.input, evalResult: evalResult.input};
}
export function CallBlock(props: CallBlockProps) {
const globalContext = useContext(GlobalContext);
const addParam = getActions(globalContext, props.setState).c;
const err = props.typeInfo.err || props.evalResult.err;
return <span className="functionBlock dropdownContainer">
<CallContext value={{addParam}}>
<FunctionHeader {...props} addParam={addParam} />
<div className="functionParams">
<div className="outputParam">
{/* Sequence of input parameters */}
<InputParams
{...props}
addParam={addParam}
/>
:: <TypeInfoBlock typeInfo={props.typeInfo} />
</div>
</div>
</CallContext>
{(err !== undefined) &&
<span className="">
<div className="errorMessage">
{err.message.trim()}
</div>
</span>}
</span>;
}
function FunctionHeader(props) {
const globalContext = useContext(GlobalContext);
const nestedProperties = nestedFnProperties(props);
if (props.state.fn.kind === "call" && globalContext?.syntacticSugar) {
// if the function we're calling is itself the result of a function call,
// then we are anonymous, and so we don't draw a function name
return <FunctionHeader {...nestedProperties} />;
}
else {
// end of recursion - draw function name
return <span className="functionName">
&nbsp;&#119891;&#119899;&nbsp;
<ExprBlock {...nestedProperties} />
</span>;
}
}
function InputParams(props) {
const globalContext = useContext(GlobalContext);
const isOffending = props.typeInfo.err;
return <div className={"inputParam" + (isOffending ? " offending" : "")}>
{props.state.fn.kind === "call"
&& globalContext?.syntacticSugar
&& <InputParams
{...nestedFnProperties(props as CallBlockProps)}
/>}
{/* Our own input param */}
<ExprBlock
{...nestedInputProperties(props as CallBlockProps)}
/>
</div>;
}