monaco editor

pull/1701/head^2
yann300 3 years ago
parent 8f7c476449
commit bc4cee9952
  1. 2
      apps/debugger/src/app/debugger-api.ts
  2. 2
      apps/remix-ide-e2e/nightwatch.ts
  3. 30
      apps/remix-ide-e2e/src/commands/addFile.ts
  4. 2
      apps/remix-ide-e2e/src/commands/checkAnnotations.ts
  5. 2
      apps/remix-ide-e2e/src/commands/checkAnnotationsNotPresent.ts
  6. 22
      apps/remix-ide-e2e/src/commands/editorScroll.ts
  7. 4
      apps/remix-ide-e2e/src/commands/getEditorValue.ts
  8. 17
      apps/remix-ide-e2e/src/commands/scrollToLine.ts
  9. 5
      apps/remix-ide-e2e/src/commands/setEditorValue.ts
  10. 7
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  11. 5
      apps/remix-ide-e2e/src/tests/ballot_0_4_11.spec.ts
  12. 12
      apps/remix-ide-e2e/src/tests/debugger.spec.ts
  13. 128
      apps/remix-ide-e2e/src/tests/editor.spec.ts
  14. 4
      apps/remix-ide-e2e/src/tests/fileManager_api.spec.ts
  15. 14
      apps/remix-ide-e2e/src/tests/recorder.spec.ts
  16. 2
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  17. 4
      apps/remix-ide-e2e/src/tests/solidityImport.spec.ts
  18. 2
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  19. 2
      apps/remix-ide-e2e/src/types/index.d.ts
  20. 593
      apps/remix-ide/src/app.js
  21. 84
      apps/remix-ide/src/app/editor/SourceHighlighters.js
  22. 16
      apps/remix-ide/src/app/editor/contextualListener.js
  23. 497
      apps/remix-ide/src/app/editor/editor.js
  24. 86
      apps/remix-ide/src/app/editor/sourceHighlighter.js
  25. 2
      apps/remix-ide/src/app/panels/file-panel.js
  26. 3
      apps/remix-ide/src/app/panels/tab-proxy.js
  27. 1
      apps/remix-ide/src/blockchain/providers/vm.js
  28. 2
      apps/remix-ide/src/lib/cmdInterpreterAPI.js
  29. 2
      libs/remix-ui/editor/src/index.ts
  30. 0
      libs/remix-ui/editor/src/lib/monaco-editor/monaco-editor.css
  31. 11
      libs/remix-ui/editor/src/lib/monaco-editor/monaco-editor.spec.tsx
  32. 16
      libs/remix-ui/editor/src/lib/monaco-editor/monaco-editor.tsx
  33. 11
      libs/remix-ui/editor/src/lib/remix-ui-editor.css
  34. 300
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  35. 7709
      libs/remix-ui/editor/src/types/monaco.d.ts
  36. 8
      libs/remix-ui/static-analyser/src/lib/ErrorRenderer.tsx
  37. 22
      package-lock.json
  38. 25
      package.json

@ -44,6 +44,8 @@ export const DebuggerApiMixin = (Base) => class extends Base {
}
async highlight (lineColumnPos, path) {
await this.call('fileManager', 'open', path)
await this.call('editor', 'gotoLine', lineColumnPos.start.line, lineColumnPos.start.column)
await this.call('editor', 'highlight', lineColumnPos, path)
}

