diff --git a/package-lock.json b/package-lock.json index e3b2b0b..977c299 100644 --- a/package-lock.json +++ b/package-lock.json @@ -467,22 +467,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -1409,16 +1409,17 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -2160,9 +2161,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", @@ -2766,9 +2767,9 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -3244,9 +3245,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/vite": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", - "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz", + "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", "dev": true, "dependencies": { "esbuild": "^0.18.10", diff --git a/src/components/editor/monaco/chuck-lang.ts b/src/components/editor/monaco/chuck-lang.ts new file mode 100644 index 0000000..895c558 --- /dev/null +++ b/src/components/editor/monaco/chuck-lang.ts @@ -0,0 +1,390 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment + +import { monaco } from "./monacoLite"; +import { chuck_modules, chuck_libraries } from "./chuck-modules"; +import ckdocJSON from "./ckdoc.json"; + +// Documentation Type for ckdoc +interface docType { + title: string; + description: string; + constructors: string[]; + functions: string[]; + examples: string[]; + link: string; +} +const ckdoc: { [key: string]: docType } = ckdocJSON; + +// Register a new language for Monaco +monaco.languages.register({ id: "chuck" }); + +// Register a tokens provider for the language +monaco.languages.setMonarchTokensProvider("chuck", { + // Set defaultToken to invalid to see what you do not tokenize yet + // defaultToken: 'invalid', + + keywords: [ + "if", + "else", + "while", + "until", + "for", + "repeat", + "break", + "continue", + "return", + "switch", + "class", + "extends", + "public", + "static", + "pure", + "this", + "super", + "interface", + "implements", + "protected", + "private", + "function", + "fun", + "spork", + "new", + "const", + "global", + "now", + "true", + "false", + "maybe", + "null", + "NULL", + "me", + "pi", + "samp", + "ms", + "second", + "minute", + "hour", + "day", + "week", + "eon", + "dac", + "adc", + "blackhole", + "bunghole", + ], + + typeKeywords: [ + "int", + "float", + "time", + "dur", + "void", + "vec3", + "vec4", + "complex", + "polar", + "string", + ], + + library: ["Object", "Event", "Shred", "Math", "Machine", "Std"], + + operators: [ + "++", + "--", + ":", + "+", + "-", + "*", + "/", + "%", + "::", + "==", + "!=", + "<", + ">", + "<=", + ">=", + "&&", + "||", + "&", + "|", + "^", + ">>", + "<<", + "=", + "?", + "!", + "~", + "<<<", + ">>>", + "=>", + "!=>", + "=^", + "=v", + "@=>", + "+=>", + "-=>", + "*=>", + "/=>", + "&=>", + "|=>", + "^=>", + ">>=>", + "<<=>", + "%=>", + "@", + "@@", + "->", + "<-", + ], + + ugens: chuck_modules, + + // We include these common regular expressions + // eslint-disable-next-line no-useless-escape + symbols: /[=>](?!@symbols)/, "@brackets"], + [ + /@symbols/, + { + cases: { + "@operators": "operator", + "@default": "", + }, + }, + ], + + // @ annotations. + // As an example, we emit a debugging log message on these tokens. + // Note: message are supressed during the first load -- change some lines to see them. + [/@\s*[a-zA-Z_$][\w$]*/, { token: "annotation" }], + + // numbers + [/\d*\.\d+([eE][-+]?\d+)?/, "number.float"], + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+/, "number"], + + // delimiter: after number because of .\d floats + [/[;,.]/, "delimiter"], + + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + + // characters + [/'[^\\']'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], + + comment: [ + // eslint-disable-next-line + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "comment", "@pop"], + // eslint-disable-next-line + [/[\/*]/, "comment"], + ], + + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + }, +}); + +// Register comment support for the new language +monaco.languages.setLanguageConfiguration("chuck", { + comments: { + lineComment: "//", + blockComment: ["/*", "*/"], + }, +}); + +// Register a completion item provider for the new language +monaco.languages.registerCompletionItemProvider("chuck", { + provideCompletionItems: (model, position) => { + const word = model.getWordUntilPosition(position); + const range = { + startLineNumber: position.lineNumber, + endLineNumber: position.lineNumber, + startColumn: word.startColumn, + endColumn: word.endColumn, + }; + + const textUntilPosition = model.getValueInRange({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column, + }); + + const statement_suggestions = [ + { + label: "random", + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: "Math.random2f(${1:condition})", + insertTextRules: + monaco.languages.CompletionItemInsertTextRule + .InsertAsSnippet, + range: range, + }, + { + label: "ifelse", + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: [ + "if (${1:condition}) {", + "\t$0", + "} else {", + "\t", + "}", + ].join("\n"), + insertTextRules: + monaco.languages.CompletionItemInsertTextRule + .InsertAsSnippet, + documentation: "If-Else Statement", + range: range, + }, + { + label: "for", + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: [ + "for (0 => int i; i < ${1:condition}; ++i)", + "{", + "\t$0", + "}", + ].join("\n"), + insertTextRules: + monaco.languages.CompletionItemInsertTextRule + .InsertAsSnippet, + documentation: "For Statement", + range: range, + }, + { + label: "while", + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: ["while (${1:condition})", "{", "\t$0", "}"].join( + "\n" + ), + insertTextRules: + monaco.languages.CompletionItemInsertTextRule + .InsertAsSnippet, + documentation: "While Statement", + range: range, + }, + ]; + + const words = textUntilPosition + .split(/\W+/) + .concat(chuck_modules) + .filter( + (word) => + !statement_suggestions.map((s) => s.label).includes(word) + ); + const uniqueWords = Array.from(new Set(words)); + const word_suggestions = uniqueWords.map((word) => ({ + label: word, + kind: monaco.languages.CompletionItemKind.Text, + insertText: word, + range: range, + })); + + const suggestions = word_suggestions.concat(statement_suggestions); + return { suggestions: suggestions }; + }, +}); + +// Register a hover provider for the new language +monaco.languages.registerHoverProvider("chuck", { + provideHover: function (model, position) { + // Get the word at current mouse position + const word: monaco.editor.IWordAtPosition = + model.getWordAtPosition(position)!; // ! TS check that word is not null + const token: string = word?.word; + + // If we have a hover for that word + // if (chuck_modules.includes(token)) { + if (ckdoc[token]) { + const word_doc: docType = ckdoc[token]; + return { + // Where to show the hover + range: new monaco.Range( + position.lineNumber, + word.startColumn, + position.lineNumber, + word.endColumn + ), + // Hover contents + contents: [ + { + value: word_doc.title, + }, + { + value: word_doc.description, + }, + { + value: word_doc.constructors.join("\n\n"), + }, + { + value: word_doc.functions.join("\n\n"), + }, + { + value: word_doc.examples.join("\n\n"), + }, + { + value: word_doc.link, + }, + ], + }; + } + + // If we don't have a hover + return null; + }, +}); + +export const editorConfig = monaco.editor.createModel("", "chuck");