Merge pull request #102 from mindofmatthew/codemirror-6-highlights

Work on Codemirror 6 highlighting
This commit is contained in:
Felix Roos 2022-04-28 23:45:53 +02:00 committed by GitHub
commit f00e28ced3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 47 deletions

View File

@ -1,5 +1,5 @@
import React, { useCallback, useLayoutEffect, useRef, useState, useEffect } from 'react'; import React, { useCallback, useLayoutEffect, useRef, useState, useEffect } from 'react';
import CodeMirror6, { highlightEvent } from './CodeMirror6'; import CodeMirror6, { setHighlights } from './CodeMirror6';
import cx from './cx'; import cx from './cx';
import logo from './logo.svg'; import logo from './logo.svg';
import playStatic from './static.mjs'; import playStatic from './static.mjs';
@ -91,7 +91,8 @@ function App() {
tune: decoded || randomTune, tune: decoded || randomTune,
defaultSynth, defaultSynth,
// onDraw: useCallback((time, event) => markEvent(editor)(time, event), [editor]), // onDraw: useCallback((time, event) => markEvent(editor)(time, event), [editor]),
onDraw: useCallback((_, e, code) => code && highlightEvent(e, view, code), [view]), // onDraw: useCallback((_, e, code) => code && highlightEvent(e, view, code), [view]),
onDraw: () => {},
}); });
const [uiHidden, setUiHidden] = useState(false); const [uiHidden, setUiHidden] = useState(false);
const logBox = useRef(); const logBox = useRef();
@ -118,6 +119,29 @@ function App() {
return () => window.removeEventListener('keydown', handleKeyPress); return () => window.removeEventListener('keydown', handleKeyPress);
}, [pattern, code, activateCode, cycle]); }, [pattern, code, activateCode, cycle]);
useEffect(() => {
if (view) {
if (pattern && cycle.started) {
let frame = requestAnimationFrame(updateHighlights);
function updateHighlights() {
let audioTime = Tone.Transport.seconds;
let timespan = new strudel.TimeSpan(audioTime, audioTime + 1 / 60);
let events = pattern.query(new strudel.State(timespan));
view.dispatch({ effects: setHighlights.of(events) });
frame = requestAnimationFrame(updateHighlights);
}
return () => {
cancelAnimationFrame(frame);
};
} else {
view.dispatch({ effects: setHighlights.of([]) });
}
}
}, [pattern, cycle.started]);
useWebMidi({ useWebMidi({
ready: useCallback( ready: useCallback(
({ outputs }) => { ({ outputs }) => {

View File

@ -7,8 +7,7 @@ import { javascript } from '@codemirror/lang-javascript';
import { materialPalenight } from './themes/material-palenight'; import { materialPalenight } from './themes/material-palenight';
const highlightMark = Decoration.mark({ class: 'cm-highlight' }); const highlightMark = Decoration.mark({ class: 'cm-highlight' });
const addHighlight = StateEffect.define(); export const setHighlights = StateEffect.define();
const removeHighlight = StateEffect.define();
const highlightTheme = EditorView.baseTheme({ const highlightTheme = EditorView.baseTheme({
'.cm-highlight': { outline: '1px solid #FFCA28' }, '.cm-highlight': { outline: '1px solid #FFCA28' },
// '.cm-highlight': { background: '#FFCA28' }, // '.cm-highlight': { background: '#FFCA28' },
@ -19,60 +18,29 @@ const highlightField = StateField.define({
}, },
update(highlights, tr) { update(highlights, tr) {
try { try {
highlights = highlights.map(tr.changes);
for (let e of tr.effects) { for (let e of tr.effects) {
if (e.is(addHighlight)) { if (e.is(setHighlights)) {
highlights = highlights.update({ highlights = Decoration.set(
add: [highlightMark.range(e.value.from, e.value.to)], e.value
}); .flatMap((event) => event.context.locations || [])
} .map(({ start, end }) => {
if (e.is(removeHighlight)) { let from = tr.newDoc.line(start.line).from + start.column;
highlights = highlights.update({ let to = tr.newDoc.line(end.line).from + end.column;
filter: (f, t, value) => { return highlightMark.range(from, to);
if (f === e.value.from && t === e.value.to) { }),
return false; true,
} );
return true;
// console.log('filter', f,t,value, e.value.from, e.value.to);
},
});
} }
} }
return highlights; return highlights;
} catch (err) { } catch (err) {
// console.warn('highlighting error', err); console.warn('highlighting error', err);
return highlights; return highlights;
} }
}, },
provide: (f) => EditorView.decorations.from(f), provide: (f) => EditorView.decorations.from(f),
}); });
// let timeouts = [];
export const highlightEvent = (event, view, code) => {
if (!view) {
return;
}
const ranges = event.context?.locations?.map(({ start, end }) => {
return [start, end].map(({ line, column }) => positionToOffset({ line: line - 1, ch: column }, code));
});
const effects = ranges.map(([from, to]) => addHighlight.of({ from, to }));
if (!effects.length) return false;
if (!view.state.field(highlightField, false)) {
effects.push(StateEffect.appendConfig.of([highlightField, highlightTheme]));
}
view.dispatch({ effects });
// const index = timeouts.length;
// timeouts = timeouts.filter(time)
/* const timeout = */ setTimeout(() => {
const effects = ranges.map(([from, to]) => removeHighlight.of({ from, to }));
view.dispatch({ effects });
// timeouts.splice(index, 1);
}, event.duration * 1000);
// timeouts.pusn({timeout,);
};
export default function CodeMirror({ value, onChange, onViewChanged, onCursor, options, editorDidMount }) { export default function CodeMirror({ value, onChange, onViewChanged, onCursor, options, editorDidMount }) {
return ( return (
<> <>
@ -88,6 +56,8 @@ export default function CodeMirror({ value, onChange, onViewChanged, onCursor, o
extensions={[ extensions={[
javascript(), javascript(),
materialPalenight, materialPalenight,
highlightField,
highlightTheme,
// theme, language, ... // theme, language, ...
]} ]}
/> />