Merge pull request #2262 from ethereum/ui_for_auto_exec

UI for auto exec
flakyhunt
yann300 3 years ago committed by GitHub
commit 1e2ffc39cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      apps/remix-ide-e2e/src/tests/libraryDeployment.test.ts
  2. 8
      apps/remix-ide/src/app.js
  3. 3
      apps/remix-ide/src/app/panels/layout.ts
  4. 93
      apps/remix-ide/src/app/tabs/compile-and-run.ts
  5. 62
      apps/remix-ide/src/app/tabs/intelligent-script-executor.ts
  6. 12
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  7. 14
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  8. 9
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  9. 12
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  10. 9
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  11. 9
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  12. 9
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  13. 9
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  14. 9
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  15. 2
      apps/remix-ide/src/remixAppManager.js
  16. 5
      apps/solidity-compiler/src/app/compiler-api.ts
  17. 1
      libs/remix-lib/src/types/ICompilerApi.ts
  18. 80
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  19. 5
      libs/remix-ui/solidity-compiler/src/lib/css/style.css
  20. 1
      libs/remix-ui/workspace/src/lib/templates/examples.ts

@ -41,7 +41,7 @@ module.exports = {
.clickLaunchIcon('settings')
.click('*[data-id="settingsTabGenerateContractMetadataLabel"]')
.clickLaunchIcon('solidity')
.click('#compileTabView button[title="Compile"]') // that should generate the JSON artefact
.click('#compileTabView button[data-id="compilerContainerCompileBtn"]') // that should generate the JSON artefact
.clickLaunchIcon('udapp')
.verifyContracts(['test'])
.clickLaunchIcon('udapp')

@ -5,7 +5,7 @@ import { RemixAppManager } from './remixAppManager'
import { ThemeModule } from './app/tabs/theme-module'
import { NetworkModule } from './app/tabs/network-module'
import { Web3ProviderModule } from './app/tabs/web3-provider'
import { IntelligentScriptExecutor } from './app/tabs/intelligent-script-executor'
import { CompileAndRun } from './app/tabs/compile-and-run'
import { SidePanel } from './app/components/side-panel'
import { HiddenPanel } from './app/components/hidden-panel'
import { VerticalIcons } from './app/components/vertical-icons'
@ -184,7 +184,7 @@ class AppComponent {
name: 'offsettolinecolumnconverter'
})
// ----------------- run script after each compilation results -----------
const intelligentScriptExecutor = new IntelligentScriptExecutor()
const compileAndRun = new CompileAndRun()
// -------------------Terminal----------------------------------------
makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl))
const terminal = new Terminal(
@ -227,7 +227,7 @@ class AppComponent {
contextualListener,
terminal,
web3Provider,
intelligentScriptExecutor,
compileAndRun,
fetchAndCompile,
dGitProvider,
storagePlugin,
@ -352,7 +352,7 @@ class AppComponent {
await this.appManager.activatePlugin(['settings', 'config'])
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough','storage', 'search','intelligentScriptExecutor'])
await this.appManager.activatePlugin(['walkthrough','storage', 'search','compileAndRun'])
this.appManager.on(
'filePanel',

@ -75,9 +75,6 @@ export class Layout extends Plugin {
} else if (e.code === 'KeyA') {
// Ctrl+Shift+A
this.call('menuicons', 'select', 'pluginManager')
} else if (e.code === 'KeyS') {
// Ctrl+Shift+S
this.call('menuicons', 'select', 'settings')
}
e.preventDefault()
}

@ -0,0 +1,93 @@
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || []
export const profile = {
name: 'compileAndRun',
displayName: 'Compile and Run',
description: 'after each compilation, run the script defined in Natspec.',
methods: ['runScriptAfterCompilation'],
version: packageJson.version,
kind: 'none'
}
type listener = (event: KeyboardEvent) => void
export class CompileAndRun extends Plugin {
executionListener: listener
targetFileName: string
constructor () {
super(profile)
this.executionListener = async (e) => {
// ctrl+e or command+e
const file = await this.call('fileManager', 'file')
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.keyCode === 83 && file !== '') {
if (file.endsWith('.sol')) {
e.preventDefault()
this.targetFileName = file
await this.call('solidity', 'compile', file)
_paq.push(['trackEvent', 'ScriptExecutor', 'compile_solidity'])
} else if (file.endsWith('.js') || file.endsWith('.ts')) {
e.preventDefault()
this.runScript(file, false)
_paq.push(['trackEvent', 'ScriptExecutor', 'run_script'])
}
}
}
}
runScriptAfterCompilation (fileName: string) {
this.targetFileName = fileName
_paq.push(['trackEvent', 'ScriptExecutor', 'request_run_script'])
}
async runScript (fileName, clearAllInstances) {
await this.call('terminal', 'log', `running ${fileName} ...`)
try {
const exists = await this.call('fileManager', 'exists', fileName)
if (!exists) {
await this.call('terminal', 'log', `${fileName} does not exist.`)
return
}
const content = await this.call('fileManager', 'readFile', fileName)
if (clearAllInstances) {
await this.call('udapp', 'clearAllInstances')
}
await this.call('scriptRunner', 'execute', content)
} catch (e) {
this.call('notification', 'toast', e.message || e)
}
}
onActivation () {
window.document.addEventListener('keydown', this.executionListener)
this.on('compilerMetadata', 'artefactsUpdated', async (fileName, contract) => {
if (this.targetFileName === contract.file) {
if (contract.object && contract.object.devdoc['custom:dev-run-script']) {
this.targetFileName = null
const file = contract.object.devdoc['custom:dev-run-script']
if (file) {
this.runScript(file, true)
_paq.push(['trackEvent', 'ScriptExecutor', 'run_script_after_compile'])
} else {
this.call('notification', 'toast', 'You have not set a script to run. Set it with @custom:dev-run-script NatSpec tag.')
}
} else {
this.call('notification', 'toast', 'You have not set a script to run. Set it with @custom:dev-run-script NatSpec tag.')
}
}
})
}
onDeactivation () {
window.document.removeEventListener('keydown', this.executionListener)
this.off('compilerMetadata', 'artefactsUpdated')
}
}

@ -1,62 +0,0 @@
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
export const profile = {
name: 'intelligentScriptExecutor',
displayName: 'Intelligent Script Executor',
description: 'after each compilation, run the script defined in Natspec.',
methods: [],
version: packageJson.version,
kind: 'none'
}
type listener = (event: KeyboardEvent) => void
export class IntelligentScriptExecutor extends Plugin {
executionListener: listener
targetFileName: string
constructor () {
super(profile)
this.executionListener = async (e) => {
// ctrl+e or command+e
const file = await this.call('fileManager', 'file')
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.keyCode === 83 && file !== '') {
if (file.endsWith('.sol')) {
e.preventDefault()
this.targetFileName = file
await this.call('solidity', 'compile', file)
} else if (file.endsWith('.js') || file.endsWith('.ts')) {
e.preventDefault()
this.runScript(file, false)
}
}
}
}
async runScript (fileName, clearAllInstances) {
await this.call('terminal', 'log', `running ${fileName} ...`)
const content = await this.call('fileManager', 'readFile', fileName)
if (clearAllInstances) {
await this.call('udapp', 'clearAllInstances')
}
await this.call('scriptRunner', 'execute', content)
}
onActivation () {
window.document.addEventListener('keydown', this.executionListener)
this.on('compilerMetadata', 'artefactsUpdated', async (fileName, contract) => {
if (this.targetFileName === contract.file && contract.object && contract.object.devdoc['custom:dev-run-script']) {
this.targetFileName = null
const file = contract.object.devdoc['custom:dev-run-script']
if (file) this.runScript(file, true)
}
})
}
onDeactivation () {
window.document.removeEventListener('keydown', this.executionListener)
this.off('compilerMetadata', 'artefactsUpdated')
}
}

@ -30,6 +30,7 @@
--warning:#dd5600;
--danger:#c71c22;
--light:#f8f9fa;
--text:#343a40;
--dark:#343a40;
--body-bg: #fff;
--text-bg-mark: #fcf8e3;
@ -5186,12 +5187,11 @@ a.close.disabled {
border-left-color:#000
}
.tooltip-inner {
max-width:200px;
padding:.25rem .5rem;
color:#fff;
text-align:center;
background-color:#000;
border-radius:.25rem
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: var(--light);
border-radius: .25rem
}
.popover {
position:absolute;

@ -32,6 +32,7 @@
--warning:#f80;
--danger:#c00;
--light:#222;
--text:#adafae;
--dark:#adafae;
--body-bg: #060606;
--text-bg-mark: #fcf8e3;
@ -5189,13 +5190,12 @@ a.close.disabled {
border-left-color:#282828
}
.tooltip-inner {
max-width:200px;
padding:.25rem .5rem;
color:#fff;
text-align:center;
background-color:#282828;
border-radius:.25rem
}
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: var(--light);
border-radius: .25rem
}
.popover {
position:absolute;
top:0;

@ -30,6 +30,7 @@
--warning:#f39c12;
--danger:#e74c3c;
--light:#ecf0f1;
--text:#7b8a8b;
--dark:#7b8a8b;
--body-bg: #fff;
--text-bg-mark: #fcf8e3;
@ -4226,8 +4227,12 @@ a.close.disabled {
left:0; border-width:.4rem 0 .4rem .4rem; border-left-color:#000
}
.tooltip-inner {
max-width:200px; padding:.25rem .5rem; color:#fff; text-align:center; background-color:#000; border-radius:.25rem
}
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: var(--light);
border-radius: .25rem
}
.popover {
position:absolute; top:0; left:0; z-index:1060; display:block; max-width:276px; font-family:Lato,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; font-style:normal; font-weight:400; line-height:1.5; text-align:left; text-align:start; text-decoration:none; text-shadow:none; text-transform:none; letter-spacing:normal; word-break:normal; word-spacing:normal; white-space:normal; line-break:auto; font-size:.825rem; word-wrap:break-word; background-color:#fff; background-clip:padding-box; border:1px solid rgba(0,0,0,.2); border-radius:.3rem
}

@ -30,6 +30,7 @@
--info:#3399f3;
--warning:#d47500;
--danger:#cd0200;
--text:#eee;
--light:#eee;
--dark:#333;
--body-bg:#fff;
@ -5187,12 +5188,11 @@ a.close.disabled {
border-left-color:#000
}
.tooltip-inner {
max-width:200px;
padding:.25rem .5rem;
color:#fff;
text-align:center;
background-color:#000;
border-radius:.25rem
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: var(--light);
border-radius: .25rem
}
.popover {
position:absolute;

@ -5240,12 +5240,11 @@ a.close.disabled {
border-left-color: #000;
}
.tooltip-inner {
max-width: 200px;
padding: 3px 8px;
color: #d5d5d5;
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: #000;
border-radius: 2px;
background-color: var(--light);
border-radius: .25rem
}
.popover {
position: absolute;

@ -5701,12 +5701,11 @@ a.close.disabled {
}
.tooltip-inner {
max-width: 200px;
padding: 0.25rem 0.5rem;
color: #fff;
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: #000;
border-radius: 0.25rem;
background-color: var(--light);
border-radius: .25rem
}
.popover {

@ -5236,12 +5236,11 @@ a.close.disabled {
border-left-color: #000;
}
.tooltip-inner {
max-width: 200px;
padding: 3px 8px;
color: #fff;
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: #000;
border-radius: 2px;
background-color: var(--light);
border-radius: .25rem
}
.popover {
position: absolute;

@ -5697,12 +5697,11 @@ a.close.disabled {
}
.tooltip-inner {
max-width: 200px;
padding: 0.25rem 0.5rem;
color: #fff;
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: #000;
border-radius: 0.25rem;
background-color: var(--light);
border-radius: .25rem
}
.popover {

@ -5703,12 +5703,11 @@ a.close.disabled {
}
.tooltip-inner {
max-width: 200px;
padding: 0.25rem 0.5rem;
color: #eeede9;
padding: .25rem .5rem;
color: var(--text);
text-align: center;
background-color: #000;
border-radius: 0.25rem;
background-color: var(--light);
border-radius: .25rem
}
.popover {

@ -8,7 +8,7 @@ const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'hardhat-provider', 'intelligentScriptExecutor', 'search']
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'hardhat-provider', 'compileAndRun', 'search']
const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd)

@ -97,6 +97,10 @@ export const CompilerApiMixin = (Base) => class extends Base {
return this.call('contentImport', 'resolveAndSave', url)
}
runScriptAfterCompilation (fileName: string) {
this.call('compileAndRun', 'runScriptAfterCompilation', fileName)
}
compileWithHardhat (configFile) {
return this.call('hardhat', 'compile', configFile)
}
@ -330,6 +334,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
e.preventDefault()
if(await this.getAppParameter('hardhat-compilation')) this.compileTabLogic.runCompiler('hardhat')
else if(await this.getAppParameter('truffle-compilation')) this.compileTabLogic.runCompiler('truffle')
else this.compileTabLogic.runCompiler(undefined)
}
}
window.document.addEventListener('keydown', this.data.eventHandlers.onKeyDown)

@ -37,6 +37,7 @@ export interface ICompilerApi {
readFile: (file: string) => Promise<string>
open: (file: string) => void
saveCurrentFile: () => void
runScriptAfterCompilation: (fileName: string) => void,
logToTerminal: (log: terminalLog) => void

@ -8,6 +8,7 @@ import { compilerReducer, compilerInitialState } from './reducers/compiler'
import { resetEditorMode, listenToEvents } from './actions/compiler'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import { getValidLanguage } from '@remix-project/remix-solidity'
import { CopyToClipboard } from '@remix-ui/clipboard'
import './css/style.css'
@ -332,6 +333,19 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
compileTabLogic.runCompiler(externalCompType)
}
const compileAndRun = () => {
const currentFile = api.currentFile
if (!isSolFileSelected()) return
_setCompilerVersionFromPragma(currentFile)
let externalCompType
if (hhCompilation) externalCompType = 'hardhat'
else if (truffleCompilation) externalCompType = 'truffle'
api.runScriptAfterCompilation(currentFile)
compileTabLogic.runCompiler(externalCompType)
}
const _updateVersionSelector = (version, customUrl = '') => {
// update selectedversion of previous one got filtered out
let selectedVersion = version
@ -523,7 +537,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
onChangeRuns(settings.runs)
}
return (
return (
<section>
<article>
<header className='remixui_compilerSection border-bottom'>
@ -596,7 +610,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="form-check-label custom-control-label" htmlFor="enableHardhat">Enable Hardhat Compilation</label>
<a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/hardhat.html#enable-hardhat-compilation' target={'_blank'}>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="overlay-tooltip">
<Tooltip className="text-nowrap" id="overlay-tooltip-hardhat">
<span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Hardhat Compilation</span>
</Tooltip>
}>
@ -612,7 +626,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="form-check-label custom-control-label" htmlFor="enableTruffle">Enable Truffle Compilation</label>
<a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/' target={'_blank'}>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="overlay-tooltip">
<Tooltip className="text-nowrap" id="overlay-tooltip-truffle">
<span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Truffle Compilation</span>
</Tooltip>
}>
@ -621,12 +635,62 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</a>
</div>
}
<button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary d-block w-100 text-break remixui_disabled mt-3" title="Compile" onClick={compile} disabled={disableCompileButton}>
<span>
{ <i ref={compileIcon} className="fas fa-sync remixui_iconbtn" aria-hidden="true"></i> }
Compile { typeof state.compiledFileName === 'string' ? extractNameFromKey(state.compiledFileName) || '<no file selected>' : '<no file selected>' }
</span>
<div>
<button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block d-block w-100 text-break remixui_disabled mb-1 mt-3" onClick={compile} disabled={disableCompileButton}>
<OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile">
<div className="text-left">
<div><b>Ctrl+S</b> for compiling</div>
</div>
</Tooltip>
}>
<span>
{ <i ref={compileIcon} className="fas fa-sync remixui_iconbtn" aria-hidden="true"></i> }
Compile { typeof state.compiledFileName === 'string' ? extractNameFromKey(state.compiledFileName) || '<no file selected>' : '<no file selected>' }
</span>
</OverlayTrigger>
</button>
<div className='d-flex align-items-center'>
<button id="compileAndRunBtn" data-id="compilerContainerCompileAndRunBtn" className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3" onClick={compileAndRun} disabled={disableCompileButton}>
<OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run">
<div className="text-left">
<div><b>Ctrl+Shift+S</b> for compiling and script execution</div>
</div>
</Tooltip>
}>
<span>
Compile and Run script
</span>
</OverlayTrigger>
</button>
<OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run-doc">
<div className="text-left p-2">
<div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div>
<pre>
<code>
/**<br />
* @title ContractName<br />
* @dev ContractDescription<br />
* @custom:dev-run-script file_path<br />
*/<br />
contract ContractName {'{}'}<br />
</code>
</pre>
Click to know more
</div>
</Tooltip>
}>
<a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 mt-3 mb-1 fas fa-info text-dark"></i></a>
</OverlayTrigger>
<CopyToClipboard tip="Copy tag to use in contract NatSpec" getContent={() => '@custom:dev-run-script file_path'} direction='top'>
<button className="btn remixui_copyButton ml-2 mt-3 mb-1 text-dark">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
</button>
</CopyToClipboard>
</div>
</div>
</header>
</article>
</section>

@ -171,6 +171,11 @@
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
}
.remixui_solidityCompileAndRunButton {
width: 94%;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }

@ -7,6 +7,7 @@ pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {

Loading…
Cancel
Save