UI and a Lisp experiment

This commit is contained in:
Yura Dupyn 2026-04-25 01:10:49 +02:00
parent 38ff06ea45
commit f55b437037
24 changed files with 2746 additions and 89 deletions

126
src/ui/SyntaxPane.tsx Normal file
View file

@ -0,0 +1,126 @@
import { For, Show } from 'solid-js';
import type { JSX } from 'solid-js';
import type { CodePointSpan } from 'source-region';
import type { ParseError } from '../parser';
import type { ConcreteSyntax } from '../syntax';
import { Expr } from '../syntax';
import { errorDetail, errorLabel, errorTitle } from './format';
import type { HoverTarget } from './types';
export function ErrorList(props: {
errors: ParseError[];
values: ConcreteSyntax[];
onHover: (target: HoverTarget | undefined) => void;
}) {
return (
<div class="scroll-stack">
<div class="section-label">Errors</div>
<div class="error-list">
<For each={props.errors}>
{(error) => (
<HoverBlock
class="error-card"
label={errorLabel(error)}
span={error.span}
onHover={props.onHover}
>
<div class="item-title">{errorTitle(error)}</div>
<div class="item-meta">{errorDetail(error)}</div>
</HoverBlock>
)}
</For>
</div>
<Show when={props.values.length > 0}>
<div class="section-label">Recovered Expressions</div>
<ExpressionList values={props.values} onHover={props.onHover} />
</Show>
</div>
);
}
export function ExpressionList(props: {
values: ConcreteSyntax[];
onHover: (target: HoverTarget | undefined) => void;
}) {
return (
<div class="expr-list">
<For each={props.values}>
{(value, index) => (
<ExprView
expr={value}
label={`expression ${index() + 1}`}
onHover={props.onHover}
/>
)}
</For>
</div>
);
}
function ExprView(props: {
expr: ConcreteSyntax;
label: string;
onHover: (target: HoverTarget | undefined) => void;
}) {
if (props.expr.tag === "literal") {
return (
<HoverBlock
class="expr-node literal-node"
label={`${props.label}: ${props.expr.value.tag}`}
span={props.expr.span}
onHover={props.onHover}
>
<span class="node-kind">{props.expr.value.tag}</span>
<span class="node-value">{literalValue(props.expr)}</span>
</HoverBlock>
);
}
return (
<HoverBlock
class="expr-node list-node"
label={`${props.label}: list`}
span={props.expr.span}
onHover={props.onHover}
>
<div class="list-node-header">
<span class="node-kind">list</span>
<span class="item-meta">{props.expr.values.length} children</span>
</div>
<div class="list-children">
<For each={props.expr.values}>
{(child, index) => (
<ExprView
expr={child}
label={`${props.label}.${index() + 1}`}
onHover={props.onHover}
/>
)}
</For>
</div>
</HoverBlock>
);
}
function HoverBlock(props: {
class: string;
label: string;
span: CodePointSpan;
onHover: (target: HoverTarget | undefined) => void;
children: JSX.Element;
}) {
return (
<div
class={props.class}
onMouseEnter={() => props.onHover({ label: props.label, span: props.span })}
onMouseLeave={() => props.onHover(undefined)}
>
{props.children}
</div>
);
}
function literalValue(expr: ConcreteSyntax): string {
return expr.tag === "literal" ? Expr.show(expr) : "";
}