use state query in repl

+ move locations to context
This commit is contained in:
Felix Roos 2022-02-25 20:10:49 +01:00
parent 29bdfb2d90
commit f1362b1c94
5 changed files with 25 additions and 23 deletions

View file

@ -18,7 +18,7 @@ export default function CodeMirror({ value, onChange, options, editorDidMount }:
} }
export const markEvent = (editor) => (event) => { export const markEvent = (editor) => (event) => {
const locs = event.value.locations; const locs = event.context.locations;
if (!locs || !editor) { if (!locs || !editor) {
return; return;
} }

1
repl/src/types.d.ts vendored
View file

@ -15,6 +15,7 @@ export declare interface Hap<T = any> {
whole: TimeSpan; whole: TimeSpan;
part: TimeSpan; part: TimeSpan;
value: T; value: T;
context: any;
show: () => string; show: () => string;
} }
export declare interface Pattern<T = any> { export declare interface Pattern<T = any> {

View file

@ -1,12 +1,12 @@
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useState } from 'react';
import type { ToneEventCallback } from 'tone'; import type { ToneEventCallback } from 'tone';
import * as Tone from 'tone'; import * as Tone from 'tone';
import { TimeSpan } from '../../strudel.mjs'; import { TimeSpan, State } from '../../strudel.mjs';
import type { Hap } from './types'; import type { Hap } from './types';
export declare interface UseCycleProps { export declare interface UseCycleProps {
onEvent: ToneEventCallback<any>; onEvent: ToneEventCallback<any>;
onQuery?: (query: TimeSpan) => Hap[]; onQuery?: (state: State) => Hap[];
onSchedule?: (events: Hap[], cycle: number) => void; onSchedule?: (events: Hap[], cycle: number) => void;
ready?: boolean; // if false, query will not be called on change props ready?: boolean; // if false, query will not be called on change props
} }
@ -21,7 +21,7 @@ function useCycle(props: UseCycleProps) {
// pull events with onQuery + count up to next cycle // pull events with onQuery + count up to next cycle
const query = (cycle = activeCycle()) => { const query = (cycle = activeCycle()) => {
const timespan = new TimeSpan(cycle, cycle + 1); const timespan = new TimeSpan(cycle, cycle + 1);
const events = onQuery?.(timespan) || []; const events = onQuery?.(new State(timespan)) || [];
onSchedule?.(events, cycle); onSchedule?.(events, cycle);
// cancel events after current query. makes sure no old events are player for rescheduled cycles // cancel events after current query. makes sure no old events are player for rescheduled cycles
// console.log('schedule', cycle); // console.log('schedule', cycle);
@ -46,6 +46,7 @@ function useCycle(props: UseCycleProps) {
time: event.part.begin.valueOf(), time: event.part.begin.valueOf(),
duration: event.whole.end.sub(event.whole.begin).valueOf(), duration: event.whole.end.sub(event.whole.begin).valueOf(),
value: event.value, value: event.value,
context: event.context,
}; };
onEvent(time, toneEvent); onEvent(time, toneEvent);
}, event.part.begin.valueOf()); }, event.part.begin.valueOf());

View file

