Merge pull request #186 from tidalcycles/fix-codemirror-bug

Fix codemirror bug
This commit is contained in:
Felix Roos 2022-08-14 11:30:59 +02:00 committed by GitHub
commit b5e2bdf0f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 43 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback, useMemo, useRef, useLayoutEffect } from 'react'; import React, { useCallback, useState, useEffect, useMemo, useRef, useLayoutEffect } from 'react';
import _CodeMirror from '@uiw/react-codemirror'; import _CodeMirror from '@uiw/react-codemirror';
import { Decoration, EditorView } from '@codemirror/view'; import { Decoration, EditorView } from '@codemirror/view';
import { StateEffect, StateField } from '@codemirror/state'; import { StateEffect, StateField } from '@codemirror/state';
@ -89,41 +89,57 @@ const highlightField = StateField.define({
try { try {
for (let e of tr.effects) { for (let e of tr.effects) {
if (e.is(setHighlights)) { if (e.is(setHighlights)) {
highlights = Decoration.set(e.value.flatMap((hap) => (hap.context.locations || []).map(({ start, end }) => { const marks = e.value.map(
const color = hap.context.color || "#FFCA28"; (hap) => (hap.context.locations || []).map(({ start, end }) => {
let from = tr.newDoc.line(start.line).from + start.column; const color = hap.context.color || "#FFCA28";
let to = tr.newDoc.line(end.line).from + end.column; let from = tr.newDoc.line(start.line).from + start.column;
const l = tr.newDoc.length; let to = tr.newDoc.line(end.line).from + end.column;
if (from > l || to > l) { const l = tr.newDoc.length;
return; if (from > l || to > l) {
} return;
const mark = Decoration.mark({ attributes: { style: `outline: 1.5px solid ${color};` } }); }
return mark.range(from, to); const mark = Decoration.mark({ attributes: { style: `outline: 1.5px solid ${color};` } });
})).filter(Boolean), true); return mark.range(from, to);
})
).flat().filter(Boolean) || [];
highlights = Decoration.set(marks, true);
} }
} }
return highlights; return highlights;
} catch (err) { } catch (err) {
return highlights; return Decoration.set([]);
} }
}, },
provide: (f) => EditorView.decorations.from(f) provide: (f) => EditorView.decorations.from(f)
}); });
const extensions = [javascript(), strudelTheme, highlightField, flashField];
function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) { function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) {
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(_CodeMirror, { const handleOnChange = useCallback(
value, (value2) => {
onChange: (value2) => { onChange?.(value2);
onChange(value2);
}, },
onCreateEditor: (view) => { [onChange]
onViewChanged(view); );
const handleOnCreateEditor = useCallback(
(view) => {
onViewChanged?.(view);
}, },
onUpdate: (viewUpdate) => { [onViewChanged]
);
const handleOnUpdate = useCallback(
(viewUpdate) => {
if (viewUpdate.selectionSet && onSelectionChange) { if (viewUpdate.selectionSet && onSelectionChange) {
onSelectionChange(viewUpdate.state.selection); onSelectionChange?.(viewUpdate.state.selection);
} }
}, },
extensions: [javascript(), strudelTheme, highlightField, flashField] [onSelectionChange]
);
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(_CodeMirror, {
value,
onChange: handleOnChange,
onCreateEditor: handleOnCreateEditor,
onUpdate: handleOnUpdate,
extensions
})); }));
} }
@ -300,7 +316,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
}, },
[pattern], [pattern],
), ),
onSchedule: useCallback((_events, cycle) => logCycle(_events, cycle), []), onSchedule: useCallback((_events, cycle) => logCycle(_events), []),
ready: !!pattern && !!activeCode, ready: !!pattern && !!activeCode,
}); });

View File

@ -5,6 +5,7 @@ import { StateField, StateEffect } from '@codemirror/state';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import strudelTheme from '../themes/strudel-theme'; import strudelTheme from '../themes/strudel-theme';
import './style.css'; import './style.css';
import { useCallback } from 'react';
export const setFlash = StateEffect.define(); export const setFlash = StateEffect.define();
const flashField = StateField.define({ const flashField = StateField.define({
@ -48,9 +49,9 @@ const highlightField = StateField.define({
try { try {
for (let e of tr.effects) { for (let e of tr.effects) {
if (e.is(setHighlights)) { if (e.is(setHighlights)) {
highlights = Decoration.set( const marks =
e.value e.value
.flatMap((hap) => .map((hap) =>
(hap.context.locations || []).map(({ start, end }) => { (hap.context.locations || []).map(({ start, end }) => {
const color = hap.context.color || '#FFCA28'; const color = hap.context.color || '#FFCA28';
let from = tr.newDoc.line(start.line).from + start.column; let from = tr.newDoc.line(start.line).from + start.column;
@ -64,37 +65,51 @@ const highlightField = StateField.define({
return mark.range(from, to); return mark.range(from, to);
}), }),
) )
.filter(Boolean), .flat()
true, .filter(Boolean) || [];
); highlights = Decoration.set(marks, true);
} }
} }
return highlights; return highlights;
} catch (err) { } catch (err) {
// console.warn('highlighting error', err); // console.warn('highlighting error', err);
return highlights; return Decoration.set([]);
} }
}, },
provide: (f) => EditorView.decorations.from(f), provide: (f) => EditorView.decorations.from(f),
}); });
const extensions = [javascript(), strudelTheme, highlightField, flashField];
export default function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) { export default function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) {
const handleOnChange = useCallback(
(value) => {
onChange?.(value);
},
[onChange],
);
const handleOnCreateEditor = useCallback(
(view) => {
onViewChanged?.(view);
},
[onViewChanged],
);
const handleOnUpdate = useCallback(
(viewUpdate) => {
if (viewUpdate.selectionSet && onSelectionChange) {
onSelectionChange?.(viewUpdate.state.selection);
}
},
[onSelectionChange],
);
return ( return (
<> <>
<_CodeMirror <_CodeMirror
value={value} value={value}
onChange={(value) => { onChange={handleOnChange}
onChange(value); onCreateEditor={handleOnCreateEditor}
}} onUpdate={handleOnUpdate}
onCreateEditor={(view) => { extensions={extensions}
onViewChanged(view);
}}
onUpdate={(viewUpdate) => {
if (viewUpdate.selectionSet && onSelectionChange) {
onSelectionChange(viewUpdate.state.selection);
}
}}
extensions={[javascript(), strudelTheme, highlightField, flashField]}
/> />
</> </>
); );