From 4b9afb46a5c36b1dc899223cde2b089d1c7dce18 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 8 Sep 2023 16:35:05 +0200 Subject: [PATCH 1/6] base of autocompletion --- src/EditorSetup.ts | 50 +----- src/documentation/inlineHelp.ts | 305 ++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+), 43 deletions(-) create mode 100644 src/documentation/inlineHelp.ts diff --git a/src/EditorSetup.ts b/src/EditorSetup.ts index fb7ce23..28f73b9 100644 --- a/src/EditorSetup.ts +++ b/src/EditorSetup.ts @@ -1,11 +1,13 @@ +import { inlineHoveringTips } from "./documentation/inlineHelp"; + import { keymap, highlightSpecialChars, drawSelection, highlightActiveLine, dropCursor, - rectangularSelection, - crosshairCursor, + // rectangularSelection, + // crosshairCursor, highlightActiveLineGutter, } from "@codemirror/view"; import { Extension, EditorState } from "@codemirror/state"; @@ -24,45 +26,6 @@ import { } from "@codemirror/autocomplete"; import { lintKeymap } from "@codemirror/lint"; -// (The superfluous function calls around the list of extensions work -// around current limitations in tree-shaking software.) - -/// This is an extension value that just pulls together a number of -/// extensions that you might want in a basic editor. It is meant as a -/// convenient helper to quickly set up CodeMirror without installing -/// and importing a lot of separate packages. -/// -/// Specifically, it includes... -/// -/// - [the default command bindings](#commands.defaultKeymap) -/// - [line numbers](#view.lineNumbers) -/// - [special character highlighting](#view.highlightSpecialChars) -/// - [the undo history](#commands.history) -/// - [a fold gutter](#language.foldGutter) -/// - [custom selection drawing](#view.drawSelection) -/// - [drop cursor](#view.dropCursor) -/// - [multiple selections](#state.EditorState^allowMultipleSelections) -/// - [reindentation on input](#language.indentOnInput) -/// - [the default highlight style](#language.defaultHighlightStyle) (as fallback) -/// - [bracket matching](#language.bracketMatching) -/// - [bracket closing](#autocomplete.closeBrackets) -/// - [autocompletion](#autocomplete.autocompletion) -/// - [rectangular selection](#view.rectangularSelection) and [crosshair cursor](#view.crosshairCursor) -/// - [active line highlighting](#view.highlightActiveLine) -/// - [active line gutter highlighting](#view.highlightActiveLineGutter) -/// - [selection match highlighting](#search.highlightSelectionMatches) -/// - [search](#search.searchKeymap) -/// - [linting](#lint.lintKeymap) -/// -/// (You'll probably want to add some language package to your setup -/// too.) -/// -/// This extension does not allow customization. The idea is that, -/// once you decide you want to configure your editor more precisely, -/// you take this package's source (which is just a bunch of imports -/// and an array literal), copy it into your own code, and adjust it -/// as desired. - export const editorSetup: Extension = (() => [ highlightActiveLineGutter(), highlightSpecialChars(), @@ -75,10 +38,11 @@ export const editorSetup: Extension = (() => [ bracketMatching(), closeBrackets(), autocompletion(), - rectangularSelection(), - crosshairCursor(), + // rectangularSelection(), + // crosshairCursor(), highlightActiveLine(), highlightSelectionMatches(), + inlineHoveringTips, keymap.of([ ...closeBracketsKeymap, ...defaultKeymap, diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts new file mode 100644 index 0000000..4657499 --- /dev/null +++ b/src/documentation/inlineHelp.ts @@ -0,0 +1,305 @@ +import { hoverTooltip } from "@codemirror/view"; +import { type EditorView } from "@codemirror/view"; + +interface InlineCompletion { + name: string; + category: string; + description: string; + example: string; +} + +type CompletionDatabase = { + [key: string]: InlineCompletion; +}; + +const completionDatabase: CompletionDatabase = { + attack: { + name: "attack", + category: "synthesis", + description: "ADSR envelope attack time (in seconds)", + example: "sound('sawtooth').attack(.5).out()", + }, + decay: { + name: "decay", + category: "synthesis", + description: "ADSR envelope decay time (in seconds)", + example: "sound('sawtooth').decay(.5).out()", + }, + sustain: { + name: "sustain", + category: "synthesis", + description: "ADSR envelope sustain level (0-1)", + example: "sound('sawtooth').sustain(.5).out()", + }, + release: { + name: "release", + category: "synthesis", + description: "ADSR envelope release time (in seconds)", + example: "sound('sawtooth').release(.5).out()", + }, + fmi: { + name: "fmi", + category: "audio", + description: "FM synth modulator index", + example: "sound('fm').fmi([1,2].beat()).out()", + }, + fmh: { + name: "fmh", + category: "audio", + description: "FM synth modulator ratio", + example: "sound('fm').fmi(2).fmh(2).out()", + }, + repeatAll: { + name: "repeatAll", + category: "patterns", + description: "Repeat every array elements n times", + example: "[0,1,2,3].repeatAll(2)", + }, + quant: { + name: "quant", + category: "functions", + description: "Quantize a value in the given array", + example: "quant(30, [0,1,2,3])", + }, + log: { + name: "log", + category: "javascript", + description: "Log a value in the console", + example: "log('Hello, world')", + }, + div: { + name: "div", + category: "patterns", + description: + "Returns next value every n beats or true and false alternatively", + example: "div(4, 50) // 2 beats of true, 2 beats of false, 50/50.", + }, + n: { + name: "n", + category: "audio", + description: "Sample number or synth oscillator partials count", + example: "sound('dr').n([1,2].beat()).out()", + }, + note: { + name: "note", + category: "patterns", + description: "MIDI note number (0-127)", + example: "sound('jvbass').note(50).out()", + }, + vel: { + name: "vel", + category: "audio", + description: "Velocity or sound volume (0-1)", + example: "sound('cp').vel(.5).out()", + }, + palindrome: { + name: "palindrome", + category: "patterns", + description: "Returns palindrome of the current array", + example: "[0,1,2,3].palindrome()", + }, + cutoff: { + name: "cutoff", + category: "filter", + description: "Lowpass filter cutoff frequency", + example: "sound('cp').cutoff(1000).out()", + }, + speed: { + name: "speed", + category: "sampling", + description: "Sample playback speed", + example: "sound('cp').speed(.5).out()", + }, + delay: { + name: "delay", + category: "effect", + description: "Delay effect dry/wet", + example: "sound('cp').delay(.5).out()", + }, + delayfb: { + name: "delayfb", + category: "effect", + description: "Delay effect feedback amount (0-1)", + example: "sound('cp').delay(0.2).delayfb(.5).out()", + }, + delaytime: { + name: "delaytime", + category: "effect", + description: "Delay effect delay time (in seconds)", + example: "sound('cp').delay(0.2).delaytime(.5).out()", + }, + gain: { + name: "gain", + category: "audio", + description: "Playback volume", + example: "sound('cp').gain(.5).out()", + }, + bar: { + name: "bar", + category: "patterns", + description: "Returns list index for the current bar (with wrapping)", + example: "[0,1,2,3].bar()", + }, + beat: { + name: "beat", + category: "patterns", + description: "Returns list index for the current beat (with wrapping)", + example: "[0,1,2,3].beat()", + }, + room: { + name: "room", + category: "effect", + description: "Reverb effect room amount", + example: "sound('cp').room(.5).out()", + }, + size: { + name: "size", + category: "effect", + description: "Reverb effect room size", + example: "sound('cp').size(.5).out()", + }, + usine: { + name: "usine", + category: "modulation", + description: "Unipolar sinusoïdal low-frequency oscillator", + example: "usine(5) // 5 hz oscillation", + }, + sine: { + name: "usine", + category: "modulation", + description: "Sinusoïdal low-frequency oscillator", + example: "usine(5) // 5 hz oscillation", + }, + utriangle: { + name: "utriangle", + category: "modulation", + description: "Unipolar triangular low-frequency oscillator", + example: "utriangle(5) // 5 hz oscillation", + }, + triangle: { + name: "triangle", + category: "modulation", + description: "Triangular low-frequency oscillator", + example: "triangle(5) // 5 hz oscillation", + }, + usaw: { + name: "usaw", + category: "modulation", + description: "Unipolar sawtooth low-frequency oscillator", + example: "usaw(5) // 5 hz oscillation", + }, + saw: { + name: "saw", + category: "modulation", + description: "Sawtooth low-frequency oscillator", + example: "saw(5) // 5 hz oscillation", + }, + square: { + name: "square", + category: "modulation", + description: "Square low-frequency oscillator", + example: "square(5) // 5 hz oscillation", + }, + usquare: { + name: "usquare", + category: "modulation", + description: "Unipolar square low-frequency oscillator", + example: "usquare(5) // 5 hz oscillation", + }, + rhythm: { + name: "rhythm", + category: "rhythm", + description: "Variant of the euclidian algorithm function", + example: "rhythm(.5, 3, 8) // time, pulses, steps", + }, + let: { + name: "let", + category: "javascript", + description: "Variable assignation", + example: "let baba = 10", + }, + mod: { + name: "mod", + category: "rhythm", + description: "return true every n pulsations.", + example: "mod(1) :: log(rand(1,5))", + }, + rand: { + name: "rand", + category: "randomness", + description: "random floating point number between x and y", + example: "rand(1, 10) // between 1 and 10", + }, + irand: { + name: "irand", + category: "randomness", + description: "random integer number between x and y", + example: "irand(1, 10) // between 1 and 10", + }, + pick: { + name: "pick", + category: "randomness", + description: "Pick a value in the given array", + example: "[1,4,10].pick()", + }, + sound: { + name: "sound", + category: "audio", + description: "Base function to play audio (samples / synths)", + example: "sound('bd').out()", + }, + snd: { + name: "snd", + category: "audio", + description: + "Base function to play audio (samples / synths). Alias for sound.", + example: "sound('bd').out()", + }, + bpm: { + name: "bpm", + category: "time", + description: "Get or set the current beats per minute.", + example: "bpm(135) // set the bpm to 135", + }, + out: { + name: "out", + category: "audio", + description: "Connect the sound() chain to the output", + example: "sound('clap').out()", + }, +}; + +export const inlineHoveringTips = hoverTooltip( + (view: any, pos: any, side: any) => { + let { from, to, text } = view.state.doc.lineAt(pos); + let start = pos, + end = pos; + while (start > from && /\w/.test(text[start - from - 1])) start--; + while (end < to && /\w/.test(text[end - from])) end++; + if ((start == pos && side < 0) || (end == pos && side > 0)) return null; + return { + pos: start, + end, + above: true, + create(view: EditorView) { + if ( + text.slice(start - from, end - from) in completionDatabase === + false + ) { + return { dom: document.createElement("div") }; + } + let completion = + completionDatabase[text.slice(start - from, end - from)] || {}; + let divContent = ` +

${completion.name} [${completion.category}]

+

${completion.description}

+
${completion.example}
+ `; + let dom = document.createElement("div"); + dom.classList.add("px-4", "py-2", "bg-neutral-700", "rounded-lg"); + dom.innerHTML = divContent; + return { dom }; + }, + }; + } +); From a1cdacda87a2d8e4e42be6da91c304ae9e0c38c9 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 8 Sep 2023 16:50:06 +0200 Subject: [PATCH 2/6] pushing more helpers --- src/documentation/inlineHelp.ts | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index 4657499..241c9b8 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -37,6 +37,12 @@ const completionDatabase: CompletionDatabase = { description: "ADSR envelope release time (in seconds)", example: "sound('sawtooth').release(.5).out()", }, + fmwave: { + name: "fmwave", + category: "synthesis", + description: "FM synth modulator waveform", + example: "sound('fm').fmwave('sine').out()", + }, fmi: { name: "fmi", category: "audio", @@ -218,12 +224,56 @@ const completionDatabase: CompletionDatabase = { description: "Variable assignation", example: "let baba = 10", }, + onbeat: { + name: "onbeat", + category: "rhythm", + description: "Return true when targetted beat(s) is/are reached", + example: "onbeat([1,2,3]) // true on beats 1, 2 and 3", + }, + oncount: { + name: "oncount", + category: "rhythm", + description: + "Return true when targetted beat(s) is/are reached in the given period", + example: + "oncount([1,2,3], 4) // true on beats 1, 2 and 3 in a 4 beats period", + }, mod: { name: "mod", category: "rhythm", description: "return true every n pulsations.", example: "mod(1) :: log(rand(1,5))", }, + modp: { + name: "modp", + category: "rhythm", + description: "return true every n ticks.", + example: "modp(8) :: log(rand(1,5))", + }, + euclid: { + name: "euclid", + category: "rhythm", + description: "Iterator-based euclidian rhythm generator", + example: "euclid($(1), 3, 8) // iterator, pulses", + }, + oneuclid: { + name: "oneuclid", + category: "rhythm", + description: "Variant of the euclidian rhythm generator", + example: "oneuclid(3, 8) // iterator, pulses", + }, + bin: { + name: "bin", + category: "rhythm", + description: "Convert a decimal number to binary rhythm generator", + example: "bin($(1), 9223) // iterator, number to convert", + }, + binrhythm: { + name: "binrhythm", + category: "rhythm", + description: "Binary rhythm generator", + example: "binrhythm(9223) :: sound('cp').out()", + }, rand: { name: "rand", category: "randomness", @@ -267,6 +317,12 @@ const completionDatabase: CompletionDatabase = { description: "Connect the sound() chain to the output", example: "sound('clap').out()", }, + script: { + name: "script", + category: "core", + description: "Execute one or more local scripts", + example: "mod(1) :: script(1)", + }, }; export const inlineHoveringTips = hoverTooltip( From 49b7b20b20eaf00d8129c0eb643b060c74b8de28 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 8 Sep 2023 16:55:03 +0200 Subject: [PATCH 3/6] adding the binrhythm function --- src/API.ts | 15 ++++++++++++++- src/documentation/time.ts | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/API.ts b/src/API.ts index e366308..ce4da5f 100644 --- a/src/API.ts +++ b/src/API.ts @@ -1072,7 +1072,7 @@ export class UserAPI { integral_part = integral_part == 0 ? this.nominator() : integral_part; let decimal_part = Math.floor((beat - integral_part) * this.ppqn() + 1); // This was once revelead to me in a dream - if (decimal_part <= 0) + if (decimal_part <= 0) decimal_part = decimal_part + this.ppqn() * this.nominator(); final_pulses.push( integral_part === this.beat() && this.pulse() === decimal_part @@ -1219,6 +1219,19 @@ export class UserAPI { return tobin[iterator % tobin.length]; }; + public binrhythm = (div: number, n: number): boolean => { + /** + * Returns a binary cycle of size n, divided by div. + * + * @param div - The divisor of the binary cycle + * @param n - The number to convert to binary + * @returns boolean value based on the binary sequence + */ + let convert: string = n.toString(2); + let tobin: boolean[] = convert.split("").map((x: string) => x === "1"); + return this.mod(div) && tobin.div(div); + }; + // ============================================================= // Low Frequency Oscillators // ============================================================= diff --git a/src/documentation/time.ts b/src/documentation/time.ts index b61b39d..93ca4be 100644 --- a/src/documentation/time.ts +++ b/src/documentation/time.ts @@ -182,6 +182,7 @@ rhythm(speed, 7, 12) :: snd('east').n(9).out() - bin(iterator: number, n: number): boolean: a binary rhythm generator. It transforms the given number into its binary representation (_e.g_ 34 becomes 100010). It then returns a boolean value based on the iterator in order to generate a rhythm. +- binrhythm(divisor: number, n: number): boolean: boolean: iterator-less version of the binary rhythm generator. ${makeExample( "Change the integers for a surprise rhythm!", @@ -192,6 +193,15 @@ mod(.5) && bin($(2), 48) && snd('sd').out() true )} +${makeExample( + "binrhythm for fast cool binary rhythms!", + ` +binrhythm(.5, 13) && snd('kick').out() +binrhythm(.5, 18) && snd('sd').out() +`, + true +)} + ${makeExample( "Calling 911", ` From be7b15ba660e992dcefcc4fb5e55aada6e58b433 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 8 Sep 2023 17:15:09 +0200 Subject: [PATCH 4/6] pushing even more completions --- src/documentation/inlineHelp.ts | 218 ++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index 241c9b8..7c374d8 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -55,6 +55,30 @@ const completionDatabase: CompletionDatabase = { description: "FM synth modulator ratio", example: "sound('fm').fmi(2).fmh(2).out()", }, + fmattack: { + name: "fmattack", + category: "synthesis", + description: "FM synth modulator ADSR envelope attack time (in seconds)", + example: "sound('sine').fmi(2).fmattack(.5).out()", + }, + fmdecay: { + name: "fmdecay", + category: "synthesis", + description: "FM synth modulator ADSR envelope decay time (in seconds)", + example: "sound('sine').fmi(2).fmdecay(.5).out()", + }, + fmsustain: { + name: "fmsustain", + category: "synthesis", + description: "FM synth modulator ADSR envelope sustain level (0-1)", + example: "sound('sine').fmi(2).fmsustain(.5).out()", + }, + fmrelease: { + name: "fmrelease", + category: "synthesis", + description: "FM synth modulator ADSR envelope release time (in seconds)", + example: "sound('sine').fmi(2).fmrelease(.5).out()", + }, repeatAll: { name: "repeatAll", category: "patterns", @@ -110,12 +134,67 @@ const completionDatabase: CompletionDatabase = { description: "Lowpass filter cutoff frequency", example: "sound('cp').cutoff(1000).out()", }, + resonance: { + name: "resonance", + category: "filter", + description: "Lowpass filter resonance", + example: "sound('cp').resonance(1).out()", + }, + hcutoff: { + name: "hcutoff", + category: "filter", + description: "Highpass filter cutoff frequency", + example: "sound('cp').hcutoff(1000).out()", + }, + hresonance: { + name: "hresonance", + category: "filter", + description: "Highpass filter resonance", + example: "sound('cp').hresonance(1).out()", + }, + bandf: { + name: "bandf", + category: "filter", + description: "Bandpass filter cutoff frequency", + example: "sound('cp').bandf(1000).out()", + }, + bandq: { + name: "bandq", + category: "filter", + description: "Bandpass filter resonance", + example: "sound('cp').bandq(1).out()", + }, + vowel: { + name: "vowel", + category: "filter", + description: "Vowel filter type", + example: "sound('cp').vowel('a').out()", + }, + coarse: { + name: "coarse", + category: "synthesis", + description: "Artificial sample-rate lowering", + example: "mod(.5)::snd('pad').coarse($(1) % 16).clip(.5).out();", + }, + crush: { + name: "crush", + category: "synthesis", + description: + "Bitcrushing effect. 1 is extreme, superior values are more subtle.", + example: "", + }, speed: { name: "speed", category: "sampling", description: "Sample playback speed", example: "sound('cp').speed(.5).out()", }, + shape: { + name: "shape", + category: "synthesis", + description: "Waveshaping distorsion", + example: "sound('cp').shape(.5).out()", + }, delay: { name: "delay", category: "effect", @@ -274,6 +353,12 @@ const completionDatabase: CompletionDatabase = { description: "Binary rhythm generator", example: "binrhythm(9223) :: sound('cp').out()", }, + prob: { + name: "prob", + category: "randomness", + description: "Return true with a probability of n %", + example: "prob(50) // 50% probability", + }, rand: { name: "rand", category: "randomness", @@ -323,6 +408,139 @@ const completionDatabase: CompletionDatabase = { description: "Execute one or more local scripts", example: "mod(1) :: script(1)", }, + warp: { + name: "warp", + category: "core", + description: "jumps to the n tick of the clock.", + example: "warp(1) :: log('back to the big bang!')", + }, + beat_warp: { + name: "beat_warp", + category: "core", + description: "jumps to the n beat of the clock.", + example: "beat_warp(1) :: log('back to the first beat!')", + }, + divbar: { + name: "divbar", + category: "time", + description: + "works just like div but at the level of bars instead of beats", + example: "divbar(2)::mod(1)::snd('kick').out()", + }, + onbar: { + name: "onbar", + category: "time", + description: "return true when targetted bar(s) is/are reached in period", + example: "onbar(4, 4)::mod(.5)::snd('hh').out();", + }, + begin: { + name: "begin", + category: "sampling", + description: "Audio playback start time (0-1)", + example: "sound('cp').begin(.5).out()", + }, + end: { + name: "end", + category: "sampling", + description: "Audio playback end time (0-1)", + example: "sound('cp').end(.5).out()", + }, + mouseX: { + name: "mouseX", + category: "mouse", + description: "Mouse X position (big float)", + example: "log(mouseX())", + }, + mouseY: { + name: "mouseY", + category: "mouse", + description: "Mouse Y position (big float)", + example: "log(mouseY())", + }, + noteX: { + name: "noteX", + category: "mouse", + description: "Mouse X position (as MIDI note)", + example: "log(noteX())", + }, + noteY: { + name: "noteY", + category: "mouse", + description: "Mouse Y position (as MIDI note)", + example: "log(noteY())", + }, + cut: { + name: "cut", + category: "sampling", + description: "Cutting sample when other sample met on same orbit (0 or 1)", + example: "sound('cp').cut(1).out()", + }, + pan: { + name: "pan", + category: "audio", + description: "Stereo panning (-1 to 1)", + example: "sound('cp').pan(-1).out()", + }, + zrand: { + name: "zrand", + category: "synthesis", + description: "ZzFX randomisation factor", + example: "sound('zzfx').zrand(.5).out()", + }, + curve: { + name: "curve", + category: "synthesis", + description: "ZzFX waveshaping (0-3)", + example: "sound('zzfx').curve(1).out()", + }, + slide: { + name: "slide", + category: "synthesis", + description: "ZzFX pitch slide", + example: "sound('zzfx').slide(1).out()", + }, + deltaSlide: { + name: "deltaSlide", + category: "synthesis", + description: "ZzFX pitch delta slide", + example: "sound('zzfx').deltaSlide(1).out()", + }, + pitchJump: { + name: "pitchJump", + category: "synthesis", + description: "ZzFX pitch jump", + example: "sound('zzfx').pitchJump(1).out()", + }, + pitchJumpTime: { + name: "pitchJumpTime", + category: "synthesis", + description: "ZzFX pitch jump time (time before jump)", + example: "sound('zzfx').pitchJumpTime(1).out()", + }, + zcrush: { + name: "zcrush", + category: "synthesis", + description: "ZzFX bitcrushing", + example: "sound('zzfx').zcrush(1).out()", + }, + zdelay: { + name: "zdelay", + category: "synthesis", + description: "ZzFX delay", + example: "sound('zzfx').zdelay(1).out()", + }, + tremolo: { + name: "tremolo", + category: "synthesis", + description: "ZzFX weird tremolo effect", + example: "sound('zzfx').tremolo(1).out()", + }, + speak: { + name: "speak", + category: "synthesis", + description: "Text to speech synthesizer", + example: "mod(2) :: speak('Topos!','fr',irand(0,5))", + }, }; export const inlineHoveringTips = hoverTooltip( From 0f2dbf4ca819e0e96b536f21078ad509512c989e Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 8 Sep 2023 17:24:00 +0200 Subject: [PATCH 5/6] adding even more things --- src/documentation/inlineHelp.ts | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index 7c374d8..a186ce0 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -541,6 +541,108 @@ const completionDatabase: CompletionDatabase = { description: "Text to speech synthesizer", example: "mod(2) :: speak('Topos!','fr',irand(0,5))", }, + midi_outputs: { + name: "midi_outputs", + category: "midi", + description: "List of available MIDI outputs", + example: "midi_outputs()", + }, + midi_output: { + name: "midi_output", + category: "midi", + description: "Set the current MIDI output", + example: "midi_output('IAC Driver Bus 1')", + }, + midi: { + name: "midi", + category: "midi", + description: "Send a MIDI message", + example: "midi(144, 60, 100)", + }, + control_change: { + name: "control_change", + category: "midi", + description: "Send a MIDI control change message", + example: "control_change({control: 1, value: 60, channel: 10})", + }, + program_change: { + name: "program_change", + category: "midi", + description: "Send a MIDI program change message", + example: "program_change(1, 10)", + }, + sysex: { + name: "sysex", + category: "midi", + description: "Send a MIDI sysex message", + example: "sysex(0xF0, 0x7D, 0x00, 0x06, 0x01, 0xF7)", + }, + midi_clock: { + name: "midi_clock", + category: "midi", + description: "Send a MIDI clock message", + example: "midi_clock()", + }, + degrade: { + name: "degrade", + category: "patterns", + description: "Removes n% of the given array randomly", + example: "[0,1,2,3].degrade(20)", + }, + loop: { + name: "loop", + category: "patterns", + description: "Loop over the given array using an iterator", + example: "[0,1,2,3].loop($(1))", + }, + $: { + name: "$", + category: "patterns", + description: "Iterator", + example: "[0,1,2,3].loop($(1))", + }, + shuffle: { + name: "shuffle", + category: "patterns", + description: "Shuffle the given array", + example: "[0,1,2,3].shuffle()", + }, + rotate: { + name: "rotate", + category: "patterns", + description: "Rotate the given array to the right for n indexes", + example: "[0,1,2,3].rotate(2)", + }, + unique: { + name: "unique", + category: "patterns", + description: "Remove duplicates from the given array", + example: "[0,1,2,3,3,3].unique()", + }, + add: { + name: "add", + category: "patterns", + description: "Add a value to each element of the given array", + example: "[0,1,2,3].add(1)", + }, + sub: { + name: "sub", + category: "patterns", + description: "Substract a value to each element of the given array", + example: "[0,1,2,3].sub(1)", + }, + mul: { + name: "mul", + category: "patterns", + description: "Multiply each element of the given array by a value", + example: "[0,1,2,3].mul(2)", + }, + division: { + name: "div", + category: "patterns", + description: "Divide each element of the given array by a value", + example: "[0,1,2,3].division(2)", + }, }; export const inlineHoveringTips = hoverTooltip( From ba06a356984baebca24663464dc61840c693be8c Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Fri, 8 Sep 2023 17:39:58 +0200 Subject: [PATCH 6/6] add menu option for hovering tips --- index.html | 4 ++++ src/AppSettings.ts | 16 +++++++++++++--- src/EditorSetup.ts | 3 --- src/main.ts | 38 +++++++++++++++++++++++++++++--------- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/index.html b/index.html index 53d0141..54167a1 100644 --- a/index.html +++ b/index.html @@ -230,6 +230,10 @@
+
+
+ +
diff --git a/src/AppSettings.ts b/src/AppSettings.ts index 9a2d483..3cd2908 100644 --- a/src/AppSettings.ts +++ b/src/AppSettings.ts @@ -42,6 +42,8 @@ export interface Settings { * @param universes - The set universes to use (e.g. saved files) * @param selected_universe - The name of the selected universe * @param line_numbers - Whether or not to show line numbers + * @param time_position - Whether or not to show time position + * @param tips - Whether or not to show tips */ vimMode: boolean; theme: string; @@ -50,7 +52,8 @@ export interface Settings { universes: Universes; selected_universe: string; line_numbers: boolean; - time_position: boolean; + time_position: boolean; + tips: boolean; } export const template_universe = { @@ -105,6 +108,9 @@ export class AppSettings { * @param universes - The set universes to use (e.g. saved files) * @param selected_universe - The name of the selected universe * @param line_numbers - Whether or not to show line numbers + * @param time_position - Whether or not to show time position + * @param tips - Whether or not to show tips + */ public vimMode: boolean = false; @@ -114,7 +120,8 @@ export class AppSettings { public universes: Universes; public selected_universe: string = "Default"; public line_numbers: boolean = true; - public time_position: boolean = true; + public time_position: boolean = true; + public tips: boolean = true; constructor() { const settingsFromStorage = JSON.parse( @@ -131,6 +138,7 @@ export class AppSettings { this.selected_universe = settingsFromStorage.selected_universe; this.line_numbers = settingsFromStorage.line_numbers; this.time_position = settingsFromStorage.time_position; + this.tips = settingsFromStorage.tips; } else { this.universes = template_universes; } @@ -152,7 +160,8 @@ export class AppSettings { universes: this.universes, selected_universe: this.selected_universe, line_numbers: this.line_numbers, - time_position: this.time_position + time_position: this.time_position, + tips: this.tips, }; } @@ -173,6 +182,7 @@ export class AppSettings { this.selected_universe = settings.selected_universe; this.line_numbers = settings.line_numbers; this.time_position = settings.time_position; + this.tips = settings.tips; localStorage.setItem("topos", JSON.stringify(this.data)); } } diff --git a/src/EditorSetup.ts b/src/EditorSetup.ts index 28f73b9..b2a50ba 100644 --- a/src/EditorSetup.ts +++ b/src/EditorSetup.ts @@ -1,5 +1,3 @@ -import { inlineHoveringTips } from "./documentation/inlineHelp"; - import { keymap, highlightSpecialChars, @@ -42,7 +40,6 @@ export const editorSetup: Extension = (() => [ // crosshairCursor(), highlightActiveLine(), highlightSelectionMatches(), - inlineHoveringTips, keymap.of([ ...closeBracketsKeymap, ...defaultKeymap, diff --git a/src/main.ts b/src/main.ts index 57d71a9..dd7fbcc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { examples } from "./examples/excerpts"; import { EditorState, Compartment } from "@codemirror/state"; import { ViewUpdate, lineNumbers, keymap } from "@codemirror/view"; import { javascript } from "@codemirror/lang-javascript"; +import { inlineHoveringTips } from "./documentation/inlineHelp"; import { toposTheme } from "./themes/toposTheme"; import { markdown } from "@codemirror/lang-markdown"; import { Extension, Prec } from "@codemirror/state"; @@ -72,6 +73,7 @@ export class Editor { fontSize: Compartment; withLineNumbers: Compartment; vimModeCompartment: Compartment; + hoveringCompartment: Compartment; chosenLanguage: Compartment; currentDocumentationPane: string = "introduction"; exampleCounter: number = 0; @@ -185,6 +187,10 @@ export class Editor { "show-time-position" ) as HTMLInputElement; + // Hovering tips checkbox + tips_checkbox: HTMLInputElement = document.getElementById( + "show-tips" + ) as HTMLInputElement; // Editor mode selection normal_mode_button: HTMLButtonElement = document.getElementById( @@ -231,12 +237,12 @@ export class Editor { this.universes[this.selected_universe].global.committed = random_example; this.universes[this.selected_universe].global.candidate = random_example; - this.line_numbers_checkbox.checked = this.settings.line_numbers; - this.time_position_checkbox.checked = this.settings.time_position; - if (!this.settings.time_position) { - document.getElementById('timeviewer')!.classList.add('hidden'); - } - + this.line_numbers_checkbox.checked = this.settings.line_numbers; + this.time_position_checkbox.checked = this.settings.time_position; + this.tips_checkbox.checked = this.settings.tips; + if (!this.settings.time_position) { + document.getElementById("timeviewer")!.classList.add("hidden"); + } // ================================================================================ // Audio context and clock @@ -258,6 +264,7 @@ export class Editor { // ================================================================================ this.vimModeCompartment = new Compartment(); + this.hoveringCompartment = new Compartment(); this.withLineNumbers = new Compartment(); this.chosenLanguage = new Compartment(); this.fontSize = new Compartment(); @@ -280,6 +287,7 @@ export class Editor { this.withLineNumbers.of(lines), this.fontSize.of(fontModif), this.vimModeCompartment.of(vimPlugin), + this.hoveringCompartment.of(this.settings.tips ? inlineHoveringTips : []), editorSetup, toposTheme, this.chosenLanguage.of(javascript()), @@ -537,6 +545,7 @@ export class Editor { ); this.line_numbers_checkbox.checked = this.settings.line_numbers; this.time_position_checkbox.checked = this.settings.time_position; + this.tips_checkbox.checked = this.settings.tips; let modal_settings = document.getElementById("modal-settings"); let editor = document.getElementById("editor"); modal_settings?.classList.remove("invisible"); @@ -595,12 +604,23 @@ export class Editor { }); }); - this.time_position_checkbox.addEventListener("change", () => { - let timeviewer = document.getElementById("timeviewer") as HTMLElement; + let timeviewer = document.getElementById("timeviewer") as HTMLElement; let checked = this.time_position_checkbox.checked ? true : false; this.settings.time_position = checked; - checked ? timeviewer.classList.remove('hidden') : timeviewer.classList.add('hidden'); + checked + ? timeviewer.classList.remove("hidden") + : timeviewer.classList.add("hidden"); + }); + + this.tips_checkbox.addEventListener("change", () => { + let checked = this.tips_checkbox.checked ? true : false; + this.settings.tips = checked; + this.view.dispatch({ + effects: this.hoveringCompartment.reconfigure( + checked ? inlineHoveringTips : [] + ), + }); }); this.vim_mode_button.addEventListener("click", () => {