diff --git a/apps/solidity-compiler/src/app/compiler-api.ts b/apps/solidity-compiler/src/app/compiler-api.ts index 08bc50888e..74eec1d57f 100644 --- a/apps/solidity-compiler/src/app/compiler-api.ts +++ b/apps/solidity-compiler/src/app/compiler-api.ts @@ -222,9 +222,10 @@ export const CompilerApiMixin = (Base) => class extends Base { } this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) - this.data.eventHandlers.onCompilerLoaded = () => { + this.data.eventHandlers.onCompilerLoaded = (version) => { this.data.loading = false this.statusChanged({ key: 'none' }) + this.emit('compilerLoaded', version) } this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts index 8052a1870a..9bcaaf7a7b 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts @@ -38,7 +38,7 @@ const testFiles: string[] = [ 'forLoopIteratesOverDynamicArray.sol' ] -let compilationResults: Record = {} +const compilationResults: Record = {} test('setup', function (t: test.Test) { solcOrg.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, compiler) => { diff --git a/libs/remix-ui/editor/src/lib/cairoSyntax.ts b/libs/remix-ui/editor/src/lib/cairoSyntax.ts deleted file mode 100644 index c467b32a64..0000000000 --- a/libs/remix-ui/editor/src/lib/cairoSyntax.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* eslint-disable */ -export const cairoConf = { - comments: { - lineComment: '#' - }, - brackets: [ - ['{', '}'], - ['[', ']'], - ['(', ')'], - ['%{', '%}'] - ], - autoClosingPairs: [ - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: '%{', close: '%}' }, - { open: "'", close: "'", notIn: ['string', 'comment'] } - ], - surroundingPairs: [ - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: '%{', close: '%}' }, - { open: "'", close: "'" } - ] -} - - -export const cairoLang = { - defaultToken: '', - tokenPostfix: '.cairo', - - brackets: [ - { token: 'delimiter.curly', open: '{', close: '}' }, - { token: 'delimiter.parenthesis', open: '(', close: ')' }, - { token: 'delimiter.square', open: '[', close: ']' }, - { token: 'delimiter.curly', open: '%{', close: '%}' } - ], - - keywords: [ - // control - 'if', - 'else', - 'end', - - // meta - 'alloc_locals', - 'as', - 'assert', - 'cast', - 'const', - 'dw', - 'felt', - 'from', - 'func', - 'import', - 'let', - 'local', - 'member', - 'nondet', - 'return', - 'static_assert', - 'struct', - 'tempvar', - 'with_attr', - 'with', - - // register - 'ap', - 'fp', - - // opcode - 'call', - 'jmp', - 'ret', - 'abs', - 'rel' - ], - - operators: ['=', ':', '==', '++', '+', '-', '*', '**', '/', '&', '%', '_'], - - // we include these common regular expressions - symbols: /[=>](?!@symbols)/, '@brackets'], - [ - /@symbols/, - { - cases: { - '@operators': 'delimiter', - '@default': '' - } - } - ], - - // numbers - [/(@numberHex)/, 'number.hex'], - [/(@numberDecimal)/, 'number'], - - // strings - [/'[^']*'/, 'string'] - ], - - whitespace: [ - [/\s+/, 'white'], - [/(^#.*$)/, 'comment'] - ] - } -} 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 0b5b950180..30e8323faf 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -2,8 +2,9 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint import { RemixUiEditorContextView, astNode } from '@remix-ui/editor-context-view' import Editor, { loader } from '@monaco-editor/react' import { reducerActions, reducerListener, initialState } from './actions/editor' -import { language, conf } from './syntax' -import { cairoLang, cairoConf } from './cairoSyntax' +import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity' +import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo' +import { zokratesTokensProvider, zokratesLanguageConfig } from './syntaxes/zokrates' import './remix-ui-editor.css' import { loadTypes } from './web-types' @@ -255,6 +256,8 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity') } else if (file.language === 'cairo') { monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') + } else if (file.language === 'zokrates') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') } }, [props.currentFile]) @@ -464,12 +467,17 @@ export const EditorUI = (props: EditorUIProps) => { // Register a new language monacoRef.current.languages.register({ id: 'remix-solidity' }) monacoRef.current.languages.register({ id: 'remix-cairo' }) + monacoRef.current.languages.register({ id: 'remix-zokrates' }) + // Register a tokens provider for the language - monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', language) - monacoRef.current.languages.setLanguageConfiguration('remix-solidity', conf) + monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider) + monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig) + + monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoTokensProvider) + monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoLanguageConfig) - monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoLang) - monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoConf) + monacoRef.current.languages.setMonarchTokensProvider('remix-zokrates', zokratesTokensProvider) + monacoRef.current.languages.setLanguageConfiguration('remix-zokrates', zokratesLanguageConfig) loadTypes(monacoRef.current) } diff --git a/libs/remix-ui/editor/src/lib/syntaxes/cairo.ts b/libs/remix-ui/editor/src/lib/syntaxes/cairo.ts new file mode 100644 index 0000000000..f6550946d3 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntaxes/cairo.ts @@ -0,0 +1,132 @@ +/* eslint-disable */ +export const cairoLanguageConfig = { + comments: { + lineComment: "#", + }, + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["%{", "%}"], + ], + autoClosingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: "%{", close: "%}" }, + { open: "'", close: "'", notIn: ["string", "comment"] }, + ], + surroundingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: "%{", close: "%}" }, + { open: "'", close: "'" }, + ], +} + +export const cairoTokensProvider = { + defaultToken: "", + tokenPostfix: ".cairo", + + brackets: [ + { token: "delimiter.curly", open: "{", close: "}" }, + { token: "delimiter.parenthesis", open: "(", close: ")" }, + { token: "delimiter.square", open: "[", close: "]" }, + { token: "delimiter.curly", open: "%{", close: "%}" }, + ], + + keywords: [ + // control + "if", + "else", + "end", + + // meta + "alloc_locals", + "as", + "assert", + "cast", + "const", + "dw", + "felt", + "from", + "func", + "import", + "let", + "local", + "member", + "nondet", + "return", + "static_assert", + "struct", + "tempvar", + "with_attr", + "with", + + // register + "ap", + "fp", + + // opcode + "call", + "jmp", + "ret", + "abs", + "rel", + ], + + operators: ["=", ":", "==", "++", "+", "-", "*", "**", "/", "&", "%", "_"], + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, "@brackets"], + [ + /@symbols/, + { + cases: { + "@operators": "delimiter", + "@default": "", + }, + }, + ], + + // numbers + [/(@numberHex)/, "number.hex"], + [/(@numberDecimal)/, "number"], + + // strings + [/'[^']*'/, "string"], + ], + + whitespace: [ + [/\s+/, "white"], + [/(^#.*$)/, "comment"], + ], + }, +} diff --git a/libs/remix-ui/editor/src/lib/syntax.ts b/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts similarity index 99% rename from libs/remix-ui/editor/src/lib/syntax.ts rename to libs/remix-ui/editor/src/lib/syntaxes/solidity.ts index 121dced50f..dfb388fda4 100644 --- a/libs/remix-ui/editor/src/lib/syntax.ts +++ b/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -export const conf = { +export const solidityLanguageConfig = { comments: { lineComment: '//', blockComment: ['/*', '*/'] @@ -19,7 +19,7 @@ export const conf = { ] } -export const language = { +export const solidityTokensProvider = { defaultToken: '', tokenPostfix: '.sol', diff --git a/libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts b/libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts new file mode 100644 index 0000000000..89a72ff361 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts @@ -0,0 +1,144 @@ +/* eslint-disable */ +export const zokratesLanguageConfig = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'] + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ['<', '>'] + ], + autoClosingPairs: [ + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '[', close: ']', notIn: ['string', 'comment'] }, + { open: '(', close: ')', notIn: ['string', 'comment'] }, + { open: '<', close: '>', notIn: ['string', 'comment'] } + ] +} + +export const zokratesTokensProvider = { + defaultToken: "", + tokenPostfix: ".zok", + + keywords: [ + "log", + "assert", + "as", + "bool", + "const", + "def", + "else", + "false", + "field", + "for", + "if", + "import", + "from", + "in", + "mut", + "private", + "public", + "return", + "struct", + "true", + "type", + "u8", + "u16", + "u32", + "u64", + ], + + typeKeywords: ["bool", "field", "u8", "u16", "u32", "u64"], + + operators: [ + "=", + ">", + "<", + "!", + "?", + ":", + "==", + "<=", + ">=", + "!=", + "&&", + "||", + "+", + "-", + "*", + "**", + "/", + "&", + "|", + "^", + "%", + "<<", + ">>", + ], + + decimalSuffix: /(u16|u32|u64|u8|f)?/, + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, "@brackets"], + [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], + + // numbers + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+(@decimalSuffix)/, "number"], + + // delimiter + [/[;,.]/, "delimiter"], + + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + ], + + comment: [ + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "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/solidity-unit-testing/src/lib/solidity-unit-testing.tsx b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx index 8f55fe2ea7..e580a9448b 100644 --- a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx +++ b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx @@ -1,4 +1,5 @@ import React, { useState, useRef, useEffect, ReactElement } from 'react' // eslint-disable-line +import * as semver from 'semver' import { eachOfSeries } from 'async' // eslint-disable-line import type Web3 from 'web3' import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity' @@ -154,8 +155,23 @@ export const SolidityUnitTesting = (props: Record) => { // eslint-d await setCurrentPath(defaultPath) }) + const truncateVersion = (version: string) => { + const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version) + return tmp ? tmp[1] : version + } + testTab.fileManager.events.on('noFileSelected', async () => { await updateForNewCurrent() }) - testTab.fileManager.events.on('currentFileChanged', async (file: string) => await updateForNewCurrent(file)) + testTab.fileManager.events.on('currentFileChanged', async (file: string) => { + await updateForNewCurrent(file) + }) + testTab.on('solidity', 'compilerLoaded', async (version: string) => { + const { currentVersion } = testTab.compileTab.getCurrentCompilerConfig() + + if (!semver.gt(truncateVersion(currentVersion), '0.4.12')) { + setDisableRunButton(true) + setRunButtonTitle('Please select Solidity compiler version greater than 0.4.12.') + } + }) }, []) // eslint-disable-line diff --git a/libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx b/libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx index 2a67c82cf8..62876bc1c0 100644 --- a/libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx +++ b/libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx @@ -3,16 +3,20 @@ import React from 'react' //eslint-disable-line interface StaticAnalyserButtonProps { onClick: (event) => void buttonText: string, - disabled?: boolean + disabled?: boolean, + title?: string } const StaticAnalyserButton = ({ onClick, buttonText, - disabled + disabled, + title }: StaticAnalyserButtonProps) => { + let classList = "btn btn-sm w-25 btn-primary" + classList += disabled ? " disabled" : "" return ( - ) diff --git a/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx b/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx index cdce6d95d6..5e143dec1f 100644 --- a/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx +++ b/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState, useReducer, useRef } from 'react' // eslint import Button from './Button/StaticAnalyserButton' // eslint-disable-line import { util } from '@remix-project/remix-lib' import _ from 'lodash' +import * as semver from 'semver' import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line import { RemixUiCheckbox } from '@remix-ui/checkbox' // eslint-disable-line import ErrorRenderer from './ErrorRenderer' // eslint-disable-line @@ -64,14 +65,30 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { const [autoRun, setAutoRun] = useState(true) const [slitherEnabled, setSlitherEnabled] = useState(false) const [showSlither, setShowSlither] = useState(false) + const [isSupportedVersion, setIsSupportedVersion] = useState(false) let [showLibsWarning, setShowLibsWarning] = useState(false) // eslint-disable-line prefer-const const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules)) const [warningState, setWarningState] = useState({}) + const [runButtonTitle, setRunButtonTitle] = useState('Run Static Analysis') const warningContainer = useRef(null) const allWarnings = useRef({}) const [state, dispatch] = useReducer(analysisReducer, initialState) + const setDisableForRun = (version: string) => { + const truncateVersion = (version: string) => { + const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version) + return tmp ? tmp[1] : version + } + if (version != '' && !semver.gt(truncateVersion(version), '0.4.12')) { + setIsSupportedVersion(false) + setRunButtonTitle('Sselect Solidity compiler version greater than 0.4.12.') + } else { + setIsSupportedVersion(true) + setRunButtonTitle('Run static analysis') + } + } + useEffect(() => { compilation(props.analysisModule, dispatch) }, [props]) @@ -91,6 +108,10 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { return () => { } }, [state]) + useEffect(() => { + props.analysisModule.call('solidity', 'getCompilerState').then((compilerState) => setDisableForRun(compilerState.currentVersion)) + }, []) + useEffect(() => { props.analysisModule.on('filePanel', 'setWorkspace', (currentWorkspace) => { // Reset warning state @@ -119,20 +140,24 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { setSlitherEnabled(false) } }) + + props.analysisModule.on('solidity', 'compilerLoaded', async (version: string) => { + setDisableForRun(version) + }) return () => { } }, [props]) const message = (name, warning, more, fileName, locationString) : string => { return (` - - ${name} - ${warning} - ${more - ? (more) - : ( ) - } - Pos: ${locationString} - ` + + ${name} + ${warning} + ${more + ? (more) + : ( ) + } + Pos: ${locationString} + ` ) } @@ -183,6 +208,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { } const run = async (lastCompilationResult, lastCompilationSource, currentFile) => { + if (!isSupportedVersion) return if (state.data !== null) { if (lastCompilationResult && (categoryIndex.length > 0 || slitherEnabled)) { const warningMessage = [] @@ -474,7 +500,12 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { label="Autorun" onChange={() => {}} /> -