Merge pull request #2596 from ethereum/newEngine

WIP Using the new engine
pull/5370/head
yann300 5 years ago committed by GitHub
commit 2bbb52563f
  1. 49
      package-lock.json
  2. 2
      package.json
  3. 69
      src/app.js
  4. 3
      src/app/components/hidden-panel.js
  5. 5
      src/app/components/local-plugin.js
  6. 4
      src/app/components/main-panel.js
  7. 4
      src/app/components/panel.js
  8. 68
      src/app/components/plugin-manager-component.js
  9. 20
      src/app/components/side-panel.js
  10. 3
      src/app/files/fileManager.js
  11. 34
      src/app/panels/main-view.js
  12. 15
      src/app/panels/tab-proxy.js
  13. 17
      src/app/panels/terminal.js
  14. 2
      src/app/tabs/test-tab.js
  15. 3
      src/app/udapp/make-udapp.js
  16. 7
      src/app/ui/auto-complete-popup.js
  17. 6
      src/app/ui/txLogger.js
  18. 64
      src/remixAppManager.js
  19. 31
      test-browser/tests/pluginManager.js

49
package-lock.json generated

@ -1778,9 +1778,9 @@
"integrity": "sha512-ePDxG9UuU9Kobk90ZUjtmDW8IT9U7aRb1/Rl9683MRNM+ur0ocHL2v7TPH2ajTiVSBUFbbeW8vKIt9jrb0JIAA==" "integrity": "sha512-ePDxG9UuU9Kobk90ZUjtmDW8IT9U7aRb1/Rl9683MRNM+ur0ocHL2v7TPH2ajTiVSBUFbbeW8vKIt9jrb0JIAA=="
}, },
"@remixproject/engine": { "@remixproject/engine": {
"version": "0.2.0", "version": "0.2.0-alpha.4",
"resolved": "https://registry.npmjs.org/@remixproject/engine/-/engine-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@remixproject/engine/-/engine-0.2.0-alpha.4.tgz",
"integrity": "sha512-J8UqkQSANp6hKMP5G4/rMYf6j0RJdBYSm4mfkwkpRAZi2/01nPjuBmgbaQ4tFNcwCOjdAMp24ySAKaCj0ciPDA==" "integrity": "sha512-AY6HaF7Y4fR1oOdz60B2zt+gGftaT5fZWSl5ka7UuDHZUzeouNMx4O1+Uk4376Mv+M3vdmpGFo6KgfsZj6wSJw=="
}, },
"@resolver-engine/core": { "@resolver-engine/core": {
"version": "0.3.3", "version": "0.3.3",
@ -6737,8 +6737,7 @@
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -6756,13 +6755,11 @@
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -6775,18 +6772,15 @@
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -6889,8 +6883,7 @@
}, },
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"bundled": true, "bundled": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -6900,7 +6893,6 @@
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -6913,20 +6905,17 @@
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.9.0", "version": "2.9.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -6943,7 +6932,6 @@
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -7024,8 +7012,7 @@
}, },
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -7035,7 +7022,6 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -7111,8 +7097,7 @@
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -7142,7 +7127,6 @@
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -7160,7 +7144,6 @@
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -7199,13 +7182,11 @@
}, },
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"bundled": true, "bundled": true
"optional": true
} }
} }
}, },
@ -17777,7 +17758,7 @@
"requires": { "requires": {
"underscore": "1.8.3", "underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.27", "web3-core-helpers": "1.0.0-beta.27",
"websocket": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" "websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2"
}, },
"dependencies": { "dependencies": {
"debug": { "debug": {

@ -78,7 +78,7 @@
"yo-yoify": "^3.7.3" "yo-yoify": "^3.7.3"
}, },
"dependencies": { "dependencies": {
"@remixproject/engine": "^0.2.0", "@remixproject/engine": "^0.2.0-alpha.4",
"http-server": "^0.11.1", "http-server": "^0.11.1",
"remixd": "0.1.8-alpha.10", "remixd": "0.1.8-alpha.10",
"standard": "^8.5.0" "standard": "^8.5.0"

@ -35,12 +35,14 @@ const DebuggerTab = require('./app/tabs/debugger-tab')
const TestTab = require('./app/tabs/test-tab') const TestTab = require('./app/tabs/test-tab')
const FilePanel = require('./app/panels/file-panel') const FilePanel = require('./app/panels/file-panel')
const Editor = require('./app/editor/editor') const Editor = require('./app/editor/editor')
const Terminal = require('./app/panels/terminal')
const ContextualListener = require('./app/editor/contextualListener')
import { basicLogo } from './app/ui/svgLogo' import { basicLogo } from './app/ui/svgLogo'
import { RunTab, makeUdapp } from './app/udapp' import { RunTab, makeUdapp } from './app/udapp'
import PanelsResize from './lib/panels-resize' import PanelsResize from './lib/panels-resize'
import { Engine } from '@remixproject/engine'
import { RemixAppManager } from './remixAppManager' import { RemixAppManager } from './remixAppManager'
import { FramingService } from './framingService' import { FramingService } from './framingService'
import { MainView } from './app/panels/main-view' import { MainView } from './app/panels/main-view'
@ -229,12 +231,14 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// APP_MANAGER // APP_MANAGER
const appManager = new RemixAppManager({}) const appManager = new RemixAppManager({})
const engine = new Engine(appManager)
await engine.onload()
const workspace = appManager.pluginLoader.get() const workspace = appManager.pluginLoader.get()
// SERVICES // SERVICES
// ----------------- import content servive ---------------------------- // ----------------- import content servive ------------------------
const contentImport = new CompilerImport() const contentImport = new CompilerImport()
// ----------------- theme servive ---------------------------- // ----------------- theme servive ---------------------------------
const themeModule = new ThemeModule(registry) const themeModule = new ThemeModule(registry)
registry.put({api: themeModule, name: 'themeModule'}) registry.put({api: themeModule, name: 'themeModule'})
themeModule.initTheme(() => { themeModule.initTheme(() => {
@ -247,6 +251,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const editor = new Editor({}, themeModule) // wrapper around ace editor const editor = new Editor({}, themeModule) // wrapper around ace editor
registry.put({api: editor, name: 'editor'}) registry.put({api: editor, name: 'editor'})
editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile())
// ----------------- fileManager servive ---------------------------- // ----------------- fileManager servive ----------------------------
const fileManager = new FileManager(editor) const fileManager = new FileManager(editor)
registry.put({api: fileManager, name: 'filemanager'}) registry.put({api: fileManager, name: 'filemanager'})
@ -254,20 +259,38 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const blockchain = new Blockchain(registry.get('config').api) const blockchain = new Blockchain(registry.get('config').api)
const pluginUdapp = new PluginUDapp(blockchain) const pluginUdapp = new PluginUDapp(blockchain)
// ----------------- compilation metadata generation servive ---------------------------- // ----------------- compilation metadata generation servive ---------
const compilerMetadataGenerator = new CompilerMetadata(blockchain, fileManager, registry.get('config').api) const compilerMetadataGenerator = new CompilerMetadata(blockchain, fileManager, registry.get('config').api)
// ----------------- compilation result service (can keep track of compilation results) ---------------------------- // ----------------- compilation result service (can keep track of compilation results) ----------------------------
const compilersArtefacts = new CompilersArtefacts() // store all the compilation results (key represent a compiler name) const compilersArtefacts = new CompilersArtefacts() // store all the compilation results (key represent a compiler name)
registry.put({api: compilersArtefacts, name: 'compilersartefacts'}) registry.put({api: compilersArtefacts, name: 'compilersartefacts'})
const {eventsDecoder, txlistener} = makeUdapp(blockchain, compilersArtefacts, (domEl) => mainview.getTerminal().logHtml(domEl)) // ----------------- network service (resolve network id / name) -----
// ----------------- network service (resolve network id / name) ----------------------------
const networkModule = new NetworkModule(blockchain) const networkModule = new NetworkModule(blockchain)
// ----------------- convert offset to line/column service ---------------------------- // ----------------- convert offset to line/column service -----------
var offsetToLineColumnConverter = new OffsetToLineColumnConverter() const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'}) registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'})
appManager.register([ // -------------------Terminal----------------------------------------
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
}
}
)
makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl))
const contextualListener = new ContextualListener({editor})
engine.register([
contentImport, contentImport,
themeModule, themeModule,
editor, editor,
@ -275,24 +298,24 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
compilerMetadataGenerator, compilerMetadataGenerator,
compilersArtefacts, compilersArtefacts,
networkModule, networkModule,
offsetToLineColumnConverter offsetToLineColumnConverter,
contextualListener,
terminal
]) ])
// LAYOUT & SYSTEM VIEWS // LAYOUT & SYSTEM VIEWS
const appPanel = new MainPanel() const appPanel = new MainPanel()
const mainview = new MainView(editor, appPanel, fileManager, appManager, txlistener, eventsDecoder, blockchain) const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal)
registry.put({ api: mainview, name: 'mainview' }) registry.put({ api: mainview, name: 'mainview' })
appManager.register([ engine.register(appPanel)
appPanel
])
// those views depend on app_manager // those views depend on app_manager
const menuicons = new VerticalIcons(appManager) const menuicons = new VerticalIcons(appManager)
const landingPage = new LandingPage(appManager, menuicons) const landingPage = new LandingPage(appManager, menuicons)
const sidePanel = new SidePanel(appManager, menuicons) const sidePanel = new SidePanel(appManager, menuicons)
const hiddenPanel = new HiddenPanel() const hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager) const pluginManagerComponent = new PluginManagerComponent(appManager, engine)
const filePanel = new FilePanel(appManager) const filePanel = new FilePanel(appManager)
let settings = new SettingsTab( let settings = new SettingsTab(
registry.get('config').api, registry.get('config').api,
@ -306,7 +329,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
self._view.sidepanel.appendChild(sidePanel.render()) self._view.sidepanel.appendChild(sidePanel.render())
document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body
appManager.register([ engine.register([
menuicons, menuicons,
landingPage, landingPage,
sidePanel, sidePanel,
@ -344,7 +367,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
new Renderer() new Renderer()
) )
appManager.register([ engine.register([
compileTab, compileTab,
run, run,
debug, debug,
@ -354,17 +377,17 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
]) ])
try { try {
appManager.register(await appManager.registeredPlugins()) engine.register(await appManager.registeredPlugins())
} catch (e) { } catch (e) {
console.log('couldn\'t register iframe plugins', e.message) console.log('couldn\'t register iframe plugins', e.message)
} }
await appManager.activate(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'offsetToLineColumnConverter']) await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'offsetToLineColumnConverter'])
await appManager.activate(['mainPanel']) await appManager.activatePlugin(['mainPanel', 'menuicons'])
await appManager.activate(['menuicons', 'home', 'sidePanel', 'pluginManager', 'fileExplorers', 'settings']) await appManager.activatePlugin(['home', 'sidePanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'terminal'])
// Set workspace after initial activation // Set workspace after initial activation
if (Array.isArray(workspace)) await appManager.activate(workspace) if (Array.isArray(workspace)) await appManager.activatePlugin(workspace)
// Load and start the service who manager layout and frame // Load and start the service who manager layout and frame
const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature) const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature)
@ -390,6 +413,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
} }
if (isElectron()) { if (isElectron()) {
appManager.activate(['remixd']) appManager.activatePlugin('remixd')
} }
} }

