Skip to content

Commit 0c6b619

Browse files
authored
add modern AST toggle (#692)
* add AST toggle UI * wire it up * migrate AstNode.svelte * lint
1 parent 47bd88b commit 0c6b619

File tree

5 files changed

+93
-48
lines changed

5 files changed

+93
-48
lines changed

packages/editor/src/lib/Workspace.svelte.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const default_extensions = [
7878
interface ExposedCompilerOptions {
7979
generate: 'client' | 'server';
8080
dev: boolean;
81+
modernAst: boolean;
8182
}
8283

8384
export class Workspace {
@@ -87,7 +88,8 @@ export class Workspace {
8788

8889
#compiler_options = $state.raw<ExposedCompilerOptions>({
8990
generate: 'client',
90-
dev: false
91+
dev: false,
92+
modernAst: true
9193
});
9294
compiled = $state<Record<string, Compiled>>({});
9395

packages/repl/src/lib/Output/AstNode.svelte

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,72 @@
11
<script lang="ts">
2+
import AstNode from './AstNode.svelte';
23
import { get_repl_context } from '../context';
34
import { tick } from 'svelte';
45
import type { CompileResult } from 'svelte/compiler';
56
67
type Ast = CompileResult['ast'];
78
8-
export let key = '';
9-
export let value: Ast;
10-
export let collapsed = true;
11-
export let path_nodes: Ast[] = [];
12-
export let autoscroll = true;
9+
interface Props {
10+
key?: string;
11+
value: Ast;
12+
collapsed?: boolean;
13+
path_nodes?: Ast[];
14+
autoscroll?: boolean;
15+
}
16+
17+
let {
18+
key = '',
19+
value,
20+
collapsed = $bindable(true),
21+
path_nodes = [],
22+
autoscroll = true
23+
}: Props = $props();
1324
1425
const { toggleable } = get_repl_context();
1526
16-
let list_item_el: HTMLLIElement;
27+
let list_item_el = $state() as HTMLLIElement;
1728
18-
$: is_root = path_nodes[0] === value;
19-
$: is_leaf = path_nodes[path_nodes.length - 1] === value;
20-
$: is_ast_array = Array.isArray(value);
21-
$: is_collapsable = value && typeof value === 'object';
22-
$: is_markable =
29+
let is_root = $derived(path_nodes[0] === value);
30+
let is_leaf = $derived(path_nodes[path_nodes.length - 1] === value);
31+
let is_ast_array = $derived(Array.isArray(value));
32+
let is_collapsable = $derived(value && typeof value === 'object');
33+
let is_markable = $derived(
2334
is_collapsable &&
24-
'start' in value &&
25-
'end' in value &&
26-
typeof value.start === 'number' &&
27-
typeof value.end === 'number';
28-
$: key_text = key ? `${key}:` : '';
35+
'start' in value &&
36+
'end' in value &&
37+
typeof value.start === 'number' &&
38+
typeof value.end === 'number'
39+
);
40+
let key_text = $derived(key ? `${key}:` : '');
41+
42+
let preview_text = $state('');
2943
30-
let preview_text = '';
31-
$: {
32-
if (!is_collapsable || !collapsed) break $;
44+
$effect(() => {
45+
if (!is_collapsable || !collapsed) return;
3346
3447
if (is_ast_array) {
35-
if (!('length' in value)) break $;
48+
if (!('length' in value)) return;
3649
3750
preview_text = `[ ${value.length} element${value.length === 1 ? '' : 's'} ]`;
3851
} else {
3952
preview_text = `{ ${Object.keys(value).join(', ')} }`;
4053
}
41-
}
42-
43-
$: collapsed = !path_nodes.includes(value);
44-
45-
$: if (autoscroll && is_leaf && !$toggleable) {
46-
// wait for all nodes to render before scroll
47-
tick().then(() => {
48-
if (list_item_el) {
49-
list_item_el.scrollIntoView();
50-
}
51-
});
52-
}
54+
});
55+
56+
$effect(() => {
57+
collapsed = !path_nodes.includes(value);
58+
});
59+
60+
$effect(() => {
61+
if (autoscroll && is_leaf && !$toggleable) {
62+
// wait for all nodes to render before scroll
63+
tick().then(() => {
64+
if (list_item_el) {
65+
list_item_el.scrollIntoView();
66+
}
67+
});
68+
}
69+
});
5370
5471
function handle_mark_text(e: MouseEvent | FocusEvent) {
5572
if (is_markable) {
@@ -79,27 +96,27 @@
7996
<li
8097
bind:this={list_item_el}
8198
class:marked={!is_root && is_leaf}
82-
on:mouseover={handle_mark_text}
83-
on:focus={handle_mark_text}
84-
on:mouseleave={handle_unmark_text}
99+
onmouseover={handle_mark_text}
100+
onfocus={handle_mark_text}
101+
onmouseleave={handle_unmark_text}
85102
>
86103
{#if !is_root && is_collapsable}
87-
<button class="ast-toggle" class:open={!collapsed} on:click={() => (collapsed = !collapsed)}>
104+
<button class="ast-toggle" class:open={!collapsed} onclick={() => (collapsed = !collapsed)}>
88105
{key_text}
89106
</button>
90107
{:else if key_text}
91108
<span>{key_text}</span>
92109
{/if}
93110
{#if is_collapsable}
94111
{#if collapsed && !is_root}
95-
<button class="preview" on:click={() => (collapsed = !collapsed)}>
112+
<button class="preview" onclick={() => (collapsed = !collapsed)}>
96113
{preview_text}
97114
</button>
98115
{:else}
99116
<span>{is_ast_array ? '[' : '{'}</span>
100117
<ul>
101118
{#each Object.entries(value) as [k, v]}
102-
<svelte:self key={is_ast_array ? '' : k} value={v} {path_nodes} {autoscroll} />
119+
<AstNode key={is_ast_array ? '' : k} value={v} {path_nodes} {autoscroll} />
103120
{/each}
104121
</ul>
105122
<span>{is_ast_array ? ']' : '}'}</span>

packages/repl/src/lib/Output/AstView.svelte

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
<script lang="ts">
2+
import { Checkbox } from '@sveltejs/site-kit/components';
23
import Message from '../Message.svelte';
34
import AstNode from './AstNode.svelte';
45
import type { CompileResult } from 'svelte/compiler';
6+
import type { Workspace } from 'editor';
57
68
type Ast = CompileResult['ast'];
79
8-
export let ast: Ast;
9-
export let autoscroll = true;
10+
interface Props {
11+
workspace: Workspace;
12+
ast: Ast;
13+
autoscroll?: boolean;
14+
}
15+
16+
let { workspace, ast, autoscroll = true }: Props = $props();
1017
1118
// $cursor_index may go over the max since ast computation is usually slower.
1219
// clamping this helps prevent the collapse view flashing
1320
// TODO reimplement
1421
let max_cursor_index = 0;
1522
// $: max_cursor_index = !ast ? $cursorIndex : Math.min($cursorIndex, get_ast_max_end(ast));
1623
17-
$: path_nodes = find_deepest_path(max_cursor_index, [ast]) || [];
24+
let path_nodes = $derived(find_deepest_path(max_cursor_index, [ast]) || []);
1825
1926
function find_deepest_path(cursor: number, paths: Ast[]): Ast[] | undefined {
2027
const value = paths[paths.length - 1];
@@ -65,7 +72,19 @@
6572
{/if}
6673
</code>
6774
</pre>
75+
6876
<Message kind="info">The AST is not public API and may change at any point in time</Message>
77+
78+
<label>
79+
modern
80+
81+
<Checkbox
82+
checked={workspace.compiler_options.modernAst}
83+
onchange={(modernAst) => {
84+
workspace.update_compiler_options({ modernAst });
85+
}}
86+
/>
87+
</label>
6988
</div>
7089

7190
<style>
@@ -99,4 +118,14 @@
99118
margin: 0;
100119
list-style-type: none;
101120
}
121+
122+
label {
123+
position: absolute;
124+
top: 1rem;
125+
right: 1rem;
126+
display: inline-flex;
127+
gap: 1rem;
128+
align-items: center;
129+
font: var(--sk-font-ui-small);
130+
}
102131
</style>

packages/repl/src/lib/Output/Output.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@
8181
css_workspace.update_file(css);
8282
});
8383
});
84-
85-
let ast = $derived(current?.result?.ast);
8684
</script>
8785

8886
<div class="view-toggle">
@@ -132,9 +130,9 @@
132130
</div>
133131

134132
<!-- ast output -->
135-
{#if ast}
133+
{#if current?.result}
136134
<div class="tab-content" class:visible={!is_markdown && view === 'ast'}>
137-
<AstView {ast} autoscroll={!is_markdown && view === 'ast'} />
135+
<AstView {workspace} ast={current.result.ast} autoscroll={!is_markdown && view === 'ast'} />
138136
</div>
139137
{/if}
140138

packages/site-kit/src/lib/components/Checkbox.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
/* display: block; */
1313
position: relative;
1414
height: 1em;
15-
width: calc(100% - 0.6em);
16-
max-width: 2em;
15+
width: 2em;
1716
top: -2px;
1817
border-radius: 0.5em;
1918
-webkit-appearance: none;

0 commit comments

Comments
 (0)