@ -46,7 +46,7 @@ module.exports = {
'chrome-runAndDeploy': {
desiredCapabilities: {
browserName: 'chrome',
browserName: 'firefox',
javascriptEnabled: true,
acceptSslCerts: true,
'goog:chromeOptions': {

@ -17,16 +17,26 @@ function addFile (browser: NightwatchBrowser, name: string, content: NightwatchC
browser.clickLaunchIcon('udapp')
.clickLaunchIcon('filePanel')
.click('li[data-id="treeViewLitreeViewItemREADME.txt"]') // focus on root directory
.click('[data-id="fileExplorerNewFilecreateNewFile"]')
.waitForElementContainsText('*[data-id$="/blank"]', '', 60000)
.sendKeys('*[data-id$="/blank"] .remixui_items', name)
.sendKeys('*[data-id$="/blank"] .remixui_items', browser.Keys.ENTER)
.pause(2000)
.waitForElementVisible(`li[data-id="treeViewLitreeViewItem${name}"]`, 60000)
.setEditorValue(content.content)
.pause(1000)
.perform(function () {
done()
.elements('css selector', `li[data-id="treeViewLitreeViewItem${name}"]`, (res) => {
console.log(res)
if (res.value && (res.value as any).length > 0) {
browser.openFile(name)
.perform(function () {
done()
})
} else {
browser.click('[data-id="fileExplorerNewFilecreateNewFile"]')
.waitForElementContainsText('*[data-id$="/blank"]', '', 60000)
.sendKeys('*[data-id$="/blank"] .remixui_items', name)
.sendKeys('*[data-id$="/blank"] .remixui_items', browser.Keys.ENTER)
.pause(2000)
.waitForElementVisible(`li[data-id="treeViewLitreeViewItem${name}"]`, 60000)
.setEditorValue(content.content)
.pause(1000)
.perform(function () {
done()
})
}
})
}

@ -3,7 +3,7 @@ import { NightwatchBrowser } from 'nightwatch'
class checkAnnotations extends EventEmitter {
command (this: NightwatchBrowser, type: string, line: number): NightwatchBrowser {
this.api.assert.containsText(`.ace_${type}`, line.toString()).perform(() => this.emit('complete'))
this.api.assert.containsText(`.margin-view-overlays .${type} + div`, line.toString()).perform(() => this.emit('complete'))
return this
}
}

@ -3,7 +3,7 @@ import { NightwatchBrowser } from 'nightwatch'
class checkAnnotationsNotPresent extends EventEmitter {
command (this: NightwatchBrowser, type: string): NightwatchBrowser {
this.api.waitForElementNotPresent(`.ace_${type}`).perform(() => this.emit('complete'))
this.api.waitForElementNotPresent(`.margin-view-overlays .${type}`).perform(() => this.emit('complete'))
return this
}
}

@ -1,22 +0,0 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
// fix for editor scroll
class ScrollEditor extends EventEmitter {
command (this: NightwatchBrowser, direction: 'up' | 'down', numberOfTimes: number): NightwatchBrowser {
const browser = this.api
browser.waitForElementPresent('.ace_text-input')
for (let i = 0; i < numberOfTimes; i++) {
if (direction.toLowerCase() === 'up') browser.sendKeys('.ace_text-input', browser.Keys.ARROW_UP)
if (direction.toLowerCase() === 'down') browser.sendKeys('.ace_text-input', browser.Keys.ARROW_DOWN)
}
browser.perform((done) => {
done()
this.emit('complete')
})
return this
}
}
module.exports = ScrollEditor

@ -5,9 +5,9 @@ class GetEditorValue extends EventEmitter {
command (this: NightwatchBrowser, callback: (content: string) => void): NightwatchBrowser {
this.api.perform((client, done) => {
this.api.execute(function () {
const elem: any = document.getElementById('input')
const elem: any = document.getElementById('editorView')
return elem.editor.getValue()
return elem.currentContent()
}, [], (result) => {
done()
const value = typeof result.value === 'string' ? result.value : null

@ -0,0 +1,17 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class ScrollToLine extends EventEmitter {
command (this: NightwatchBrowser, line: number): NightwatchBrowser {
this.api.execute(function (line) {
const elem: any = document.getElementById('editorView')
elem.gotoLine(line)
}, [line], () => {
this.emit('complete')
})
return this
}
}
module.exports = ScrollToLine

@ -3,11 +3,12 @@ import EventEmitter from 'events'
class SetEditorValue extends EventEmitter {
command (this: NightwatchBrowser, value: string, callback?: VoidFunction): NightwatchBrowser {
console.log('set editor', value)
this.api.perform((client, done) => {
this.api.execute(function (value) {
const elem: any = document.getElementById('input')
const elem: any = document.getElementById('editorView')
elem.editor.session.setValue(value)
elem.setCurrentContent(value)
}, [value], () => {
done()
if (callback) {

@ -81,8 +81,9 @@ module.exports = {
'Deploy and use Ballot using external web3': function (browser: NightwatchBrowser) {
browser
.click('option[value="web3"]')
.pause(5000)
.openFile('Untitled.sol')
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]')
.modalFooterOKClick()
.execute(function () {
const env: any = document.getElementById('selectExEnvOptions')
@ -92,8 +93,8 @@ module.exports = {
browser.assert.ok(result.value === 'web3', 'Web3 Provider not selected')
})
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.pause(2000)
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.clickInstance(0)

@ -28,7 +28,6 @@ module.exports = {
'Deploy Ballot': function (browser: NightwatchBrowser) {
browser.pause(500)
.testContracts('Untitled.sol', sources[0]['Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.setValue('input[placeholder="uint8 _numProposals"]', '2')
@ -77,11 +76,13 @@ module.exports = {
'Deploy and use Ballot using external web3': function (browser: NightwatchBrowser) {
browser
.openFile('Untitled.sol')
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]')
.modalFooterOKClick()
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.pause(2000)
.setValue('input[placeholder="uint8 _numProposals"]', '2')
.click('*[data-id="Deploy - transact (not payable)"]')
.clickInstance(0)

@ -62,9 +62,13 @@ module.exports = {
},
'Should jump through breakpoints': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.click('.ace_gutter-cell:nth-of-type(10)')
.click('.ace_gutter-cell:nth-of-type(20)')
browser.waitForElementVisible('#editorView')
.execute(() => {
(window as any).addRemixBreakpoint(11)
}, [], () => {})
.execute(() => {
(window as any).addRemixBreakpoint(21)
}, [], () => {})
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000)
@ -127,7 +131,7 @@ module.exports = {
But the debugger uses now validSourcelocation, which means file is not -1.
In that case the source highlight at 261 should be the same as for step 262
*/
.waitForElementPresent('.highlightLine7')
.waitForElementPresent('.highlightLine8')
.goToVMTraceStep(266)
.pause(1000)
.checkVariableDebug('soliditylocals', localVariable_step266_ABIEncoder) // locals should not be initiated at this point, only idAsk should

@ -15,37 +15,35 @@ module.exports = {
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('contracts')
.openFile('contracts/1_Storage.sol')
.waitForElementVisible('*[data-id="editorInput"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '12px')
.waitForElementVisible('#editorView')
.checkElementStyle('.view-lines', 'font-size', '14px')
.click('*[data-id="tabProxyZoomIn"]')
.click('*[data-id="tabProxyZoomIn"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '14px')
.checkElementStyle('.view-lines', 'font-size', '16px')
},
'Should zoom out editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '14px')
browser.waitForElementVisible('#editorView')
.checkElementStyle('.view-lines', 'font-size', '16px')
.click('*[data-id="tabProxyZoomOut"]')
.click('*[data-id="tabProxyZoomOut"]')
.checkElementStyle('*[data-id="editorInput"]', 'font-size', '12px')
.checkElementStyle('.view-lines', 'font-size', '14px')
},
'Should display compile error in editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.waitForElementVisible('*[class="ace_content"]')
.click('*[class="ace_content"]')
.editorScroll('down', 27) // scroll down to line 27 and add the error word
.sendKeys('*[class="ace_text-input"]', 'error')
.waitForElementVisible('.ace_error', 120000)
.checkAnnotations('error', 28)
browser.waitForElementVisible('#editorView')
.setEditorValue(storageContractWithError + 'error')
.pause(2000)
.waitForElementVisible('.margin-view-overlays .fa-exclamation-square', 120000)
.checkAnnotations('fa-exclamation-square', 29) // error
.clickLaunchIcon('udapp')
.checkAnnotationsNotPresent('error')
.checkAnnotationsNotPresent('fa-exclamation-square') // error
.clickLaunchIcon('solidity')
.checkAnnotations('error', 28)
.checkAnnotations('fa-exclamation-square', 29) // error
},
'Should minimize and maximize codeblock in editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
'Should minimize and maximize codeblock in editor': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.waitForElementVisible('.ace_open')
.click('.ace_start:nth-of-type(1)')
.waitForElementVisible('.ace_closed')
@ -54,27 +52,29 @@ module.exports = {
},
'Should add breakpoint to editor': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
.waitForElementNotPresent('.ace_breakpoint')
.click('.ace_gutter-cell:nth-of-type(1)')
.waitForElementVisible('.ace_breakpoint')
browser.waitForElementVisible('#editorView')
.waitForElementNotPresent('.margin-view-overlays .fa-circle')
.execute(() => {
(window as any).addRemixBreakpoint(1)
}, [], () => {})
.waitForElementVisible('.margin-view-overlays .fa-circle')
},
'Should load syntax highlighter for ace light theme': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="editorInput"]')
'Should load syntax highlighter for ace light theme': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.checkElementStyle('.ace_keyword', 'color', aceThemes.light.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.light.comment)
.checkElementStyle('.ace_function', 'color', aceThemes.light.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.light.variable)
},
'Should load syntax highlighter for ace dark theme': function (browser: NightwatchBrowser) {
'Should load syntax highlighter for ace dark theme': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]')
.click('*[data-id="verticalIconsKindsettings"]')
.waitForElementVisible('*[data-id="settingsTabThemeLabelDark"]')
.click('*[data-id="settingsTabThemeLabelDark"]')
.pause(2000)
.waitForElementVisible('*[data-id="editorInput"]')
.waitForElementVisible('#editorView')
/* @todo(#2863) ch for class and not colors
.checkElementStyle('.ace_keyword', 'color', aceThemes.dark.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.dark.comment)
@ -87,24 +87,21 @@ module.exports = {
// include all files here because switching between plugins in side-panel removes highlight
browser
.addFile('sourcehighlight.js', sourcehighlightScript)
.addFile('removeSourcehighlightScript.js', removeSourcehighlightScript)
.addFile('removeAllSourcehighlightScript.js', removeAllSourcehighlightScript)
.openFile('sourcehighlight.js')
.executeScript('remix.exeCurrent()')
.editorScroll('down', 60)
.pause(1000)
.waitForElementPresent('.highlightLine32', 60000)
.pause(1000)
.checkElementStyle('.highlightLine32', 'background-color', 'rgb(8, 108, 181)')
.pause(1000)
.waitForElementPresent('.highlightLine40', 60000)
.pause(1000)
.checkElementStyle('.highlightLine40', 'background-color', 'rgb(8, 108, 181)')
.waitForElementPresent('.highlightLine50', 60000)
.checkElementStyle('.highlightLine50', 'background-color', 'rgb(8, 108, 181)')
.scrollToLine(32)
.waitForElementPresent('.highlightLine33', 60000)
.checkElementStyle('.highlightLine33', 'background-color', 'rgb(52, 152, 219)')
.scrollToLine(40)
.waitForElementPresent('.highlightLine41', 60000)
.checkElementStyle('.highlightLine41', 'background-color', 'rgb(52, 152, 219)')
.scrollToLine(50)
.waitForElementPresent('.highlightLine51', 60000)
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
},
'Should remove 1 highlight from source code': function (browser: NightwatchBrowser) {
'Should remove 1 highlight from source code': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.pause(2000)
@ -113,9 +110,9 @@ module.exports = {
.click('li[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.click('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.waitForElementNotPresent('.highlightLine32', 60000)
.checkElementStyle('.highlightLine40', 'background-color', 'rgb(8, 108, 181)')
.checkElementStyle('.highlightLine50', 'background-color', 'rgb(8, 108, 181)')
.waitForElementNotPresent('.highlightLine33', 60000)
.checkElementStyle('.highlightLine41', 'background-color', 'rgb(52, 152, 219)')
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
},
'Should remove all highlights from source code': function (browser: NightwatchBrowser) {
@ -126,9 +123,9 @@ module.exports = {
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.click('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.pause(2000)
.waitForElementNotPresent('.highlightLine32', 60000)
.waitForElementNotPresent('.highlightLine40', 60000)
.waitForElementNotPresent('.highlightLine50', 60000)
.waitForElementNotPresent('.highlightLine33', 60000)
.waitForElementNotPresent('.highlightLine41', 60000)
.waitForElementNotPresent('.highlightLine51', 60000)
.end()
}
}
@ -152,6 +149,7 @@ const sourcehighlightScript = {
content: `
(async () => {
try {
await remix.call('fileManager', 'open', 'contracts/3_Ballot.sol')
const pos = {
start: {
line: 32,
@ -194,18 +192,6 @@ const sourcehighlightScript = {
`
}
const removeSourcehighlightScript = {
content: `
(async () => {
try {
await remix.call('editor', 'discardHighlightAt', 32, 'contracts/3_Ballot.sol')
} catch (e) {
console.log(e.message)
}
})()
`
}
const removeAllSourcehighlightScript = {
content: `
(async () => {
@ -217,3 +203,33 @@ const removeAllSourcehighlightScript = {
})()
`
}
const storageContractWithError = `
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}`

@ -36,7 +36,9 @@ module.exports = {
.executeScript('remix.exeCurrent()')
.pause(2000)
.openFile('new_contract.sol')
.assert.containsText('[data-id="editorInput"]', 'pragma solidity ^0.6.0')
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('pragma solidity ^0.6.0') !== -1, 'content does not contain "pragma solidity ^0.6.0"')
})
},
'Should execute `readFile` api from file manager external api': function (browser: NightwatchBrowser) {

@ -129,7 +129,7 @@ contract t2est {
const records = `{
"accounts": {
"account{2}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"
"account{10}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"
},
"linkReferences": {
"testLib": "created{1512830014773}"
@ -146,7 +146,7 @@ const records = `{
"linkReferences": {},
"inputs": "()",
"type": "constructor",
"from": "account{2}"
"from": "account{10}"
}
},
{
@ -172,7 +172,7 @@ const records = `{
"name": "",
"type": "constructor",
"inputs": "(uint256)",
"from": "account{2}"
"from": "account{10}"
}
},
{
@ -188,7 +188,7 @@ const records = `{
"name": "set",
"inputs": "(uint256,address)",
"type": "function",
"from": "account{2}"
"from": "account{10}"
}
}
],
@ -287,7 +287,7 @@ const records = `{
const scenario = {
accounts: {
'account{2}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
'account{10}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
},
linkReferences: {},
transactions: [
@ -305,7 +305,7 @@ const scenario = {
name: '',
type: 'constructor',
inputs: '(uint256)',
from: 'account{2}'
from: 'account{10}'
}
},
{
@ -320,7 +320,7 @@ const scenario = {
name: 'set',
inputs: '(uint256)',
type: 'function',
from: 'account{2}'
from: 'account{10}'
}
}
],

@ -85,7 +85,7 @@ module.exports = {
.waitForElementPresent('#staticanalysisresult .warning', 2000, true, function () {
browser
.click('[data-id="staticAnalysisModuleMiscellaneous1"')
.waitForElementPresent('.highlightLine15', 60000)
.waitForElementPresent('.highlightLine16', 60000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(
'function _sendLogPayload(bytes memory payload) private view {') !== -1,

@ -75,7 +75,9 @@ module.exports = {
.waitForElementVisible('[data-id="https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"]', 120000)
.scrollAndClick('[data-id="https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"]') // click on error which point to ERC20 code
.pause(5000)
.waitForElementContainsText('#input', 'contract ERC20 is Context, IERC20', 60000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('contract ERC20 is Context, IERC20') !== -1, 'content does not contain "contract ERC20 is Context, IERC20"')
})
},
'Test NPM Import (with unpkg.com)': function (browser: NightwatchBrowser) {

@ -53,7 +53,7 @@ module.exports = {
'Call web3.eth.getAccounts() using JavaScript VM': function (browser: NightwatchBrowser) {
browser
.executeScript('web3.eth.getAccounts()')
.waitForElementContainsText('*[data-id="terminalJournal"]', '["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2","0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c","0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db","0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB","0x617F2E2fD72FD9D5503197092aC168c91465E7f2","0x17F6AD8Ef982297579C203069C1DbfFE4348c372","0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C","0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678","0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7","0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C","0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC","0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB","0x583031D1113aD414F02576BD6afaBfb302140225","0xdD870fA1b7C4700F2BD7f44238821C26f7392148"]', 80000)
.waitForElementContainsText('*[data-id="terminalJournal"]', '"0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C", "0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC", "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148"', 80000)
},
'Call web3.eth.getAccounts() using Web3 Provider': function (browser: NightwatchBrowser) {

@ -27,9 +27,9 @@ declare module 'nightwatch' {
debugTransaction(index: number): NightwatchBrowser,
checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser,
openFile(name: string): NightwatchBrowser,
editorScroll(direction: 'up' | 'down', numberOfTimes: number): NightwatchBrowser,
renamePath(path: string, newFileName: string, renamedPath: string): NightwatchBrowser,
rightClick(cssSelector: string): NightwatchBrowser,
scrollToLine(line: number): NightwatchBrowser,
waitForElementContainsText(id: string, value: string, timeout?: number): NightwatchBrowser,
getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser,
modalFooterCancelClick(): NightwatchBrowser,

@ -162,8 +162,7 @@ class App {
}
init () {
var self = this
run.apply(self)
this.run().catch(console.error)
}
render () {
@ -202,317 +201,319 @@ class App {
`
return self._view.el
}
}
module.exports = App
async function run () {
var self = this
// check the origin and warn message
if (window.location.hostname === 'yann300.github.io') {
modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.')
} else if (window.location.hostname === 'remix-alpha.ethereum.org' ||
(window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) {
modalDialogCustom.alert('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.')
} else if (window.location.protocol.indexOf('http') === 0 &&
window.location.hostname !== 'remix.ethereum.org' &&
window.location.hostname !== 'localhost' &&
window.location.hostname !== '127.0.0.1') {
modalDialogCustom.alert(`The Remix IDE has moved to http://remix.ethereum.org.\n
This instance of Remix you are visiting WILL NOT BE UPDATED.\n
Please make a backup of your contracts and start using http://remix.ethereum.org`)
}
if (window.location.protocol.indexOf('https') === 0) {
toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.')
}
async run () {
var self = this
const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080']
// workaround for Electron support
if (!isElectron() && !hosts.includes(window.location.host)) {
// Oops! Accidentally trigger refresh or bookmark.
window.onbeforeunload = function () {
return 'Are you sure you want to leave?'
// check the origin and warn message
if (window.location.hostname === 'yann300.github.io') {
modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.')
} else if (window.location.hostname === 'remix-alpha.ethereum.org' ||
(window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) {
modalDialogCustom.alert('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.')
} else if (window.location.protocol.indexOf('http') === 0 &&
window.location.hostname !== 'remix.ethereum.org' &&
window.location.hostname !== 'localhost' &&
window.location.hostname !== '127.0.0.1') {
modalDialogCustom.alert(`The Remix IDE has moved to http://remix.ethereum.org.\n
This instance of Remix you are visiting WILL NOT BE UPDATED.\n
Please make a backup of your contracts and start using http://remix.ethereum.org`)
}
if (window.location.protocol.indexOf('https') === 0) {
toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.')
}
}
// APP_MANAGER
const appManager = self.appManager
const pluginLoader = appManager.pluginLoader
const workspace = pluginLoader.get()
const engine = new RemixEngine()
engine.register(appManager)
// SERVICES
// ----------------- theme service ---------------------------------
const themeModule = new ThemeModule(registry)
registry.put({ api: themeModule, name: 'themeModule' })
themeModule.initTheme(() => {
setTimeout(() => {
document.body.removeChild(self._view.splashScreen)
self._view.el.style.visibility = 'visible'
}, 1500)
})
// ----------------- editor service ----------------------------
const editor = new Editor({}, themeModule) // wrapper around ace editor
registry.put({ api: editor, name: 'editor' })
editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile())
// ----------------- fileManager service ----------------------------
const fileManager = new FileManager(editor, appManager)
registry.put({ api: fileManager, name: 'filemanager' })
// ----------------- dGit provider ---------------------------------
const dGitProvider = new DGitProvider()
// ----------------- import content service ------------------------
const contentImport = new CompilerImports()
const blockchain = new Blockchain(registry.get('config').api)
// ----------------- compilation metadata generation service ---------
const compilerMetadataGenerator = new CompilerMetadata()
// ----------------- compilation result service (can keep track of compilation results) ----------------------------
const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name)
registry.put({ api: compilersArtefacts, name: 'compilersartefacts' })
// service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it
const fetchAndCompile = new FetchAndCompile()
// ----------------- network service (resolve network id / name) -----
const networkModule = new NetworkModule(blockchain)
// ----------------- represent the current selected web3 provider ----
const web3Provider = new Web3ProviderModule(blockchain)
const hardhatProvider = new HardhatProvider(blockchain)
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
registry.put({ api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter' })
// -------------------Terminal----------------------------------------
makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl))
const terminal = new Terminal(
{ appManager, blockchain },
{
getPosition: (event) => {
var limitUp = 36
var limitDown = 20
var height = window.innerHeight
var newpos = (event.pageY < limitUp) ? limitUp : event.pageY
newpos = (newpos < height - limitDown) ? newpos : height - limitDown
return height - newpos
const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080']
// workaround for Electron support
if (!isElectron() && !hosts.includes(window.location.host)) {
// Oops! Accidentally trigger refresh or bookmark.
window.onbeforeunload = function () {
return 'Are you sure you want to leave?'
}
},
registry
)
const contextualListener = new ContextualListener({ editor })
engine.register([
blockchain,
contentImport,
themeModule,
editor,
fileManager,
compilerMetadataGenerator,
compilersArtefacts,
networkModule,
offsetToLineColumnConverter,
contextualListener,
terminal,
web3Provider,
fetchAndCompile,
dGitProvider,
hardhatProvider
])
// LAYOUT & SYSTEM VIEWS
const appPanel = new MainPanel()
const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal)
registry.put({ api: mainview, name: 'mainview' })
engine.register([
appPanel,
mainview.tabProxy
])
// those views depend on app_manager
const menuicons = new VerticalIcons(appManager)
const sidePanel = new SidePanel(appManager, menuicons)
const hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager, engine)
const filePanel = new FilePanel(appManager)
const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel, contentImport)
const settings = new SettingsTab(
registry.get('config').api,
editor,
appManager
)
// adding Views to the DOM
self._view.mainpanel.appendChild(mainview.render())
self._view.iconpanel.appendChild(menuicons.render())
self._view.sidepanel.appendChild(sidePanel.render())
document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body
engine.register([
menuicons,
landingPage,
hiddenPanel,
sidePanel,
filePanel,
pluginManagerComponent,
settings
])
const queryParams = new QueryParams()
const params = queryParams.get()
const onAcceptMatomo = () => {
_paq.push(['forgetUserOptOut'])
// @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used
document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'
settings.updateMatomoAnalyticsChoice(true)
const el = document.getElementById('modal-dialog')
el.parentElement.removeChild(el)
startWalkthroughService()
}
const onDeclineMatomo = () => {
settings.updateMatomoAnalyticsChoice(false)
_paq.push(['optUserOut'])
const el = document.getElementById('modal-dialog')
el.parentElement.removeChild(el)
startWalkthroughService()
}
const startWalkthroughService = () => {
const walkthroughService = new WalkthroughService(localStorage)
if (!params.code && !params.url && !params.minimizeterminal && !params.gist && !params.minimizesidepanel) {
walkthroughService.start()
}
}
// Ask to opt in to Matomo for remix, remix-alpha and remix-beta
const matomoDomains = {
'remix-alpha.ethereum.org': 27,
'remix-beta.ethereum.org': 25,
'remix.ethereum.org': 23
}
if (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics')) {
modalDialog(
'Help us to improve Remix IDE',
yo`
<div>
<p>An Opt-in version of <a href="https://matomo.org" target="_blank">Matomo</a>, an open source data analytics platform is being used to improve Remix IDE.</p>
<p>We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.</p>
<p>All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: <a href="https://matomo.ethereum.org/index.php?module=MultiSites&action=index&idSite=23&period=day&date=yesterday" target="_blank">take a look</a>.</p>
<p>We do not collect nor store any personally identifiable information (PII).</p>
<p>For more info, see: <a href="https://medium.com/p/66ef69e14931/" target="_blank">Matomo Analyitcs on Remix iDE</a>.</p>
<p>You can change your choice in the Settings panel anytime.</p>
<div class="d-flex justify-content-around pt-3 border-top">
<button class="btn btn-primary ${css.matomoBtn}" onclick=${() => onAcceptMatomo()}>Sure</button>
<button class="btn btn-secondary ${css.matomoBtn}" onclick=${() => onDeclineMatomo()}>Decline</button>
</div>
</div>`,
{
label: '',
fn: null
},
// APP_MANAGER
const appManager = self.appManager
const pluginLoader = appManager.pluginLoader
const workspace = pluginLoader.get()
const engine = new RemixEngine()
engine.register(appManager)
// SERVICES
// ----------------- theme service ---------------------------------
const themeModule = new ThemeModule(registry)
registry.put({ api: themeModule, name: 'themeModule' })
themeModule.initTheme(() => {
setTimeout(() => {
document.body.removeChild(self._view.splashScreen)
self._view.el.style.visibility = 'visible'
}, 1500)
})
// ----------------- editor service ----------------------------
const editor = new Editor() // wrapper around ace editor
registry.put({ api: editor, name: 'editor' })
editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile())
// ----------------- fileManager service ----------------------------
const fileManager = new FileManager(editor, appManager)
registry.put({ api: fileManager, name: 'filemanager' })
// ----------------- dGit provider ---------------------------------
const dGitProvider = new DGitProvider()
// ----------------- import content service ------------------------
const contentImport = new CompilerImports()
const blockchain = new Blockchain(registry.get('config').api)
// ----------------- compilation metadata generation service ---------
const compilerMetadataGenerator = new CompilerMetadata()
// ----------------- compilation result service (can keep track of compilation results) ----------------------------
const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name)
registry.put({ api: compilersArtefacts, name: 'compilersartefacts' })
// service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it
const fetchAndCompile = new FetchAndCompile()
// ----------------- network service (resolve network id / name) -----
const networkModule = new NetworkModule(blockchain)
// ----------------- represent the current selected web3 provider ----
const web3Provider = new Web3ProviderModule(blockchain)
const hardhatProvider = new HardhatProvider(blockchain)
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
registry.put({ api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter' })
// -------------------Terminal----------------------------------------
const terminal = new Terminal(
{ appManager, blockchain },
{
label: '',
fn: null
getPosition: (event) => {
var limitUp = 36
var limitDown = 20
var height = window.innerHeight
var newpos = (event.pageY < limitUp) ? limitUp : event.pageY
newpos = (newpos < height - limitDown) ? newpos : height - limitDown
return height - newpos
}
}
)
} else {
startWalkthroughService()
}
makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl))
const contextualListener = new ContextualListener({ editor })
engine.register([
blockchain,
contentImport,
themeModule,
editor,
fileManager,
compilerMetadataGenerator,
compilersArtefacts,
networkModule,
offsetToLineColumnConverter,
contextualListener,
terminal,
web3Provider,
fetchAndCompile,
dGitProvider,
hardhatProvider
])
// LAYOUT & SYSTEM VIEWS
const appPanel = new MainPanel()
const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal)
registry.put({ api: mainview, name: 'mainview' })
engine.register([
appPanel,
mainview.tabProxy
])
// those views depend on app_manager
const menuicons = new VerticalIcons(appManager)
const sidePanel = new SidePanel(appManager, menuicons)
const hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager, engine)
const filePanel = new FilePanel(appManager)
const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel, contentImport)
const settings = new SettingsTab(
registry.get('config').api,
editor,
appManager
)
// CONTENT VIEWS & DEFAULT PLUGINS
const compileTab = new CompileTab(registry.get('config').api, registry.get('filemanager').api)
const run = new RunTab(
blockchain,
registry.get('config').api,
registry.get('filemanager').api,
registry.get('editor').api,
filePanel,
registry.get('compilersartefacts').api,
networkModule,
mainview,
registry.get('fileproviders/browser').api
)
const analysis = new AnalysisTab(registry)
const debug = new DebuggerTab()
const test = new TestTab(
registry.get('filemanager').api,
registry.get('offsettolinecolumnconverter').api,
filePanel,
compileTab,
appManager,
contentImport
)
engine.register([
compileTab,
run,
debug,
analysis,
test,
filePanel.remixdHandle,
filePanel.gitHandle,
filePanel.hardhatHandle,
filePanel.slitherHandle
])
if (isElectron()) {
appManager.activatePlugin('remixd')
}
// adding Views to the DOM
self._view.mainpanel.appendChild(mainview.render())
self._view.iconpanel.appendChild(menuicons.render())
self._view.sidepanel.appendChild(sidePanel.render())
document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body
engine.register([
menuicons,
landingPage,
hiddenPanel,
sidePanel,
filePanel,
pluginManagerComponent,
settings
])
const queryParams = new QueryParams()
const params = queryParams.get()
const onAcceptMatomo = () => {
_paq.push(['forgetUserOptOut'])
// @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used
document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'
settings.updateMatomoAnalyticsChoice(true)
const el = document.getElementById('modal-dialog')
el.parentElement.removeChild(el)
startWalkthroughService()
}
const onDeclineMatomo = () => {
settings.updateMatomoAnalyticsChoice(false)
_paq.push(['optUserOut'])
const el = document.getElementById('modal-dialog')
el.parentElement.removeChild(el)
startWalkthroughService()
}
try {
engine.register(await appManager.registeredPlugins())
} catch (e) {
console.log('couldn\'t register iframe plugins', e.message)
}
const startWalkthroughService = () => {
const walkthroughService = new WalkthroughService(localStorage)
if (!params.code && !params.url && !params.minimizeterminal && !params.gist && !params.minimizesidepanel) {
walkthroughService.start()
}
}
await appManager.activatePlugin(['theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await appManager.activatePlugin(['home'])
await appManager.activatePlugin(['settings'])
await appManager.activatePlugin(['hiddenPanel', 'filePanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport'])
await appManager.registerContextMenuItems()
// Set workspace after initial activation
if (Array.isArray(workspace)) {
appManager.activatePlugin(workspace).then(async () => {
try {
if (params.deactivate) {
await appManager.deactivatePlugin(params.deactivate.split(','))
// Ask to opt in to Matomo for remix, remix-alpha and remix-beta
const matomoDomains = {
'remix-alpha.ethereum.org': 27,
'remix-beta.ethereum.org': 25,
'remix.ethereum.org': 23
}
if (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics')) {
modalDialog(
'Help us to improve Remix IDE',
yo`
<div>
<p>An Opt-in version of <a href="https://matomo.org" target="_blank">Matomo</a>, an open source data analytics platform is being used to improve Remix IDE.</p>
<p>We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.</p>
<p>All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: <a href="https://matomo.ethereum.org/index.php?module=MultiSites&action=index&idSite=23&period=day&date=yesterday" target="_blank">take a look</a>.</p>
<p>We do not collect nor store any personally identifiable information (PII).</p>
<p>For more info, see: <a href="https://medium.com/p/66ef69e14931/" target="_blank">Matomo Analyitcs on Remix iDE</a>.</p>
<p>You can change your choice in the Settings panel anytime.</p>
<div class="d-flex justify-content-around pt-3 border-top">
<button class="btn btn-primary ${css.matomoBtn}" onclick=${() => onAcceptMatomo()}>Sure</button>
<button class="btn btn-secondary ${css.matomoBtn}" onclick=${() => onDeclineMatomo()}>Decline</button>
</div>
</div>`,
{
label: '',
fn: null
},
{
label: '',
fn: null
}
} catch (e) {
console.log(e)
}
)
} else {
startWalkthroughService()
}
if (params.code) {
// if code is given in url we focus on solidity plugin
menuicons.select('solidity')
} else {
// If plugins are loaded from the URL params, we focus on the last one.
if (pluginLoader.current === 'queryParams' && workspace.length > 0) menuicons.select(workspace[workspace.length - 1])
}
// CONTENT VIEWS & DEFAULT PLUGINS
const compileTab = new CompileTab(registry.get('config').api, registry.get('filemanager').api)
const run = new RunTab(
blockchain,
registry.get('config').api,
registry.get('filemanager').api,
registry.get('editor').api,
filePanel,
registry.get('compilersartefacts').api,
networkModule,
mainview,
registry.get('fileproviders/browser').api
)
const analysis = new AnalysisTab(registry)
const debug = new DebuggerTab()
const test = new TestTab(
registry.get('filemanager').api,
registry.get('offsettolinecolumnconverter').api,
filePanel,
compileTab,
appManager,
contentImport
)
if (params.call) {
const callDetails = params.call.split('//')
if (callDetails.length > 1) {
toolTip(`initiating ${callDetails[0]} ...`)
// @todo(remove the timeout when activatePlugin is on 0.3.0)
appManager.call(...callDetails).catch(console.error)
}
engine.register([
compileTab,
run,
debug,
analysis,
test,
filePanel.remixdHandle,
filePanel.gitHandle,
filePanel.hardhatHandle,
filePanel.slitherHandle
])
if (isElectron()) {
appManager.activatePlugin('remixd')
}
try {
engine.register(await appManager.registeredPlugins())
} catch (e) {
console.log('couldn\'t register iframe plugins', e.message)
}
await appManager.activatePlugin(['theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await appManager.activatePlugin(['home'])
await appManager.activatePlugin(['settings'])
await appManager.activatePlugin(['hiddenPanel', 'filePanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport'])
await appManager.registerContextMenuItems()
// Set workspace after initial activation
appManager.on('editor', 'editorMounted', () => {
if (Array.isArray(workspace)) {
appManager.activatePlugin(workspace).then(async () => {
try {
if (params.deactivate) {
await appManager.deactivatePlugin(params.deactivate.split(','))
}
} catch (e) {
console.log(e)
}
if (params.code) {
// if code is given in url we focus on solidity plugin
menuicons.select('solidity')
} else {
// If plugins are loaded from the URL params, we focus on the last one.
if (pluginLoader.current === 'queryParams' && workspace.length > 0) menuicons.select(workspace[workspace.length - 1])
}
if (params.call) {
const callDetails = params.call.split('//')
if (callDetails.length > 1) {
toolTip(`initiating ${callDetails[0]} ...`)
// @todo(remove the timeout when activatePlugin is on 0.3.0)
appManager.call(...callDetails).catch(console.error)
}
}
}).catch(console.error)
} else {
// activate solidity plugin
appManager.activatePlugin(['solidity', 'udapp'])
}
}).catch(console.error)
} else {
// activate solidity plugin
appManager.activatePlugin(['solidity', 'udapp'])
}
})
// Load and start the service who manager layout and frame
const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature)
// Load and start the service who manager layout and frame
const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature)
if (params.embed) framingService.embed()
framingService.start(params)
if (params.embed) framingService.embed()
framingService.start(params)
}
}
module.exports = App

@ -1,84 +0,0 @@
'use strict'
const SourceHighlighter = require('./sourceHighlighter')
class SourceHighlighters {
constructor () {
this.highlighters = {}
}
highlight (position, filePath, hexColor, from) {
// eslint-disable-next-line
try {
if (!this.highlighters[from]) this.highlighters[from] = []
const sourceHighlight = new SourceHighlighter()
if (
!this.highlighters[from].length ||
(this.highlighters[from].length && !this.highlighters[from].find((el) => {
return el.source === filePath && el.position === position
}))
) {
sourceHighlight.currentSourceLocationFromfileName(position, filePath, hexColor)
this.highlighters[from].push(sourceHighlight)
}
} catch (e) {
throw e
}
}
// highlights all locations for @from plugin
highlightAllFrom (from) {
// eslint-disable-next-line
try {
if (!this.highlighters[from]) return
let sourceHighlight
for (const index in this.highlighters[from]) {
sourceHighlight = new SourceHighlighter()
sourceHighlight.currentSourceLocationFromfileName(
this.highlighters[from][index].position,
this.highlighters[from][index].source,
this.highlighters[from][index].style
)
this.highlighters[from][index] = sourceHighlight
}
} catch (e) {
throw e
}
}
discardHighlight (from) {
if (this.highlighters[from]) {
for (const index in this.highlighters[from]) this.highlighters[from][index].currentSourceLocation(null)
}
this.highlighters[from] = []
}
discardAllHighlights () {
for (const from in this.highlighters) {
this.discardHighlight(from)
}
}
hideHighlightsExcept (toStay) {
for (const highlighter in this.highlighters) {
for (const index in this.highlighters[highlighter]) {
this.highlighters[highlighter][index].currentSourceLocation(null)
}
}
this.highlightAllFrom(toStay)
}
discardHighlightAt (line, filePath, from) {
if (this.highlighters[from]) {
for (const index in this.highlighters[from]) {
const highlight = this.highlighters[from][index]
if (highlight.source === filePath &&
(highlight.position.start.line === line || highlight.position.end.line === line)) {
highlight.currentSourceLocation(null)
this.highlighters[from].splice(index, 1)
}
}
}
}
}
module.exports = SourceHighlighters

@ -4,7 +4,6 @@ import * as packageJson from '../../../../../package.json'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
const { AstWalker } = require('@remix-project/remix-astwalker')
const csjs = require('csjs-inject')
const EventManager = require('../../lib/events')
const globalRegistry = require('../../global/registry')
@ -127,14 +126,6 @@ class ContextualListener extends Plugin {
const lastCompilationResult = this._deps.compilersArtefacts.__last
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0) {
let lineColumn = this._deps.offsetToLineColumnConverter.offsetToLineColumn(position, position.file, lastCompilationResult.getSourceCode().sources, lastCompilationResult.getAsts())
const css = csjs`
.highlightref_fullLine {
position: absolute;
z-index: 2;
opacity: 0.1;
background-color: var(--info);
}
`
if (node.nodes && node.nodes.length) {
// If node has children, highlight the entire line. if not, just highlight the current source position of the node.
lineColumn = {
@ -150,7 +141,7 @@ class ContextualListener extends Plugin {
}
const fileName = lastCompilationResult.getSourceName(position.file)
if (fileName) {
return this.editor.addMarker(lineColumn, fileName, css.highlightref_fullLine)
return this.call('editor', 'highlight', lineColumn, fileName)
}
}
return null
@ -178,10 +169,7 @@ class ContextualListener extends Plugin {
}
_stopHighlighting () {
for (const eventKey in this._activeHighlights) {
const event = this._activeHighlights[eventKey]
this.editor.removeMarker(event.eventId, event.fileTarget)
}
this.call('editor', 'discardHighlight')
this.event.trigger('stopHighlighting', [])
this._activeHighlights = []
}

@ -1,200 +1,103 @@
'use strict'
import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { EditorUI } from '@remix-ui/editor' // eslint-disable-line
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
const EventManager = require('../../lib/events')
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const ace = require('brace')
const globalRegistry = require('../../global/registry')
const SourceHighlighters = require('./SourceHighlighters')
const Range = ace.acequire('ace/range').Range
require('brace/ext/language_tools')
require('brace/ext/searchbox')
const langTools = ace.acequire('ace/ext/language_tools')
require('ace-mode-solidity/build/remix-ide/mode-solidity')
require('ace-mode-move/build/remix-ide/mode-move')
require('ace-mode-zokrates')
require('ace-mode-lexon')
require('brace/mode/javascript')
require('brace/mode/python')
require('brace/mode/json')
require('brace/mode/rust')
require('brace/theme/chrome') // for all light themes
require('brace/theme/chaos') // for all dark themes
require('../../assets/js/editor/darkTheme') // a custom one for remix 'Dark' theme
const css = csjs`
.ace-editor {
width : 100%;
}
`
document.head.appendChild(yo`
<style>
.ace-tm .ace_gutter,
.ace-tm .ace_gutter-active-line,
.ace-tm .ace_marker-layer .ace_active-line {
background-color: var(--secondary);
}
.ace_gutter-cell.ace_breakpoint{
background-color: var(--secondary);
}
</style>
`)
const profile = {
displayName: 'Editor',
name: 'editor',
description: 'service - editor',
version: packageJson.version,
methods: ['highlight', 'discardHighlight', 'discardHighlightAt', 'clearAnnotations', 'addAnnotation', 'gotoLine']
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine']
}
class Editor extends Plugin {
constructor (opts = {}, themeModule) {
constructor () {
super(profile)
// Dependancies
this._components = {}
this._components.registry = globalRegistry
this._deps = {
config: this._components.registry.get('config').api
config: this._components.registry.get('config').api,
themeModule: this._components.registry.get('themeModule').api
}
this._themes = {
light: 'chrome',
dark: 'chaos',
remixDark: 'remixDark'
light: 'light',
dark: 'vs-dark',
remixDark: 'remix-dark'
}
themeModule.events.on('themeChanged', (theme) => {
this.setTheme(theme.name === 'Dark' ? 'remixDark' : theme.quality)
})
const translateTheme = (theme) => this._themes[theme.name === 'Dark' ? 'remixDark' : theme.quality]
this._deps.themeModule.events.on('themeChanged', (theme) => {
this.currentTheme = translateTheme(theme)
this.renderComponent()
})
this.currentTheme = translateTheme(this._deps.themeModule.currentTheme())
this.models = []
// Init
this.event = new EventManager()
this.sessions = {}
this.sourceAnnotationsPerFile = []
this.sourceAnnotationsPerFile = {}
this.markerPerFile = {}
this.readOnlySessions = {}
this.previousInput = ''
this.saveTimeout = null
this.sourceHighlighters = new SourceHighlighters()
this.emptySession = this._createSession('')
this.emptySession = null
this.modes = {
sol: 'ace/mode/solidity',
yul: 'ace/mode/solidity',
mvir: 'ace/mode/move',
js: 'ace/mode/javascript',
py: 'ace/mode/python',
vy: 'ace/mode/python',
zok: 'ace/mode/zokrates',
lex: 'ace/mode/lexon',
txt: 'ace/mode/text',
json: 'ace/mode/json',
abi: 'ace/mode/json',
rs: 'ace/mode/rust'
sol: 'sol',
yul: 'sol',
mvir: 'move',
js: 'javascript',
py: 'python',
vy: 'python',
zok: 'zokrates',
lex: 'lexon',
txt: 'text',
json: 'json',
abi: 'json',
rs: 'rust'
}
// Editor Setup
const el = yo`<div id="input" data-id="editorInput"></div>`
this.editor = ace.edit(el)
ace.acequire('ace/ext/language_tools')
// Unmap ctrl-l & cmd-l
this.editor.commands.bindKeys({
'ctrl-L': null,
'Command-L': null
})
// shortcuts for "Ctrl-"" and "Ctrl+"" to increase/decrease font size of the editor
this.editor.commands.addCommand({
name: 'increasefontsizeEqual',
bindKey: { win: 'Ctrl-=', mac: 'Command-=' },
exec: (editor) => {
this.editorFontSize(1)
},
readOnly: true
})
this.onBreakPointAdded = (file, line) => this.triggerEvent('breakpointAdded', [file, line])
this.onBreakPointCleared = (file, line) => this.triggerEvent('breakpointCleared', [file, line])
this.editor.commands.addCommand({
name: 'increasefontsizePlus',
bindKey: { win: 'Ctrl-+', mac: 'Command-+' },
exec: (editor) => {
this.editorFontSize(1)
},
readOnly: true
})
this.editor.commands.addCommand({
name: 'decreasefontsize',
bindKey: { win: 'Ctrl--', mac: 'Command--' },
exec: (editor) => {
this.editorFontSize(-1)
},
readOnly: true
})
this.onDidChangeContent = (file) => this._onChange(file)
this.editor.setShowPrintMargin(false)
this.editor.resize(true)
this.onEditorMounted = () => this.triggerEvent('editorMounted', [])
this.editor.setOptions({
enableBasicAutocompletion: true,
enableLiveAutocompletion: true
})
// to be implemented by the react component
this.api = {}
}
el.className += ' ' + css['ace-editor']
el.editor = this.editor // required to access the editor during tests
this.render = () => el
render () {
if (this.el) return this.el
// Completer for editor
const flowCompleter = {
getCompletions: (editor, session, pos, prefix, callback) => {
// @TODO add here other propositions
this.el = document.createElement('div')
this.el.setAttribute('id', 'editorView')
this.el.currentContent = () => this.currentContent() // used by e2e test
this.el.setCurrentContent = (value) => {
if (this.sessions[this.currentFile]) {
this.sessions[this.currentFile].setValue(value)
this._onChange(this.currentFile)
}
}
langTools.addCompleter(flowCompleter)
// zoom with Ctrl+wheel
window.addEventListener('wheel', (e) => {
if (e.ctrlKey && Math.abs(e.wheelY) > 5) {
this.editorFontSize(e.wheelY > 0 ? 1 : -1)
}
})
// EVENTS LISTENERS
this.el.gotoLine = (line) => this.gotoLine(line, 0)
// Gutter Mouse down
this.editor.on('guttermousedown', e => {
const target = e.domEvent.target
if (target.className.indexOf('ace_gutter-cell') === -1) {
return
}
const row = e.getDocumentPosition().row
const breakpoints = e.editor.session.getBreakpoints()
for (const k in breakpoints) {
if (k === row.toString()) {
this.triggerEvent('breakpointCleared', [this.currentSession, row])
e.editor.session.clearBreakpoint(row)
e.stop()
return
}
}
this.setBreakpoint(row)
this.triggerEvent('breakpointAdded', [this.currentSession, row])
e.stop()
})
this.renderComponent()
return this.el
}
// Do setup on initialisation here
this.editor.on('changeSession', () => {
this._onChange()
this.triggerEvent('sessionSwitched', [])
this.editor.getSession().on('change', () => {
this._onChange()
this.sourceHighlighters.discardAllHighlights()
this.triggerEvent('contentChanged', [])
})
})
renderComponent () {
ReactDOM.render(
<EditorUI editorAPI={this.api} theme={this.currentTheme} currentFile={this.currentFile} sourceAnnotationsPerFile={this.sourceAnnotationsPerFile} markerPerFile={this.markerPerFile} onBreakPointAdded={this.onBreakPointAdded} onBreakPointCleared={this.onBreakPointCleared} onDidChangeContent={this.onDidChangeContent} onEditorMounted={this.onEditorMounted} />
, this.el)
}
triggerEvent (name, params) {
@ -204,12 +107,11 @@ class Editor extends Plugin {
onActivation () {
this.on('sidePanel', 'focusChanged', (name) => {
this.sourceHighlighters.hideHighlightsExcept(name)
this.keepAnnotationsFor(name)
this.keepDecorationsFor(name, 'sourceAnnotationsPerFile')
this.keepDecorationsFor(name, 'markerPerFile')
})
this.on('sidePanel', 'pluginDisabled', (name) => {
this.sourceHighlighters.discardHighlight(name)
this.clearAllAnnotationsFor(name)
this.clearAllDecorationsFor(name)
})
}
@ -218,30 +120,18 @@ class Editor extends Plugin {
this.off('sidePanel', 'pluginDisabled')
}
highlight (position, filePath, hexColor) {
const { from } = this.currentRequest
this.sourceHighlighters.highlight(position, filePath, hexColor, from)
}
discardHighlight () {
const { from } = this.currentRequest
this.sourceHighlighters.discardHighlight(from)
}
discardHighlightAt (line, filePath) {
const { from } = this.currentRequest
this.sourceHighlighters.discardHighlightAt(line, filePath, from)
}
setTheme (type) {
this.editor.setTheme('ace/theme/' + this._themes[type])
this.api.setTheme(this._themes[type])
}
_onChange () {
_onChange (file) {
const currentFile = this._deps.config.get('currentFile')
if (!currentFile) {
return
}
if (currentFile !== file) {
return
}
const input = this.get(currentFile)
if (!input) {
return
@ -257,16 +147,17 @@ class Editor extends Plugin {
if (this.saveTimeout) {
window.clearTimeout(this.saveTimeout)
}
this.triggerEvent('contentChanged', [])
this.saveTimeout = window.setTimeout(() => {
this.triggerEvent('requiringToSaveCurrentfile', [])
}, 5000)
}
_switchSession (path) {
this.currentSession = path
this.editor.setSession(this.sessions[this.currentSession])
this.editor.setReadOnly(this.readOnlySessions[this.currentSession])
this.editor.focus()
this.triggerEvent('sessionSwitched', [])
this.currentFile = path
this.renderComponent()
}
/**
@ -283,17 +174,26 @@ class Editor extends Plugin {
}
/**
* Create an Ace session
* Create an editor session
* @param {string} path path of the file
* @param {string} content Content of the file to open
* @param {string} mode Ace Mode for this file [Default is `text`]
* @param {string} mode Mode for this file [Default is `text`]
*/
_createSession (content, mode) {
const s = new ace.EditSession(content)
s.setMode(mode || 'ace/mode/text')
s.setUndoManager(new ace.UndoManager())
s.setTabSize(4)
s.setUseSoftTabs(true)
return s
_createSession (path, content, mode) {
this.api.addModel(content, mode, path, false)
return {
path,
language: mode,
setValue: (content) => {
this.api.setValue(path, content)
},
getValue: () => {
return this.api.getValue(path, content)
},
dispose: () => {
this.api.disposeModel(path)
}
}
}
/**
@ -301,36 +201,16 @@ class Editor extends Plugin {
* @param {string} string
*/
find (string) {
return this.editor.find(string)
return this.api.findMatches(this.currentFile, string)
}
/**
* Display an Empty read-only session
*/
displayEmptyReadOnlySession () {
this.currentSession = null
this.editor.setSession(this.emptySession)
this.editor.setReadOnly(true)
}
/**
* Sets a breakpoint on the row number
* @param {number} row Line index of the breakpoint
* @param {string} className Class of the breakpoint
*/
setBreakpoint (row, className) {
this.editor.session.setBreakpoint(row, className)
}
/**
* Increment the font size (in pixels) for the editor text.
* @param {number} incr The amount of pixels to add to the font.
*/
editorFontSize (incr) {
const newSize = this.editor.getFontSize() + incr
if (newSize >= 6) {
this.editor.setFontSize(newSize)
}
this.currentFile = null
this.api.addModel('', 'text', '_blank', true)
this.api.setCurrentPath('_blank')
}
/**
@ -338,8 +218,8 @@ class Editor extends Plugin {
* @param {string} text New text to be place.
*/
setText (text) {
if (this.currentSession && this.sessions[this.currentSession]) {
this.sessions[this.currentSession].setValue(text)
if (this.currentFile && this.sessions[this.currentFile]) {
this.sessions[this.currentFile].setValue(text)
}
}
@ -356,7 +236,7 @@ class Editor extends Plugin {
- URL not prepended with the file explorer. We assume (as it is in the whole app, that this is a "browser" URL
*/
if (!this.sessions[path]) {
const session = this._createSession(content, this._getMode(path))
const session = this._createSession(path, content, this._getMode(path))
this.sessions[path] = session
this.readOnlySessions[path] = false
} else if (this.sessions[path].getValue() !== content) {
@ -372,7 +252,7 @@ class Editor extends Plugin {
*/
openReadOnly (path, content) {
if (!this.sessions[path]) {
const session = this._createSession(content, this._getMode(path))
const session = this._createSession(path, content, this._getMode(path))
this.sessions[path] = session
this.readOnlySessions[path] = true
}
@ -394,8 +274,8 @@ class Editor extends Plugin {
* @return {String} content of the file referenced by @arg path
*/
get (path) {
if (!path || this.currentSession === path) {
return this.editor.getValue()
if (!path || this.currentFile === path) {
return this.api.getValue(path)
} else if (this.sessions[path]) {
return this.sessions[path].getValue()
}
@ -407,29 +287,23 @@ class Editor extends Plugin {
* @return {String} path of the current session
*/
current () {
if (this.editor.getSession() === this.emptySession) {
return
}
return this.currentSession
return this.currentFile
}
/**
* The position of the cursor
*/
getCursorPosition () {
return this.editor.session.doc.positionToIndex(
this.editor.getCursorPosition(),
0
)
return this.api.getCursorPosition()
}
/**
* Remove the current session from the list of sessions.
*/
discardCurrentSession () {
if (this.sessions[this.currentSession]) {
delete this.sessions[this.currentSession]
this.currentSession = null
if (this.sessions[this.currentFile]) {
delete this.sessions[this.currentFile]
this.currentFile = null
}
}
@ -438,73 +312,52 @@ class Editor extends Plugin {
* @param {string} path
*/
discard (path) {
if (this.sessions[path]) delete this.sessions[path]
if (this.currentSession === path) this.currentSession = null
if (this.sessions[path]) {
this.sessions[path].dispose()
delete this.sessions[path]
}
if (this.currentFile === path) this.currentFile = null
}
/**
* Resize the editor, and sets whether or not line wrapping is enabled.
* @param {boolean} useWrapMode Enable (or disable) wrap mode
* Increment the font size (in pixels) for the editor text.
* @param {number} incr The amount of pixels to add to the font.
*/
resize (useWrapMode) {
this.editor.resize()
const session = this.editor.getSession()
session.setUseWrapMode(useWrapMode)
if (session.getUseWrapMode()) {
const characterWidth = this.editor.renderer.characterWidth
const contentWidth = this.editor.container.ownerDocument.getElementsByClassName(
'ace_scroller'
)[0].clientWidth
if (contentWidth > 0) {
session.setWrapLimit(parseInt(contentWidth / characterWidth, 10))
}
editorFontSize (incr) {
const newSize = this.api.getFontSize() + incr
if (newSize >= 6) {
this.api.setFontSize(newSize)
}
}
/**
* Adds a new marker to the given `Range`.
* @param {*} lineColumnPos
* @param {string} source Path of the session to add the mark on.
* @param {string} cssClass css to apply to the mark.
* Resize the editor, and sets whether or not line wrapping is enabled.
* @param {boolean} useWrapMode Enable (or disable) wrap mode
*/
addMarker (lineColumnPos, source, cssClass) {
const currentRange = new Range(
lineColumnPos.start.line,
lineColumnPos.start.column,
lineColumnPos.end.line,
lineColumnPos.end.column
)
if (this.sessions[source]) {
return this.sessions[source].addMarker(currentRange, cssClass)
}
return null
resize (useWrapMode) {
this.api.setWordWrap(useWrapMode)
}
/**
* Scrolls to a line. If center is true, it puts the line in middle of screen (or attempts to).
* @param {number} line The line to scroll to
* @param {boolean} center If true
* @param {boolean} animate If true animates scrolling
* @param {Function} callback Function to be called when the animation has finished
* Moves the cursor and focus to the specified line and column number
* @param {number} line
* @param {number} col
*/
scrollToLine (line, center, animate, callback) {
this.editor.scrollToLine(line, center, animate, callback)
gotoLine (line, col) {
this.api.focus()
this.api.revealLine(line + 1, col)
}
/**
* Remove a marker from the session
* @param {string} markerId Id of the marker
* @param {string} source Path of the session
* Scrolls to a line. If center is true, it puts the line in middle of screen (or attempts to).
* @param {number} line The line to scroll to
*/
removeMarker (markerId, source) {
if (this.sessions[source]) {
this.sessions[source].removeMarker(markerId)
}
scrollToLine (line) {
this.api.revealLine(line)
}
/**
* Clears all the annotations for the given @arg filePath and @arg plugin, if none is given, the current sesssion is used.
* Clears all the decorations for the given @arg filePath and @arg plugin, if none is given, the current sesssion is used.
* An annotation has the following shape:
column: -1
row: -1
@ -512,64 +365,79 @@ class Editor extends Plugin {
type: "warning"
* @param {String} filePath
* @param {String} plugin
* @param {String} typeOfDecoration
*/
clearAnnotationsByPlugin (filePath, plugin) {
clearDecorationsByPlugin (filePath, plugin, typeOfDecoration) {
if (filePath && !this.sessions[filePath]) throw new Error('file not found' + filePath)
const session = this.sessions[filePath] || this.editor.getSession()
const path = filePath || this.currentSession
const path = filePath || this.currentFile
const currentAnnotations = this.sourceAnnotationsPerFile[path]
const currentAnnotations = this[typeOfDecoration][path]
if (!currentAnnotations) return
const newAnnotations = []
for (const annotation of currentAnnotations) {
if (annotation.from !== plugin) newAnnotations.push(annotation)
}
this.sourceAnnotationsPerFile[path] = newAnnotations
this._setAnnotations(session, path)
this[typeOfDecoration][path] = newAnnotations
this.renderComponent()
}
keepAnnotationsFor (name) {
if (!this.currentSession) return
if (!this.sourceAnnotationsPerFile[this.currentSession]) return
keepDecorationsFor (name, typeOfDecoration) {
if (!this.currentFile) return
if (!this[typeOfDecoration][this.currentFile]) return
const annotations = this.sourceAnnotationsPerFile[this.currentSession]
const annotations = this[typeOfDecoration][this.currentFile]
for (const annotation of annotations) {
annotation.hide = annotation.from !== name
}
this._setAnnotations(this.editor.getSession(), this.currentSession)
this.renderComponent()
}
/**
* Clears all the annotations for the given @arg filePath, the plugin name is retrieved from the context, if none is given, the current sesssion is used.
* Clears all the decorations and for all the sessions for the given @arg plugin
* An annotation has the following shape:
column: -1
row: -1
text: "browser/Untitled1.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.↵"
type: "warning"
* @param {String} filePath
* @param {String} plugin
*/
clearAnnotations (filePath) {
const { from } = this.currentRequest
this.clearAnnotationsByPlugin(filePath, from)
clearAllDecorationsFor (plugin) {
for (const session in this.sessions) {
this.clearDecorationsByPlugin(session, plugin, 'sourceAnnotationsPerFile')
this.clearDecorationsByPlugin(session, plugin, 'markerPerFile')
}
}
/**
* Clears all the annotations and for all the sessions for the given @arg plugin
* Clears all the annotations for the given @arg filePath, the plugin name is retrieved from the context, if none is given, the current sesssion is used.
* An annotation has the following shape:
column: -1
row: -1
text: "browser/Untitled1.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.↵"
type: "warning"
* @param {String} filePath
* @param {String} plugin
*/
clearAllAnnotationsFor (plugin) {
for (const session in this.sessions) {
this.clearAnnotationsByPlugin(session, plugin)
}
clearAnnotations (filePath) {
filePath = filePath || this.currentFile
const { from } = this.currentRequest
this.clearDecorationsByPlugin(filePath, from, 'sourceAnnotationsPerFile')
}
async addDecoration (decoration, filePath, typeOfDecoration) {
if (!filePath) return
filePath = await this.call('fileManager', 'getPathFromUrl', filePath)
filePath = filePath.file
if (!this.sessions[filePath]) throw new Error('file not found' + filePath)
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()
}
/**
@ -582,32 +450,23 @@ class Editor extends Plugin {
* @param {Object} annotation
* @param {String} filePath
*/
addAnnotation (annotation, filePath) {
if (filePath && !this.sessions[filePath]) throw new Error('file not found' + filePath)
const session = this.sessions[filePath] || this.editor.getSession()
const path = filePath || this.currentSession
const { from } = this.currentRequest
if (!this.sourceAnnotationsPerFile[path]) this.sourceAnnotationsPerFile[path] = []
annotation.from = from
this.sourceAnnotationsPerFile[path].push(annotation)
this._setAnnotations(session, path)
async addAnnotation (annotation, filePath) {
filePath = filePath || this.currentFile
await this.addDecoration(annotation, filePath, 'sourceAnnotationsPerFile')
}
_setAnnotations (session, path) {
const annotations = this.sourceAnnotationsPerFile[path]
session.setAnnotations(annotations.filter((element) => !element.hide))
async highlight (position, filePath) {
filePath = filePath || this.currentFile
await this.call('fileManager', 'open', filePath)
this.gotoLine(position.start.line, position.start.column)
await this.addDecoration({ position }, filePath, 'markerPerFile')
}
/**
* Moves the cursor and focus to the specified line and column number
* @param {number} line
* @param {number} col
*/
gotoLine (line, col) {
this.editor.focus()
this.editor.gotoLine(line + 1, col - 1, true)
discardHighlight () {
const { from } = this.currentRequest
for (const session in this.sessions) {
this.clearDecorationsByPlugin(session, from, 'markerPerFile')
}
}
}

@ -1,86 +0,0 @@
'use strict'
const csjs = require('csjs-inject')
const globlalRegistry = require('../../global/registry')
class SourceHighlighter {
constructor (localRegistry) {
this._components = {}
this._components.registry = localRegistry || globlalRegistry
// dependencies
this._deps = {
editor: this._components.registry.get('editor').api,
config: this._components.registry.get('config').api,
fileManager: this._components.registry.get('filemanager').api,
compilerArtefacts: this._components.registry.get('compilersartefacts').api
}
this.position = null
this.statementMarker = null
this.fullLineMarker = null
this.source = null
}
currentSourceLocation (lineColumnPos, location) {
if (this.statementMarker) this._deps.editor.removeMarker(this.statementMarker, this.source)
if (this.fullLineMarker) this._deps.editor.removeMarker(this.fullLineMarker, this.source)
const lastCompilationResult = this._deps.compilerArtefacts.__last
if (location && location.file !== undefined && lastCompilationResult) {
const path = lastCompilationResult.getSourceName(location.file)
if (path) {
this.currentSourceLocationFromfileName(lineColumnPos, path)
}
}
}
async currentSourceLocationFromfileName (lineColumnPos, filePath, style) {
if (this.statementMarker) this._deps.editor.removeMarker(this.statementMarker, this.source)
if (this.fullLineMarker) this._deps.editor.removeMarker(this.fullLineMarker, this.source)
this.statementMarker = null
this.fullLineMarker = null
this.source = null
if (lineColumnPos && lineColumnPos.start && lineColumnPos.end) {
this.source = filePath
this.style = style || 'var(--info)'
// if (!this.source) this.source = this._deps.fileManager.currentFile()
if (this._deps.fileManager.currentFile() !== this.source) {
await this._deps.fileManager.open(this.source)
this.source = this._deps.fileManager.currentFile()
}
const css = csjs`
.highlightcode {
position:absolute;
z-index:20;
opacity: 0.3;
background-color: ${this.style};
}
.highlightcode_fullLine {
position:absolute;
z-index:20;
opacity: 0.5;
background-color: ${this.style};
}
.customBackgroundColor {
background-color: ${this.style};
}
`
this.statementMarker = this._deps.editor.addMarker(lineColumnPos, this.source, css.highlightcode.className + ' ' + css.customBackgroundColor.className + ' ' + `highlightLine${lineColumnPos.start.line}`)
this._deps.editor.scrollToLine(lineColumnPos.start.line, true, true, function () {})
this.position = lineColumnPos
if (lineColumnPos.start.line === lineColumnPos.end.line) {
this.fullLineMarker = this._deps.editor.addMarker({
start: {
line: lineColumnPos.start.line,
column: 0
},
end: {
line: lineColumnPos.start.line + 1,
column: 0
}
}, this.source, css.highlightcode_fullLine.className)
}
}
}
}
module.exports = SourceHighlighter

@ -70,7 +70,7 @@ module.exports = class Filepanel extends ViewPlugin {
}
render () {
this.initWorkspace().then(() => this.getWorkspaces()).catch(console.error)
this.on('editor', 'editorMounted', () => this.initWorkspace().then(() => this.getWorkspaces()).catch(console.error))
return this.el
}

@ -297,7 +297,7 @@ export class TabProxy extends Plugin {
this._view.filetabs.canAdd = false
const zoomBtns = yo`
<div class="d-flex flex-row justify-content-center align-items-center">
<div class="d-flex flex-row justify-content-center align-items-center" title="Zoom in/out">
<span data-id="tabProxyZoomOut" class="btn btn-sm px-1 fas fa-search-minus text-dark" onclick=${() => this.onZoomOut()}></span>
<span data-id="tabProxyZoomIn" class="btn btn-sm px-1 fas fa-search-plus text-dark" onclick=${() => this.onZoomIn()}></span>
</div>
@ -307,6 +307,7 @@ export class TabProxy extends Plugin {
this._view.tabs = yo`
<div style="display: -webkit-box; max-height: 32px">
${zoomBtns}
<i class="d-flex flex-row justify-content-center align-items-center far fa-sliders-v px-1" title="press F1 when focusing the editor to show advanced configuration settings"></i>
${this._view.filetabs}
</div>
`

@ -20,7 +20,6 @@ class VMProvider {
this.accounts = {}
this.RemixSimulatorProvider = new Provider({ fork: this.executionContext.getCurrentFork() })
this.RemixSimulatorProvider.init()
this.RemixSimulatorProvider.Accounts.resetAccounts()
this.web3 = new Web3(this.RemixSimulatorProvider)
extend(this.web3)
this.accounts = {}

@ -6,7 +6,6 @@ var EventManager = require('../lib/events')
var toolTip = require('../app/ui/tooltip')
var globalRegistry = require('../global/registry')
var SourceHighlighter = require('../app/editor/sourceHighlighter')
var GistHandler = require('./gist-handler')
class CmdInterpreterAPI {
@ -17,7 +16,6 @@ class CmdInterpreterAPI {
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._components.terminal = terminal
self._components.sourceHighlighter = new SourceHighlighter()
self._components.fileImport = new CompilerImports()
self._components.gistHandler = new GistHandler()
self._deps = {

@ -1 +1 @@
export * from './lib/remix-ui-editor';
export * from './lib/remix-ui-editor'

@ -1,11 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import MonacoEditor from './monaco-editor';
describe(' MonacoEditor', () => {
it('should render successfully', () => {
const { baseElement } = render(<MonacoEditor />);
expect(baseElement).toBeTruthy();
});
});

@ -1,16 +0,0 @@
import React from 'react';
import './monaco-editor.css';
/* eslint-disable-next-line */
export interface MonacoEditorProps {}
export const MonacoEditor = (props: MonacoEditorProps) => {
return (
<div>
<h1>Welcome to monaco-editor!</h1>
</div>
);
};
export default MonacoEditor;

@ -0,0 +1,11 @@
.hover-row {
white-space: pre;
margin-left : 10px;
background : var(--light);
font-weight : bold;
font-family : monospace;
padding : 10px;
border-radius : 10px;
height: auto;
width: auto;
}

@ -1,16 +1,296 @@
import React from 'react';
import React, { useState, useRef, useEffect } from 'react'
import Editor from '@monaco-editor/react'
import './remix-ui-editor.css';
import './remix-ui-editor.css'
type cursorPosition = {
startLineNumber: number,
startColumn: number,
endLineNumber: number,
endColumn: number
}
type sourceAnnotation = {
row: number,
column: number,
text: string,
type: 'error' | 'warning' | 'info'
hide: boolean
from: string // plugin name
}
type sourceMarker = {
position: {
start: {
line: number
column: number
},
end: {
line: number
column: number
}
},
from: string // plugin name
hide: boolean
}
type sourceAnnotationMap = {
[key: string]: [sourceAnnotation];
}
type sourceMarkerMap = {
[key: string]: [sourceMarker];
}
/* eslint-disable-next-line */
export interface RemixUiEditorProps {}
export interface EditorUIProps {
theme: string
currentFile: string
sourceAnnotationsPerFile: sourceAnnotationMap
markerPerFile: sourceMarkerMap
onBreakPointAdded: (file: string, line: number) => void
onBreakPointCleared: (file: string, line: number) => void
onDidChangeContent: (file: string) => void
onEditorMounted: () => void
editorAPI:{
findMatches: (uri: string, value: string) => any
addModel: (value: string, language: string, uri: string, readOnly: boolean) => void
disposeModel: (uri: string) => void,
setFontSize: (fontSize: number) => void,
getFontSize: () => number,
getValue: (uri: string) => string
getCursorPosition: () => cursorPosition
revealLine: (line: number, column: number) => void
focus: () => void
setWordWrap: (wrap: boolean) => void
setValue: (uri: string, value: string) => void
}
}
export const EditorUI = (props: EditorUIProps) => {
const [models, setModels] = useState({})
const [, setCurrentBreakpoints] = useState({})
const [currentAnnotations, setCurrentAnnotations] = useState({})
const [currentMarkers, setCurrentMarkers] = useState({})
const editorRef = useRef(null)
const monacoRef = useRef(null)
const currentFileRef = useRef('')
useEffect(() => {
if (!monacoRef.current) return
monacoRef.current.editor.setTheme(props.theme)
}, [props.theme])
const setAnnotationsbyFile = (uri) => {
if (props.sourceAnnotationsPerFile[uri]) {
const model = models[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 = models[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: `bg-info highlightLine${marker.position.start.line + 1}`
}
})
}
}
setCurrentMarkers(prevState => {
prevState[uri] = model.deltaDecorations(currentMarkers[uri] || [], newMarkers)
return prevState
})
}
}
useEffect(() => {
if (!editorRef.current) return
currentFileRef.current = props.currentFile
editorRef.current.setModel(models[props.currentFile].model)
editorRef.current.updateOptions({ readOnly: models[props.currentFile].readOnly })
setAnnotationsbyFile(props.currentFile)
setMarkerbyFile(props.currentFile)
}, [props.currentFile])
useEffect(() => {
setAnnotationsbyFile(props.currentFile)
}, [JSON.stringify(props.sourceAnnotationsPerFile)])
useEffect(() => {
setMarkerbyFile(props.currentFile)
}, [JSON.stringify(props.markerPerFile)])
props.editorAPI.findMatches = (uri: string, value: string) => {
if (!editorRef.current) return
const model = models[uri]?.model
if (model) return model.findMatches(value)
}
props.editorAPI.addModel = (value: string, language: string, uri: string, readOnly: boolean) => {
if (!monacoRef.current) return
if (models[uri]) return // already existing
const model = monacoRef.current.editor.createModel(value, language, monacoRef.current.Uri.parse(uri))
model.onDidChangeContent(() => props.onDidChangeContent(uri))
setModels(prevState => {
prevState[uri] = { language, uri, readOnly, model }
return prevState
})
}
props.editorAPI.disposeModel = (uri: string) => {
const model = models[uri]?.model
if (model) model.dispose()
setModels(prevState => {
delete prevState[uri]
return prevState
})
}
props.editorAPI.getValue = (uri: string) => {
if (!editorRef.current) return
const model = models[uri]?.model
if (model) {
return model.getValue()
}
}
props.editorAPI.setValue = (uri: string, value: string) => {
if (!editorRef.current) return
const model = models[uri]?.model
if (model) {
model.setValue(value)
}
}
props.editorAPI.getCursorPosition = () => {
if (!monacoRef.current) return
const model = models[currentFileRef.current]?.model
if (model) {
return model.getOffsetAt(editorRef.current.getPosition())
}
}
props.editorAPI.revealLine = (line: number, column: number) => {
if (!editorRef.current) return
editorRef.current.revealLine(line)
editorRef.current.setPosition({ column, lineNumber: line })
}
props.editorAPI.focus = () => {
if (!editorRef.current) return
editorRef.current.focus()
}
props.editorAPI.setFontSize = (size: number) => {
if (!editorRef.current) return
editorRef.current.updateOptions({ fontSize: size })
}
props.editorAPI.getFontSize = () => {
if (!editorRef.current) return
return editorRef.current.getOption(42).fontSize
}
props.editorAPI.setWordWrap = (wrap: boolean) => {
if (!editorRef.current) return
editorRef.current.updateOptions({ wordWrap: wrap ? 'on' : 'off' })
}
(window as any).addRemixBreakpoint = (position) => { // make it available from e2e testing...
const model = editorRef.current.getModel()
if (model) {
setCurrentBreakpoints(prevState => {
const currentFile = currentFileRef.current
if (!prevState[currentFile]) prevState[currentFile] = {}
const decoration = Object.keys(prevState[currentFile]).filter((line) => parseInt(line) === position.lineNumber)
if (decoration.length) {
props.onBreakPointCleared(currentFile, position.lineNumber)
model.deltaDecorations([prevState[currentFile][position.lineNumber]], [])
delete prevState[currentFile][position.lineNumber]
} else {
props.onBreakPointAdded(currentFile, position.lineNumber)
const decorationIds = model.deltaDecorations([], [{
range: new monacoRef.current.Range(position.lineNumber, 1, position.lineNumber, 1),
options: {
isWholeLine: false,
glyphMarginClassName: 'fas fa-circle text-info'
}
}])
prevState[currentFile][position.lineNumber] = decorationIds[0]
}
return prevState
})
}
}
function handleEditorDidMount (editor) {
editorRef.current = editor
monacoRef.current.editor.setTheme(props.theme)
props.onEditorMounted()
editor.onMouseUp((e) => {
if (e && e.target && e.target.toString().startsWith('GUTTER')) {
(window as any).addRemixBreakpoint(e.target.position)
}
})
}
function handleEditorWillMount (monaco) {
monacoRef.current = monaco
// see https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-exposed-colors
const backgroundColor = window.getComputedStyle(document.documentElement).getPropertyValue('--light').trim()
const infoColor = window.getComputedStyle(document.documentElement).getPropertyValue('--info').trim()
monaco.editor.defineTheme('remix-dark', {
base: 'vs-dark',
inherit: true, // can also be false to completely replace the builtin rules
rules: [{ background: backgroundColor.replace('#', '') }],
colors: {
'editor.background': backgroundColor,
'editorSuggestWidget.background': backgroundColor,
'editorSuggestWidget.selectedBackground': backgroundColor,
'editorSuggestWidget.highlightForeground': infoColor
}
})
}
export const RemixUiEditor = (props: RemixUiEditorProps) => {
return (
<div>
<h1>Welcome to remix-ui-editor!</h1>
</div>
);
};
<Editor
width="100%"
height="100%"
path={props.currentFile}
language={models[props.currentFile] ? models[props.currentFile].language : 'text'}
onMount={handleEditorDidMount}
beforeMount={handleEditorWillMount}
options= { { glyphMargin: true } }
/>
)
}
export default RemixUiEditor;
export default EditorUI

File diff suppressed because it is too large Load Diff

@ -25,9 +25,11 @@ const ErrorRenderer = ({ message, opt, editor }: ErrorRendererProps) => {
return result
}
const handlePointToErrorOnClick = (location, fileName) => {
editor.call('editor', 'discardHighlight')
editor.call('editor', 'highlight', location, fileName)
const handlePointToErrorOnClick = async (location, fileName) => {
await editor.call('editor', 'discardHighlight')
await editor.call('fileManager', 'open', fileName)
await editor.call('editor', 'gotoLine', location.start.line, location.start.column)
await editor.call('editor', 'highlight', location, fileName)
}
if (!message) return

22
package-lock.json generated

@ -7216,6 +7216,23 @@
}
}
},
"@monaco-editor/loader": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.2.0.tgz",
"integrity": "sha512-cJVCG/T/KxXgzYnjKqyAgsKDbH9mGLjcXxN6AmwumBwa2rVFkwvGcUj1RJtD0ko4XqLqJxwqsN/Z/KURB5f1OQ==",
"requires": {
"state-local": "^1.0.6"
}
},
"@monaco-editor/react": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.3.1.tgz",
"integrity": "sha512-f+0BK1PP/W5I50hHHmwf11+Ea92E5H1VZXs+wvKplWUWOfyMa1VVwqkJrXjRvbcqHL+XdIGYWhWNdi4McEvnZg==",
"requires": {
"@monaco-editor/loader": "^1.2.0",
"prop-types": "^15.7.2"
}
},
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@ -44085,6 +44102,11 @@
"pkg-conf": "^2.0.0"
}
},
"state-local": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="
},
"static-extend": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",

@ -45,7 +45,7 @@
"workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph",
"help": "nx help",
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,remix-ui-plugin-manager,remix-ui-terminal",
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,remix-ui-plugin-manager,remix-ui-terminal,remix-ui-editor",
"build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"publish:libs": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs",
@ -63,33 +63,33 @@
"nightwatch_local_firefox": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=firefox",
"nightwatch_local_chrome": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=chrome",
"nightwatch_local_ballot": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/ballot.test.js --env=chrome",
"nightwatch_local_ballot_0_4_11": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/ballot_0_4_11.spec.js --env=chrome",
"nightwatch_local_ballot_0_4_11": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/ballot_0_4_11.spec.js --env=firefox",
"nightwatch_local_usingWorker": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/usingWebWorker.test.js --env=chrome",
"nightwatch_local_libraryDeployment": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/libraryDeployment.test.js --env=chrome",
"nightwatch_local_solidityImport": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/solidityImport.spec.js --env=chrome",
"nightwatch_local_recorder": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/recorder.spec.js --env=chrome",
"nightwatch_local_recorder": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/recorder.spec.js --env=firefox",
"nightwatch_local_transactionExecution": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/transactionExecution.spec.js --env=chrome",
"nightwatch_local_staticAnalysis": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/staticAnalysis.spec.js --env=chrome",
"nightwatch_local_signingMessage": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/signingMessage.test.js --env=chrome",
"nightwatch_local_specialFunctions": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/specialFunctions.test.js --env=chrome",
"nightwatch_local_solidityUnitTests": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.js --env=chrome",
"nightwatch_local_remixd": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/remixd.test.js --env=chrome",
"nightwatch_local_remixd": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/remixd.test.js --env=firefox",
"nightwatch_local_terminal": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/terminal.test.js --env=chrome",
"nightwatch_local_gist": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/gist.spec.js --env=chrome",
"nightwatch_local_workspace": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/workspace.test.js --env=chrome",
"nightwatch_local_workspace": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/workspace.test.js --env=firefox",
"nightwatch_local_defaultLayout": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/defaultLayout.test.js --env=chrome",
"nightwatch_local_pluginManager": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/pluginManager.js --env=chrome",
"nightwatch_local_publishContract": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/publishContract.test.js --env=chrome",
"nightwatch_local_generalSettings": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/generalSettings.test.js --env=chrome",
"nightwatch_local_fileExplorer": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/fileExplorer.test.js --env=chrome",
"nightwatch_local_debugger": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/debugger.spec.js --env=chrome",
"nightwatch_local_editor": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/editor.spec.js --env=chrome",
"nightwatch_local_generalSettings": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/generalSettings.test.js --env=firefox",
"nightwatch_local_fileExplorer": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/fileExplorer.test.js --env=firefox",
"nightwatch_local_debugger": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/debugger.spec.js --env=firefox",
"nightwatch_local_editor": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/editor.spec.js --env=firefox",
"nightwatch_local_compiler": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/compiler_api.test.js --env=chrome",
"nightwatch_local_txListener": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/txListener.test.js --env=chrome",
"nightwatch_local_fileManager": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/fileManager_api.spec.js --env=chrome",
"nightwatch_local_fileManager": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/fileManager_api.spec.js --env=firefox",
"nightwatch_local_runAndDeploy": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/runAndDeploy.js --env=chrome-runAndDeploy",
"nightwatch_local_url": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/url.spec.js --env=chrome",
"nightwatch_local_verticalIconscontextmenu": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/verticalIconsPanel.spec.js --env=chrome",
"nightwatch_local_url": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/url.spec.js --env=firefox",
"nightwatch_local_verticalIconscontextmenu": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/verticalIconsPanel.spec.js --env=firefox",
"onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint",
"remixd": "nx build remixd && nx serve remixd --folder=./apps/remix-ide/contracts --remixide=http://127.0.0.1:8080",
"selenium": "selenium-standalone start",
@ -140,6 +140,7 @@
"@ethereumjs/common": "^2.5.0",
"@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3",
"@monaco-editor/react": "^4.3.1",
"@remixproject/engine": "^0.3.20",
"@remixproject/engine-web": "^0.3.20",
"@remixproject/plugin": "^0.3.20",

Loading…
Cancel
Save