import { useContext, useEffect, useRef, useState } from "react"; import { getSymbol, getType, symbolFunction } from "dope2"; import { CallBlock, type CallBlockState } from "./CallBlock"; import { InputBlock, type InputBlockState, type SuggestionType } from "./InputBlock"; import { Type } from "./Type"; import { DeepError, evalEditorBlock } from "./eval"; import { CommandContext } from "./CommandContext"; import "./Editor.css"; import { EnvContext } from "./EnvContext"; import type { LambdaBlockState } from "./LambdaBlock"; import { LetInBlock, type LetInBlockState } from "./LetInBlock"; import { initialEditorState } from "./configurations"; import { focusNextElement, focusPrevElement } from "./util/dom_trickery"; export type EditorState = InputBlockState | CallBlockState | LetInBlockState | LambdaBlockState; export type SetStateFn = (state: InType) => OutType; export interface State2Props { state: InType; setState: (callback: SetStateFn) => void; } interface EditorProps extends State2Props { filter: (suggestion: SuggestionType) => boolean; onCancel: () => void; } function getCommands(type) { const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', '=', '.']; if (getSymbol(type) === symbolFunction) { commands.push('c'); } return commands; } function getShortCommands(type) { if (getSymbol(type) === symbolFunction) { return 'c|Tab|.'; } return 'Tab|.'; } function removeFocus(state: EditorState): EditorState { if (state.kind === "input") { return {...state, focus: false}; } if (state.kind === "call") { return {...state, fn: removeFocus(state.fn), input: removeFocus(state.input), }; } return state; } export function Editor({state, setState, onCancel, filter}: EditorProps) { const env = useContext(EnvContext); const [needCommand, setNeedCommand] = useState(false); const commandInputRef = useRef(null); useEffect(() => { if (needCommand) { commandInputRef.current?.focus(); } }, [needCommand]); const globalContext = useContext(CommandContext); const onCommand = (e: React.KeyboardEvent) => { // const type = getType(state.resolved); // const commands = getCommands(type); const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', '=', '.', 'c']; if (!commands.includes(e.key)) { return; } e.preventDefault(); setNeedCommand(false); // u -> pass Up if (e.key === "e" || e.key === "Enter" || e.key === "Tab" && !e.shiftKey) { // onResolve(state); globalContext?.doHighlight.eval(); return; } if (e.key === "Tab" && e.shiftKey) { setNeedCommand(false); focusPrevElement(); } // c -> Call if (e.key === "c") { // we become CallBlock setState(state => ({ kind: "call", fn: removeFocus(state), input: initialEditorState, resolved: undefined, })); globalContext?.doHighlight.call(); // focusNextElement(); return; } // t -> Transform if (e.key === "t" || e.key === ".") { // we become CallBlock setState(state => ({ kind: "call", fn: initialEditorState, input: removeFocus(state), resolved: undefined, })); globalContext?.doHighlight.transform(); return; } if (e.key === "Backspace" || e.key === "ArrowLeft") { focusPrevElement(); return; } if (e.key === "ArrowRight") { focusNextElement(); return; } // l -> Let ... in ... // = -> assign to name if (e.key === 'l' || e.key === '=') { // we become LetInBlock setState(state => ({ kind: "let", inner: removeFocus(initialEditorState), name: "", value: removeFocus(state), resolved: undefined, })); globalContext?.doHighlight.let(); return; } }; const renderBlock = () => { switch (state.kind) { case "input": return EditorState)=>void} filter={filter} onCancel={onCancel} />; case "call": return EditorState)=>void} filter={filter} />; case "let": return EditorState)=>void} />; case "lambda": return <>; } } const resolved = evalEditorBlock(state, env); return <> {renderBlock()} { (resolved && !(resolved instanceof DeepError)) ?
::
: <> } `} onKeyDown={onCommand} value={""} onChange={() => {}} /> ; }