a bit more progress

This commit is contained in:
Joeri Exelmans 2025-05-12 00:02:14 +02:00
parent a9ae4f9888
commit 2b0d8bc2c6
8 changed files with 109 additions and 28 deletions

View file

@ -5,7 +5,7 @@
}
.functionName {
text-align: center;
/* text-align: center; */
}
@ -20,7 +20,7 @@
}
.inputParam {
height: 20px;
/* height: 20px; */
margin-right: 20px;
display: inline-block;
background-color: rgba(242, 253, 146);

View file

@ -22,7 +22,7 @@ interface CallBlockProps extends State2Props<CallBlockState> {
}
export function CallBlock({ state: {kind, env, fn, input, resolved, rollback }, setState, onResolve }: CallBlockProps) {
const setResolved = (resolved: Dynamic) => {
const setResolved = (resolved?: Dynamic) => {
setState({kind, env, fn, input, resolved, rollback});
}
@ -53,7 +53,11 @@ export function CallBlock({ state: {kind, env, fn, input, resolved, rollback },
makeTheCall(input, fnState);
}
else {
setFn(fnState);
// setFn(fnState);
setResolved(undefined);
onResolve({
kind, env, fn: fnState, input, resolved: undefined, rollback
});
}
}
const onInputResolve = (inputState) => {

View file

@ -7,7 +7,7 @@ import { useEffect, useState } from "react";
import { Type } from "./Type";
import "./Editor.css"
import { focusNextElement } from "./util/dom_trickery";
import { focusNextElement, focusPrevElement } from "./util/dom_trickery";
interface LetInBlockState {
kind: "let";
@ -49,22 +49,43 @@ interface EditorProps extends State2Props<EditorState> {
const dontFilter = () => true;
function getCommands(type) {
const commands = ['u', 't', 'Enter', 'Backspace', 'ArrowLeft', 'Tab'];
if (getSymbol(type) === symbolFunction) {
commands.push('c');
}
return commands;
}
export function Editor({state, setState, onResolve, onCancel}: EditorProps) {
const [proxyState, setProxyState] = useState<'unresolved'|'command'|'resolved'>('unresolved');
const [needCommand, setNeedCommand] = useState(false);
const onMyResolve = (editorState: EditorState) => {
setState(editorState);
if (editorState.resolved) {
setProxyState('command');
setNeedCommand(true);
}
else {
setProxyState('unresolved');
// unresolved
setNeedCommand(false);
onResolve(editorState); // pass up the fact that we're unresolved
}
}
// const onMyCancel
const onCommand = (e: React.KeyboardEvent) => {
const type = getType(state.resolved);
if (e.key === "c" && getSymbol(type) === symbolFunction) {
e.preventDefault();
const commands = getCommands(type);
if (!commands.includes(e.key)) {
return;
}
e.preventDefault();
setNeedCommand(false);
// u -> pass Up
if (e.key === "u" || e.key === "Enter" || e.key === "Tab") {
onResolve(state);
return;
}
// c -> Call
if (e.key === "c") {
// we become CallBlock
setState({
kind: "call",
@ -74,11 +95,24 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) {
resolved: undefined,
rollback: state,
});
setProxyState('resolved');
return;
}
if (e.key === "u" || e.key === "Enter") {
setProxyState('resolved');
onResolve(state);
// t -> Transform
if (e.key === "t") {
// we become CallBlock
setState({
kind: "call",
env: state.env,
fn: initialEditorState,
input: state,
resolved: undefined,
rollback: state,
});
return;
}
if (e.key === "Backspace" || e.key === "ArrowLeft") {
focusPrevElement();
return;
}
};
@ -100,7 +134,7 @@ export function Editor({state, setState, onResolve, onCancel}: EditorProps) {
(state.resolved)
? <div className="typeSignature">
:: <Type type={getType(state.resolved)} />
{ (proxyState === 'command')
{ (needCommand)
? <input autoFocus={true} className="editable command" placeholder="<enter command>" onKeyDown={onCommand} value=""/>
: <></>
}

View file

@ -14,7 +14,11 @@
font-variation-settings: "wdth" 100;
}
.suggestions {
.suggestions {
display: none;
}
.suggestions {
display: block;
text-align: left;
position: absolute;
border: solid 1px dodgerblue;

View file

@ -8,6 +8,7 @@ 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";
@ -27,9 +28,6 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt
const ref = useRef<any>(null);
useEffect(() => {
ref.current?.focus();
if (ref.current) {
// ref.current.textContent = text;
}
}, []);
const [i, setI] = useState(0); // selected suggestion
@ -111,6 +109,10 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt
setRightMostCaretPosition(ref.current);
e.preventDefault();
}
else {
onSelectSuggestion(suggestions[i]);
e.preventDefault();
}
}
},
ArrowDown: () => {
@ -149,21 +151,38 @@ export function InputBlock({ state: {kind, env, text, resolved, rollback}, setSt
return <span>
<span className="">
<input ref={ref} placeholder="start typing..." autoFocus={true} className="editable" value={text} onInput={onInput} onKeyDown={onKeyDown} onFocus={() => setHaveFocus(true)} onBlur={() => setHaveFocus(false)}/>
<input ref={ref} placeholder="start typing..." className="editable" value={text} onInput={onInput} onKeyDown={onKeyDown} onFocus={() => setHaveFocus(true)} onBlur={() => setTimeout(() => setHaveFocus(false), 200)}/>
<span className="text-block suggest">{singleSuggestion}</span>
</span>
{
(haveFocus)
? <Suggestions suggestions={suggestions} onSelect={onSelectSuggestion} i={i} setI={setI} />
: <></>
}
<ShowIf cond={haveFocus}>
<Suggestions
suggestions={suggestions}
onSelect={onSelectSuggestion}
i={i} setI={setI} />
</ShowIf>
</span>;
}
function Suggestions({ suggestions, onSelect, i, setI }) {
const onMouseEnter = j => () => {
setI(j);
};
const onMouseDown = j => () => {
setI(j);
onSelect(suggestions[i]);
};
return (suggestions.length > 0) ?
<div className="suggestions">
{suggestions.map(([name, dynamic], j) => <div key={`${j}_${name}`} className={i === j ? "selected" : ""} onClick={() => onSelect(suggestions[i])}>{name} :: <Type type={getType(dynamic)} /></div>)}
<div className={"suggestions"}>
{suggestions.map(([name, dynamic], j) =>
<div
key={`${j}_${name}`}
className={i === j ? "selected" : ""}
onMouseEnter={onMouseEnter(j)}
onMouseDown={onMouseDown(j)}>
{name} :: <Type type={getType(dynamic)} />
</div>)
}
</div>
: <></>;
}

9
src/ShowIf.tsx Normal file
View file

@ -0,0 +1,9 @@
// syntactic sugar
export function ShowIf({cond, children}) {
if (cond) {
return <>{children}</>;
}
else {
return <></>;
}
}

View file

@ -1,4 +1,4 @@
import {getType, getInst, getSymbol, Double, Int, symbolFunction, symbolProduct, symbolSum, symbolDict, symbolSet, symbolList, eqType, match} from "dope2";
import {getType, getInst, getSymbol, Double, Int, symbolFunction, symbolProduct, symbolSum, symbolDict, symbolSet, symbolList, eqType, match, getLeft, getRight} from "dope2";
import "./Value.css";
@ -20,6 +20,9 @@ export function Value({dynamic}) {
// return <BinaryType type={type} cssClass="productType" infix="&#10799;" prefix="" suffix=""/>;
case symbolSum:
return <ValueSum val={inst} leftType={type.params[0](type)} rightType={type.params[1](type)}/>;
case symbolProduct:
return <ValueProduct val={inst} leftType={type.params[0](type)} rightType={type.params[1](type)}/>;
// case symbolDict:
// return <BinaryType type={type} cssClass="dictType" infix="&rArr;" prefix="{" suffix="}"/>;
// case symbolSet:
@ -51,4 +54,7 @@ function ValueSum({val, leftType, rightType}) {
return match(val)
(l => <>L <Value dynamic={{i:l, t:leftType}}/></>)
(r => <>R <Value dynamic={{i:r, t:rightType}}/></>);
}
function ValueProduct({val, leftType, rightType}) {
return <>(<Value dynamic={{i:getLeft(val), t:leftType}}/>,&nbsp;<Value dynamic={{i:getRight(val), t:rightType}} />)</>;
}

View file

@ -10,4 +10,9 @@ body {
font-variation-settings:
"wdth" 100;
}
:focus-within:not(body) {
/* outline: 2px solid black; */
/* background-color: aqua; */
}