Skip to content

Commit a48342a

Browse files
committed
better bidirectional highlighting on AST
1 parent d7b0ecd commit a48342a

File tree

4 files changed

+47
-12
lines changed

4 files changed

+47
-12
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class Workspace {
114114
#current = $state.raw() as File;
115115

116116
#handlers = {
117-
hover: new Set<(pos: number) => void>(),
117+
hover: new Set<(pos: number | null) => void>(),
118118
select: new Set<(from: number, to: number) => void>()
119119
};
120120

@@ -307,7 +307,7 @@ export class Workspace {
307307
this.#files = this.#files.slice(0, to_index).concat(from).concat(this.#files.slice(to_index));
308308
}
309309

310-
onhover(fn: (pos: number) => void) {
310+
onhover(fn: (pos: number | null) => void) {
311311
$effect(() => {
312312
this.#handlers.hover.add(fn);
313313

@@ -535,6 +535,11 @@ export class Workspace {
535535
handler(pos);
536536
}
537537
}
538+
},
539+
mouseleave: (event, view) => {
540+
for (const handler of this.#handlers.hover) {
541+
handler(null);
542+
}
538543
}
539544
})
540545
];

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,14 @@
8282
<details
8383
bind:open
8484
onfocusin={(e) => (e.stopPropagation(), onhover(value))}
85-
onfocusout={(e) => onhover(null)}
86-
onmouseover={() => onhover(value)}
85+
onfocusout={() => onhover(null)}
86+
onmouseover={(e) => (e.stopPropagation(), onhover(value))}
8787
onmouseleave={() => onhover(null)}
88+
ontoggle={(e) => {
89+
if (e.currentTarget.open && value && typeof value.start === 'number') {
90+
workspace.highlight_range(value, true);
91+
}
92+
}}
8893
>
8994
<summary>
9095
{#if key}
@@ -104,7 +109,15 @@
104109
{/if}
105110
</summary>
106111

107-
<ul>
112+
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
113+
<ul
114+
onclick={(e) => {
115+
if (value && typeof value.start === 'number') {
116+
workspace.highlight_range(value, true);
117+
e.stopPropagation();
118+
}
119+
}}
120+
>
108121
{#each Object.entries(value) as [k, v]}
109122
<AstNode
110123
key={is_array ? undefined : k}

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
1616
let { workspace, ast, active = true }: Props = $props();
1717
18-
let cursor = $state(0);
18+
let cursor = $state<number | null>(0);
1919
2020
let path_nodes = $derived(find_deepest_path(cursor, [ast]) || []);
2121
22-
function find_deepest_path(cursor: number, paths: Ast[]): Ast[] | undefined {
22+
function find_deepest_path(cursor: number | null, paths: Ast[]): Ast[] | undefined {
23+
if (cursor === null) return null;
2324
const value = paths[paths.length - 1];
2425
25-
if (!value) return;
26+
if (!value) return null;
2627
2728
for (const v of Object.values(value)) {
2829
if (typeof v === 'object') {
@@ -43,8 +44,23 @@
4344
}
4445
}
4546
46-
workspace.onhover((pos) => {
47-
cursor = pos;
47+
$effect(() => {
48+
if (active) {
49+
workspace.onhover((pos) => {
50+
cursor = pos;
51+
});
52+
}
53+
});
54+
55+
$effect(() => {
56+
if (active) {
57+
const leaf = path_nodes.at(-1) ?? null;
58+
workspace.highlight_range(leaf);
59+
}
60+
61+
return () => {
62+
workspace.highlight_range(null);
63+
};
4864
});
4965
</script>
5066

@@ -62,6 +78,7 @@
6278
node === null ||
6379
(node.type !== undefined && node.start !== undefined && node.end !== undefined)
6480
) {
81+
cursor = node && node.start + 1;
6582
workspace.highlight_range(node);
6683
}
6784
}}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@
173173
}
174174
};
175175
176-
workspace.onhover((pos) => from_input(pos, false));
176+
workspace.onhover((pos) => (pos === null ? clear() : from_input(pos, false)));
177177
workspace.onselect((from, to) => from === to && from_input(from, true));
178178
179-
output.onhover((pos) => from_output(pos, false));
179+
output.onhover((pos) => (pos === null ? clear() : from_output(pos, false)));
180180
output.onselect((from, to) => from === to && from_output(from, true));
181181
}
182182
});

0 commit comments

Comments
 (0)