From 8744bad0b88dd6936a140c536b91b0ddd7179640 Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Mon, 12 May 2025 16:26:25 +0200 Subject: [PATCH] nicer, nested rendering when currying --- src/CallBlock.css | 20 ++++++- src/CallBlock.tsx | 135 ++++++++++++++++++++++++++++++--------------- src/Editor.tsx | 20 ++++--- src/InputBlock.css | 2 + src/InputBlock.tsx | 32 +++++------ src/LetInBlock.tsx | 3 +- src/ShowIf.tsx | 9 --- src/Type.css | 6 ++ src/index.css | 8 +-- 9 files changed, 144 insertions(+), 91 deletions(-) delete mode 100644 src/ShowIf.tsx diff --git a/src/CallBlock.css b/src/CallBlock.css index 40127b0..11d369d 100644 --- a/src/CallBlock.css +++ b/src/CallBlock.css @@ -23,18 +23,32 @@ content: ""; position: absolute; border: solid 10px transparent; - border-left-color: rgba(242, 253, 146); + border-left-color: rgb(242, 253, 146); margin-left: 0px; /* z-index: 1; */ } - .inputParam { /* height: 20px; */ margin-right: 20px; display: inline-block; - background-color: rgba(242, 253, 146); + background-color: rgb(242, 253, 146); } + +.inputParam .inputParam { + background-color: rgb(180, 248, 214); +} +.inputParam .inputParam:after { + border-left-color: rgb(180, 248, 214); +} +.inputParam .inputParam .inputParam { + background-color: rgb(153, 212, 214); +} +.inputParam .inputParam .inputParam:after { + border-left-color: rgb(153, 212, 214); +} + + .typeAnnot { display: inline-block; vertical-align: top; diff --git a/src/CallBlock.tsx b/src/CallBlock.tsx index 8bb4872..ae5116a 100644 --- a/src/CallBlock.tsx +++ b/src/CallBlock.tsx @@ -8,24 +8,35 @@ import { Type } from "./Type"; import { Value } from "./Value"; import { focusPrevElement } from "./util/dom_trickery"; -export interface CallBlockState { +export interface CallBlockState< + FnState=EditorState, + InputState=EditorState, +> { kind: "call"; env: any; - fn: EditorState; - input: EditorState; + fn: FnState; + input: InputState; resolved: undefined | Dynamic; } -interface CallBlockProps extends State2Props { +interface CallBlockProps< + FnState=EditorState, + InputState=EditorState, +> extends State2Props> { onResolve: (resolved: EditorState) => void; } -export function CallBlock({ state: {kind, env, fn, input, resolved }, setState, onResolve }: CallBlockProps) { +function headlessCallBlock({state: {kind, env, fn, input, resolved }, setState, onResolve}: CallBlockProps) { const [unifyError, setUnifyError] = useState(undefined); + const setFn = (fn: EditorState) => { + setState({kind, env, fn, input, resolved}); + } + const setInput = (input: EditorState) => { + setState({kind, env, fn, input, resolved}); + } const setResolved = (resolved?: Dynamic) => { setState({kind, env, fn, input, resolved}); } - const makeTheCall = (input, fn) => { console.log('makeTheCall...') try { @@ -46,14 +57,7 @@ export function CallBlock({ state: {kind, env, fn, input, resolved }, setState, kind, env, fn, input, resolved: undefined }) } - } - - const setFn = (fn: EditorState) => { - setState({kind, env, fn, input, resolved}); - } - const setInput = (input: EditorState) => { - setState({kind, env, fn, input, resolved}); - } + }; const onFnResolve = (fnState) => { console.log('my fn resolved') if (input.resolved) { @@ -66,7 +70,7 @@ export function CallBlock({ state: {kind, env, fn, input, resolved }, setState, kind, env, fn: fnState, input, resolved: undefined }); } - } + }; const onInputResolve = (inputState) => { console.log('my input resolved') if (fn.resolved) { @@ -79,47 +83,86 @@ export function CallBlock({ state: {kind, env, fn, input, resolved }, setState, kind, env, fn, input: inputState, resolved: undefined }); } - } + }; const onFnCancel = () => { - + setState(input); } const onInputCancel = () => { setState(fn); } + // const filterCompatibleInputs = ([_name, dynamic]: [string, Dynamic]) => { + // if (fn.resolved) { + // try { + // assignFn(getType(fn.resolved), getType(dynamic)); + // } catch (e) { + // if (!(e instanceof UnifyError)) { + // throw e; + // } + // return false; + // } + // } + // return true; + // } + return {unifyError, setFn, setInput, onFnResolve, onInputResolve, onFnCancel, onInputCancel}; +} - const filterCompatibleInputs = ([_name, dynamic]: [string, Dynamic]) => { - if (fn.resolved) { - try { - assignFn(getType(fn.resolved), getType(dynamic)); - } catch (e) { - if (!(e instanceof UnifyError)) { - throw e; - } - return false; - } - } - return true; - } - +export function CallBlock({ state, setState, onResolve }: CallBlockProps) { + const {unifyError, setFn, setInput, onFnResolve, onInputResolve, onFnCancel, onInputCancel} + = headlessCallBlock({ state, setState, onResolve }); return -
- 𝑓𝑛  - -
+
-
- -
- { resolved - ? - : <> } - { unifyError - ? <>{unifyError.toString()} - : <> - } + {/* Sequence of input parameters */} + + + {/* Output (or Error) */} + {state.resolved && } + {unifyError && unifyError.toString()}
; } + +function FunctionHeader({ fn }) { + if (fn.kind === "call") { + // 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 + + // recurse: + return ; + } + else { + // end of recursion - draw function name + return
+ 𝑓𝑛  + {/*todo*/}} + onResolve={() => {}} onCancel={() => {/*todo*/}}/> +
; + } +} + +function InputParams({ fn, input, setInput, onInputResolve, onInputCancel }) { + return
+ {(fn.kind === "call") && + // if the function we're calling is itself the result of a function call, + // then we render its input parameter nested in our own input parameter box, which is way more readable + + // Input(s) of the function we're calling: + {/*todo*/}} + onInputResolve={() => {/*todo*/}} + onInputCancel={() => {/*todo*/}}/> + } + + {/* Our own input */} + +
; +}; diff --git a/src/Editor.tsx b/src/Editor.tsx index 6455a81..2bb7f09 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -1,4 +1,4 @@ -import { getSymbol, getType, module2Env, ModuleStd, symbolFunction } from "dope2"; +import { getSymbol, getType, module2Env, ModuleStd, symbolFunction, getDefaultTypeParser } from "dope2"; import { InputBlock, type InputBlockState } from "./InputBlock"; import { type Dynamic, type State2Props } from "./util/extra"; @@ -16,7 +16,6 @@ interface LambdaBlockState { paramName: string; expr: EditorState; resolved: undefined | Dynamic; - rollback?: EditorState; } export type EditorState = @@ -25,12 +24,14 @@ export type EditorState = | LetInBlockState | LambdaBlockState; +const mkType = getDefaultTypeParser(); export const initialEditorState: EditorState = { kind: "input", - env: module2Env(ModuleStd), + env: module2Env(ModuleStd.concat([ + ["functionWith3Params", {i: i=>j=>k=>i+j+k, t: mkType("Int->Int->Int->Int")}], + ])), text: "", resolved: undefined, - rollback: undefined, }; interface EditorProps extends State2Props { @@ -84,7 +85,6 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) { fn: state, input: initialEditorState, resolved: undefined, - rollback: state, }); return; } @@ -97,7 +97,6 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) { fn: initialEditorState, input: state, resolved: undefined, - rollback: state, }); return; } @@ -116,7 +115,6 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) { name: "", value: state, resolved: undefined, - rollback: state, }); return; } @@ -141,7 +139,13 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) { ?
:: { (needCommand) - ? + ? : <> }
diff --git a/src/InputBlock.css b/src/InputBlock.css index a458fbf..2492cbb 100644 --- a/src/InputBlock.css +++ b/src/InputBlock.css @@ -22,6 +22,8 @@ display: block; text-align: left; position: absolute; + margin-top: 7px; + margin-left: 4px; border: solid 1px dodgerblue; cursor: pointer; max-height: calc(100vh - 44px); diff --git a/src/InputBlock.tsx b/src/InputBlock.tsx index ec09c0b..f1afa5d 100644 --- a/src/InputBlock.tsx +++ b/src/InputBlock.tsx @@ -8,14 +8,12 @@ import { Type } from "./Type"; import "./InputBlock.css"; import type { Dynamic, State2Props } from "./util/extra"; import type { EditorState } from "./Editor"; -import { ShowIf } from "./ShowIf"; export interface InputBlockState { kind: "input"; env: any; text: string; resolved: undefined | Dynamic; - rollback?: EditorState; } interface InputBlockProps extends State2Props { @@ -24,7 +22,7 @@ interface InputBlockProps extends State2Props { onCancel: () => void; } -export function InputBlock({ state: {kind, env, text, resolved, rollback}, setState, filter, onResolve, onCancel }: InputBlockProps) { +export function InputBlock({ state: {kind, env, text, resolved}, setState, filter, onResolve, onCancel }: InputBlockProps) { const ref = useRef(null); useEffect(() => { ref.current?.focus(); @@ -34,10 +32,10 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt const [haveFocus, setHaveFocus] = useState(false); // whether to render suggestions or not const setText = (text: string) => { - setState({kind, env, text, resolved, rollback}); + setState({kind, env, text, resolved}); } const setResolved = (resolved: Dynamic) => { - setState({kind, env, text, resolved, rollback}); + setState({kind, env, text, resolved}); } const singleSuggestion = trie.growPrefix(env.name2dyn)(text); @@ -60,7 +58,6 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt }, [text]); const onSelectSuggestion = ([name, dynamic]) => { - console.log(name); // setText(name); // ref.current.textContent = name; // setRightMostCaretPosition(ref.current); @@ -72,7 +69,6 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt env, text: name, resolved: dynamic, - rollback, }); }; @@ -84,7 +80,6 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt env, text: e.target.value, resolved: undefined, - rollback, }); } }; @@ -151,6 +146,16 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt return + {/* Dropdown suggestions */} + {haveFocus && + + + + } + {/* Input box */} setHaveFocus(true)} onBlur={() => setHaveFocus(false)} - /> + spellCheck={false}/> + {/* Single 'grey' suggestion */} {singleSuggestion} - - - - + ; } diff --git a/src/LetInBlock.tsx b/src/LetInBlock.tsx index 490eb96..df1bebe 100644 --- a/src/LetInBlock.tsx +++ b/src/LetInBlock.tsx @@ -9,7 +9,6 @@ export interface LetInBlockState { value: EditorState; inner: EditorState; resolved: undefined | Dynamic; - rollback?: EditorState; } interface LetInBlockProps extends State2Props { @@ -17,6 +16,6 @@ interface LetInBlockProps extends State2Props { } -export function LetInBlock({env, name, value, inner, resolved, rollback, onResolve}) { +export function LetInBlock({env, name, value, inner, resolved, onResolve}) { } \ No newline at end of file diff --git a/src/ShowIf.tsx b/src/ShowIf.tsx deleted file mode 100644 index 182f239..0000000 --- a/src/ShowIf.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// syntactic sugar -export function ShowIf({cond, children}) { - if (cond) { - return <>{children}; - } - else { - return <>; - } -} \ No newline at end of file diff --git a/src/Type.css b/src/Type.css index d11a299..c103aae 100644 --- a/src/Type.css +++ b/src/Type.css @@ -6,6 +6,12 @@ .type { margin:1px; display: inline-block; + + font-family: "Roboto", sans-serif; + font-optical-sizing: auto; + font-weight: 400; + font-style: normal; + font-variation-settings: "wdth" 100; } .functionType { diff --git a/src/index.css b/src/index.css index 7529615..ee875fd 100644 --- a/src/index.css +++ b/src/index.css @@ -7,12 +7,6 @@ body { font-optical-sizing: auto; font-weight: 400; font-style: normal; - font-variation-settings: - "wdth" 100; - + font-variation-settings: "wdth" 100; } -:focus-within:not(body) { - /* outline: 2px solid black; */ - /* background-color: aqua; */ -} \ No newline at end of file