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()
}
onResetResults () {
this.renderComponent()
}
// onResetResults () {
// this.renderComponent()
// }
onSetWorkspace () {
this.renderComponent()
@ -60,6 +60,10 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
this.renderComponent()
}
onFileClosed () {
this.renderComponent()
}
onCompilationFinished () {
this.renderComponent()
}

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

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

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

@ -7,32 +7,18 @@ import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css'
export const ContractSelection = (props: ContractSelectionProps) => {
const { api, contractMap, contractsDetails, modal } = props
const [contractList, setContractList] = useState([])
const { api, contractsDetails, contractList, modal } = props
const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null)
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)
}, [contractMap, contractsDetails])
}, [contractList])
const resetStorage = () => {
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) => {
setSelectedContract(contractName)
}
@ -214,13 +200,13 @@ export const ContractSelection = (props: ContractSelectionProps) => {
<div className="remixui_contractHelperButtons">
<div className="input-group">
<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">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>ABI</span>
</button>
</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">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>Bytecode</span>

@ -1,5 +1,5 @@
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 { ContractSelection } from './contract-selection' // 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'
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({
isHardhatProject: false,
currentFile,
contractsDetails: {},
contractMap: {},
loading: false,
compileTabLogic: null,
compiler: null,
@ -32,6 +30,9 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
})
const [currentVersion, setCurrentVersion] = useState('')
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(() => {
(async () => {
@ -40,15 +41,24 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
})()
}, [])
api.onCurrentFileChanged = (currentFile: string) => {
setState(prevState => {
return { ...prevState, currentFile }
})
useEffect(() => {
if (badgeStatus[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 => {
return { ...prevState, currentFile: '', contractsDetails: {}, contractMap: {} }
return { ...prevState, currentFile }
})
}
@ -63,12 +73,31 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
setState(prevState => {
return { ...prevState, currentFile: '' }
})
setCompileErrors({} as Record<string, CompileErrors>)
}
api.onCompilationFinished = (contractsDetails: any, contractMap: any) => {
setState(prevState => {
return { ...prevState, contractsDetails, contractMap }
})
api.onCompilationFinished = (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any>, target?: string }) => {
const { contractMap, contractsDetails, target } = compilationDetails
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) => {
@ -120,21 +149,23 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
<>
<div id="compileTabView">
<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} />
<div className="remixui_errorBlobs p-4" data-id="compiledErrors">
<span data-id={`compilationFinishedWith_${currentVersion}`}></span>
{ compileErrors.error && <Renderer message={compileErrors.error.formattedMessage || compileErrors.error} plugin={api} opt={{ type: compileErrors.error.severity || 'error', errorType: compileErrors.error.type }} /> }
{ compileErrors.error && (compileErrors.error.mode === 'panic') && modal('Error', panicMessage(compileErrors.error.formattedMessage), 'Close', null) }
{ compileErrors.errors && compileErrors.errors.length && compileErrors.errors.map((err, index) => {
if (hideWarnings) {
if (err.severity !== 'warning') {
{ contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
{ compileErrors[currentFile] &&
<div className="remixui_errorBlobs p-4" data-id="compiledErrors">
<span data-id={`compilationFinishedWith_${currentVersion}`}></span>
{ 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[currentFile].error && (compileErrors[currentFile].error.mode === 'panic') && modal('Error', panicMessage(compileErrors[currentFile].error.formattedMessage), 'Close', null) }
{ compileErrors[currentFile].errors && compileErrors[currentFile].errors.length && compileErrors[currentFile].errors.map((err, index) => {
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 }} />
}
} else {
return <Renderer key={index} message={err.formattedMessage} plugin={api} opt={{ type: err.severity, errorType: err.type }} />
}
}) }
</div>
}) }
</div>
}
</div>
<Toaster message={state.toasterMsg} />
<ModalDialog

@ -18,9 +18,30 @@ export interface CompilerContainerProps {
}
export interface ContractSelectionProps {
api: ICompilerApi,
contractMap: {
file: string
} | Record<string, any>,
contractList: { file: string, name: string }[],
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
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