@ -13,7 +13,8 @@ const profile = {
name: 'hiddenPanel', name: 'hiddenPanel',
displayName: 'Hidden Panel', displayName: 'Hidden Panel',
description: '', description: '',
version: packageJson.version version: packageJson.version,
methods: []
} }
export class HiddenPanel extends AbstractPanel { export class HiddenPanel extends AbstractPanel {

@ -9,10 +9,9 @@ const defaultProfile = {
} }
module.exports = class LocalPlugin { module.exports = class LocalPlugin {
/** /**
* Open a modal to create a local plugin * Open a modal to create a local plugin
* @param {PluginApi[]} plugins The list of the plugins in the store * @param {Profile[]} plugins The list of the plugins in the store
* @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile * @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile
*/ */
open (plugins) { open (plugins) {
@ -26,7 +25,7 @@ module.exports = class LocalPlugin {
reject(err) reject(err)
} }
} }
modalDialog('Local Plugin', this.form(plugins), modalDialog('Local Plugin', this.form(),
{ fn: () => onValidation() }, { fn: () => onValidation() },
{ fn: () => resolve() } { fn: () => resolve() }
) )

@ -20,8 +20,8 @@ const profile = {
} }
export class MainPanel extends AbstractPanel { export class MainPanel extends AbstractPanel {
constructor (options) { constructor () {
super(profile, options) super(profile)
} }
render () { render () {

@ -1,7 +1,7 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const yo = require('yo-yo') const yo = require('yo-yo')
const { HostPlugin } = require('@remixproject/engine') import { HostPlugin } from '@remixproject/engine'
const css = csjs` const css = csjs`
.plugins { .plugins {
@ -28,7 +28,7 @@ const css = csjs`
/** Abstract class used for hosting the view of a plugin */ /** Abstract class used for hosting the view of a plugin */
export class AbstractPanel extends HostPlugin { export class AbstractPanel extends HostPlugin {
constructor (profile, opts) { constructor (profile) {
super(profile) super(profile)
this.events = new EventEmitter() this.events = new EventEmitter()
this.contents = {} this.contents = {}

@ -72,7 +72,7 @@ const profile = {
class PluginManagerComponent extends ViewPlugin { class PluginManagerComponent extends ViewPlugin {
constructor (appManager) { constructor (appManager, engine) {
super(profile) super(profile)
this.event = new EventEmitter() this.event = new EventEmitter()
this.appManager = appManager this.appManager = appManager
@ -85,37 +85,46 @@ class PluginManagerComponent extends ViewPlugin {
this.appManager.event.on('activate', () => { this.reRender() }) this.appManager.event.on('activate', () => { this.reRender() })
this.appManager.event.on('deactivate', () => { this.reRender() }) this.appManager.event.on('deactivate', () => { this.reRender() })
this.appManager.event.on('added', () => { this.reRender() }) this.appManager.event.on('added', () => { this.reRender() })
this.engine = engine
} }
renderItem (name) { isActive (name) {
const api = this.appManager.getOne(name) return this.appManager.actives.includes(name)
if (!api) return }
const isActive = this.appManager.isActive(name)
const displayName = (api.profile.displayName) ? api.profile.displayName : name renderItem (profile) {
const displayName = (profile.displayName) ? profile.displayName : profile.name
// Check version of the plugin // Check version of the plugin
let versionWarning let versionWarning
// Alpha // Alpha
if (api.profile.version && api.profile.version.match(/\b(\w*alpha\w*)\b/g)) { if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) {
versionWarning = yo`<small title="Version Alpha" class="${css.versionWarning} plugin-version">alpha</small>` versionWarning = yo`<small title="Version Alpha" class="${css.versionWarning} plugin-version">alpha</small>`
} }
// Beta // Beta
if (api.profile.version && api.profile.version.match(/\b(\w*beta\w*)\b/g)) { if (profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)) {
versionWarning = yo`<small title="Version Beta" class="${css.versionWarning} plugin-version">beta</small>` versionWarning = yo`<small title="Version Beta" class="${css.versionWarning} plugin-version">beta</small>`
} }
const activationButton = isActive const activationButton = this.isActive(profile.name)
? yo` ? yo`
<button onclick="${_ => this.appManager.deactivateOne(name)}" class="btn btn-secondary btn-sm" data-id="pluginManagerComponentDeactivateButton${name}"> <button
onclick="${_ => this.appManager.deactivatePlugin(profile.name)}"
class="btn btn-secondary btn-sm" data-id="pluginManagerComponentDeactivateButton${profile.name}"
>
Deactivate Deactivate
</button>` </button>
`
: yo` : yo`
<button onclick="${_ => this.appManager.activateOne(name)}" class="btn btn-success btn-sm" data-id="pluginManagerComponentActivateButton${name}"> <button
onclick="${_ => this.appManager.activatePlugin(profile.name)}"
class="btn btn-success btn-sm" data-id="pluginManagerComponentActivateButton${profile.name}"
>
Activate Activate
</button>` </button>`
return yo` return yo`
<article id="remixPluginManagerListItem_${name}" class="list-group-item py-1 plugins-list-group-item" title="${displayName}" > <article id="remixPluginManagerListItem_${profile.name}" class="list-group-item py-1 plugins-list-group-item" title="${displayName}" >
<div class="${css.row} justify-content-between align-items-center mb-2"> <div class="${css.row} justify-content-between align-items-center mb-2">
<h6 class="${css.displayName} plugin-name"> <h6 class="${css.displayName} plugin-name">
${displayName} ${displayName}
@ -123,7 +132,7 @@ class PluginManagerComponent extends ViewPlugin {
</h6> </h6>
${activationButton} ${activationButton}
</div> </div>
<p class="${css.description} text-body plugin-text">${api.profile.description}</p> <p class="${css.description} text-body plugin-text">${profile.description}</p>
</article> </article>
` `
} }
@ -141,10 +150,9 @@ class PluginManagerComponent extends ViewPlugin {
if (this.appManager.getIds().includes(profile.name)) { if (this.appManager.getIds().includes(profile.name)) {
throw new Error('This name has already been used') throw new Error('This name has already been used')
} }
const plugin = profile.type === 'iframe' ? new IframePlugin(profile) : new WebsocketPlugin(profile) const plugin = profile.type === 'iframe' ? new IframePlugin(profile) : new WebsocketPlugin(profile)
this.appManager.registerOne(plugin) this.engine.register(plugin)
this.appManager.activateOne(profile.name) this.appManager.activatePlugin(plugin.name)
} catch (err) { } catch (err) {
// TODO : Use an alert to handle this error instead of a console.log // TODO : Use an alert to handle this error instead of a console.log
console.log(`Cannot create Plugin : ${err.message}`) console.log(`Cannot create Plugin : ${err.message}`)
@ -154,25 +162,25 @@ class PluginManagerComponent extends ViewPlugin {
render () { render () {
// Filtering helpers // Filtering helpers
const isFiltered = (api) => (api.profile.displayName ? api.profile.displayName : api.name).toLowerCase().includes(this.filter) const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter)
const isNotRequired = ({profile}) => !this.appManager.isRequired(profile.name) const isNotRequired = (profile) => !this.appManager.isRequired(profile.name)
const isNotHome = ({profile}) => profile.name !== 'home' const isNotHome = (profile) => profile.name !== 'home'
const sortByName = (a, b) => { const sortByName = (profileA, profileB) => {
const nameA = ((a.profile.displayName) ? a.profile.displayName : a.profile.name).toUpperCase() const nameA = ((profileA.displayName) ? profileA.displayName : profileA.name).toUpperCase()
const nameB = ((b.profile.displayName) ? b.profile.displayName : b.profile.name).toUpperCase() const nameB = ((profileB.displayName) ? profileB.displayName : profileB.name).toUpperCase()
return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0 return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0
} }
// Filter all active and inactive modules that are not required // Filter all active and inactive modules that are not required
const { actives, inactives } = this.appManager.getAll() const {actives, inactives} = this.appManager.getAll()
.filter(isFiltered) .filter(isFiltered)
.filter(isNotRequired) .filter(isNotRequired)
.filter(isNotHome) .filter(isNotHome)
.sort(sortByName) .sort(sortByName)
.reduce(({actives, inactives}, api) => { .reduce(({actives, inactives}, profile) => {
return this.appManager.isActive(api.name) return this.isActive(profile.name)
? { actives: [...actives, api.name], inactives } ? { actives: [...actives, profile], inactives }
: { inactives: [...inactives, api.name], actives } : { inactives: [...inactives, profile], actives }
}, { actives: [], inactives: [] }) }, { actives: [], inactives: [] })
const activeTile = actives.length !== 0 const activeTile = actives.length !== 0
@ -203,11 +211,11 @@ class PluginManagerComponent extends ViewPlugin {
<section data-id="pluginManagerComponentPluginManagerSection"> <section data-id="pluginManagerComponentPluginManagerSection">
${activeTile} ${activeTile}
<div class="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile"> <div class="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile">
${actives.map(name => this.renderItem(name))} ${actives.map(profile => this.renderItem(profile))}
</div> </div>
${inactiveTile} ${inactiveTile}
<div class="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentInactiveTile"> <div class="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentInactiveTile">
${inactives.map(name => this.renderItem(name))} ${inactives.map(profile => this.renderItem(profile))}
</div> </div>
</section> </section>
${settings} ${settings}

@ -48,10 +48,6 @@ const css = csjs`
} }
` `
const options = {
default: true
}
const sidePanel = { const sidePanel = {
name: 'sidePanel', name: 'sidePanel',
displayName: 'Side Panel', displayName: 'Side Panel',
@ -64,9 +60,10 @@ const sidePanel = {
export class SidePanel extends AbstractPanel { export class SidePanel extends AbstractPanel {
constructor (appManager, verticalIcons) { constructor (appManager, verticalIcons) {
super(sidePanel, options) super(sidePanel)
this.appManager = appManager this.appManager = appManager
this.header = this.renderHeader() this.header = yo`<header></header>`
this.renderHeader()
this.verticalIcons = verticalIcons this.verticalIcons = verticalIcons
// Toggle content // Toggle content
@ -107,18 +104,18 @@ export class SidePanel extends AbstractPanel {
* Display content and update the header * Display content and update the header
* @param {String} name The name of the plugin to display * @param {String} name The name of the plugin to display
*/ */
showContent (name) { async showContent (name) {
super.showContent(name) super.showContent(name)
yo.update(this.header, this.renderHeader()) this.renderHeader()
} }
/** The header of the side panel */ /** The header of the side panel */
renderHeader () { async renderHeader () {
let name = ' - ' let name = ' - '
let docLink = '' let docLink = ''
let versionWarning let versionWarning
if (this.active) { if (this.active) {
const { profile } = this.appManager.getOne(this.active) const profile = await this.appManager.getProfile(this.active)
name = profile.displayName ? profile.displayName : profile.name name = profile.displayName ? profile.displayName : profile.name
docLink = profile.documentation ? yo`<a href="${profile.documentation}" class="${css.titleInfo}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : '' docLink = profile.documentation ? yo`<a href="${profile.documentation}" class="${css.titleInfo}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : ''
if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) { if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) {
@ -130,13 +127,14 @@ export class SidePanel extends AbstractPanel {
} }
} }
return yo` const header = yo`
<header class="${css.swapitHeader} px-3"> <header class="${css.swapitHeader} px-3">
<h6 class="${css.swapitTitle}" data-id="sidePanelSwapitTitle">${name}</h6> <h6 class="${css.swapitTitle}" data-id="sidePanelSwapitTitle">${name}</h6>
${docLink} ${docLink}
${versionWarning} ${versionWarning}
</header> </header>
` `
yo.update(this.header, header)
} }
render () { render () {

@ -9,6 +9,7 @@ const toaster = require('../ui/tooltip')
const modalDialogCustom = require('../ui/modal-dialog-custom') const modalDialogCustom = require('../ui/modal-dialog-custom')
const helper = require('../../lib/helper.js') const helper = require('../../lib/helper.js')
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { isNative } from '../../remixAppManager.js'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
/* /*
@ -172,7 +173,7 @@ class FileManager extends Plugin {
toaster.hide() toaster.hide()
} }
if (this.currentRequest) { if (this.currentRequest) {
if (this.currentRequest.isFromNative) { if (isNative(this.currentRequest.from)) {
this._setFileInternal(path, content) this._setFileInternal(path, content)
return return
} }

@ -1,11 +1,9 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var Terminal = require('./terminal')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var { TabProxy } = require('./tab-proxy.js') var { TabProxy } = require('./tab-proxy.js')
var ContextualListener = require('../editor/contextualListener')
var ContextView = require('../editor/contextView') var ContextView = require('../editor/contextView')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
@ -20,7 +18,7 @@ var css = csjs`
` `
export class MainView { export class MainView {
constructor (editor, mainPanel, fileManager, appManager, txListener, eventsDecoder, blockchain) { constructor (contextualListener, editor, mainPanel, fileManager, appManager, terminal) {
var self = this var self = this
self.event = new EventManager() self.event = new EventManager()
self._view = {} self._view = {}
@ -29,9 +27,9 @@ export class MainView {
self.editor = editor self.editor = editor
self.fileManager = fileManager self.fileManager = fileManager
self.mainPanel = mainPanel self.mainPanel = mainPanel
self.txListener = txListener self.txListener = globalRegistry.get('txlistener').api
self.eventsDecoder = eventsDecoder self._components.terminal = terminal
self.blockchain = blockchain self._components.contextualListener = contextualListener
this.appManager = appManager this.appManager = appManager
this.init() this.init()
} }
@ -88,32 +86,10 @@ export class MainView {
} }
} }
var contextualListener = new ContextualListener({editor: self.editor}) const contextView = new ContextView({contextualListener: self._components.contextualListener, editor: self.editor})
this.appManager.registerOne(contextualListener)
this.appManager.activate('contextualListener')
var contextView = new ContextView({contextualListener, editor: self.editor})
self._components.contextualListener = contextualListener
self._components.contextView = contextView self._components.contextView = contextView
self._components.terminal = new Terminal({
appManager: this.appManager,
eventsDecoder: this.eventsDecoder,
txListener: this.txListener,
blockchain: this.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
}
})
self._components.terminal.event.register('resize', delta => self._adjustLayout('top', delta)) self._components.terminal.event.register('resize', delta => self._adjustLayout('top', delta))
if (self.txListener) { if (self.txListener) {
self._components.terminal.event.register('listenOnNetWork', (listenOnNetWork) => { self._components.terminal.event.register('listenOnNetWork', (listenOnNetWork) => {

@ -49,25 +49,24 @@ export class TabProxy {
}) })
}) })
appManager.event.on('activate', (name) => { appManager.event.on('activate', ({ name, location, displayName, icon }) => {
const { profile } = appManager.getOne(name) if (location === 'mainPanel') {
if (profile.location === 'mainPanel') {
this.addTab( this.addTab(
name, name,
profile.displayName, displayName,
() => this.event.emit('switchApp', name), () => this.event.emit('switchApp', name),
() => { () => {
this.event.emit('closeApp', name) this.event.emit('closeApp', name)
this.appManager.deactivateOne(name) this.appManager.deactivatePlugin(name)
}, },
profile.icon icon
) )
this.switchTab(name) this.switchTab(name)
} }
}) })
appManager.event.on('deactivate', (name) => { appManager.event.on('deactivate', (profile) => {
this.removeTab(name) this.removeTab(profile.name)
}) })
appManager.event.on('ensureActivated', (name) => { appManager.event.on('ensureActivated', (name) => {

@ -45,7 +45,7 @@ class Terminal extends Plugin {
self._api = api self._api = api
self._opts = opts self._opts = opts
self.data = { self.data = {
lineLength: opts.lineLength || 80, lineLength: opts.lineLength || 80, // ????
session: [], session: [],
activeFilters: { commands: {}, input: '' }, activeFilters: { commands: {}, input: '' },
filterFns: {} filterFns: {}
@ -95,12 +95,7 @@ class Terminal extends Plugin {
self._jsSandboxContext = {} self._jsSandboxContext = {}
self._jsSandboxRegistered = {} self._jsSandboxRegistered = {}
// TODO move this to the application start. Put it in mainView. if (opts.shell) self._shell = opts.shell // ???
// We should have a HostPlugin which add the terminal.
opts.appManager.register(this)
opts.appManager.activate('terminal')
if (opts.shell) self._shell = opts.shell
register(self) register(self)
} }
logHtml (html) { logHtml (html) {
@ -139,7 +134,7 @@ class Terminal extends Plugin {
self._view.inputSearch = yo`<input self._view.inputSearch = yo`<input
spellcheck="false" spellcheck="false"
type="text" type="text"
class="${css.filter} form-control" class="border ${css.filter} form-control"
id="searchInput" id="searchInput"
onkeydown=${filter} onkeydown=${filter}
placeholder="Search with transaction hash or address"> placeholder="Search with transaction hash or address">
@ -438,10 +433,10 @@ class Terminal extends Plugin {
self._shell('remix.help()', self.commands, () => {}) self._shell('remix.help()', self.commands, () => {})
self.commands.html(intro) self.commands.html(intro)
self._components.txLogger = new TxLogger(self._opts.eventsDecoder, self._opts.txListener, this, self.blockchain) self._components.txLogger = new TxLogger(this, self.blockchain)
self._components.txLogger.event.register('debuggingRequested', (hash) => { self._components.txLogger.event.register('debuggingRequested', async (hash) => {
// TODO should probably be in the run module // TODO should probably be in the run module
if (!self._opts.appManager.isActive('debugger')) self._opts.appManager.activateOne('debugger') if (!await self._opts.appManager.isActive('debugger')) await self._opts.appManager.activatePlugin('debugger')
this.call('debugger', 'debug', hash) this.call('debugger', 'debug', hash)
this.call('menuicons', 'select', 'debugger') this.call('menuicons', 'select', 'debugger')
}) })

@ -227,7 +227,7 @@ module.exports = class TestTab extends ViewPlugin {
updateRunAction (currentFile) { updateRunAction (currentFile) {
let el = yo`<button id="runTestsTabRunAction" class="${css.runButton} btn btn-primary" onclick="${this.runTests.bind(this)}">Run Tests</button>` let el = yo`<button id="runTestsTabRunAction" class="${css.runButton} btn btn-primary" onclick="${this.runTests.bind(this)}">Run Tests</button>`
const isSolidityActive = this.appManager.isActive('solidity') const isSolidityActive = this.appManager.actives.includes('solidity')
if (!currentFile || !isSolidityActive) { if (!currentFile || !isSolidityActive) {
el.setAttribute('disabled', 'disabled') el.setAttribute('disabled', 'disabled')
if (!currentFile) el.setAttribute('title', 'No file selected') if (!currentFile) el.setAttribute('title', 'No file selected')

@ -52,6 +52,5 @@ export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) {
} }
}) })
txlistener.startListening() txlistener.startListening()
registry.put({api: eventsDecoder, name: 'eventsDecoder'})
return {txlistener, eventsDecoder}
} }

@ -189,13 +189,12 @@ class AutoCompletePopup {
extendAutocompletion () { extendAutocompletion () {
// TODO: this is not using the appManager interface. Terminal should be put as module // TODO: this is not using the appManager interface. Terminal should be put as module
this.opts.appManager.event.on('activate', (id) => { this.opts.appManager.event.on('activate', async (profile) => {
const profile = this.opts.appManager.getOne(id).profile
if (!profile.methods) return if (!profile.methods) return
profile.methods.forEach((method) => { profile.methods.forEach((method) => {
const key = `remix.call({name: '${id}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })` const key = `remix.call({name: '${profile.name}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })`
const keyValue = {} const keyValue = {}
keyValue[key] = `call ${id} - ${method}` keyValue[key] = `call ${profile.name} - ${method}`
if (this.extraCommands.includes(keyValue)) return if (this.extraCommands.includes(keyValue)) return
this.extraCommands.push(keyValue) this.extraCommands.push(keyValue)
}) })

@ -116,7 +116,7 @@ var css = csjs`
* *
*/ */
class TxLogger { class TxLogger {
constructor (eventsDecoder, txListener, terminal, blockchain) { constructor (terminal, blockchain) {
this.event = new EventManager() this.event = new EventManager()
this.seen = {} this.seen = {}
function filterTx (value, query) { function filterTx (value, query) {
@ -125,8 +125,8 @@ class TxLogger {
} }
return false return false
} }
this.eventsDecoder = eventsDecoder this.eventsDecoder = globlalRegistry.get('eventsDecoder').api
this.txListener = txListener this.txListener = globlalRegistry.get('txlistener').api
this.terminal = terminal this.terminal = terminal
// dependencies // dependencies
this._deps = { this._deps = {

@ -1,75 +1,77 @@
/* global localStorage, fetch */ /* global localStorage, fetch */
import { PluginEngine, IframePlugin } from '@remixproject/engine' import { PluginManager, IframePlugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { PermissionHandler } from './app/ui/persmission-handler'
import QueryParams from './lib/query-params' import QueryParams from './lib/query-params'
const requiredModules = [ // services + layout views + system views const requiredModules = [ // services + layout views + system views
'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'fileManager', 'contentImport', 'manager', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'fileManager', 'contentImport',
'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'fileExplorers', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'fileExplorers',
'terminal', 'settings', 'pluginManager'] 'terminal', 'settings', 'pluginManager']
const settings = { export function isNative (name) {
permissionHandler: new PermissionHandler(), const nativePlugins = ['vyper', 'workshops', 'ethdoc', 'etherscan']
autoActivate: false, return nativePlugins.includes(name) || requiredModules.includes(name)
natives: ['vyper', 'workshops', 'ethdoc', 'etherscan'] // Force iframe plugin to be seen as native
} }
export class RemixAppManager extends PluginEngine { export class RemixAppManager extends PluginManager {
constructor (plugins) { constructor (plugins) {
super(plugins, settings) super()
this.event = new EventEmitter() this.event = new EventEmitter()
this.registered = {}
this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json' this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json'
this.pluginLoader = new PluginLoader() this.pluginLoader = new PluginLoader()
} }
onActivated (plugin) { async canActivate (from, to) {
return from.name === 'manager'
}
async canDeactivate (from, to) {
return from.name === 'manager'
}
async canCall (From, to, method) {
// todo This is the dafault behaviour, we could save user choises in session scope
return true
}
onPluginActivated (plugin) {
this.pluginLoader.set(plugin, this.actives) this.pluginLoader.set(plugin, this.actives)
this.event.emit('activate', plugin.name) this.event.emit('activate', plugin)
} }
getAll () { getAll () {
return Object.keys(this.registered).map((p) => { return Object.keys(this.profiles).map((p) => {
return this.registered[p] return this.profiles[p]
}) })
} }
getOne (name) {
return this.registered[name]
}
getIds () { getIds () {
return Object.keys(this.registered) return Object.keys(this.profiles)
} }
onDeactivated (plugin) { onPluginDeactivated (plugin) {
this.pluginLoader.set(plugin, this.actives) this.pluginLoader.set(plugin, this.actives)
this.event.emit('deactivate', plugin.name) this.event.emit('deactivate', plugin)
} }
onRegistration (plugin) { onRegistration (plugin) {
if (!this.registered) this.registered = {}
this.registered[plugin.name] = plugin
this.event.emit('added', plugin.name) this.event.emit('added', plugin.name)
} }
// TODO check whether this can be removed async ensureActivated (apiName) {
ensureActivated (apiName) { await this.activatePlugin(apiName)
if (!this.isActive(apiName)) this.activateOne(apiName)
this.event.emit('ensureActivated', apiName) this.event.emit('ensureActivated', apiName)
} }
// TODO check whether this can be removed async ensureDeactivated (apiName) {
ensureDeactivated (apiName) { await this.deactivatePlugin(apiName)
if (this.isActive(apiName)) this.deactivateOne(apiName)
this.event.emit('ensureDeactivated', apiName) this.event.emit('ensureDeactivated', apiName)
} }
deactivateOne (name) { deactivatePlugin (name) {
if (requiredModules.includes(name)) return if (requiredModules.includes(name)) return
super.deactivateOne(name) super.deactivatePlugin(name)
} }
isRequired (name) { isRequired (name) {

@ -22,20 +22,20 @@ module.exports = {
'Should Search for plugins': function (browser) { 'Should Search for plugins': function (browser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentSearchInput"]') .click('*[data-id="pluginManagerComponentSearchInput"]')
.keys('debugger') .keys('debugger')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtondebugger"]') .waitForElementVisible('*[data-id="pluginManagerComponentActivateButtondebugger"]')
.clearValue('*[data-id="pluginManagerComponentSearchInput"]') .clearValue('*[data-id="pluginManagerComponentSearchInput"]')
.click('*[data-id="pluginManagerComponentSearchInput"]') .click('*[data-id="pluginManagerComponentSearchInput"]')
.keys('Deploy & run transactions') .keys('Deploy & run transactions')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonudapp"]') .waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonudapp"]')
.clearValue('*[data-id="pluginManagerComponentSearchInput"]') .clearValue('*[data-id="pluginManagerComponentSearchInput"]')
.click('*[data-id="pluginManagerComponentSearchInput"]') .click('*[data-id="pluginManagerComponentSearchInput"]')
.keys('ZoKrates') .keys('ZoKrates')
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonZoKrates"]') .waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonZoKrates"]')
.clearValue('*[data-id="pluginManagerComponentSearchInput"]') .clearValue('*[data-id="pluginManagerComponentSearchInput"]')
.click('*[data-id="pluginManagerComponentSearchInput"]') .click('*[data-id="pluginManagerComponentSearchInput"]')
.keys(browser.Keys.ENTER) .keys(browser.Keys.ENTER)
}, },
'Should activate plugins': function (browser) { 'Should activate plugins': function (browser) {
@ -58,6 +58,7 @@ module.exports = {
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonudapp"]') .waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonudapp"]')
}, },
/*
'Should grant plugin permission (ZOKRATES)': function (browser) { 'Should grant plugin permission (ZOKRATES)': function (browser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerSettingsButton"]') .click('*[data-id="pluginManagerSettingsButton"]')
@ -93,6 +94,7 @@ module.exports = {
.assert.containsText('*[data-id="pluginManagerSettingsPermissionForm"]', 'No Permission requested yet') .assert.containsText('*[data-id="pluginManagerSettingsPermissionForm"]', 'No Permission requested yet')
.modalFooterOKClick() .modalFooterOKClick()
}, },
*/
'Should connect a local plugin': function (browser) { 'Should connect a local plugin': function (browser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
@ -125,6 +127,5 @@ module.exports = {
.assert.containsText('*[data-id="tooltipPopup"]', 'Cannot create Plugin : This name has already been used') .assert.containsText('*[data-id="tooltipPopup"]', 'Cannot create Plugin : This name has already been used')
.end() .end()
}, },
tearDown: sauce tearDown: sauce
} }

Loading…
Cancel
Save