From ef42ca692a0a166f5005cd3a551b3912d6edb571 Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 27 Apr 2022 20:57:08 -0400 Subject: [PATCH] Work on Codemirror 6 highlighting --- repl/src/App.js | 28 +++++++++++++++++-- repl/src/CodeMirror6.jsx | 60 ++++++++++------------------------------ 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/repl/src/App.js b/repl/src/App.js index ffd0a27a..2448ec62 100644 --- a/repl/src/App.js +++ b/repl/src/App.js @@ -1,5 +1,5 @@ import React, { useCallback, useLayoutEffect, useRef, useState, useEffect } from 'react'; -import CodeMirror6, { highlightEvent } from './CodeMirror6'; +import CodeMirror6, { setHighlights } from './CodeMirror6'; import cx from './cx'; import logo from './logo.svg'; import playStatic from './static.mjs'; @@ -91,7 +91,8 @@ function App() { tune: decoded || randomTune, defaultSynth, // 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 logBox = useRef(); @@ -118,6 +119,29 @@ function App() { return () => window.removeEventListener('keydown', handleKeyPress); }, [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({ ready: useCallback( ({ outputs }) => { diff --git a/repl/src/CodeMirror6.jsx b/repl/src/CodeMirror6.jsx index f82f5d62..c182b702 100644 --- a/repl/src/CodeMirror6.jsx +++ b/repl/src/CodeMirror6.jsx @@ -7,8 +7,7 @@ import { javascript } from '@codemirror/lang-javascript'; import { materialPalenight } from './themes/material-palenight'; const highlightMark = Decoration.mark({ class: 'cm-highlight' }); -const addHighlight = StateEffect.define(); -const removeHighlight = StateEffect.define(); +export const setHighlights = StateEffect.define(); const highlightTheme = EditorView.baseTheme({ '.cm-highlight': { outline: '1px solid #FFCA28' }, // '.cm-highlight': { background: '#FFCA28' }, @@ -19,60 +18,29 @@ const highlightField = StateField.define({ }, update(highlights, tr) { try { - highlights = highlights.map(tr.changes); for (let e of tr.effects) { - if (e.is(addHighlight)) { - highlights = highlights.update({ - add: [highlightMark.range(e.value.from, e.value.to)], - }); - } - if (e.is(removeHighlight)) { - highlights = highlights.update({ - filter: (f, t, value) => { - if (f === e.value.from && t === e.value.to) { - return false; - } - return true; - // console.log('filter', f,t,value, e.value.from, e.value.to); - }, - }); + if (e.is(setHighlights)) { + highlights = Decoration.set( + e.value + .flatMap((event) => event.context.locations || []) + .map(({ start, end }) => { + let from = tr.newDoc.line(start.line).from + start.column; + let to = tr.newDoc.line(end.line).from + end.column; + return highlightMark.range(from, to); + }), + true, + ); } } return highlights; } catch (err) { - // console.warn('highlighting error', err); + console.warn('highlighting error', err); return highlights; } }, 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 }) { return ( <> @@ -88,6 +56,8 @@ export default function CodeMirror({ value, onChange, onViewChanged, onCursor, o extensions={[ javascript(), materialPalenight, + highlightField, + highlightTheme, // theme, language, ... ]} />