|
1 | 1 | import { EditorView, basicSetup } from "codemirror";
|
2 |
| -import { onCleanup } from "solid-js"; |
| 2 | +import { createEffect, onCleanup } from "solid-js"; |
3 | 3 | import { yaml } from "@codemirror/lang-yaml";
|
4 | 4 | import { indentWithTab } from "@codemirror/commands";
|
5 | 5 | import { oneDark } from "@codemirror/theme-one-dark";
|
6 |
| -import { keymap } from "@codemirror/view"; |
| 6 | +import { keymap, ViewPlugin } from "@codemirror/view"; |
7 | 7 | import { customLinter } from "./linter";
|
8 | 8 | import { lintGutter } from "@codemirror/lint";
|
9 | 9 |
|
10 | 10 | export interface CodeMirrorProps {
|
11 |
| - content?: string; |
| 11 | + initialContent?: string; |
| 12 | + showStep?: string; |
| 13 | + onChange?: (content: string) => void; |
12 | 14 | }
|
13 | 15 |
|
14 | 16 | export function CodeMirror(props: CodeMirrorProps) {
|
15 | 17 | let view = new EditorView({
|
16 |
| - doc: props.content, |
| 18 | + doc: props.initialContent, |
17 | 19 | extensions: [
|
18 | 20 | basicSetup,
|
19 | 21 | oneDark,
|
20 | 22 | yaml(),
|
21 | 23 | customLinter,
|
22 | 24 | lintGutter(),
|
23 | 25 | keymap.of([indentWithTab]),
|
| 26 | + ViewPlugin.define((view) => ({ |
| 27 | + update(update) { |
| 28 | + if (update.docChanged) { |
| 29 | + props.onChange?.(view.state.doc.toString()); |
| 30 | + } |
| 31 | + }, |
| 32 | + })), |
24 | 33 | ],
|
25 | 34 | });
|
26 | 35 |
|
| 36 | + function findStep(step: string) { |
| 37 | + let i = 1; |
| 38 | + for (const line of view.state.doc.iterLines()) { |
| 39 | + if (line.startsWith(step)) return i; |
| 40 | + i++; |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + async function scrollToStep(step: string) { |
| 45 | + if (!step) return; |
| 46 | + const lineNumber = await findStep(step); |
| 47 | + if (lineNumber == undefined) return; |
| 48 | + const line = view.state.doc.line(lineNumber); |
| 49 | + if (!line) return; |
| 50 | + const block = view.lineBlockAt(line.from); |
| 51 | + view.scrollDOM.scrollTo({ ...block, behavior: "smooth" }); |
| 52 | + } |
| 53 | + |
| 54 | + createEffect(async () => { |
| 55 | + if (props.showStep) await scrollToStep(props.showStep); |
| 56 | + }); |
| 57 | + |
27 | 58 | onCleanup(() => view.destroy());
|
28 | 59 |
|
29 | 60 | return <>{view.dom}</>;
|
|
0 commit comments