From 51e817468919045b10da51febaa6484f736295a1 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Wed, 7 Jun 2023 12:26:23 +0200 Subject: [PATCH] metadata: add quotes syntax for titles --- test/metadata.test.mjs | 68 ++++++++++++++++++++++++---- website/src/pages/learn/code.mdx | 2 +- website/src/pages/learn/metadata.mdx | 6 +++ website/src/pages/metadata_parser.js | 10 +++- website/src/repl/tunes.mjs | 56 +++++++++++------------ 5 files changed, 102 insertions(+), 40 deletions(-) diff --git a/test/metadata.test.mjs b/test/metadata.test.mjs index 9487ebe2..cbd0f8a3 100644 --- a/test/metadata.test.mjs +++ b/test/metadata.test.mjs @@ -63,6 +63,51 @@ describe.concurrent('Metadata parser', () => { }); }); + it('loads a title tag with quotes syntax', async () => { + const tune = `// "Awesome song"`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + }); + }); + + it('loads a title tag with quotes syntax among other tags', async () => { + const tune = `// "Awesome song" made @by Sam`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads a title tag with quotes syntax from block comment', async () => { + const tune = `/* "Awesome song" +@by Sam */`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('does not load a title tag with quotes syntax after a prefix', async () => { + const tune = `// I don't care about those "metadata".`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + + it('does not load a title tag with quotes syntax after an other comment', async () => { + const tune = `// I don't care about those +// "metadata"`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + + it('does not load a title tag with quotes syntax after other tags', async () => { + const tune = `/* +@by Sam aka "Lady Strudel" + "Sandyyy" +*/`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam aka "Lady Strudel"', '"Sandyyy"'], + }); + }); + it('loads a tag list with comma-separated values syntax', async () => { const tune = `// @by Sam, Sandy`; expect(getMetadata(tune)).toStrictEqual({ @@ -159,15 +204,6 @@ describe.concurrent('Metadata parser', () => { }); }); - it('loads empty tags from block comment', async () => { - const tune = `/* @title -@by */`; - expect(getMetadata(tune)).toStrictEqual({ - title: '', - by: [], - }); - }); - it('loads tags with whitespaces from inline comments', async () => { const tune = ` // @title Awesome song // @by Sam Tagada `; @@ -186,6 +222,20 @@ describe.concurrent('Metadata parser', () => { }); }); + it('loads empty tags from block comment', async () => { + const tune = `/* @title +@by */`; + expect(getMetadata(tune)).toStrictEqual({ + title: '', + by: [], + }); + }); + + it('does not load tags if there is not', async () => { + const tune = `note("a3 c#4 e4 a4")`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + it('does not load code that looks like a metadata tag', async () => { const tune = `const str1 = '@title Awesome song'`; // need a lexer to avoid this one, but it's a pretty rare use case: diff --git a/website/src/pages/learn/code.mdx b/website/src/pages/learn/code.mdx index 5e2b37b4..b74002aa 100644 --- a/website/src/pages/learn/code.mdx +++ b/website/src/pages/learn/code.mdx @@ -67,7 +67,7 @@ It is a handy way to quickly turn code on and off. Try uncommenting this line by deleting `//` and refreshing the pattern. You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. -You might noticed that some comments in the REPL samples include some words starting with a "@", like `@title` or `@license`. +You might noticed that some comments in the REPL samples include some words starting with a "@", like `@by` or `@license`. Those are just a convention to define some information about the music. We will talk about it in the [Music metadata](/learn/metadata) section. # Strings diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index 7038644d..27f46921 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -38,6 +38,12 @@ Or define multiple tags in one line: // @title Hey Hoo @by Sam Tagada @license CC BY-NC-SA ``` +The `title` tag has an alternative syntax using quotes (must be defined at the very begining): + +```js +// "Hey Hoo" @by Sam Tagada +``` + ## Tags list Available tags are: diff --git a/website/src/pages/metadata_parser.js b/website/src/pages/metadata_parser.js index b4448c2a..6a92a8a6 100644 --- a/website/src/pages/metadata_parser.js +++ b/website/src/pages/metadata_parser.js @@ -2,10 +2,16 @@ const ALLOW_MANY = ['by', 'url', 'genre', 'license']; export function getMetadata(raw_code) { const comment_regexp = /\/\*([\s\S]*?)\*\/|\/\/(.*)$/gm; + const comments = [...raw_code.matchAll(comment_regexp)].map((c) => (c[1] || c[2] || '').trim()); const tags = {}; - for (const match of raw_code.matchAll(comment_regexp)) { - const tag_matches = (match[1] || match[2] || '').trim().split('@').slice(1); + const [prefix, title] = (comments[0] || '').split('"'); + if (prefix.trim() === '' && title !== undefined) { + tags['title'] = title; + } + + for (const comment of comments) { + const tag_matches = comment.split('@').slice(1); for (const tag_match of tag_matches) { let [tag, tag_value] = tag_match.split(/ (.*)/s); tag = tag.trim(); diff --git a/website/src/repl/tunes.mjs b/website/src/repl/tunes.mjs index 0e60ab26..f2d492e9 100644 --- a/website/src/repl/tunes.mjs +++ b/website/src/repl/tunes.mjs @@ -121,7 +121,7 @@ stack( .room(1) //.pianoroll({fold:1})`; -export const caverave = `// @title Caverave +export const caverave = `// "Caverave" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -186,7 +186,7 @@ export const barryHarris = `// adapted from a Barry Harris excercise .color('#00B8D4') `; -export const blippyRhodes = `// @title Blippy Rhodes +export const blippyRhodes = `// "Blippy Rhodes" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -230,7 +230,7 @@ stack( ).fast(3/2) //.pianoroll({fold:1})`; -export const wavyKalimba = `// @title Wavy kalimba +export const wavyKalimba = `// "Wavy kalimba" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -262,7 +262,7 @@ stack( .delay(.2) `; -export const festivalOfFingers = `// @title Festival of fingers +export const festivalOfFingers = `// "Festival of fingers" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -279,7 +279,7 @@ stack( .note().piano()`; // iter, echo, echoWith -export const undergroundPlumber = `// @title Underground plumber +export const undergroundPlumber = `// "Underground plumber" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos // @details inspired by Friendship - Let's not talk about it (1979) :) @@ -307,7 +307,7 @@ stack( .fast(2/3) .pianoroll({})`; -export const bridgeIsOver = `// @title Bridge is over +export const bridgeIsOver = `// "Bridge is over" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos, bassline by BDP - The Bridge Is Over @@ -330,7 +330,7 @@ stack( .pianoroll() `; -export const goodTimes = `// @title Good times +export const goodTimes = `// "Good times" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -375,7 +375,7 @@ stack( .pianoroll({maxMidi:100,minMidi:20})`; */ -export const echoPiano = `// @title Echo piano +export const echoPiano = `// "Echo piano" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -424,7 +424,7 @@ stack( .legato(.5) ).fast(2).note()`; -export const randomBells = `// @title Random bells +export const randomBells = `// "Random bells" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -449,7 +449,7 @@ stack( .pianoroll({minMidi:20,maxMidi:120,background:'transparent'}) `; -export const waa2 = `// @title Waa2 +export const waa2 = `// "Waa2" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -467,7 +467,7 @@ n( .room(.5) `; -export const hyperpop = `// @title Hyperpop +export const hyperpop = `// "Hyperpop" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -520,7 +520,7 @@ stack( ).slow(2) // strudel disable-highlighting`; -export const festivalOfFingers3 = `// @title Festival of fingers 3 +export const festivalOfFingers3 = `// "Festival of fingers 3" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -540,7 +540,7 @@ export const festivalOfFingers3 = `// @title Festival of fingers 3 .note().piano() //.pianoroll({maxMidi:160})`; -export const meltingsubmarine = `// @title Melting submarine +export const meltingsubmarine = `// "Melting submarine" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -580,7 +580,7 @@ stack( ) .slow(3/2)`; -export const outroMusic = `// @title Outro music +export const outroMusic = `// "Outro music" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -611,7 +611,7 @@ samples({ //.pianoroll({autorange:1,vertical:1,fold:0}) `; -export const bassFuge = `// @title Bass fuge +export const bassFuge = `// "Bass fuge" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -639,7 +639,7 @@ x=>x.add(7).color('steelblue') .stack(s("bd:1*2,~ sd:0,[~ hh:0]*2")) .pianoroll({vertical:1})`; -export const chop = `// @title Chop +export const chop = `// "Chop" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -654,7 +654,7 @@ s("p") .sustain(.6) `; -export const delay = `// @title Delay +export const delay = `// "Delay" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -665,7 +665,7 @@ stack( .delayfeedback(".6 | .8") ).sometimes(x=>x.speed("-1"))`; -export const orbit = `// @title Orbit +export const orbit = `// "Orbit" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -681,7 +681,7 @@ stack( .orbit(2) ).sometimes(x=>x.speed("-1"))`; -export const belldub = `// @title Belldub +export const belldub = `// "Belldub" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -716,7 +716,7 @@ stack( .mask("<1 0>/8") ).slow(5)`; -export const dinofunk = `// @title Dinofunk +export const dinofunk = `// "Dinofunk" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -739,7 +739,7 @@ note("Abm7".voicings('lefthand').struct("x(3,8,1)".slow(2))), note("").s('dino').delay(.8).slow(8).room(.5) )`; -export const sampleDemo = `// @title Sample demo +export const sampleDemo = `// "Sample demo" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -757,7 +757,7 @@ stack( .release(.1).room(.5) )`; -export const holyflute = `// @title Holy flute +export const holyflute = `// "Holy flute" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -772,7 +772,7 @@ export const holyflute = `// @title Holy flute .pianoroll({fold:0,autorange:0,vertical:0,cycles:12,smear:0,minMidi:40}) `; -export const flatrave = `// @title Flatrave +export const flatrave = `// "Flatrave" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -797,7 +797,7 @@ stack( .decay(.05).sustain(0).delay(.2).degradeBy(.5).mask("<0 1>/16") )`; -export const amensister = `// @title Amensister +export const amensister = `// "Amensister" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -833,7 +833,7 @@ stack( n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5)) ).reset("")`; -export const juxUndTollerei = `// @title Jux und tollerei +export const juxUndTollerei = `// "Jux und tollerei" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -848,7 +848,7 @@ note("c3 eb3 g3 bb3").palindrome() .delay(.5).delaytime(.1).delayfeedback(.4) .pianoroll()`; -export const csoundDemo = `// @title CSound demo +export const csoundDemo = `// "CSound demo" // @license with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -884,7 +884,7 @@ endin\` //.pianoroll() .csound('CoolSynth')`; -export const loungeSponge = `// @title Lounge sponge +export const loungeSponge = `// "Lounge sponge" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos, livecode.orc by Steven Yi @@ -904,7 +904,7 @@ stack( s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909') )`; -export const arpoon = `// @title Arpoon +export const arpoon = `// "Arpoon" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos