Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
41 changes: 38 additions & 3 deletions packages/editor/src/lib/Workspace.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { CompileError, CompileResult } from 'svelte/compiler';
import { Compartment, EditorState } from '@codemirror/state';
import { Compartment, EditorState, StateEffect, StateField } from '@codemirror/state';
import { compile_file } from './compile-worker';
import { BROWSER } from 'esm-env';
import { basicSetup, EditorView } from 'codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { html } from '@codemirror/lang-html';
import { svelte } from '@replit/codemirror-lang-svelte';
import { autocomplete_for_svelte } from '@sveltejs/site-kit/codemirror';
import { keymap } from '@codemirror/view';
import { Decoration, keymap, type DecorationSet } from '@codemirror/view';
import { acceptCompletion } from '@codemirror/autocomplete';
import { indentWithTab } from '@codemirror/commands';
import { indentUnit } from '@codemirror/language';
Expand Down Expand Up @@ -51,6 +51,32 @@ function file_type(file: Item) {
return file.name.split('.').pop();
}

const set_highlight = StateEffect.define<{ start: number; end: number } | null>();

const highlight_field = StateField.define<DecorationSet>({
create() {
return Decoration.none;
},
update(highlights, tr) {
// Apply the effect
for (let effect of tr.effects) {
if (effect.is(set_highlight)) {
if (effect.value) {
const { start, end } = effect.value;
const deco = Decoration.mark({ class: 'highlight' }).range(start, end);
return Decoration.set([deco]);
} else {
// Clear highlight
return Decoration.none;
}
}
}
// Map decorations for document changes
return highlights.map(tr.changes);
},
provide: (field) => EditorView.decorations.from(field)
});

const tab_behaviour = new Compartment();
const vim_mode = new Compartment();

Expand All @@ -60,7 +86,8 @@ const default_extensions = [
tab_behaviour.of(keymap.of([{ key: 'Tab', run: acceptCompletion }])),
indentUnit.of('\t'),
theme,
vim_mode.of([])
vim_mode.of([]),
highlight_field
];

export interface ExposedCompilerOptions {
Expand Down Expand Up @@ -225,6 +252,14 @@ export class Workspace {
});
}

highlight_range(node: { start: number; end: number } | null) {
if (!this.#view) return;

this.#view.dispatch({
effects: set_highlight.of(node)
});
}

mark_saved() {
this.modified = {};
}
Expand Down
5 changes: 5 additions & 0 deletions packages/editor/src/lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,9 @@
}
}
}

.highlight {
background: hsl(60, 100%, 80%);
padding: 4px 0;
}
}
13 changes: 11 additions & 2 deletions packages/repl/src/lib/Output/AstNode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
path_nodes?: Ast[];
autoscroll?: boolean;
depth?: number;
onhover: (node: { type: string; start: number; end: number } | null) => void;
}

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

const { toggleable } = get_repl_context();

Expand Down Expand Up @@ -97,7 +98,14 @@
{/if}
</span>
{:else}
<details bind:open>
<!-- svelte-ignore a11y_mouse_events_have_key_events (seems like a false positive) -->
<details
bind:open
onfocusin={() => onhover(value)}
onfocusout={(e) => onhover(null)}
onmouseover={() => onhover(value)}
onmouseleave={() => onhover(null)}
>
<summary>
{#if key}
<span class="key">{key}</span>:
Expand All @@ -123,6 +131,7 @@
value={v}
{path_nodes}
{autoscroll}
{onhover}
depth={depth + 1}
/>
{/each}
Expand Down
14 changes: 13 additions & 1 deletion packages/repl/src/lib/Output/AstView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,19 @@
<code>
{#if typeof ast === 'object'}
<ul>
<AstNode value={ast} {path_nodes} {autoscroll} />
<AstNode
value={ast}
{path_nodes}
{autoscroll}
onhover={(node) => {
if (
node === null ||
(node.type !== undefined && node.start !== undefined && node.end !== undefined)
) {
workspace.highlight_range(node);
}
}}
/>
</ul>
{:else}
<p>No AST available</p>
Expand Down