::
diff --git a/src/LetInBlock.tsx b/src/LetInBlock.tsx
index 1f2aed3..1f97681 100644
--- a/src/LetInBlock.tsx
+++ b/src/LetInBlock.tsx
@@ -1,14 +1,13 @@
-import { useContext, useEffect, useRef } from "react";
+import { useContext } from "react";
import { ExprBlock, type ExprBlockState } from "./ExprBlock";
import { EnvContext } from "./EnvContext";
import { evalEditorBlock, makeInnerEnv, scoreResolved, type ResolvedType } from "./eval";
import { type State2Props } from "./ExprBlock";
-import { autoInputWidth } from "./util/dom_trickery";
import { GlobalContext } from "./GlobalContext";
import "./LetInBlock.css";
-import type { CallBlockState } from "./CallBlock";
+import { Input } from "./Input";
export interface LetInBlockState {
kind: "let";
@@ -39,9 +38,6 @@ function DeclColumns({state: {name, value, inner, focus}, setState, suggestionPr
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
const setValue = callback => setState(state => ({...state, value: callback(state.value)}));
- const onChangeName = (e: React.ChangeEvent
) => {
- setState(state => ({...state, name: e.target.value}));
- }
const valueSuggestionPriority = (suggestion: ResolvedType) => {
const innerEnv = makeInnerEnv(env, name, suggestion);
@@ -49,28 +45,22 @@ function DeclColumns({state: {name, value, inner, focus}, setState, suggestionPr
return scoreResolved(resolved, suggestionPriority);
};
- const nameRef = useRef(null);
- useEffect(() => {
- if (focus) {
- nameRef.current?.focus();
- }
- }, [focus]);
-
- useEffect(() => autoInputWidth(nameRef, name, 60), [nameRef, name]);
-
const [valueResolved] = evalEditorBlock(value, env);
const innerEnv = makeInnerEnv(env, name, valueResolved);
return <>
let
- {}}
+ onCancel={() => {}}
+ onTextChange={name => setState(state => ({...state, name}))}
+ setFocus={focus => setState(state => ({...state, focus}))}
+ extraHandlers={{}}
/>
=
diff --git a/src/Type.css b/src/Type.css
index d97e4b0..c87040c 100644
--- a/src/Type.css
+++ b/src/Type.css
@@ -12,6 +12,8 @@
font-weight: 400;
font-style: normal;
font-variation-settings: "wdth" 100;
+
+ color: darkgrey;
}
.functionType {
diff --git a/src/actions.ts b/src/actions.ts
new file mode 100644
index 0000000..dd77a0f
--- /dev/null
+++ b/src/actions.ts
@@ -0,0 +1,60 @@
+import { initialEditorState } from "./configurations";
+import { removeFocus } from "./eval";
+
+export const actionShortcuts: [string, string[], string][] = [
+ ["call" , ['c'], "expr ⌴" ],
+ ["transform", ['.'], "⌴ expr" ],
+ ["assign" , ['a'], "let (⌴ = expr) in ⌴"],
+ ["declare" , ['d'], "let (⌴ = ⌴) in expr"],
+ ["lambda" , ['l'], "λ⌴. expr" ],
+];
+
+export function getActions(globalContext, setState) {
+ return {
+ c: () => {
+ setState(state => ({
+ kind: "call",
+ fn: removeFocus(state),
+ input: initialEditorState,
+ }));
+ globalContext?.doHighlight.call();
+ },
+ '.': () => {
+ setState(state => ({
+ kind: "call",
+ fn: initialEditorState,
+ input: removeFocus(state),
+ }));
+ globalContext?.doHighlight.transform();
+ },
+ a: () => {
+ setState(state => ({
+ kind: "let",
+ name: "",
+ focus: true,
+ value: removeFocus(state),
+ inner: removeFocus(initialEditorState),
+ }));
+ globalContext?.doHighlight.assign();
+ },
+ d: () => {
+ setState(state => ({
+ kind: "let",
+ name: "",
+ focus: true,
+ value: removeFocus(initialEditorState),
+ inner: removeFocus(state),
+ }));
+ globalContext?.doHighlight.declare();
+ },
+ l: () => {
+ setState(state => ({
+ kind: "lambda",
+ paramName: "",
+ focus: true,
+ expr: removeFocus(state),
+ }));
+ globalContext?.doHighlight.lambda();
+ },
+ };
+}
diff --git a/src/eval.ts b/src/eval.ts
index cceb6a5..34ddb8b 100644
--- a/src/eval.ts
+++ b/src/eval.ts
@@ -4,6 +4,7 @@ import type { ExprBlockState } from "./ExprBlock";
import type { InputValueType } from "./InputBlock";
const IS_DEV = (import.meta.env.MODE === "development");
+const VERBOSE = false;
export interface Environment {
names: any;
@@ -43,12 +44,6 @@ export interface Unknown {
// the value of every block is either known (Dynamic), an error, or unknown
export type ResolvedType = Dynamic | DeepError | Unknown;
-// export const evalEditorBlock = (s: ExprBlockState, env: Environment): [ResolvedType,Environment] => {
-// const [resolved] = proxyEditorBlock(s, env);
-// const [t, newEnv] = recomputeTypeVarsForEnv(resolved.t, env);
-// return [{...resolved, t }, newEnv];
-// };
-
class NotFoundError extends Error {}
export const evalEditorBlock = (s: ExprBlockState, env: Environment): [ResolvedType,Environment] => {
@@ -74,10 +69,10 @@ export function evalInputBlock(text: string, value: InputValueType, env: Environ
const found = trie.get(env.names)(text);
if (found) {
if (found.kind === "unknown") {
- console.log('returning', text, 'as-is');
+ // console.log('returning', text, 'as-is');
return [found, env]; // don't rewrite lambda parameters
}
- console.log('rewriting', text);
+ // console.log('rewriting', text);
return recomputeTypeVarsForEnv(text, found, env);
}
}
@@ -171,7 +166,7 @@ function evalCallBlock3(fnResolved: ResolvedType, inputResolved: ResolvedType, e
const [abstractOutputType, env2] = makeTypeVar(env, "");
const matchFnType = fnType(_ => inputResolved.t)(_ => abstractOutputType);
- if (IS_DEV) {
+ if (VERBOSE) {
console.log('========= evalCallBlock3 =========')
console.log('env :', env);
console.log('fnKind :', fnResolved.kind);
@@ -197,7 +192,7 @@ function evalCallBlock3(fnResolved: ResolvedType, inputResolved: ResolvedType, e
// we don't want to 'bubble up' our outType substitution, because it's just a temporary variable
const unificationWithoutOutType = new Map([...unification].filter(([symbol]) => symbol !== abstractOutputType.symbol));
- if (IS_DEV) {
+ if (VERBOSE) {
console.log('unification :', prettyU(unification));
console.log('unificationInvR :', prettyRU(unificationR));
console.log('unifiedFnType :', prettyT(unifiedFnType));
@@ -212,7 +207,7 @@ function evalCallBlock3(fnResolved: ResolvedType, inputResolved: ResolvedType, e
// const grandUnification = unificationWithoutOutType;
- if (IS_DEV) {
+ if (VERBOSE) {
console.log('grandUnification :', prettyU(grandUnification));
console.log('==================================')
}
@@ -278,8 +273,6 @@ function evalCallBlock3(fnResolved: ResolvedType, inputResolved: ResolvedType, e
}
throw e;
}
- // }
- // throw e;
}
}
@@ -296,7 +289,7 @@ const prettyRU = (rUni: Map) => {
export function evalLambdaBlock(paramName: string, expr: ExprBlockState, env: Environment): [ResolvedType,Environment] {
const [paramType, staticInnerEnv] = makeTypeVar(env, paramName);
- if (IS_DEV) {
+ if (VERBOSE) {
console.log('====== begin evalLambdaBlock ======')
console.log('paramName :', paramName);
console.log('paramType :', prettyT(paramType));
@@ -345,7 +338,7 @@ export function evalLambdaBlock(paramName: string, expr: ExprBlockState, env: En
// const [lambdaResolvedNormalized, resultEnv] = recomputeTypeVarsForEnv(paramName, lambdaResolved, env);
- if (IS_DEV) {
+ if (VERBOSE) {
console.log('======= end evalLambdaBlock =======')
console.log('paramType :', prettyT(paramType));
console.log('exprType :', prettyT(exprResolved.t));
@@ -353,7 +346,6 @@ export function evalLambdaBlock(paramName: string, expr: ExprBlockState, env: En
console.log('exprUnificationR :', prettyRU(reduced));
console.log('lambdaType :', prettyT(lambdaT));
console.log('lambdaTypeSubsituted:', prettyT(lambdaTSubstituted));
- // console.log('normalizedT :', prettyT(lambdaResolvedNormalized.t));
console.log('===================================')
}
return [lambdaResolved, env];
diff --git a/src/util/dom_trickery.ts b/src/util/dom_trickery.ts
deleted file mode 100644
index cf7993c..0000000
--- a/src/util/dom_trickery.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import type { Ref } from "react";
-
-// If there is a caret anywhere in the document (user entering text), returns the position of the caret in the focused element
-export function getCaretPosition(): number | undefined {
- const selection = window.getSelection();
- if (selection) {
- const range = selection.getRangeAt(0);
- const clonedRange = range.cloneRange();
- return clonedRange.startOffset;
- }
-}
-
-// export function setCursorPosition(elem, pos) {
-// const range = document.createRange();
-// range.selectNode(elem);
-// range.setStart(elem, pos);
-// range.setEnd(elem, pos);
-// const selection = window.getSelection();
-// selection?.removeAllRanges();
-// selection?.addRange(range);
-// }
-
-// Move caret all the way to the right in the currently focused element
-export function setRightMostCaretPosition(elem) {
- const range = document.createRange();
- range.selectNode(elem);
- if (elem.lastChild) { // if no text is entered, there is no lastChild
- range.setStart(elem.lastChild, elem.textContent.length);
- range.setEnd(elem.lastChild, elem.textContent.length);
- const selection = window.getSelection();
- selection?.removeAllRanges();
- selection?.addRange(range);
- }
-}
-
-export function focusNextElement() {
- const editable = Array.from(document.querySelectorAll('input'));
- const index = editable.indexOf(document.activeElement);
- editable[index+1]?.focus();
-}
-
-export function focusPrevElement() {
- const editable = Array.from(document.querySelectorAll('input'));
- const index = editable.indexOf(document.activeElement);
- const prevElem = editable[index-1]
- if (prevElem) {
- prevElem.focus();
- setRightMostCaretPosition(prevElem);
- }
-}
-
-export const autoInputWidth = (inputRef: React.RefObject, text, emptyWidth=150) => {
- if (inputRef.current) {
- inputRef.current.style.width = `${text.length === 0 ? emptyWidth : (text.length*8.0)}px`;
- }
-}
diff --git a/src/util/parse.ts b/src/util/parse.ts
deleted file mode 100644
index 8b13789..0000000
--- a/src/util/parse.ts
+++ /dev/null
@@ -1 +0,0 @@
-