Merge branch 'master' into terminallog

pull/2099/head
bunsenstraat 3 years ago committed by GitHub
commit 1b9550aced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      apps/remix-ide/src/app/tabs/compile-tab.js
  2. 67
      apps/solidity-compiler/src/app/compiler-api.ts
  3. 16
      libs/remix-lib/src/types/ICompilerApi.ts
  4. 2
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  5. 22
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  6. 85
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  7. 27
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts

@ -48,9 +48,9 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
this.renderComponent() this.renderComponent()
} }
onResetResults () { // onResetResults () {
this.renderComponent() // this.renderComponent()
} // }
onSetWorkspace () { onSetWorkspace () {
this.renderComponent() this.renderComponent()
@ -60,6 +60,10 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
this.renderComponent() this.renderComponent()
} }
onFileClosed () {
this.renderComponent()
}
onCompilationFinished () { onCompilationFinished () {
this.renderComponent() this.renderComponent()
} }

@ -4,23 +4,26 @@ import type { ConfigurationSettings } from '@remix-project/remix-lib-ts'
export const CompilerApiMixin = (Base) => class extends Base { export const CompilerApiMixin = (Base) => class extends Base {
currentFile: string currentFile: string
contractMap: { compilationDetails: {
file: string contractMap: {
} | Record<string, any> file: string
} | Record<string, any>,
contractsDetails: Record<string, any>,
target?: string
}
compileErrors: any compileErrors: any
compileTabLogic: CompileTabLogic compileTabLogic: CompileTabLogic
contractsDetails: Record<string, any>
configurationSettings: ConfigurationSettings configurationSettings: ConfigurationSettings
onCurrentFileChanged: (fileName: string) => void onCurrentFileChanged: (fileName: string) => void
onResetResults: () => void // onResetResults: () => void
onSetWorkspace: (workspace: any) => void onSetWorkspace: (workspace: any) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onCompilationFinished: (contractsDetails: any, contractMap: any) => void onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void
onSessionSwitched: () => void onSessionSwitched: () => void
onContentChanged: () => void onContentChanged: () => void
onFileClosed: (name: string) => void
statusChanged: (data: { key: string, title?: string, type?: string }) => void
initCompilerApi () { initCompilerApi () {
this.configurationSettings = null this.configurationSettings = null
@ -31,15 +34,15 @@ export const CompilerApiMixin = (Base) => class extends Base {
contractEl: null contractEl: null
} }
this.contractsDetails = {} this.compilationDetails = {
contractsDetails:{},
contractMap: {}
}
this.data = { this.data = {
eventHandlers: {}, eventHandlers: {},
loading: false loading: false
} }
this.contractMap = {}
this.contractsDetails = {}
this.compileErrors = {} this.compileErrors = {}
this.compiledFileName = '' this.compiledFileName = ''
this.currentFile = '' this.currentFile = ''
@ -188,32 +191,35 @@ export const CompilerApiMixin = (Base) => class extends Base {
resetResults () { resetResults () {
this.currentFile = '' this.currentFile = ''
this.contractsDetails = {} this.compilationDetails = {
this.emit('statusChanged', { key: 'none' }) contractsDetails: {},
if (this.onResetResults) this.onResetResults() contractMap: {}
}
this.statusChanged({ key: 'none' })
// if (this.onResetResults) this.onResetResults()
} }
listenToEvents () { listenToEvents () {
this.on('editor', 'contentChanged', () => { this.on('editor', 'contentChanged', () => {
this.emit('statusChanged', { key: 'edited', title: 'the content has changed, needs recompilation', type: 'info' }) this.statusChanged({ key: 'edited', title: 'the content has changed, needs recompilation', type: 'info' })
if (this.onContentChanged) this.onContentChanged() if (this.onContentChanged) this.onContentChanged()
}) })
this.data.eventHandlers.onLoadingCompiler = (url) => { this.data.eventHandlers.onLoadingCompiler = (url) => {
this.data.loading = true this.data.loading = true
this.data.loadingUrl = url this.data.loadingUrl = url
this.emit('statusChanged', { key: 'loading', title: 'loading compiler...', type: 'info' }) this.statusChanged({ key: 'loading', title: 'loading compiler...', type: 'info' })
} }
this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler)
this.data.eventHandlers.onCompilerLoaded = () => { this.data.eventHandlers.onCompilerLoaded = () => {
this.data.loading = false this.data.loading = false
this.emit('statusChanged', { key: 'none' }) this.statusChanged({ key: 'none' })
} }
this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded)
this.data.eventHandlers.onStartingCompilation = () => { this.data.eventHandlers.onStartingCompilation = () => {
this.emit('statusChanged', { key: 'loading', title: 'compiling...', type: 'info' }) this.statusChanged({ key: 'loading', title: 'compiling...', type: 'info' })
} }
this.data.eventHandlers.onRemoveAnnotations = () => { this.data.eventHandlers.onRemoveAnnotations = () => {
@ -249,22 +255,28 @@ export const CompilerApiMixin = (Base) => class extends Base {
} }
this.on('fileManager', 'noFileSelected', this.data.eventHandlers.onNoFileSelected) this.on('fileManager', 'noFileSelected', this.data.eventHandlers.onNoFileSelected)
this.data.eventHandlers.onFileClosed = (name: string) => {
this.onFileClosed(name)
}
this.on('fileManager', 'fileClosed', this.data.eventHandlers.onFileClosed)
this.data.eventHandlers.onCompilationFinished = (success, data, source) => { this.data.eventHandlers.onCompilationFinished = (success, data, source) => {
this.compileErrors = data this.compileErrors = data
if (success) { if (success) {
// forwarding the event to the appManager infra // forwarding the event to the appManager infra
this.emit('compilationFinished', source.target, source, 'soljson', data) this.emit('compilationFinished', source.target, source, 'soljson', data)
if (data.errors && data.errors.length > 0) { if (data.errors && data.errors.length > 0) {
this.emit('statusChanged', { this.statusChanged({
key: data.errors.length, key: data.errors.length,
title: `compilation finished successful with warning${data.errors.length > 1 ? 's' : ''}`, title: `compilation finished successful with warning${data.errors.length > 1 ? 's' : ''}`,
type: 'warning' type: 'warning'
}) })
} else this.emit('statusChanged', { key: 'succeed', title: 'compilation successful', type: 'success' }) } else this.statusChanged({ key: 'succeed', title: 'compilation successful', type: 'success' })
// Store the contracts // Store the contracts
this.contractsDetails = {} this.compilationDetails.contractsDetails = {}
this.compiler.visitContracts((contract) => { this.compiler.visitContracts((contract) => {
this.contractsDetails[contract.name] = parseContracts( this.compilationDetails.contractsDetails[contract.name] = parseContracts(
contract.name, contract.name,
contract.object, contract.object,
this.compiler.getSource(contract.file) this.compiler.getSource(contract.file)
@ -272,12 +284,13 @@ export const CompilerApiMixin = (Base) => class extends Base {
}) })
} else { } else {
const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + (data.error ? 1 : 0)) const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + (data.error ? 1 : 0))
this.emit('statusChanged', { key: count, title: `compilation failed with ${count} error${count > 1 ? 's' : ''}`, type: 'error' }) this.statusChanged({ key: count, title: `compilation failed with ${count} error${count > 1 ? 's' : ''}`, type: 'error' })
} }
// Update contract Selection // Update contract Selection
this.contractMap = {} this.compilationDetails.contractMap = {}
if (success) this.compiler.visitContracts((contract) => { this.contractMap[contract.name] = contract }) if (success) this.compiler.visitContracts((contract) => { this.compilationDetails.contractMap[contract.name] = contract })
if (this.onCompilationFinished) this.onCompilationFinished(this.contractsDetails, this.contractMap) this.compilationDetails.target = source.target
if (this.onCompilationFinished) this.onCompilationFinished(this.compilationDetails)
} }
this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished) this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished)

@ -1,11 +1,14 @@
export interface ICompilerApi { export interface ICompilerApi {
currentFile: string currentFile: string
contractMap: { compilationDetails: {
file: string contractMap: {
} | Record<string, any> file: string
} | Record<string, any>,
contractsDetails: Record<string, any>,
target?: string
}
compileErrors: any compileErrors: any
compileTabLogic: any compileTabLogic: any
contractsDetails: Record<string, any>
configurationSettings: ConfigurationSettings configurationSettings: ConfigurationSettings
getCompilerParameters: () => ConfigurationSettings getCompilerParameters: () => ConfigurationSettings
@ -20,12 +23,13 @@ export interface ICompilerApi {
getCompilationResult: () => any getCompilationResult: () => any
onCurrentFileChanged: (fileName: string) => void onCurrentFileChanged: (fileName: string) => void
onResetResults: () => void, // onResetResults: () => void,
onSetWorkspace: (workspace: any) => void onSetWorkspace: (workspace: any) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onCompilationFinished: (contractsDetails: any, contractMap: any) => void onCompilationFinished: (contractsDetails: any, contractMap: any) => void
onSessionSwitched: () => void onSessionSwitched: () => void
onContentChanged: () => void onContentChanged: () => void
onFileClosed: (name: string) => void
resolveContentAndSave: (url: string) => Promise<string> resolveContentAndSave: (url: string) => Promise<string>
fileExists: (file: string) => Promise<boolean> fileExists: (file: string) => Promise<boolean>
@ -37,6 +41,8 @@ export interface ICompilerApi {
logToTerminal: (log: terminalLog) => void logToTerminal: (log: terminalLog) => void
compileWithHardhat: (configPath: string) => Promise<string> compileWithHardhat: (configPath: string) => Promise<string>
statusChanged: (data: { key: string, title?: string, type?: string }) => void,
emit?: (key: string, ...payload: any) => void
} }
export type terminalLog = { export type terminalLog = {

@ -37,7 +37,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
modal.current.addEventListener('blur', handleBlur) modal.current.addEventListener('blur', handleBlur)
} }
return () => { return () => {
modal.current.removeEventListener('blur', handleBlur) modal.current && modal.current.removeEventListener('blur', handleBlur)
} }
}, [modal.current]) }, [modal.current])

@ -7,32 +7,18 @@ import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css' import './css/style.css'
export const ContractSelection = (props: ContractSelectionProps) => { export const ContractSelection = (props: ContractSelectionProps) => {
const { api, contractMap, contractsDetails, modal } = props const { api, contractsDetails, contractList, modal } = props
const [contractList, setContractList] = useState([])
const [selectedContract, setSelectedContract] = useState('') const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null) const [storage, setStorage] = useState(null)
useEffect(() => { useEffect(() => {
const contractList = contractMap ? Object.keys(contractMap).map((key) => ({
name: key,
file: getFileName(contractMap[key].file)
})) : []
setContractList(contractList)
if (contractList.length) setSelectedContract(contractList[0].name) if (contractList.length) setSelectedContract(contractList[0].name)
}, [contractMap, contractsDetails]) }, [contractList])
const resetStorage = () => { const resetStorage = () => {
setStorage('') setStorage('')
} }
// Return the file name of a path: ex "browser/ballot.sol" -> "ballot.sol"
const getFileName = (path) => {
const part = path.split('/')
return part[part.length - 1]
}
const handleContractChange = (contractName: string) => { const handleContractChange = (contractName: string) => {
setSelectedContract(contractName) setSelectedContract(contractName)
} }
@ -214,13 +200,13 @@ export const ContractSelection = (props: ContractSelectionProps) => {
<div className="remixui_contractHelperButtons"> <div className="remixui_contractHelperButtons">
<div className="input-group"> <div className="input-group">
<div className="btn-group" role="group" aria-label="Copy to Clipboard"> <div className="btn-group" role="group" aria-label="Copy to Clipboard">
<CopyToClipboard title="Copy ABI to clipboard" content={copyABI()} direction='top'> <CopyToClipboard title="Copy ABI to clipboard" getContent={copyABI} direction='top'>
<button className="btn remixui_copyButton" title="Copy ABI to clipboard"> <button className="btn remixui_copyButton" title="Copy ABI to clipboard">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>ABI</span> <span>ABI</span>
</button> </button>
</CopyToClipboard> </CopyToClipboard>
<CopyToClipboard title="Copy ABI to clipboard" content={copyBytecode()} direction='top'> <CopyToClipboard title="Copy ABI to clipboard" getContent={copyBytecode} direction='top'>
<button className="btn remixui_copyButton" title="Copy Bytecode to clipboard"> <button className="btn remixui_copyButton" title="Copy Bytecode to clipboard">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>Bytecode</span> <span>Bytecode</span>

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react' // eslint-disable-line import React, { useEffect, useState } from 'react' // eslint-disable-line
import { SolidityCompilerProps } from './types' import { CompileErrors, ContractsFile, SolidityCompilerProps } from './types'
import { CompilerContainer } from './compiler-container' // eslint-disable-line import { CompilerContainer } from './compiler-container' // eslint-disable-line
import { ContractSelection } from './contract-selection' // eslint-disable-line import { ContractSelection } from './contract-selection' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
@ -9,12 +9,10 @@ import { Renderer } from '@remix-ui/renderer' // eslint-disable-line
import './css/style.css' import './css/style.css'
export const SolidityCompiler = (props: SolidityCompilerProps) => { export const SolidityCompiler = (props: SolidityCompilerProps) => {
const { api, api: { currentFile, compileTabLogic, contractsDetails, contractMap, compileErrors, configurationSettings } } = props const { api, api: { currentFile, compileTabLogic, configurationSettings } } = props
const [state, setState] = useState({ const [state, setState] = useState({
isHardhatProject: false, isHardhatProject: false,
currentFile, currentFile,
contractsDetails: {},
contractMap: {},
loading: false, loading: false,
compileTabLogic: null, compileTabLogic: null,
compiler: null, compiler: null,
@ -32,6 +30,9 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
}) })
const [currentVersion, setCurrentVersion] = useState('') const [currentVersion, setCurrentVersion] = useState('')
const [hideWarnings, setHideWarnings] = useState<boolean>(false) const [hideWarnings, setHideWarnings] = useState<boolean>(false)
const [compileErrors, setCompileErrors] = useState<Record<string, CompileErrors>>({ [currentFile]: api.compileErrors })
const [badgeStatus, setBadgeStatus] = useState<Record<string, { key: string, title?: string, type?: string }>>({})
const [contractsFile, setContractsFile] = useState<ContractsFile>({})
useEffect(() => { useEffect(() => {
(async () => { (async () => {
@ -40,15 +41,24 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
})() })()
}, []) }, [])
api.onCurrentFileChanged = (currentFile: string) => { useEffect(() => {
setState(prevState => { if (badgeStatus[currentFile]) {
return { ...prevState, currentFile } api.emit('statusChanged', badgeStatus[currentFile])
}) } else {
api.emit('statusChanged', { key: 'none' })
}
}, [badgeStatus[currentFile], currentFile])
// Return the file name of a path: ex "browser/ballot.sol" -> "ballot.sol"
const getFileName = (path) => {
const part = path.split('/')
return part[part.length - 1]
} }
api.onResetResults = () => { api.onCurrentFileChanged = (currentFile: string) => {
setState(prevState => { setState(prevState => {
return { ...prevState, currentFile: '', contractsDetails: {}, contractMap: {} } return { ...prevState, currentFile }
}) })
} }
@ -63,12 +73,31 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
setState(prevState => { setState(prevState => {
return { ...prevState, currentFile: '' } return { ...prevState, currentFile: '' }
}) })
setCompileErrors({} as Record<string, CompileErrors>)
} }
api.onCompilationFinished = (contractsDetails: any, contractMap: any) => { api.onCompilationFinished = (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any>, target?: string }) => {
setState(prevState => { const { contractMap, contractsDetails, target } = compilationDetails
return { ...prevState, contractsDetails, contractMap } const contractList = contractMap ? Object.keys(contractMap).map((key) => {
}) return {
name: key,
file: getFileName(contractMap[key].file)
}
}) : []
setContractsFile({ ...contractsFile, [target]: { contractList, contractsDetails } })
setCompileErrors({ ...compileErrors, [currentFile]: api.compileErrors })
}
api.onFileClosed = (name) => {
if (name === currentFile) {
setCompileErrors({ ...compileErrors, [currentFile]: {} as CompileErrors })
setBadgeStatus({ ...badgeStatus, [currentFile]: { key: 'none' } })
}
}
api.statusChanged = (data: { key: string, title?: string, type?: string }) => {
setBadgeStatus({ ...badgeStatus, [currentFile]: data })
} }
const toast = (message: string) => { const toast = (message: string) => {
@ -120,21 +149,23 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
<> <>
<div id="compileTabView"> <div id="compileTabView">
<CompilerContainer api={api} isHardhatProject={state.isHardhatProject} compileTabLogic={compileTabLogic} tooltip={toast} modal={modal} compiledFileName={currentFile} updateCurrentVersion={updateCurrentVersion} configurationSettings={configurationSettings} /> <CompilerContainer api={api} isHardhatProject={state.isHardhatProject} compileTabLogic={compileTabLogic} tooltip={toast} modal={modal} compiledFileName={currentFile} updateCurrentVersion={updateCurrentVersion} configurationSettings={configurationSettings} />
<ContractSelection api={api} contractMap={contractMap} contractsDetails={contractsDetails} modal={modal} /> { contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
<div className="remixui_errorBlobs p-4" data-id="compiledErrors"> { compileErrors[currentFile] &&
<span data-id={`compilationFinishedWith_${currentVersion}`}></span> <div className="remixui_errorBlobs p-4" data-id="compiledErrors">
{ compileErrors.error && <Renderer message={compileErrors.error.formattedMessage || compileErrors.error} plugin={api} opt={{ type: compileErrors.error.severity || 'error', errorType: compileErrors.error.type }} /> } <span data-id={`compilationFinishedWith_${currentVersion}`}></span>
{ compileErrors.error && (compileErrors.error.mode === 'panic') && modal('Error', panicMessage(compileErrors.error.formattedMessage), 'Close', null) } { compileErrors[currentFile].error && <Renderer message={compileErrors[currentFile].error.formattedMessage || compileErrors[currentFile].error} plugin={api} opt={{ type: compileErrors[currentFile].error.severity || 'error', errorType: compileErrors[currentFile].error.type }} /> }
{ compileErrors.errors && compileErrors.errors.length && compileErrors.errors.map((err, index) => { { compileErrors[currentFile].error && (compileErrors[currentFile].error.mode === 'panic') && modal('Error', panicMessage(compileErrors[currentFile].error.formattedMessage), 'Close', null) }
if (hideWarnings) { { compileErrors[currentFile].errors && compileErrors[currentFile].errors.length && compileErrors[currentFile].errors.map((err, index) => {
if (err.severity !== 'warning') { if (hideWarnings) {
if (err.severity !== 'warning') {
return <Renderer key={index} message={err.formattedMessage} plugin={api} opt={{ type: err.severity, errorType: err.type }} />
}
} else {
return <Renderer key={index} message={err.formattedMessage} plugin={api} opt={{ type: err.severity, errorType: err.type }} /> return <Renderer key={index} message={err.formattedMessage} plugin={api} opt={{ type: err.severity, errorType: err.type }} />
} }
} else { }) }
return <Renderer key={index} message={err.formattedMessage} plugin={api} opt={{ type: err.severity, errorType: err.type }} /> </div>
} }
}) }
</div>
</div> </div>
<Toaster message={state.toasterMsg} /> <Toaster message={state.toasterMsg} />
<ModalDialog <ModalDialog

@ -18,9 +18,30 @@ export interface CompilerContainerProps {
} }
export interface ContractSelectionProps { export interface ContractSelectionProps {
api: ICompilerApi, api: ICompilerApi,
contractMap: { contractList: { file: string, name: string }[],
file: string
} | Record<string, any>,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
contractsDetails: Record<string, any> contractsDetails: Record<string, any>
} }
interface CompileError {
mode?: string,
severity?: string,
formattedMessage?: string,
type?: string
}
export interface CompileErrors {
error: CompileError,
errors: CompileError[]
}
export interface CompilationDetails {
contractList: { file: string, name: string }[],
contractsDetails: Record<string, any>,
target?: string
}
export interface ContractsFile {
[currentFile: string]: CompilationDetails
}

Loading…
Cancel
Save