diff --git a/.eslintignore b/.eslintignore index 92c95c85..7d807b65 100644 --- a/.eslintignore +++ b/.eslintignore @@ -20,4 +20,5 @@ vite.config.js **/dist /src-tauri/target/**/* reverbGen.mjs -hydra.mjs \ No newline at end of file +hydra.mjs +jsdoc-synonyms.js \ No newline at end of file diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js new file mode 100644 index 00000000..09190846 --- /dev/null +++ b/jsdoc/jsdoc-synonyms.js @@ -0,0 +1,17 @@ +/* +jsdoc-synonyms.js - Add support for @synonym tag +Copyright (C) 2023 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +function defineTags(dictionary) { + dictionary.defineTag('synonyms', { + mustHaveValue: true, + onTagged: function (doclet, tag) { + doclet.synonyms_text = tag.value; + doclet.synonyms = doclet.synonyms_text.split(/[ ,]+/); + }, + }); +} + +module.exports = { defineTags: defineTags }; diff --git a/jsdoc.config.json b/jsdoc/jsdoc.config.json similarity index 78% rename from jsdoc.config.json rename to jsdoc/jsdoc.config.json index ca9c3d81..073b16c5 100644 --- a/jsdoc.config.json +++ b/jsdoc/jsdoc.config.json @@ -3,7 +3,7 @@ "includePattern": ".+\\.(js(doc|x)?|mjs)$", "excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser|dist" }, - "plugins": ["plugins/markdown"], + "plugins": ["plugins/markdown", "jsdoc/jsdoc-synonyms"], "opts": { "destination": "./out/", "recurse": true diff --git a/undocumented.mjs b/jsdoc/undocumented.mjs similarity index 91% rename from undocumented.mjs rename to jsdoc/undocumented.mjs index 281d27d8..9376a8b6 100644 --- a/undocumented.mjs +++ b/jsdoc/undocumented.mjs @@ -65,7 +65,7 @@ async function getUndocumented(path, docs) { } // read doc.json file -const { docs } = JSON.parse(await readFile(resolve(__dirname, 'doc.json'), 'utf8')); +const { docs } = JSON.parse(await readFile(resolve(__dirname, '..', 'doc.json'), 'utf8')); const paths = dependencyTree.toList({ filename: 'index.mjs', @@ -76,7 +76,9 @@ const paths = dependencyTree.toList({ // const paths = ['../packages/core/pattern.mjs', '../packages/core/hap.mjs'].map((rel) => resolve(__dirname, rel)); const undocumented = Object.fromEntries( - await Promise.all(paths.map(async (path) => [path, await getUndocumented(path, docs)])), + await Promise.all( + paths.map(async (path) => [path.replace(resolve(__dirname, '..'), ''), await getUndocumented(path, docs)]), + ), ); console.log(JSON.stringify(undocumented, null, 2)); diff --git a/package.json b/package.json index 36229611..172832ee 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,12 @@ "build": "npm run prebuild && cd website && npm run build", "preview": "cd website && npm run preview", "osc": "cd packages/osc && npm run server", - "jsdoc": "jsdoc packages/ -c jsdoc.config.json", - "jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json", + "jsdoc": "jsdoc packages/ -c jsdoc/jsdoc.config.json", + "jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc/jsdoc.config.json", "lint": "eslint . --ext mjs,js --quiet", "codeformat": "prettier --write .", "format-check": "prettier --check .", - "report-undocumented": "npm run jsdoc-json && node undocumented.mjs > undocumented.json", + "report-undocumented": "npm run jsdoc-json && node jsdoc/undocumented.mjs > undocumented.json", "check": "npm run format-check && npm run lint && npm run test", "iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf" }, diff --git a/packages/react/src/components/Autocomplete.jsx b/packages/react/src/components/Autocomplete.jsx index 677baa6e..9a0b9de4 100644 --- a/packages/react/src/components/Autocomplete.jsx +++ b/packages/react/src/components/Autocomplete.jsx @@ -2,16 +2,23 @@ import { createRoot } from 'react-dom/client'; import jsdoc from '../../../../doc.json'; const getDocLabel = (doc) => doc.name || doc.longname; +const getDocSynonyms = (doc) => [getDocLabel(doc), ...(doc.synonyms || [])]; const getInnerText = (html) => { var div = document.createElement('div'); div.innerHTML = html; return div.textContent || div.innerText || ''; }; -export function Autocomplete({ doc }) { +export function Autocomplete({ doc, label = getDocLabel(doc) }) { + const synonyms = getDocSynonyms(doc).filter((a) => a !== label); return (
-

{getDocLabel(doc)}

+

{label}

{' '} + {!!synonyms.length && ( + + Synonyms: {synonyms.join(', ')} + + )}
    {doc.params?.map(({ name, type, description }, i) => ( @@ -48,18 +55,24 @@ const jsdocCompletions = jsdoc.docs !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)), ) // https://codemirror.net/docs/ref/#autocomplete.Completion - .map((doc) /*: Completion */ => ({ - label: getDocLabel(doc), - // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. - info: () => { - const node = document.createElement('div'); - // if Autocomplete is non-interactive, it could also be rendered at build time.. - // .. using renderToStaticMarkup - createRoot(node).render(); - return node; - }, - type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type - })); + .reduce( + (acc, doc) /*: Completion */ => + acc.concat( + [getDocLabel(doc), ...(doc.synonyms || [])].map((label) => ({ + label, + // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. + info: () => { + const node = document.createElement('div'); + // if Autocomplete is non-interactive, it could also be rendered at build time.. + // .. using renderToStaticMarkup + createRoot(node).render(); + return node; + }, + type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type + })), + ), + [], + ); export const strudelAutocomplete = (context /* : CompletionContext */) => { let word = context.matchBefore(/\w*/); diff --git a/packages/react/src/components/Tooltip.jsx b/packages/react/src/components/Tooltip.jsx index a443c123..43a53476 100644 --- a/packages/react/src/components/Tooltip.jsx +++ b/packages/react/src/components/Tooltip.jsx @@ -31,6 +31,9 @@ window.addEventListener( export const strudelTooltip = hoverTooltip( (view, pos, side) => { // Word selection from CodeMirror Hover Tooltip example https://codemirror.net/examples/tooltip/#hover-tooltips + if (!ctrlDown) { + return null; + } let { from, to, text } = view.state.doc.lineAt(pos); let start = pos, end = pos; @@ -47,11 +50,13 @@ export const strudelTooltip = hoverTooltip( // Get entry from Strudel documentation let entry = jsdoc.docs.filter((doc) => getDocLabel(doc) === word)[0]; if (!entry) { - return null; - } - if (!ctrlDown) { - return null; + // Try for synonyms + entry = jsdoc.docs.filter((doc) => doc.synonyms && doc.synonyms.includes(word))[0]; + if (!entry) { + return null; + } } + return { pos: start, end, @@ -60,7 +65,7 @@ export const strudelTooltip = hoverTooltip( create(view) { let dom = document.createElement('div'); dom.className = 'strudel-tooltip'; - createRoot(dom).render(); + createRoot(dom).render(); return { dom }; }, }; diff --git a/undocumented.json b/undocumented.json index 0d28835d..0a807c90 100644 --- a/undocumented.json +++ b/undocumented.json @@ -1,17 +1,17 @@ { - "/home/felix/projects/strudel/packages/core/fraction.mjs": [ + "/packages/core/fraction.mjs": [ "gcd" ], - "/home/felix/projects/strudel/packages/core/timespan.mjs": [ + "/packages/core/timespan.mjs": [ "TimeSpan" ], - "/home/felix/projects/strudel/packages/core/hap.mjs": [ + "/packages/core/hap.mjs": [ "Hap" ], - "/home/felix/projects/strudel/packages/core/state.mjs": [ + "/packages/core/state.mjs": [ "State" ], - "/home/felix/projects/strudel/packages/core/util.mjs": [ + "/packages/core/util.mjs": [ "isNoteWithOctave", "isNote", "tokenizeNote", @@ -34,23 +34,31 @@ "mapArgs", "numeralArgs", "parseFractional", - "fractionalArgs" + "fractionalArgs", + "splitAt", + "zipWith", + "clamp", + "sol2note" ], - "/home/felix/projects/strudel/packages/core/value.mjs": [ + "/packages/core/value.mjs": [ "unionWithObj", "valued", "Value", "map" ], - "/home/felix/projects/strudel/packages/core/drawLine.mjs": [], - "/home/felix/projects/strudel/packages/core/logger.mjs": [ + "/packages/core/drawLine.mjs": [], + "/packages/core/logger.mjs": [ "logKey", "logger" ], - "/home/felix/projects/strudel/packages/core/pattern.mjs": [ + "/packages/core/pattern.mjs": [ "setStringParser", + "polyrhythm", + "pr", + "pm", "isPattern", "reify", + "fastcat", "set", "keep", "keepif", @@ -74,22 +82,33 @@ "func", "compressSpan", "compressspan", + "fastgap", "focusSpan", "focusspan", + "density", + "sparsity", "zoomArc", "zoomarc", + "inv", + "juxby", + "echowith", + "stutWith", + "stutwith", + "iterback", + "chunkback", "bypass", "duration", - "color", "colour", - "striate" + "loopat", + "loopatcps" ], - "/home/felix/projects/strudel/packages/core/controls.mjs": [], - "/home/felix/projects/strudel/packages/core/euclid.mjs": [ + "/packages/core/controls.mjs": [], + "/packages/core/euclid.mjs": [ + "bjork", "euclidrot", "euclidLegatoRot" ], - "/home/felix/projects/strudel/packages/core/signal.mjs": [ + "/packages/core/signal.mjs": [ "steady", "signal", "isaw", @@ -112,34 +131,37 @@ "degradeByWith", "undegrade" ], - "/home/felix/projects/strudel/packages/core/speak.mjs": [ + "/packages/core/speak.mjs": [ "speak" ], - "/home/felix/projects/strudel/packages/core/evaluate.mjs": [ + "/packages/core/evaluate.mjs": [ "evalScope", "evaluate" ], - "/home/felix/projects/strudel/packages/core/zyklus.mjs": [], - "/home/felix/projects/strudel/packages/core/cyclist.mjs": [ + "/packages/core/zyklus.mjs": [], + "/packages/core/cyclist.mjs": [ "Cyclist" ], - "/home/felix/projects/strudel/packages/core/time.mjs": [ + "/packages/core/time.mjs": [ "getTime", "setTime" ], - "/home/felix/projects/strudel/packages/core/repl.mjs": [ - "repl" + "/packages/core/repl.mjs": [ + "repl", + "getTrigger" ], - "/home/felix/projects/strudel/packages/core/draw.mjs": [ + "/packages/core/draw.mjs": [ "getDrawContext", - "cleanupDraw" + "cleanupDraw", + "Framer", + "Drawer" ], - "/home/felix/projects/strudel/packages/core/animate.mjs": [ + "/packages/core/animate.mjs": [ "x", "y", "w", "h", - "a", + "angle", "r", "fill", "smear", @@ -147,85 +169,121 @@ "moveXY", "zoomIn" ], - "/home/felix/projects/strudel/packages/core/pianoroll.mjs": [ - "pianoroll" + "/packages/core/pianoroll.mjs": [ + "getDrawOptions", + "drawPianoroll" ], - "/home/felix/projects/strudel/packages/core/ui.mjs": [ + "/packages/core/spiral.mjs": [], + "/packages/core/ui.mjs": [ "backgroundImage", "cleanupUi" ], - "/home/felix/projects/strudel/packages/core/gist.js": [], - "/home/felix/projects/strudel/packages/core/index.mjs": [], - "/home/felix/projects/strudel/packages/midi/midi.mjs": [ + "/packages/core/gist.js": [], + "/packages/core/index.mjs": [], + "/packages/csound/index.mjs": [ + "loadCSound", + "loadcsound", + "loadCsound", + "csound", + "loadOrc" + ], + "/packages/desktopbridge/utils.mjs": [ + "Invoke", + "isTauri" + ], + "/packages/desktopbridge/midibridge.mjs": [], + "/packages/desktopbridge/oscbridge.mjs": [], + "/packages/desktopbridge/index.mjs": [], + "/packages/midi/midi.mjs": [ "WebMidi", - "enableWebMidi" + "enableWebMidi", + "midin" ], - "/home/felix/projects/strudel/packages/midi/index.mjs": [], - "/home/felix/projects/strudel/packages/mini/krill-parser.js": [], - "/home/felix/projects/strudel/packages/mini/mini.mjs": [ + "/packages/midi/index.mjs": [], + "/packages/mini/krill-parser.js": [], + "/packages/mini/mini.mjs": [ "patternifyAST", + "getLeafLocation", + "mini2ast", + "getLeaves", + "getLeafLocations", "mini", + "m", "h", - "minify" + "minify", + "miniAllStrings" ], - "/home/felix/projects/strudel/packages/mini/index.mjs": [], - "/home/felix/projects/strudel/packages/soundfonts/fontloader.mjs": [ + "/packages/mini/index.mjs": [], + "/packages/soundfonts/gm.mjs": [], + "/packages/soundfonts/fontloader.mjs": [ "getFontBufferSource", - "getFontPitch" + "getFontPitch", + "registerSoundfonts" ], - "/home/felix/projects/strudel/packages/soundfonts/list.mjs": [ + "/packages/soundfonts/list.mjs": [ "instruments", "drums", "instrumentNames" ], - "/home/felix/projects/strudel/packages/soundfonts/sfumato.mjs": [ + "/packages/soundfonts/sfumato.mjs": [ "loadSoundfont" ], - "/home/felix/projects/strudel/packages/soundfonts/index.mjs": [], - "/home/felix/projects/strudel/packages/tonal/tonal.mjs": [], - "/home/felix/projects/strudel/packages/tonal/voicings.mjs": [ - "voicingRegistry", - "setVoicingRange" + "/packages/soundfonts/index.mjs": [], + "/packages/tonal/tonal.mjs": [], + "/packages/tonal/tonleiter.mjs": [ + "pc2chroma", + "rotateChroma", + "chroma2pc", + "tokenizeChord", + "note2pc", + "note2oct", + "note2chroma", + "midi2chroma", + "pitch2chroma", + "step2semitones", + "x2midi", + "scaleStep", + "renderVoicing", + "accidentalOffset", + "Step", + "Note" ], - "/home/felix/projects/strudel/packages/tonal/index.mjs": [], - "/home/felix/projects/strudel/packages/transpiler/transpiler.mjs": [ + "/packages/tonal/ireal.mjs": [ + "simple", + "complex" + ], + "/packages/tonal/voicings.mjs": [ + "voicingRegistry", + "setVoicingRange", + "registerVoicings", + "voicingAlias" + ], + "/packages/tonal/index.mjs": [], + "/packages/transpiler/transpiler.mjs": [ "transpiler" ], - "/home/felix/projects/strudel/packages/transpiler/index.mjs": [ + "/packages/transpiler/index.mjs": [ "evaluate" ], - "/home/felix/projects/strudel/packages/webaudio/feedbackdelay.mjs": [], - "/home/felix/projects/strudel/packages/webaudio/reverb.mjs": [], - "/home/felix/projects/strudel/packages/webaudio/sampler.mjs": [ - "getCachedBuffer", - "getSampleBufferSource", - "loadBuffer", - "reverseBuffer", - "getLoadedBuffer", - "resetLoadedSamples", - "getLoadedSamples" - ], - "/home/felix/projects/strudel/packages/webaudio/vowel.mjs": [ - "vowelFormant" - ], - "/home/felix/projects/strudel/packages/webaudio/webaudio.mjs": [ - "getAudioContext", - "panic", - "initAudio", - "initAudioOnFirstClick", + "/packages/webaudio/webaudio.mjs": [ + "webaudioOutputTrigger", "webaudioOutput", - "webaudioOutputTrigger" + "webaudioScheduler" ], - "/home/felix/projects/strudel/packages/webaudio/index.mjs": [], - "/home/felix/projects/strudel/packages/xen/xen.mjs": [ + "/packages/webaudio/scope.mjs": [ + "drawTimeScope", + "drawFrequencyScope" + ], + "/packages/webaudio/index.mjs": [], + "/packages/xen/xen.mjs": [ "edo", "xen", "tuning" ], - "/home/felix/projects/strudel/packages/xen/tunejs.js": [], - "/home/felix/projects/strudel/packages/xen/tune.mjs": [ + "/packages/xen/tunejs.js": [], + "/packages/xen/tune.mjs": [ "tune" ], - "/home/felix/projects/strudel/packages/xen/index.mjs": [], - "/home/felix/projects/strudel/index.mjs": [] + "/packages/xen/index.mjs": [], + "/index.mjs": [] } diff --git a/website/src/docs/JsDoc.jsx b/website/src/docs/JsDoc.jsx index 1ba80e0a..1a7a034f 100644 --- a/website/src/docs/JsDoc.jsx +++ b/website/src/docs/JsDoc.jsx @@ -2,19 +2,15 @@ import jsdoc from '../../../doc.json'; // doc.json is built with `npm run jsdoc- const docs = jsdoc.docs.reduce((acc, obj) => Object.assign(acc, { [obj.longname]: obj }), {}); import { MiniRepl } from './MiniRepl'; -const getTag = (title, item) => item.tags?.find((t) => t.title === title)?.text; - export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight }) { const item = docs[name]; if (!item) { console.warn('Not found: ' + name); return
    ; } - const synonyms = getTag('synonyms', item)?.split(', ') || []; const CustomHeading = `h${h}`; const description = item.description?.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => { - // console.log(_, 'a', a, 'b', b); return `${a}${b ? `#${b}` : ''}`; }) || ''; return ( @@ -22,9 +18,9 @@ export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight }) {!!h && {item.longname}} {!hideDescription && ( <> - {!!synonyms.length && ( + {!!item.synonyms_text && ( - Synonyms: {synonyms.join(', ')} + Synonyms: {item.synonyms_text} )}
    diff --git a/website/src/repl/Reference.jsx b/website/src/repl/Reference.jsx index cf6fd5b1..b43f365e 100644 --- a/website/src/repl/Reference.jsx +++ b/website/src/repl/Reference.jsx @@ -37,6 +37,11 @@ export function Reference() { {visibleFunctions.map((entry, i) => (

    {entry.name}

    + {!!entry.synonyms_text && ( +

    + Synonyms: {entry.synonyms_text} +

    + )} {/* {entry.meta.filename} */}