diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 2de1353d15..2b0a4dd912 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -51,7 +51,8 @@ class Editor extends Plugin { rs: 'rust', cairo: 'cairo', ts: 'typescript', - move: 'move' + move: 'move', + circom: 'circom' } this.activated = false diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index fe39eed3e8..373d28be1b 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -17,6 +17,7 @@ import { RemixHighLightProvider } from './providers/highlightProvider' import { RemixDefinitionProvider } from './providers/definitionProvider' import { RemixCodeActionProvider } from './providers/codeActionProvider' import './remix-ui-editor.css' +import { circomLanguageConfig, circomTokensProvider } from './syntaxes/circom' enum MarkerSeverity { @@ -322,6 +323,8 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') } else if (file.language === 'move') { monacoRef.current.editor.setModelLanguage(file.model, 'remix-move') + } else if (file.language === 'circom') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-circom') } }, [props.currentFile]) @@ -731,6 +734,7 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.languages.register({ id: 'remix-cairo' }) monacoRef.current.languages.register({ id: 'remix-zokrates' }) monacoRef.current.languages.register({ id: 'remix-move' }) + monacoRef.current.languages.register({ id: 'remix-circom' }) // Register a tokens provider for the language monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) @@ -745,6 +749,9 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.languages.setMonarchTokensProvider('remix-move', moveTokenProvider as any) monacoRef.current.languages.setLanguageConfiguration('remix-move', moveLanguageConfig as any) + monacoRef.current.languages.setMonarchTokensProvider('remix-circom', circomTokensProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-circom', circomLanguageConfig(monacoRef.current) as any) + monacoRef.current.languages.registerDefinitionProvider('remix-solidity', new RemixDefinitionProvider(props, monaco)) monacoRef.current.languages.registerDocumentHighlightProvider('remix-solidity', new RemixHighLightProvider(props, monaco)) monacoRef.current.languages.registerReferenceProvider('remix-solidity', new RemixReferenceProvider(props, monaco)) diff --git a/libs/remix-ui/editor/src/lib/syntaxes/circom.ts b/libs/remix-ui/editor/src/lib/syntaxes/circom.ts new file mode 100644 index 0000000000..6b09887c3a --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntaxes/circom.ts @@ -0,0 +1,186 @@ +/* eslint-disable */ +export const circomLanguageConfig = (monaco) => ({ + wordPattern: + /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, + + comments: { + lineComment: "//", + blockComment: ["/*", "*/"], + }, + + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ], + + onEnterRules: [ + { + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { + indentAction: monaco.languages.IndentAction.IndentOutdent, + appendText: " * ", + }, + }, + { + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + action: { + indentAction: monaco.languages.IndentAction.None, + appendText: " * ", + }, + }, + { + beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { + indentAction: monaco.languages.IndentAction.None, + appendText: "* ", + }, + }, + { + beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, + action: { + indentAction: monaco.languages.IndentAction.None, + removeText: 1, + }, + }, +], + + autoClosingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: '"', close: '"', notIn: ["string"] }, + { open: "'", close: "'", notIn: ["string", "comment"] }, + { open: "`", close: "`", notIn: ["string", "comment"] }, + { open: "/**", close: " */", notIn: ["string"] }, + ], + + folding: { + markers: { + start: new RegExp("^\\s*//\\s*#?region\\b"), + end: new RegExp("^\\s*//\\s*#?endregion\\b"), + }, + }, +}) + +export const circomTokensProvider = { + defaultToken: "", + tokenPostfix: ".circom", + + keywords: [ + "signal", + "input", + "output", + "public", + "template", + "component", + "parallel", + "custom", + "var", + "function", + "return", + "if", + "else", + "for", + "while", + "do", + "log", + "assert", + "include", + "pragma", + ], + + typeKeywords: ["input", "output", "public"], + + operators: [ + "!", + "~", + "-", + "||", + "&&", + "==", + "!=", + "<", + ">", + "<=", + ">=", + "|", + "&", + "<<", + ">>", + "+", + "-", + "*", + "/", + "\\", + "%", + "**", + "^", + "=", + "<--", + "<==", + ], + + escapes: + /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/, + + + tokenizer: { + root: [ + [ + /[a-z_$][\w$]*/, + { + cases: { + "@typeKeywords": "keyword", + "@keywords": "keyword", + "@default": "identifier", + }, + }, + ], + [/[A-Z][\w\$]*/, "type.identifier"], + + { include: "@whitespace" }, + + [/[{}()\[\]]/, "@brackets"], + + [ + /@\s*[a-zA-Z_\$][\w\$]*/, + { token: "annotation", log: "annotation token: $0" }, + ], + + [/\d*\.\d+([eE][\-+]?\d+)?/, "number.float"], + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+/, "number"], + + [/[;,.]/, "delimiter"], + + [/"([^"\\]|\\.)*$/, "string.invalid"], + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + + [/'[^\\']'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], + + comment: [ + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], + ["\\*/", "comment", "@pop"], + [/[\/*]/, "comment"], + ], + + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + }, +} diff --git a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts index e06253c325..21925ed8f8 100644 --- a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts +++ b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts @@ -80,7 +80,8 @@ export const getPathIcon = (path: string) => { ? 'fak fa-lexon' : path.endsWith('ts') ? 'small fak fa-ts-logo' : path.endsWith('.tsc') ? 'fad fa-brackets-curly' : path.endsWith('.cairo') - ? 'small fak fa-cairo' : 'far fa-file' + ? 'small fak fa-cairo' : path.endsWith('.circom') + ? 'fak fa-circom-plug1' : 'far fa-file' } export const isNumeric = (value) => {