diff --git a/apps/remix-ide-e2e/src/tests/url.spec.ts b/apps/remix-ide-e2e/src/tests/url.spec.ts
index 13227238f7..56a94e34a9 100644
--- a/apps/remix-ide-e2e/src/tests/url.spec.ts
+++ b/apps/remix-ide-e2e/src/tests/url.spec.ts
@@ -104,6 +104,21 @@ module.exports = {
.verify.elementPresent('#runs:disabled')
.click('[for="optimize"')
.verify.attributeEquals('#runs', 'value', '200')
+ },
+
+ 'Should load json files from link passed in remix URL': function (browser: NightwatchBrowser) {
+ browser
+ .url('http://localhost:8080/#optimize=false&runs=200&evmVersion=null&version=soljson-v0.6.12+commit.27d51765.js&url=https://raw.githubusercontent.com/EthVM/evm-source-verification/main/contracts/1/0x011e5846975c6463a8c6337eecf3cbf64e328884/input.json')
+ .refresh()
+ .pause(5000)
+ .waitForElementPresent('*[data-id="workspacesSelect"] option[value="code-sample"]')
+ .openFile('@openzeppelin')
+ .openFile('@openzeppelin/contracts')
+ .openFile('@openzeppelin/contracts/access')
+ .openFile('@openzeppelin/contracts/access/AccessControl.sol')
+ .openFile('contracts')
+ .openFile('contracts/governance')
+ .openFile('contracts/governance/UnionGovernor.sol')
.end()
}
}
diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js
index e7887df7e2..dcbe03bcc7 100644
--- a/apps/remix-ide/src/app/editor/editor.js
+++ b/apps/remix-ide/src/app/editor/editor.js
@@ -28,8 +28,6 @@ class Editor extends Plugin {
// Init
this.event = new EventManager()
this.sessions = {}
- this.sourceAnnotationsPerFile = {}
- this.markerPerFile = {}
this.readOnlySessions = {}
this.previousInput = ''
this.saveTimeout = null
@@ -74,8 +72,6 @@ class Editor extends Plugin {
editorAPI={state.api}
themeType={state.currentThemeType}
currentFile={state.currentFile}
- sourceAnnotationsPerFile={state.sourceAnnotationsPerFile}
- markerPerFile={state.markerPerFile}
events={state.events}
plugin={state.plugin}
/>
@@ -108,6 +104,10 @@ class Editor extends Plugin {
}
this.ref.gotoLine = (line, column) => this.gotoLine(line, column || 0)
this.ref.getCursorPosition = () => this.getCursorPosition()
+ this.ref.addMarkerPerFile = (marker, filePath) => this.addMarkerPerFile(marker, filePath)
+ this.ref.addSourceAnnotationsPerFile = (annotation, filePath) => this.addSourceAnnotationsPerFile(annotation, filePath)
+ this.ref.clearDecorationsByPlugin = (filePath, plugin, typeOfDecoration) => this.clearDecorationsByPlugin(filePath, plugin, typeOfDecoration)
+ this.ref.keepDecorationsFor = (name, typeOfDecoration) => this.keepDecorationsFor(name, typeOfDecoration)
}} id='editorView'>
@@ -118,8 +118,6 @@ class Editor extends Plugin {
api: this.api,
currentThemeType: this.currentThemeType,
currentFile: this.currentFile,
- sourceAnnotationsPerFile: this.sourceAnnotationsPerFile,
- markerPerFile: this.markerPerFile,
events: this.events,
plugin: this
})
@@ -410,27 +408,12 @@ class Editor extends Plugin {
if (filePath && !this.sessions[filePath]) throw new Error('file not found' + filePath)
const path = filePath || this.currentFile
- const currentAnnotations = this[typeOfDecoration][path]
- if (!currentAnnotations) return
-
- const newAnnotations = []
- for (const annotation of currentAnnotations) {
- if (annotation.from !== plugin) newAnnotations.push(annotation)
- }
-
- this[typeOfDecoration][path] = newAnnotations
- this.renderComponent()
+ this.api.clearDecorationsByPlugin(path, plugin, typeOfDecoration)
}
- keepDecorationsFor (name, typeOfDecoration) {
+ keepDecorationsFor (plugin, typeOfDecoration) {
if (!this.currentFile) return
- if (!this[typeOfDecoration][this.currentFile]) return
-
- const annotations = this[typeOfDecoration][this.currentFile]
- for (const annotation of annotations) {
- annotation.hide = annotation.from !== name
- }
- this.renderComponent()
+ this.api.keepDecorationsFor(this.currentFile, plugin, typeOfDecoration)
}
/**
@@ -473,10 +456,16 @@ class Editor extends Plugin {
const path = filePath || this.currentFile
const { from } = this.currentRequest
- if (!this[typeOfDecoration][path]) this[typeOfDecoration][path] = []
decoration.from = from
- this[typeOfDecoration][path].push(decoration)
- this.renderComponent()
+
+ if (typeOfDecoration === 'markerPerFile') {
+ this.api.addMarkerPerFile(decoration, path)
+ return
+ }
+ if (typeOfDecoration === 'sourceAnnotationsPerFile') {
+ this.api.addSourceAnnotationsPerFile(decoration, path)
+ return
+ }
}
/**
diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts
index 5ac6f8dd30..9c11b5863d 100644
--- a/apps/remix-ide/src/app/files/fileManager.ts
+++ b/apps/remix-ide/src/app/files/fileManager.ts
@@ -752,7 +752,7 @@ class FileManager extends Plugin {
if (provider) {
try{
const content = await provider.get(currentFile)
- this.editor.setText(content)
+ if(content) this.editor.setText(content)
}catch(error){
console.log(error)
}
diff --git a/apps/remix-ide/src/app/files/remixDProvider.js b/apps/remix-ide/src/app/files/remixDProvider.js
index a36b6d8ec9..c52055e04f 100644
--- a/apps/remix-ide/src/app/files/remixDProvider.js
+++ b/apps/remix-ide/src/app/files/remixDProvider.js
@@ -98,20 +98,19 @@ module.exports = class RemixDProvider extends FileProvider {
})
}
- get (path, cb) {
+ async get (path, cb) {
if (!this._isReady) return cb && cb('provider not ready')
var unprefixedpath = this.removePrefix(path)
- this._appManager.call('remixd', 'get', { path: unprefixedpath })
- .then((file) => {
- this.filesContent[path] = file.content
- if (file.readonly) { this._readOnlyFiles[path] = 1 }
- cb(null, file.content)
- }).catch((error) => {
- if (error) console.log(error)
- // display the last known content.
- // TODO should perhaps better warn the user that the file is not synced.
- return cb(null, this.filesContent[path])
- })
+ try{
+ const file = await this._appManager.call('remixd', 'get', { path: unprefixedpath })
+ this.filesContent[path] = file.content
+ if (file.readonly) { this._readOnlyFiles[path] = 1 }
+ if(cb) cb(null, file.content)
+ return file.content
+ } catch(error) {
+ if (error) console.log(error)
+ if(cb) return cb(null, this.filesContent[path])
+ }
}
async set (path, content, cb) {
diff --git a/apps/remix-ide/src/app/tabs/settings-tab.tsx b/apps/remix-ide/src/app/tabs/settings-tab.tsx
index 25ea5fb934..8440ffbfa7 100644
--- a/apps/remix-ide/src/app/tabs/settings-tab.tsx
+++ b/apps/remix-ide/src/app/tabs/settings-tab.tsx
@@ -47,11 +47,9 @@ module.exports = class SettingsTab extends ViewPlugin {
}
render() {
- return (
-
+ return
- );
}
updateComponent(state: any){
diff --git a/apps/remix-ide/src/app/tabs/test-tab.js b/apps/remix-ide/src/app/tabs/test-tab.js
index c2af5ce7a7..7f634bd9d1 100644
--- a/apps/remix-ide/src/app/tabs/test-tab.js
+++ b/apps/remix-ide/src/app/tabs/test-tab.js
@@ -89,12 +89,12 @@ module.exports = class TestTab extends ViewPlugin {
this.createTestLibs()
})
- this.testRunner.event.on('compilationFinished', (success, data, source) => {
+ this.testRunner.event.on('compilationFinished', (success, data, source, input, version) => {
if (success) {
this.allFilesInvolved.push(...Object.keys(data.sources))
// forwarding the event to the appManager infra
// This is listened by compilerArtefacts to show data while debugging
- this.emit('compilationFinished', source.target, source, 'soljson', data)
+ this.emit('compilationFinished', source.target, source, 'soljson', data, input, version)
}
})
}
diff --git a/apps/solidity-compiler/src/app/compiler-api.ts b/apps/solidity-compiler/src/app/compiler-api.ts
index abfe0b377b..174d40f910 100644
--- a/apps/solidity-compiler/src/app/compiler-api.ts
+++ b/apps/solidity-compiler/src/app/compiler-api.ts
@@ -261,11 +261,11 @@ export const CompilerApiMixin = (Base) => class extends Base {
this.on('fileManager', 'fileClosed', this.data.eventHandlers.onFileClosed)
- this.data.eventHandlers.onCompilationFinished = (success, data, source) => {
+ this.data.eventHandlers.onCompilationFinished = (success, data, source, input, version) => {
this.compileErrors = data
if (success) {
// forwarding the event to the appManager infra
- this.emit('compilationFinished', source.target, source, 'soljson', data)
+ this.emit('compilationFinished', source.target, source, 'soljson', data, input, version)
if (data.errors && data.errors.length > 0) {
this.statusChanged({
key: data.errors.length,
diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
index d35fa36141..96fe7358e5 100644
--- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
+++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
@@ -24,12 +24,12 @@ export class CompilerArtefacts extends Plugin {
}
onActivation () {
- const saveCompilationPerFileResult = (file, source, languageVersion, data) => {
- this.compilersArtefactsPerFile[file] = new CompilerAbstract(languageVersion, data, source)
+ const saveCompilationPerFileResult = (file, source, languageVersion, data, input?) => {
+ this.compilersArtefactsPerFile[file] = new CompilerAbstract(languageVersion, data, source, input)
}
- this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
- this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
+ this.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => {
+ this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source, input)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
@@ -48,16 +48,24 @@ export class CompilerArtefacts extends Plugin {
saveCompilationPerFileResult(file, source, languageVersion, data)
})
- this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data) => {
- this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
- saveCompilationPerFileResult(file, source, languageVersion, data)
+ this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data, input, version) => {
+ this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source, input)
+ saveCompilationPerFileResult(file, source, languageVersion, data, input)
})
}
-
+
+ /**
+ * Get artefacts for last compiled contract
+ * * @returns last compiled contract compiler abstract
+ */
getLastCompilationResult () {
return this.compilersArtefacts.__last
}
+ /**
+ * Get compilation output for contracts compiled during a session of Remix IDE
+ * @returns compilatin output
+ */
getAllContractDatas () {
const contractsData = {}
Object.keys(this.compilersArtefactsPerFile).map((targetFile) => {
@@ -72,51 +80,85 @@ export class CompilerArtefacts extends Plugin {
return contractsData
}
- async getArtefactsFromFE (path, contractName) {
+ /**
+ * Get a particular contract output/artefacts from a compiler output of a Solidity file compilation
+ * @param compilerOutput compiler output
+ * @param contractName contract name
+ * @returns arefacts object, with fully qualified name (e.g; contracts/1_Storage.sol:Storage) as key
+ */
+ _getAllContractArtefactsfromOutput (compilerOutput, contractName) {
+ const contractArtefacts = {}
+ for (const filename in compilerOutput) {
+ if(Object.keys(compilerOutput[filename]).includes(contractName)) contractArtefacts[filename + ':' + contractName] = compilerOutput[filename][contractName]
+ }
+ return contractArtefacts
+ }
+
+ /**
+ * Populate resultant object with a particular contract output/artefacts by processing all the artifacts stored in file explorer
+ * @param path path to start looking from
+ * @param contractName contract to be looked for
+ * @param contractArtefacts populated resultant artefacts object, with fully qualified name (e.g: contracts/1_Storage.sol:Storage) as key
+ * Once method execution completes, contractArtefacts object will hold all possible artefacts for contract
+ */
+ async _populateAllContractArtefactsFromFE (path, contractName, contractArtefacts) {
const dirList = await this.call('fileManager', 'dirList', path)
if(dirList && dirList.length) {
- if(dirList.includes(path + '/artifacts')) {
- const fileList = await this.call('fileManager', 'fileList', path + '/artifacts')
- const artefactsFilePaths = fileList.filter(filePath => {
- const filenameArr = filePath.split('/')
- const filename = filenameArr[filenameArr.length - 1]
- if (filename === `${contractName}.json` || filename === `${contractName}_metadata.json`) return true
- })
- if (artefactsFilePaths && artefactsFilePaths.length) {
- const content = await this.call('fileManager', 'readFile', artefactsFilePaths[1])
- const artifacts = JSON.parse(content)
- return { abi: artifacts.abi, bytecode: artifacts.data.bytecode.object }
- } else {
- for (const dirPath of dirList) {
- const result = await this.getArtefactsFromFE (dirPath, contractName)
- if (result) return result
+ for (const dirPath of dirList) {
+ // check if directory contains an 'artifacts' folder and a 'build-info' folder inside 'artifacts'
+ if(dirPath === path + '/artifacts' && await this.call('fileManager', 'exists', dirPath + '/build-info')) {
+ const buildFileList = await this.call('fileManager', 'fileList', dirPath + '/build-info')
+ // process each build-info file to populate the artefacts for contractName
+ for (const buildFile of buildFileList) {
+ let content = await this.call('fileManager', 'readFile', buildFile)
+ if (content) content = JSON.parse(content)
+ const compilerOutput = content.output.contracts
+ const artefacts = this._getAllContractArtefactsfromOutput(compilerOutput, contractName)
+ // populate the resultant object with artefacts
+ Object.assign(contractArtefacts, artefacts)
}
- }
- } else {
- for (const dirPath of dirList) {
- const result = await this.getArtefactsFromFE (dirPath, contractName)
- if (result) return result
- }
- }
+ } else await this._populateAllContractArtefactsFromFE (dirPath, contractName, contractArtefacts)
+ }
} else return
}
- async getArtefactsByContractName (contractName) {
+ /**
+ * Get artefacts for a contract (called by script-runner)
+ * @param name contract name or fully qualified name i.e.
: e.g: contracts/1_Storage.sol:Storage
+ * @returns artefacts for the contract
+ */
+ async getArtefactsByContractName (name) {
const contractsDataByFilename = this.getAllContractDatas()
- const contractsData = Object.values(contractsDataByFilename)
- if (contractsData && contractsData.length) {
- const index = contractsData.findIndex((contractsObj) => Object.keys(contractsObj).includes(contractName))
- if (index !== -1) return { abi: contractsData[index][contractName].abi, bytecode: contractsData[index][contractName].evm.bytecode.object }
+ // check if name is a fully qualified name
+ if (name.includes(':')) {
+ const fullyQualifiedName = name
+ const nameArr = fullyQualifiedName.split(':')
+ const filename = nameArr[0]
+ const contract = nameArr[1]
+ if(Object.keys(contractsDataByFilename).includes(filename) && contractsDataByFilename[filename][contract])
+ return contractsDataByFilename[filename][contract]
else {
- const result = await this.getArtefactsFromFE ('contracts', contractName)
- if (result) return result
- else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`)
+ const allContractsData = {}
+ await this._populateAllContractArtefactsFromFE ('contracts', contract, allContractsData)
+ if(allContractsData[fullyQualifiedName]) return allContractsData[fullyQualifiedName]
+ else throw new Error(`Could not find artifacts for ${fullyQualifiedName}. Compile contract to generate artifacts.`)
}
} else {
- const result = await this.getArtefactsFromFE ('contracts', contractName)
- if (result) return result
- else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`)
- }
+ const contractName = name
+ const contractArtefacts = this._getAllContractArtefactsfromOutput(contractsDataByFilename, contractName)
+ let keys = Object.keys(contractArtefacts)
+ if (!keys.length) {
+ await this._populateAllContractArtefactsFromFE ('contracts', contractName, contractArtefacts)
+ keys = Object.keys(contractArtefacts)
+ }
+ if (keys.length === 1) return contractArtefacts[keys[0]]
+ else if (keys.length > 1) {
+ throw new Error(`There are multiple artifacts for contract "${contractName}", please use a fully qualified name.\n
+ Please replace ${contractName} for one of these options wherever you are trying to read its artifact: \n
+ ${keys.join()}\n
+ OR just compile the required contract again`)
+ } else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`)
+ }
}
getCompilerAbstract (file) {
diff --git a/libs/remix-core-plugin/src/lib/compiler-metadata.ts b/libs/remix-core-plugin/src/lib/compiler-metadata.ts
index dfd4ade1dd..e7c5f3eec6 100644
--- a/libs/remix-core-plugin/src/lib/compiler-metadata.ts
+++ b/libs/remix-core-plugin/src/lib/compiler-metadata.ts
@@ -1,6 +1,7 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import { CompilerAbstract } from '@remix-project/remix-solidity'
+import { createHash } from 'crypto'
const profile = {
name: 'compilerMetadata',
@@ -28,10 +29,11 @@ export class CompilerMetadata extends Plugin {
onActivation () {
const self = this
- this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data) => {
+ this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => {
if (!await this.call('settings', 'get', 'settings/generate-contract-metadata')) return
- const compiler = new CompilerAbstract(languageVersion, data, source)
+ const compiler = new CompilerAbstract(languageVersion, data, source, input)
const path = self._extractPathOf(source.target)
+ await this.setBuildInfo(version, input, data, path)
compiler.visitContracts((contract) => {
if (contract.file !== source.target) return
(async () => {
@@ -43,6 +45,23 @@ export class CompilerMetadata extends Plugin {
})
}
+ async setBuildInfo (version, input, output, path) {
+ input = JSON.parse(input)
+ const solcLongVersion = version.replace('.Emscripten.clang', '')
+ const solcVersion = solcLongVersion.substring(0, solcLongVersion.indexOf('+commit'))
+ const format = 'hh-sol-build-info-1'
+ const json = JSON.stringify({
+ _format: format,
+ solcVersion,
+ solcLongVersion,
+ input
+ })
+ const id = createHash('md5').update(Buffer.from(json)).digest().toString('hex')
+ const buildFilename = this.joinPath(path, this.innerPath, 'build-info/' + id + '.json')
+ const buildData = {id, _format: format, solcVersion, solcLongVersion, input, output}
+ await this.call('fileManager', 'writeFile', buildFilename, JSON.stringify(buildData, null, '\t'))
+ }
+
_extractPathOf (file) {
const reg = /(.*)(\/).*/
const path = reg.exec(file)
@@ -51,14 +70,15 @@ export class CompilerMetadata extends Plugin {
async _setArtefacts (content, contract, path) {
content = content || '{}'
+ const fileName = this._JSONFileName(path, contract.name)
+ const metadataFileName = this._MetadataFileName(path, contract.name)
+
let metadata
try {
metadata = JSON.parse(content)
} catch (e) {
console.log(e)
}
- const fileName = this._JSONFileName(path, contract.name)
- const metadataFileName = this._MetadataFileName(path, contract.name)
const deploy = metadata.deploy || {}
this.networks.forEach((network) => {
diff --git a/libs/remix-core-plugin/src/lib/editor-context-listener.ts b/libs/remix-core-plugin/src/lib/editor-context-listener.ts
index b20eb6e855..39b33e17b4 100644
--- a/libs/remix-core-plugin/src/lib/editor-context-listener.ts
+++ b/libs/remix-core-plugin/src/lib/editor-context-listener.ts
@@ -42,7 +42,7 @@ export class EditorContextListener extends Plugin {
onActivation () {
this.on('editor', 'contentChanged', () => { this._stopHighlighting() })
- this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
+ this.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => {
if (languageVersion.indexOf('soljson') !== 0) return
this._stopHighlighting()
this._index = {
diff --git a/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts b/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
index ccb201aa47..b6e02bbcdd 100644
--- a/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
+++ b/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
@@ -70,7 +70,7 @@ export class OffsetToLineColumnConverter extends Plugin {
* called by plugin API
*/
activate () {
- this.on('solidity', 'compilationFinished', () => {
+ this.on('solidity', 'compilationFinished', (success, data, source, input, version) => {
this.clear()
})
}
diff --git a/libs/remix-debug/src/source/offsetToLineColumnConverter.ts b/libs/remix-debug/src/source/offsetToLineColumnConverter.ts
index 298feaa1c6..6605b660b9 100644
--- a/libs/remix-debug/src/source/offsetToLineColumnConverter.ts
+++ b/libs/remix-debug/src/source/offsetToLineColumnConverter.ts
@@ -8,7 +8,7 @@ export class OffsetToColumnConverter {
constructor (compilerEvent) {
this.lineBreakPositionsByContent = {}
if (compilerEvent) {
- compilerEvent.register('compilationFinished', (success, data, source) => {
+ compilerEvent.register('compilationFinished', (success, data, source, input, version) => {
this.clear()
})
}
diff --git a/libs/remix-solidity/src/compiler/compiler-abstract.ts b/libs/remix-solidity/src/compiler/compiler-abstract.ts
index 81cd33db28..8a7c25f72f 100644
--- a/libs/remix-solidity/src/compiler/compiler-abstract.ts
+++ b/libs/remix-solidity/src/compiler/compiler-abstract.ts
@@ -5,10 +5,12 @@ export class CompilerAbstract {
languageversion: any
data: any
source: any
- constructor (languageversion, data, source) {
+ input: any
+ constructor (languageversion, data, source, input?) {
this.languageversion = languageversion
this.data = data
this.source = source // source code
+ this.input = input // source code
}
getContracts () {
@@ -27,6 +29,10 @@ export class CompilerAbstract {
return this.data
}
+ getInput () {
+ return this.input
+ }
+
getAsts () {
return this.data.sources // ast
}
diff --git a/libs/remix-solidity/src/compiler/compiler-helpers.ts b/libs/remix-solidity/src/compiler/compiler-helpers.ts
index 8a70e6be4f..ae7af0e0f1 100644
--- a/libs/remix-solidity/src/compiler/compiler-helpers.ts
+++ b/libs/remix-solidity/src/compiler/compiler-helpers.ts
@@ -12,8 +12,8 @@ export const compile = async (compilationTargets, settings, contentResolverCallb
compiler.set('language', settings.language)
compiler.set('runs', settings.runs)
compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version))
- compiler.event.register('compilationFinished', (success, compilationData, source) => {
- resolve(new CompilerAbstract(settings.version, compilationData, source))
+ compiler.event.register('compilationFinished', (success, compilationData, source, input, version) => {
+ resolve(new CompilerAbstract(settings.version, compilationData, source, input))
})
compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, ''))
})
diff --git a/libs/remix-solidity/src/compiler/compiler-worker.ts b/libs/remix-solidity/src/compiler/compiler-worker.ts
index 7685a766f3..662a03ec06 100644
--- a/libs/remix-solidity/src/compiler/compiler-worker.ts
+++ b/libs/remix-solidity/src/compiler/compiler-worker.ts
@@ -45,6 +45,7 @@ export default function (self) { // eslint-disable-line @typescript-eslint/expli
cmd: 'compiled',
job: data.job,
data: compileJSON(data.input),
+ input: data.input,
missingInputs: missingInputs
})
}
diff --git a/libs/remix-solidity/src/compiler/compiler.ts b/libs/remix-solidity/src/compiler/compiler.ts
index 4a7903f5f5..dc2421ff9c 100644
--- a/libs/remix-solidity/src/compiler/compiler.ts
+++ b/libs/remix-solidity/src/compiler/compiler.ts
@@ -37,7 +37,7 @@ export class Compiler {
}
}
- this.event.register('compilationFinished', (success: boolean, data: CompilationResult, source: SourceWithTarget) => {
+ this.event.register('compilationFinished', (success: boolean, data: CompilationResult, source: SourceWithTarget, input: string, version: string) => {
if (success && this.state.compilationStartTime) {
this.event.trigger('compilationDuration', [(new Date().getTime()) - this.state.compilationStartTime])
}
@@ -70,7 +70,7 @@ export class Compiler {
this.gatherImports(files, missingInputs, (error, input) => {
if (error) {
this.state.lastCompilationResult = null
- this.event.trigger('compilationFinished', [false, { error: { formattedMessage: error, severity: 'error' } }, files])
+ this.event.trigger('compilationFinished', [false, { error: { formattedMessage: error, severity: 'error' } }, files, input, this.state.currentVersion])
} else if (this.state.compileJSON && input) { this.state.compileJSON(input) }
})
}
@@ -111,16 +111,17 @@ export class Compiler {
return { error: 'Deferred import' }
}
let result: CompilationResult = {}
+ let input
try {
if (source && source.sources) {
const { optimize, runs, evmVersion, language } = this.state
- const input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
+ input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
result = JSON.parse(compiler.compile(input, { import: missingInputsCallback }))
}
} catch (exception) {
result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } }
}
- this.onCompilationFinished(result, missingInputs, source)
+ this.onCompilationFinished(result, missingInputs, source, input, this.state.currentVersion)
}
this.onCompilerLoaded(compiler.version())
}
@@ -133,7 +134,7 @@ export class Compiler {
* @param source Source
*/
- onCompilationFinished (data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget): void {
+ onCompilationFinished (data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget, input?: string, version?: string): void {
let noFatalErrors = true // ie warnings are ok
const checkIfFatalError = (error: CompilationError) => {
@@ -146,7 +147,7 @@ export class Compiler {
if (!noFatalErrors) {
// There are fatal errors, abort here
this.state.lastCompilationResult = null
- this.event.trigger('compilationFinished', [false, data, source])
+ this.event.trigger('compilationFinished', [false, data, source, input, version])
} else if (missingInputs !== undefined && missingInputs.length > 0 && source && source.sources) {
// try compiling again with the new set of inputs
this.internalCompile(source.sources, missingInputs)
@@ -159,7 +160,7 @@ export class Compiler {
source: source
}
}
- this.event.trigger('compilationFinished', [true, data, source])
+ this.event.trigger('compilationFinished', [true, data, source, input, version])
}
}
@@ -182,16 +183,17 @@ export class Compiler {
return { error: 'Deferred import' }
}
let result: CompilationResult = {}
+ let input: string
try {
if (source && source.sources) {
const { optimize, runs, evmVersion, language } = this.state
- const input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
+ input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
result = JSON.parse(remoteCompiler.compile(input, { import: missingInputsCallback }))
}
} catch (exception) {
result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } }
}
- this.onCompilationFinished(result, missingInputs, source)
+ this.onCompilationFinished(result, missingInputs, source, input, version)
}
this.onCompilerLoaded(version)
}
@@ -273,7 +275,7 @@ export class Compiler {
sources = jobs[data.job].sources
delete jobs[data.job]
}
- this.onCompilationFinished(result, data.missingInputs, sources)
+ this.onCompilationFinished(result, data.missingInputs, sources, data.input, this.state.currentVersion)
}
break
}
diff --git a/libs/remix-solidity/src/compiler/types.ts b/libs/remix-solidity/src/compiler/types.ts
index 14751cfabc..709c0c97df 100644
--- a/libs/remix-solidity/src/compiler/types.ts
+++ b/libs/remix-solidity/src/compiler/types.ts
@@ -186,6 +186,7 @@ export interface MessageFromWorker {
cmd: string,
job?: number,
missingInputs?: string[],
+ input?: any,
data?: string
}
diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts
index 70487c97f3..697483d593 100644
--- a/libs/remix-tests/src/compiler.ts
+++ b/libs/remix-tests/src/compiler.ts
@@ -142,7 +142,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
},
function doCompilation (next) {
// @ts-ignore
- compiler.event.register('compilationFinished', this, (success, data, source) => {
+ compiler.event.register('compilationFinished', this, (success, data, source, input, version) => {
next(null, data)
})
compiler.compile(sources, filepath)
@@ -201,10 +201,10 @@ export function compileContractSources (sources: SrcIfc, newCompConfig: any, imp
}
},
(next) => {
- const compilationFinishedCb = (success, data, source) => {
+ const compilationFinishedCb = (success, data, source, input, version) => {
// data.error usually exists for exceptions like worker error etc.
if (!data.error) UTRunner.compiler = compiler
- if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source)
+ if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source, input, version)
next(null, data)
}
compiler.event.unregister('compilationFinished', compilationFinishedCb)
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 f268b16a71..8452fa2124 100644
--- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
+++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
@@ -38,14 +38,6 @@ type sourceMarker = {
hide: boolean
}
-type sourceAnnotationMap = {
- [key: string]: [sourceAnnotation];
-}
-
-type sourceMarkerMap = {
- [key: string]: [sourceMarker];
-}
-
loader.config({ paths: { vs: 'assets/js/monaco-editor/dev/vs' } })
/* eslint-disable-next-line */
@@ -54,8 +46,6 @@ export interface EditorUIProps {
activated: boolean
themeType: string
currentFile: string
- sourceAnnotationsPerFile: sourceAnnotationMap
- markerPerFile: sourceMarkerMap
events: {
onBreakPointAdded: (file: string, line: number) => void
onBreakPointCleared: (file: string, line: number) => void
@@ -71,17 +61,21 @@ export interface EditorUIProps {
getFontSize: () => number,
getValue: (uri: string) => string
getCursorPosition: () => cursorPosition
+ addMarkerPerFile: (marker: sourceMarker, filePath: string) => void
+ addSourceAnnotationsPerFile: (annotations: sourceAnnotation, filePath: string) => void
+ clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string) => void
+ keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string) => void
}
}
export const EditorUI = (props: EditorUIProps) => {
const [, setCurrentBreakpoints] = useState({})
- const [currentAnnotations, setCurrentAnnotations] = useState({})
- const [currentMarkers, setCurrentMarkers] = useState({})
const editorRef = useRef(null)
const monacoRef = useRef(null)
const currentFileRef = useRef('')
-
+ const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor
+ const registeredDecorations = useRef({}) // registered decorations
+
const [editorModelsState, dispatch] = useReducer(reducerActions, initialState)
const formatColor = (name) => {
@@ -226,55 +220,6 @@ export const EditorUI = (props: EditorUIProps) => {
defineAndSetTheme(monacoRef.current)
})
- const setAnnotationsbyFile = (uri) => {
- if (props.sourceAnnotationsPerFile[uri]) {
- const model = editorModelsState[uri]?.model
- const newAnnotations = []
- for (const annotation of props.sourceAnnotationsPerFile[uri]) {
- if (!annotation.hide) {
- newAnnotations.push({
- range: new monacoRef.current.Range(annotation.row + 1, 1, annotation.row + 1, 1),
- options: {
- isWholeLine: false,
- glyphMarginHoverMessage: { value: (annotation.from ? `from ${annotation.from}:\n` : '') + annotation.text },
- glyphMarginClassName: `fal fa-exclamation-square text-${annotation.type === 'error' ? 'danger' : (annotation.type === 'warning' ? 'warning' : 'info')}`
- }
- })
- }
- }
- setCurrentAnnotations(prevState => {
- prevState[uri] = model.deltaDecorations(currentAnnotations[uri] || [], newAnnotations)
- return prevState
- })
- }
- }
-
- const setMarkerbyFile = (uri) => {
- if (props.markerPerFile[uri]) {
- const model = editorModelsState[uri]?.model
- const newMarkers = []
- for (const marker of props.markerPerFile[uri]) {
- if (!marker.hide) {
- let isWholeLine = false
- if (marker.position.start.line === marker.position.end.line && marker.position.end.column - marker.position.start.column < 3) {
- // in this case we force highlighting the whole line (doesn't make sense to highlight 2 chars)
- isWholeLine = true
- }
- newMarkers.push({
- range: new monacoRef.current.Range(marker.position.start.line + 1, marker.position.start.column + 1, marker.position.end.line + 1, marker.position.end.column + 1),
- options: {
- isWholeLine,
- inlineClassName: `alert-info border-0 highlightLine${marker.position.start.line + 1}`
- }
- })
- }
- }
- setCurrentMarkers(prevState => {
- prevState[uri] = model.deltaDecorations(currentMarkers[uri] || [], newMarkers)
- return prevState
- })
- }
- }
useEffect(() => {
if (!editorRef.current) return
@@ -286,18 +231,91 @@ 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')
- }
- setAnnotationsbyFile(props.currentFile)
- setMarkerbyFile(props.currentFile)
+ }
}, [props.currentFile])
- useEffect(() => {
- setAnnotationsbyFile(props.currentFile)
- }, [JSON.stringify(props.sourceAnnotationsPerFile)])
+ const convertToMonacoDecoration = (decoration: sourceAnnotation | sourceMarker, typeOfDecoration: string) => {
+ if (typeOfDecoration === 'sourceAnnotationsPerFile') {
+ decoration = decoration as sourceAnnotation
+ return {
+ type: typeOfDecoration,
+ range: new monacoRef.current.Range(decoration.row + 1, 1, decoration.row + 1, 1),
+ options: {
+ isWholeLine: false,
+ glyphMarginHoverMessage: { value: (decoration.from ? `from ${decoration.from}:\n` : '') + decoration.text },
+ glyphMarginClassName: `fal fa-exclamation-square text-${decoration.type === 'error' ? 'danger' : (decoration.type === 'warning' ? 'warning' : 'info')}`
+ }
+ }
+ }
+ if (typeOfDecoration === 'markerPerFile') {
+ decoration = decoration as sourceMarker
+ let isWholeLine = false
+ if (decoration.position.start.line === decoration.position.end.line && decoration.position.end.column - decoration.position.start.column < 3) {
+ // in this case we force highlighting the whole line (doesn't make sense to highlight 2 chars)
+ isWholeLine = true
+ }
+ return {
+ type: typeOfDecoration,
+ range: new monacoRef.current.Range(decoration.position.start.line + 1, decoration.position.start.column + 1, decoration.position.end.line + 1, decoration.position.end.column + 1),
+ options: {
+ isWholeLine,
+ inlineClassName: `alert-info border-0 highlightLine${decoration.position.start.line + 1}`
+ }
+ }
+ }
+ }
+
+ props.editorAPI.clearDecorationsByPlugin = (filePath: string, plugin: string, typeOfDecoration: string) => {
+ const model = editorModelsState[filePath]?.model
+ if (!model) return
+ const decorations = []
+ const newRegisteredDecorations = []
+ if (registeredDecorations.current[filePath]) {
+ for (const decoration of registeredDecorations.current[filePath]) {
+ if (decoration.type === typeOfDecoration && decoration.value.from !== plugin) {
+ decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration))
+ newRegisteredDecorations.push(decoration)
+ }
+ }
+ }
+ currentDecorations.current[typeOfDecoration][filePath] = model.deltaDecorations(currentDecorations.current[typeOfDecoration][filePath], decorations)
+ registeredDecorations.current[filePath] = newRegisteredDecorations
+ }
+
+ props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string) => {
+ const model = editorModelsState[filePath]?.model
+ if (!model) return
+ const decorations = []
+ if (registeredDecorations.current[filePath]) {
+ for (const decoration of registeredDecorations.current[filePath]) {
+ if (decoration.type === typeOfDecoration && decoration.value.from === plugin) {
+ decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration))
+ }
+ }
+ }
+ currentDecorations.current[typeOfDecoration][filePath] = model.deltaDecorations(currentDecorations.current[typeOfDecoration][filePath], decorations)
+ }
+
+ const addDecoration = (decoration: sourceAnnotation | sourceMarker, filePath: string, typeOfDecoration: string) => {
+ const model = editorModelsState[filePath]?.model
+ if (!model) return
+ const monacoDecoration = convertToMonacoDecoration(decoration, typeOfDecoration)
+
+ if (!registeredDecorations.current[filePath]) registeredDecorations.current[filePath] = []
+ registeredDecorations.current[filePath].push({ value: decoration, type: typeOfDecoration })
+ if (!currentDecorations.current[typeOfDecoration][filePath]) currentDecorations.current[typeOfDecoration][filePath] = []
+
+ currentDecorations.current[typeOfDecoration][filePath].push(...model.deltaDecorations([], [monacoDecoration]))
+ }
+
+ props.editorAPI.addSourceAnnotationsPerFile = (annotation: sourceAnnotation, filePath: string) => {
+ addDecoration(annotation, filePath, 'sourceAnnotationsPerFile')
+ }
+
+ props.editorAPI.addMarkerPerFile = (marker: sourceMarker, filePath: string) => {
+ addDecoration(marker, filePath, 'markerPerFile')
+ }
- useEffect(() => {
- setMarkerbyFile(props.currentFile)
- }, [JSON.stringify(props.markerPerFile)])
props.editorAPI.findMatches = (uri: string, value: string) => {
if (!editorRef.current) return
diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts
index 18635bb1d7..464a68eed0 100644
--- a/libs/remix-ui/run-tab/src/lib/actions/index.ts
+++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts
@@ -66,7 +66,7 @@ const setupEvents = () => {
plugin.on('manager', 'pluginDeactivated', removePluginProvider.bind(plugin))
- plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
+ plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => broadcastCompilationResult(file, source, languageVersion, data, input))
plugin.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
@@ -301,9 +301,9 @@ export const signMessageWithAddress = (account: string, message: string, modalCo
})
}
-const broadcastCompilationResult = (file, source, languageVersion, data) => {
+const broadcastCompilationResult = (file, source, languageVersion, data, input?) => {
// TODO check whether the tab is configured
- const compiler = new CompilerAbstract(languageVersion, data, source)
+ const compiler = new CompilerAbstract(languageVersion, data, source, input)
plugin.compilersArtefacts[languageVersion] = compiler
plugin.compilersArtefacts.__last = compiler
diff --git a/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts b/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
index bc43ed73b1..c67aee6ddb 100644
--- a/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
+++ b/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
@@ -50,7 +50,7 @@ export const listenToEvents = (compileTabLogic: CompileTabLogic, api) => (dispat
dispatch(setCompilerMode('compilerLoaded'))
})
- compileTabLogic.compiler.event.register('compilationFinished', (success, data, source) => {
- dispatch(setCompilerMode('compilationFinished', success, data, source))
+ compileTabLogic.compiler.event.register('compilationFinished', (success, data, source, input, version) => {
+ dispatch(setCompilerMode('compilationFinished', success, data, source, input, version))
})
}
diff --git a/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts b/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
index 4f55437cb6..c8ce131cee 100644
--- a/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
+++ b/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
@@ -5,9 +5,9 @@ export const compilation = (analysisModule, dispatch) => {
analysisModule.on(
'solidity',
'compilationFinished',
- (file, source, languageVersion, data) => {
+ (file, source, languageVersion, data, input, version) => {
if (languageVersion.indexOf('soljson') !== 0) return
- dispatch({ type: 'compilationFinished', payload: { file, source, languageVersion, data } })
+ dispatch({ type: 'compilationFinished', payload: { file, source, languageVersion, data, input, version } })
}
)
}
diff --git a/libs/remix-ui/static-analyser/src/lib/reducers/staticAnalysisReducer.ts b/libs/remix-ui/static-analyser/src/lib/reducers/staticAnalysisReducer.ts
index eb59ca7872..24c9a25868 100644
--- a/libs/remix-ui/static-analyser/src/lib/reducers/staticAnalysisReducer.ts
+++ b/libs/remix-ui/static-analyser/src/lib/reducers/staticAnalysisReducer.ts
@@ -13,7 +13,9 @@ export const analysisReducer = (state, action) => {
file: action.payload.file,
source: action.payload.source,
languageVersion: action.payload.languageVersion,
- data: action.payload.data
+ data: action.payload.data,
+ input: action.payload.input,
+ version: action.payload.version
}
default:
return initialState
diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
index 73b5b3e424..904685f7a5 100644
--- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
+++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
@@ -544,7 +544,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
)
} else {
return (
- { msg ? msg.toString().replace(/,/g, '') : msg }
+ {msg? msg.toString() : null}
)
}
})