Skip to content

add modern AST toggle #692

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/editor/src/lib/Workspace.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const default_extensions = [
interface ExposedCompilerOptions {
generate: 'client' | 'server';
dev: boolean;
modernAst: boolean;
}

export class Workspace {
Expand All @@ -87,7 +88,8 @@ export class Workspace {

#compiler_options = $state.raw<ExposedCompilerOptions>({
generate: 'client',
dev: false
dev: false,
modernAst: true
});
compiled = $state<Record<string, Compiled>>({});

Expand Down
93 changes: 55 additions & 38 deletions packages/repl/src/lib/Output/AstNode.svelte
Original file line number Diff line number Diff line change
@@ -1,55 +1,72 @@
<script lang="ts">
import AstNode from './AstNode.svelte';
import { get_repl_context } from '../context';
import { tick } from 'svelte';
import type { CompileResult } from 'svelte/compiler';

type Ast = CompileResult['ast'];

export let key = '';
export let value: Ast;
export let collapsed = true;
export let path_nodes: Ast[] = [];
export let autoscroll = true;
interface Props {
key?: string;
value: Ast;
collapsed?: boolean;
path_nodes?: Ast[];
autoscroll?: boolean;
}

let {
key = '',
value,
collapsed = $bindable(true),
path_nodes = [],
autoscroll = true
}: Props = $props();

const { toggleable } = get_repl_context();

let list_item_el: HTMLLIElement;
let list_item_el = $state() as HTMLLIElement;

$: is_root = path_nodes[0] === value;
$: is_leaf = path_nodes[path_nodes.length - 1] === value;
$: is_ast_array = Array.isArray(value);
$: is_collapsable = value && typeof value === 'object';
$: is_markable =
let is_root = $derived(path_nodes[0] === value);
let is_leaf = $derived(path_nodes[path_nodes.length - 1] === value);
let is_ast_array = $derived(Array.isArray(value));
let is_collapsable = $derived(value && typeof value === 'object');
let is_markable = $derived(
is_collapsable &&
'start' in value &&
'end' in value &&
typeof value.start === 'number' &&
typeof value.end === 'number';
$: key_text = key ? `${key}:` : '';
'start' in value &&
'end' in value &&
typeof value.start === 'number' &&
typeof value.end === 'number'
);
let key_text = $derived(key ? `${key}:` : '');

let preview_text = $state('');

let preview_text = '';
$: {
if (!is_collapsable || !collapsed) break $;
$effect(() => {
if (!is_collapsable || !collapsed) return;

if (is_ast_array) {
if (!('length' in value)) break $;
if (!('length' in value)) return;

preview_text = `[ ${value.length} element${value.length === 1 ? '' : 's'} ]`;
} else {
preview_text = `{ ${Object.keys(value).join(', ')} }`;
}
}

$: collapsed = !path_nodes.includes(value);

$: if (autoscroll && is_leaf && !$toggleable) {
// wait for all nodes to render before scroll
tick().then(() => {
if (list_item_el) {
list_item_el.scrollIntoView();
}
});
}
});

$effect(() => {
collapsed = !path_nodes.includes(value);
});

$effect(() => {
if (autoscroll && is_leaf && !$toggleable) {
// wait for all nodes to render before scroll
tick().then(() => {
if (list_item_el) {
list_item_el.scrollIntoView();
}
});
}
});

function handle_mark_text(e: MouseEvent | FocusEvent) {
if (is_markable) {
Expand Down Expand Up @@ -79,27 +96,27 @@
<li
bind:this={list_item_el}
class:marked={!is_root && is_leaf}
on:mouseover={handle_mark_text}
on:focus={handle_mark_text}
on:mouseleave={handle_unmark_text}
onmouseover={handle_mark_text}
onfocus={handle_mark_text}
onmouseleave={handle_unmark_text}
>
{#if !is_root && is_collapsable}
<button class="ast-toggle" class:open={!collapsed} on:click={() => (collapsed = !collapsed)}>
<button class="ast-toggle" class:open={!collapsed} onclick={() => (collapsed = !collapsed)}>
{key_text}
</button>
{:else if key_text}
<span>{key_text}</span>
{/if}
{#if is_collapsable}
{#if collapsed && !is_root}
<button class="preview" on:click={() => (collapsed = !collapsed)}>
<button class="preview" onclick={() => (collapsed = !collapsed)}>
{preview_text}
</button>
{:else}
<span>{is_ast_array ? '[' : '{'}</span>
<ul>
{#each Object.entries(value) as [k, v]}
<svelte:self key={is_ast_array ? '' : k} value={v} {path_nodes} {autoscroll} />
<AstNode key={is_ast_array ? '' : k} value={v} {path_nodes} {autoscroll} />
{/each}
</ul>
<span>{is_ast_array ? ']' : '}'}</span>
Expand Down
35 changes: 32 additions & 3 deletions packages/repl/src/lib/Output/AstView.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
<script lang="ts">
import { Checkbox } from '@sveltejs/site-kit/components';
import Message from '../Message.svelte';
import AstNode from './AstNode.svelte';
import type { CompileResult } from 'svelte/compiler';
import type { Workspace } from 'editor';

type Ast = CompileResult['ast'];

export let ast: Ast;
export let autoscroll = true;
interface Props {
workspace: Workspace;
ast: Ast;
autoscroll?: boolean;
}

let { workspace, ast, autoscroll = true }: Props = $props();

// $cursor_index may go over the max since ast computation is usually slower.
// clamping this helps prevent the collapse view flashing
// TODO reimplement
let max_cursor_index = 0;
// $: max_cursor_index = !ast ? $cursorIndex : Math.min($cursorIndex, get_ast_max_end(ast));

$: path_nodes = find_deepest_path(max_cursor_index, [ast]) || [];
let path_nodes = $derived(find_deepest_path(max_cursor_index, [ast]) || []);

function find_deepest_path(cursor: number, paths: Ast[]): Ast[] | undefined {
const value = paths[paths.length - 1];
Expand Down Expand Up @@ -65,7 +72,19 @@
{/if}
</code>
</pre>

<Message kind="info">The AST is not public API and may change at any point in time</Message>

<label>
modern

<Checkbox
checked={workspace.compiler_options.modernAst}
onchange={(modernAst) => {
workspace.update_compiler_options({ modernAst });
}}
/>
</label>
</div>

<style>
Expand Down Expand Up @@ -99,4 +118,14 @@
margin: 0;
list-style-type: none;
}

label {
position: absolute;
top: 1rem;
right: 1rem;
display: inline-flex;
gap: 1rem;
align-items: center;
font: var(--sk-font-ui-small);
}
</style>
6 changes: 2 additions & 4 deletions packages/repl/src/lib/Output/Output.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@
css_workspace.update_file(css);
});
});

let ast = $derived(current?.result?.ast);
</script>

<div class="view-toggle">
Expand Down Expand Up @@ -132,9 +130,9 @@
</div>

<!-- ast output -->
{#if ast}
{#if current?.result}
<div class="tab-content" class:visible={!is_markdown && view === 'ast'}>
<AstView {ast} autoscroll={!is_markdown && view === 'ast'} />
<AstView {workspace} ast={current.result.ast} autoscroll={!is_markdown && view === 'ast'} />
</div>
{/if}

Expand Down
3 changes: 1 addition & 2 deletions packages/site-kit/src/lib/components/Checkbox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
/* display: block; */
position: relative;
height: 1em;
width: calc(100% - 0.6em);
max-width: 2em;
width: 2em;
top: -2px;
border-radius: 0.5em;
-webkit-appearance: none;
Expand Down
Loading