@ -81,9 +81,9 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent }: any) {
[onEvent] [onEvent]
), ),
onQuery: useCallback( onQuery: useCallback(
(span) => { (state) => {
try { try {
return pattern?.query(span) || []; return pattern?.query(state) || [];
} catch (err: any) { } catch (err: any) {
err.message = 'query error: ' + err.message; err.message = 'query error: ' + err.message;
setError(err); setError(err);

View file

@ -175,7 +175,7 @@ class Hap {
The context is to store a list of source code locations causing the event The context is to store a list of source code locations causing the event
*/ */
constructor(whole, part, value, context=[]) { constructor(whole, part, value, context = {}) {
this.whole = whole this.whole = whole
this.part = part this.part = part
this.value = value this.value = value
@ -185,12 +185,12 @@ class Hap {
withSpan(func) { withSpan(func) {
// Returns a new event with the function f applies to the event timespan. // Returns a new event with the function f applies to the event timespan.
const whole = this.whole ? func(this.whole) : undefined const whole = this.whole ? func(this.whole) : undefined
return new Hap(whole, func(this.part), this.value) return new Hap(whole, func(this.part), this.value, this.context)
} }
withValue(func) { withValue(func) {
// Returns a new event with the function f applies to the event value. // Returns a new event with the function f applies to the event value.
return new Hap(this.whole, this.part, func(this.value)) return new Hap(this.whole, this.part, func(this.value), this.context)
} }
hasOnset() { hasOnset() {
@ -317,11 +317,10 @@ class Pattern {
} }
withLocation(location) { withLocation(location) {
return this.fmap(value => { return this._withContext((context) => {
value = typeof value === 'object' && !Array.isArray(value) ? value : { value }; const locations = (context.locations || []).concat([location])
const locations = (value.locations || []).concat([location]); return { ...context, locations }
return {...value, locations } });
})
} }
withValue(func) { withValue(func) {
@ -367,7 +366,8 @@ class Pattern {
if (s == undefined) { if (s == undefined) {
return undefined return undefined
} }
return new Hap(whole_func(event_func.whole, event_val.whole), s, event_func.value(event_val.value)) // TODO: is it right to add event_val.context here?
return new Hap(whole_func(event_func.whole, event_val.whole), s, event_func.value(event_val.value), event_val.context)
} }
return flatten(event_funcs.map(event_func => removeUndefineds(event_vals.map(event_val => apply(event_func, event_val))))) return flatten(event_funcs.map(event_func => removeUndefineds(event_vals.map(event_val => apply(event_func, event_val)))))
} }
@ -396,7 +396,7 @@ class Pattern {
const new_whole = hap_func.whole const new_whole = hap_func.whole
const new_part = hap_func.part.intersection_e(hap_val.part) const new_part = hap_func.part.intersection_e(hap_val.part)
const new_value = hap_func.value(hap_val.value) const new_value = hap_func.value(hap_val.value)
const hap = new Hap(new_whole, new_part, new_value) const hap = new Hap(new_whole, new_part, new_value, hap_val.context)
haps.push(hap) haps.push(hap)
} }
} }
@ -416,7 +416,7 @@ class Pattern {
const new_whole = hap_val.whole const new_whole = hap_val.whole
const new_part = hap_func.part.intersection_e(hap_val.part) const new_part = hap_func.part.intersection_e(hap_val.part)
const new_value = hap_func.value(hap_val.value) const new_value = hap_func.value(hap_val.value)
const hap = new Hap(new_whole, new_part, new_value) const hap = new Hap(new_whole, new_part, new_value, hap_val.context)
haps.push(hap) haps.push(hap)
} }
} }
@ -461,8 +461,9 @@ class Pattern {
const pat_val = this const pat_val = this
const query = function(state) { const query = function(state) {
const withWhole = function(a, b) { const withWhole = function(a, b) {
// TODO: what to do with a.context here?
return new Hap(choose_whole(a.whole, b.whole), b.part, return new Hap(choose_whole(a.whole, b.whole), b.part,
b.value b.value, b.context
) )
} }
const match = function (a) { const match = function (a) {
@ -959,9 +960,8 @@ Pattern.prototype.bootstrap = () => {
// this is wrapped around mini patterns to offset krill parser location into the global js code space // this is wrapped around mini patterns to offset krill parser location into the global js code space
function withLocationOffset(pat, offset) { function withLocationOffset(pat, offset) {
return pat.fmap((value) => { return pat._withContext((context) => {
value = typeof value === 'object' && !Array.isArray(value) ? value : { value }; let locations = (context.locations || []);
let locations = (value.locations || []);
locations = locations.map(({ start, end }) => { locations = locations.map(({ start, end }) => {
const colOffset = start.line === 1 ? offset.start.column : 0; const colOffset = start.line === 1 ? offset.start.column : 0;
return { return {
@ -976,7 +976,7 @@ function withLocationOffset(pat, offset) {
column: end.column - 1 + colOffset, column: end.column - 1 + colOffset,
}, },
}}); }});
return {...value, locations } return {...context, locations }
}); });
} }