diff --git a/apps/remix-ide-e2e/src/tests/editor.spec.ts b/apps/remix-ide-e2e/src/tests/editor.test.ts
similarity index 71%
rename from apps/remix-ide-e2e/src/tests/editor.spec.ts
rename to apps/remix-ide-e2e/src/tests/editor.test.ts
index f0e88fe5cc..7b714a432b 100644
--- a/apps/remix-ide-e2e/src/tests/editor.spec.ts
+++ b/apps/remix-ide-e2e/src/tests/editor.test.ts
@@ -6,10 +6,10 @@ import init from '../helpers/init'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
- init(browser, done)
+ init(browser, done, 'http://127.0.0.1:8080', true)
},
- 'Should zoom in editor ': function (browser: NightwatchBrowser) {
+ 'Should zoom in editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]')
.clickLaunchIcon('filePanel')
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
@@ -22,7 +22,7 @@ module.exports = {
.checkElementStyle('.view-lines', 'font-size', '16px')
},
- 'Should zoom out editor ': function (browser: NightwatchBrowser) {
+ 'Should zoom out editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.checkElementStyle('.view-lines', 'font-size', '16px')
.click('*[data-id="tabProxyZoomOut"]')
@@ -30,7 +30,7 @@ module.exports = {
.checkElementStyle('.view-lines', 'font-size', '14px')
},
- 'Should display compile error in editor ': function (browser: NightwatchBrowser) {
+ 'Should display compile error in editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.setEditorValue(storageContractWithError + 'error')
.pause(2000)
@@ -42,7 +42,7 @@ module.exports = {
.checkAnnotations('fa-exclamation-square', 29) // error
},
- 'Should minimize and maximize codeblock in editor ': '' + function (browser: NightwatchBrowser) {
+ 'Should minimize and maximize codeblock in editor #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.waitForElementVisible('.ace_open')
.click('.ace_start:nth-of-type(1)')
@@ -51,7 +51,7 @@ module.exports = {
.waitForElementVisible('.ace_open')
},
- 'Should add breakpoint to editor ': function (browser: NightwatchBrowser) {
+ 'Should add breakpoint to editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.waitForElementNotPresent('.margin-view-overlays .fa-circle')
.execute(() => {
@@ -60,7 +60,7 @@ module.exports = {
.waitForElementVisible('.margin-view-overlays .fa-circle')
},
- 'Should load syntax highlighter for ace light theme': '' + function (browser: NightwatchBrowser) {
+ 'Should load syntax highlighter for ace light theme #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.checkElementStyle('.ace_keyword', 'color', aceThemes.light.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.light.comment)
@@ -68,7 +68,7 @@ module.exports = {
.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 #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]')
.click('*[data-id="verticalIconsKindsettings"]')
.waitForElementVisible('*[data-id="settingsTabThemeLabelDark"]')
@@ -83,7 +83,7 @@ module.exports = {
*/
},
- 'Should highlight source code ': function (browser: NightwatchBrowser) {
+ 'Should highlight source code #group1': function (browser: NightwatchBrowser) {
// include all files here because switching between plugins in side-panel removes highlight
browser
.addFile('sourcehighlight.js', sourcehighlightScript)
@@ -101,7 +101,7 @@ module.exports = {
.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 #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.pause(2000)
@@ -115,7 +115,7 @@ module.exports = {
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
},
- 'Should remove all highlights from source code ': function (browser: NightwatchBrowser) {
+ 'Should remove all highlights from source code #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
.pause(2000)
@@ -126,6 +126,54 @@ module.exports = {
.waitForElementNotPresent('.highlightLine33', 60000)
.waitForElementNotPresent('.highlightLine41', 60000)
.waitForElementNotPresent('.highlightLine51', 60000)
+ },
+
+ 'Should display the context view #group2': function (browser: NightwatchBrowser) {
+ browser
+ .openFile('contracts')
+ .openFile('contracts/1_Storage.sol')
+ .waitForElementVisible('#editorView')
+ .setEditorValue(storageContractWithError)
+ .pause(2000)
+ .execute(() => {
+ (document.getElementById('editorView') as any).gotoLine(17, 16)
+ }, [], () => {})
+ .waitForElementVisible('.contextview')
+ .waitForElementContainsText('.contextview .type', 'FunctionDefinition')
+ .waitForElementContainsText('.contextview .name', 'store')
+ .execute(() => {
+ (document.getElementById('editorView') as any).gotoLine(18, 12)
+ }, [], () => {})
+ .waitForElementContainsText('.contextview .type', 'uint256')
+ .waitForElementContainsText('.contextview .name', 'number')
+ .click('.contextview [data-action="previous"]') // declaration
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '180')
+ })
+ .click('.contextview [data-action="next"]') // back to the initial state
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '323')
+ })
+ .click('.contextview [data-action="next"]') // next reference
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '489')
+ })
+ .click('.contextview [data-action="gotoref"]') // back to the declaration
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '180')
+ })
.end()
}
}
diff --git a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
index 267daa9e1c..663c1bb791 100644
--- a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
+++ b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
@@ -4,7 +4,7 @@ import init from '../helpers/init'
import * as path from 'path'
const testData = {
- testFile1: path.resolve(__dirname + '/editor.spec.js'), // eslint-disable-line
+ testFile1: path.resolve(__dirname + '/editor.test.js'), // eslint-disable-line
testFile2: path.resolve(__dirname + '/fileExplorer.test.js'), // eslint-disable-line
testFile3: path.resolve(__dirname + '/generalSettings.test.js') // eslint-disable-line
}
@@ -105,7 +105,7 @@ module.exports = {
.setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile1)
.setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile2)
.setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile3)
- .waitForElementVisible('[data-id="treeViewLitreeViewItemeditor.spec.js"]')
+ .waitForElementVisible('[data-id="treeViewLitreeViewItemeditor.test.js"]')
.waitForElementVisible('[data-id="treeViewLitreeViewItemfileExplorer.test.js"]')
.waitForElementVisible('[data-id="treeViewLitreeViewItemgeneralSettings.test.js"]')
.end()
diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js
index e569cc5e15..d2b8a690ae 100644
--- a/apps/remix-ide/src/app.js
+++ b/apps/remix-ide/src/app.js
@@ -15,7 +15,7 @@ import { FramingService } from './framingService'
import { WalkthroughService } from './walkthroughService'
-import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports } from '@remix-project/core-plugin'
+import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports, EditorContextListener } from '@remix-project/core-plugin'
import migrateFileSystem from './migrateFileSystem'
@@ -48,7 +48,6 @@ const TestTab = require('./app/tabs/test-tab')
const FilePanel = require('./app/panels/file-panel')
const Editor = require('./app/editor/editor')
const Terminal = require('./app/panels/terminal')
-const ContextualListener = require('./app/editor/contextualListener')
class AppComponent {
constructor (api = {}, events = {}, opts = {}) {
@@ -156,7 +155,7 @@ class AppComponent {
}
}
)
- const contextualListener = new ContextualListener({ editor })
+ const contextualListener = new EditorContextListener()
self.engine.register([
blockchain,
diff --git a/apps/remix-ide/src/app/editor/contextView.js b/apps/remix-ide/src/app/editor/contextView.js
deleted file mode 100644
index da01321762..0000000000
--- a/apps/remix-ide/src/app/editor/contextView.js
+++ /dev/null
@@ -1,194 +0,0 @@
-'use strict'
-import { sourceMappingDecoder } from '@remix-project/remix-debug'
-const yo = require('yo-yo')
-const globalRegistry = require('../../global/registry')
-
-const css = require('./styles/contextView-styles')
-
-/*
- Display information about the current focused code:
- - if it's a reference, display information about the declaration
- - jump to the declaration
- - number of references
- - rename declaration/references
-*/
-class ContextView {
- constructor (opts, localRegistry) {
- this._components = {}
- this._components.registry = localRegistry || globalRegistry
- this.contextualListener = opts.contextualListener
- this.editor = opts.editor
- this._deps = {
- compilersArtefacts: this._components.registry.get('compilersartefacts').api,
- offsetToLineColumnConverter: this._components.registry.get('offsettolinecolumnconverter').api,
- config: this._components.registry.get('config').api,
- fileManager: this._components.registry.get('filemanager').api
- }
- this._view = null
- this._nodes = null
- this._current = null
- this.sourceMappingDecoder = sourceMappingDecoder
- this.previousElement = null
- this.contextualListener.event.register('contextChanged', nodes => {
- this.show()
- this._nodes = nodes
- this.update()
- })
- this.contextualListener.event.register('stopHighlighting', () => {
- })
- }
-
- render () {
- const view = yo`
-
-
- ${this._renderTarget()}
-
-
`
- if (!this._view) {
- this._view = view
- }
- return view
- }
-
- hide () {
- if (this._view) {
- this._view.style.display = 'none'
- }
- }
-
- show () {
- if (this._view) {
- this._view.style.display = 'block'
- }
- }
-
- update () {
- if (this._view) {
- yo.update(this._view, this.render())
- }
- }
-
- _renderTarget () {
- let last
- const previous = this._current
- if (this._nodes && this._nodes.length) {
- last = this._nodes[this._nodes.length - 1]
- if (isDefinition(last)) {
- this._current = last
- } else {
- const target = this.contextualListener.declarationOf(last)
- if (target) {
- this._current = target
- } else {
- this._current = null
- }
- }
- }
- if (!this._current || !previous || previous.id !== this._current.id || (this.previousElement && !this.previousElement.children.length)) {
- this.previousElement = this._render(this._current, last)
- }
- return this.previousElement
- }
-
- _jumpToInternal (position) {
- const jumpToLine = (lineColumn) => {
- if (lineColumn.start && lineColumn.start.line && lineColumn.start.column) {
- this.editor.gotoLine(lineColumn.start.line, lineColumn.end.column + 1)
- }
- }
- const lastCompilationResult = this._deps.compilersArtefacts.__last
- if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
- const lineColumn = this._deps.offsetToLineColumnConverter.offsetToLineColumn(
- position,
- position.file,
- lastCompilationResult.getSourceCode().sources,
- lastCompilationResult.getAsts())
- const filename = lastCompilationResult.getSourceName(position.file)
- // TODO: refactor with rendererAPI.errorClick
- if (filename !== this._deps.config.get('currentFile')) {
- const provider = this._deps.fileManager.fileProviderOf(filename)
- if (provider) {
- provider.exists(filename).then(exist => {
- this._deps.fileManager.open(filename)
- jumpToLine(lineColumn)
- }).catch(error => {
- if (error) return console.log(error)
- })
- }
- } else {
- jumpToLine(lineColumn)
- }
- }
- }
-
- _render (node, nodeAtCursorPosition) {
- if (!node) return yo``
- let references = this.contextualListener.referencesOf(node)
- const type = node.typeDescriptions && node.typeDescriptions.typeString ? node.typeDescriptions.typeString : node.nodeType
- references = `${references ? references.length : '0'} reference(s)`
-
- let ref = 0
- const nodes = this.contextualListener.getActiveHighlights()
- for (const k in nodes) {
- if (nodeAtCursorPosition.id === nodes[k].nodeId) {
- ref = k
- break
- }
- }
-
- // JUMP BETWEEN REFERENCES
- const jump = (e) => {
- e.target.dataset.action === 'next' ? ref++ : ref--
- if (ref < 0) ref = nodes.length - 1
- if (ref >= nodes.length) ref = 0
- this._jumpToInternal(nodes[ref].position)
- }
-
- const jumpTo = () => {
- if (node && node.src) {
- const position = this.sourceMappingDecoder.decode(node.src)
- if (position) {
- this._jumpToInternal(position)
- }
- }
- }
-
- const showGasEstimation = () => {
- if (node.nodeType === 'FunctionDefinition') {
- const result = this.contextualListener.gasEstimation(node)
- const executionCost = ' Execution cost: ' + result.executionCost + ' gas'
- const codeDepositCost = 'Code deposit cost: ' + result.codeDepositCost + ' gas'
- const estimatedGas = result.codeDepositCost ? `${codeDepositCost}, ${executionCost}` : `${executionCost}`
- return yo`
-
-
- ${estimatedGas}
-
- `
- }
- }
-
- return yo`
- ${showGasEstimation()}
-
${type}
-
${node.name}
-
-
${references}
-
-
-
- `
- }
-}
-
-function isDefinition (node) {
- return node.nodeType === 'ContractDefinition' ||
- node.nodeType === 'FunctionDefinition' ||
- node.nodeType === 'ModifierDefinition' ||
- node.nodeType === 'VariableDeclaration' ||
- node.nodeType === 'StructDefinition' ||
- node.nodeType === 'EventDefinition'
-}
-
-module.exports = ContextView
diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js
index 295d83fd59..013b1b9853 100644
--- a/apps/remix-ide/src/app/editor/editor.js
+++ b/apps/remix-ide/src/app/editor/editor.js
@@ -12,7 +12,7 @@ const profile = {
name: 'editor',
description: 'service - editor',
version: packageJson.version,
- methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine']
+ methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine', 'getCursorPosition']
}
class Editor extends Plugin {
@@ -75,7 +75,8 @@ class Editor extends Plugin {
this._onChange(this.currentFile)
}
}
- this.el.gotoLine = (line) => this.gotoLine(line, 0)
+ this.el.gotoLine = (line, column) => this.gotoLine(line, column || 0)
+ this.el.getCursorPosition = () => this.getCursorPosition()
return this.el
}
diff --git a/apps/remix-ide/src/app/panels/main-view.js b/apps/remix-ide/src/app/panels/main-view.js
index 537b296796..cad19a58f3 100644
--- a/apps/remix-ide/src/app/panels/main-view.js
+++ b/apps/remix-ide/src/app/panels/main-view.js
@@ -1,11 +1,14 @@
+
+import React from 'react' // eslint-disable-line
+import ReactDOM from 'react-dom'
+import { RemixUiEditorContextView } from '@remix-ui/editor-context-view'
+
var yo = require('yo-yo')
var EventManager = require('../../lib/events')
var globalRegistry = require('../../global/registry')
var { TabProxy } = require('./tab-proxy.js')
-var ContextView = require('../editor/contextView')
-
var csjs = require('csjs-inject')
var css = csjs`
@@ -15,6 +18,10 @@ var css = csjs`
height : 100%;
width : 100%;
}
+ .contextview {
+ opacity : 1;
+ position : relative;
+ }
`
// @todo(#650) Extract this into two classes: MainPanel (TabsProxy + Iframe/Editor) & BottomPanel (Terminal)
@@ -25,12 +32,13 @@ export class MainView {
self._view = {}
self._components = {}
self._components.registry = globalRegistry
+ self.contextualListener = contextualListener
+ self.hideContextView = false
self.editor = editor
self.fileManager = fileManager
self.mainPanel = mainPanel
self.txListener = globalRegistry.get('txlistener').api
self._components.terminal = terminal
- self._components.contextualListener = contextualListener
this.appManager = appManager
this.init()
}
@@ -39,7 +47,8 @@ export class MainView {
this.fileManager.unselectCurrentFile()
this.mainPanel.showContent(name)
this._view.editor.style.display = 'none'
- this._components.contextView.hide()
+ this.hideContextView = true
+ this.renderContextView()
this._view.mainPanel.style.display = 'block'
}
@@ -63,19 +72,22 @@ export class MainView {
// we check upstream for "fileChanged"
self._view.editor.style.display = 'block'
self._view.mainPanel.style.display = 'none'
- self._components.contextView.show()
+ this.hideContextView = false
+ this.renderContextView()
})
self.tabProxy.event.on('openFile', (file) => {
self._view.editor.style.display = 'block'
self._view.mainPanel.style.display = 'none'
- self._components.contextView.show()
+ this.hideContextView = false
+ this.renderContextView()
})
self.tabProxy.event.on('closeFile', (file) => {
})
self.tabProxy.event.on('switchApp', self.showApp.bind(self))
self.tabProxy.event.on('closeApp', (name) => {
self._view.editor.style.display = 'block'
- self._components.contextView.show()
+ this.hideContextView = false
+ this.renderContextView()
self._view.mainPanel.style.display = 'none'
})
self.tabProxy.event.on('tabCountChanged', (count) => {
@@ -90,10 +102,6 @@ export class MainView {
}
}
- const contextView = new ContextView({ contextualListener: self._components.contextualListener, editor: self.editor })
-
- self._components.contextView = contextView
-
self._components.terminal.event.register('resize', delta => self._adjustLayout('top', delta))
if (self.txListener) {
self._components.terminal.event.register('listenOnNetWork', (listenOnNetWork) => {
@@ -181,15 +189,17 @@ export class MainView {
self._view.editor.style.display = 'none'
self._view.mainPanel = self.mainPanel.render()
self._view.terminal = self._components.terminal.render()
+
self._view.mainview = yo`
${self.tabProxy.renderTabsbar()}
${self._view.editor}
${self._view.mainPanel}
- ${self._components.contextView.render()}
+
${self._view.terminal}
`
+
// INIT
self._adjustLayout('top', self.data._layout.top.offset)
@@ -200,6 +210,22 @@ export class MainView {
return self._view.mainview
}
+ renderContextView () {
+ if (!this.contextualListener.activated) return
+
+ ReactDOM.render(
+ this.contextualListener.call('editor', 'gotoLine', line, column)}
+ openFile={(file) => this.contextualListener.call('editor', 'openFile', file)}
+ getLastCompilationResult={_ => { return this.contextualListener.call('compilerArtefacts', 'getLastCompilationResult') } }
+ offsetToLineColumn={(position, file, sources, asts) => { return this.contextualListener.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } }
+ getCurrentFileName={() => { return this.contextualListener.call('fileManager', 'file') } }
+ />
+ , this._view.mainview.querySelector('.contextview'))
+ }
+
registerCommand (name, command, opts) {
var self = this
return self._components.terminal.registerCommand(name, command, opts)
diff --git a/libs/remix-core-plugin/src/index.ts b/libs/remix-core-plugin/src/index.ts
index 73c98a6181..fe8a5c661e 100644
--- a/libs/remix-core-plugin/src/index.ts
+++ b/libs/remix-core-plugin/src/index.ts
@@ -3,3 +3,4 @@ export { CompilerMetadata } from './lib/compiler-metadata'
export { FetchAndCompile } from './lib/compiler-fetch-and-compile'
export { CompilerImports } from './lib/compiler-content-imports'
export { CompilerArtefacts } from './lib/compiler-artefacts'
+export { EditorContextListener } from './lib/editor-context-listener'
diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
index 148aa292c7..715bd2e9c4 100644
--- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
+++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
@@ -4,7 +4,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = {
name: 'compilerArtefacts',
- methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas'],
+ methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult'],
events: [],
version: '0.0.1'
}
@@ -59,6 +59,10 @@ export class CompilerArtefacts extends Plugin {
})
}
+ getLastCompilationResult () {
+ return this.compilersArtefacts.__last
+ }
+
getAllContractDatas () {
const contractsData = {}
Object.keys(this.compilersArtefactsPerFile).map((targetFile) => {
diff --git a/apps/remix-ide/src/app/editor/contextualListener.js b/libs/remix-core-plugin/src/lib/editor-context-listener.ts
similarity index 64%
rename from apps/remix-ide/src/app/editor/contextualListener.js
rename to libs/remix-core-plugin/src/lib/editor-context-listener.ts
index 9eea6aeaed..72ccee7457 100644
--- a/apps/remix-ide/src/app/editor/contextualListener.js
+++ b/libs/remix-core-plugin/src/lib/editor-context-listener.ts
@@ -1,47 +1,48 @@
'use strict'
import { Plugin } from '@remixproject/engine'
-import * as packageJson from '../../../../../package.json'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
const { AstWalker } = require('@remix-project/remix-astwalker')
-const EventManager = require('../../lib/events')
-const globalRegistry = require('../../global/registry')
const profile = {
name: 'contextualListener',
methods: [],
events: [],
- version: packageJson.version
+ version: '0.0.1'
}
/*
trigger contextChanged(nodes)
*/
-class ContextualListener extends Plugin {
- constructor (opts) {
+export class EditorContextListener extends Plugin {
+ _index: any
+ _activeHighlights: Array
+ astWalker: any
+ currentPosition: any
+ currentFile: String
+ nodes: Array
+ results: any
+ estimationObj: any
+ creationCost: any
+ codeDepositCost: any
+ contract: any
+ activated: boolean
+
+ constructor () {
super(profile)
- this.event = new EventManager()
- this._components = {}
- this._components.registry = globalRegistry
- this.editor = opts.editor
- this.pluginManager = opts.pluginManager
- this._deps = {
- compilersArtefacts: this._components.registry.get('compilersartefacts').api,
- config: this._components.registry.get('config').api,
- offsetToLineColumnConverter: this._components.registry.get('offsettolinecolumnconverter').api
- }
+ this.activated = false
this._index = {
Declarations: {},
FlatReferences: {}
}
this._activeHighlights = []
- this.editor.event.register('contentChanged', () => { this._stopHighlighting() })
- this.sourceMappingDecoder = sourceMappingDecoder
this.astWalker = new AstWalker()
}
onActivation () {
+ this.on('editor', 'contentChanged', () => { this._stopHighlighting() })
+
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
if (languageVersion.indexOf('soljson') !== 0) return
this._stopHighlighting()
@@ -52,11 +53,18 @@ class ContextualListener extends Plugin {
this._buildIndex(data, source)
})
- setInterval(() => {
- if (this._deps.compilersArtefacts.__last && this._deps.compilersArtefacts.__last.languageversion.indexOf('soljson') === 0) {
- this._highlightItems(this.editor.getCursorPosition(), this._deps.compilersArtefacts.__last, this._deps.config.get('currentFile'))
+ setInterval(async () => {
+ const compilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult')
+ if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) {
+ this._highlightItems(
+ await this.call('editor', 'getCursorPosition'),
+ compilationResult,
+ await this.call('fileManager', 'file')
+ )
}
}, 1000)
+
+ this.activated = true
}
getActiveHighlights () {
@@ -74,7 +82,7 @@ class ContextualListener extends Plugin {
return this._index.Declarations[node.id]
}
- _highlightItems (cursorPosition, compilationResult, file) {
+ async _highlightItems (cursorPosition, compilationResult, file) {
if (this.currentPosition === cursorPosition) return
if (this.currentFile !== file) {
this.currentFile = file
@@ -85,12 +93,12 @@ class ContextualListener extends Plugin {
this.currentPosition = cursorPosition
this.currentFile = file
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) {
- const nodes = this.sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
+ const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
this.nodes = nodes
if (nodes && nodes.length && nodes[nodes.length - 1]) {
- this._highlightExpressions(nodes[nodes.length - 1], compilationResult)
+ await this._highlightExpressions(nodes[nodes.length - 1], compilationResult)
}
- this.event.trigger('contextChanged', [nodes])
+ this.emit('contextChanged', nodes)
}
}
@@ -111,21 +119,19 @@ class ContextualListener extends Plugin {
}
}
- _highlight (node, compilationResult) {
+ async _highlight (node, compilationResult) {
if (!node) return
- const position = this.sourceMappingDecoder.decode(node.src)
- const eventId = this._highlightInternal(position, node)
- const lastCompilationResult = this._deps.compilersArtefacts.__last
- if (eventId && lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0) {
- this._activeHighlights.push({ eventId, position, fileTarget: lastCompilationResult.getSourceName(position.file), nodeId: node.id })
+ const position = sourceMappingDecoder.decode(node.src)
+ await this._highlightInternal(position, node, compilationResult)
+ if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) {
+ this._activeHighlights.push({ position, fileTarget: compilationResult.getSourceName(position.file), nodeId: node.id })
}
}
- _highlightInternal (position, node) {
+ async _highlightInternal (position, node, compilationResult) {
if (node.nodeType === 'Block') return
- 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())
+ if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) {
+ let lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, position.file, compilationResult.getSourceCode().sources, compilationResult.getAsts())
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 = {
@@ -139,38 +145,38 @@ class ContextualListener extends Plugin {
}
}
}
- const fileName = lastCompilationResult.getSourceName(position.file)
+ const fileName = compilationResult.getSourceName(position.file)
if (fileName) {
- return this.call('editor', 'highlight', lineColumn, fileName, '', { focus: false })
+ return await this.call('editor', 'highlight', lineColumn, fileName, '', { focus: false })
}
}
return null
}
- _highlightExpressions (node, compilationResult) {
- const highlights = (id) => {
+ async _highlightExpressions (node, compilationResult) {
+ const highlights = async (id) => {
if (this._index.Declarations && this._index.Declarations[id]) {
const refs = this._index.Declarations[id]
for (const ref in refs) {
const node = refs[ref]
- this._highlight(node, compilationResult)
+ await this._highlight(node, compilationResult)
}
}
}
if (node && node.referencedDeclaration) {
- highlights(node.referencedDeclaration)
+ await highlights(node.referencedDeclaration)
const current = this._index.FlatReferences[node.referencedDeclaration]
- this._highlight(current, compilationResult)
+ await this._highlight(current, compilationResult)
} else {
- highlights(node.id)
- this._highlight(node, compilationResult)
+ await highlights(node.id)
+ await this._highlight(node, compilationResult)
}
this.results = compilationResult
}
_stopHighlighting () {
this.call('editor', 'discardHighlight')
- this.event.trigger('stopHighlighting', [])
+ this.emit('stopHighlighting')
this._activeHighlights = []
}
@@ -229,5 +235,3 @@ class ContextualListener extends Plugin {
return '(' + params.toString() + ')'
}
}
-
-module.exports = ContextualListener
diff --git a/libs/remix-ui/editor-context-view/.babelrc b/libs/remix-ui/editor-context-view/.babelrc
new file mode 100644
index 0000000000..09d67939cc
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/.babelrc
@@ -0,0 +1,4 @@
+{
+ "presets": ["@nrwl/react/babel"],
+ "plugins": []
+}
diff --git a/libs/remix-ui/editor-context-view/.eslintrc b/libs/remix-ui/editor-context-view/.eslintrc
new file mode 100644
index 0000000000..9d709f91d0
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/.eslintrc
@@ -0,0 +1,19 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "extends": "../../../.eslintrc",
+ "globals": {
+ "Atomics": "readonly",
+ "SharedArrayBuffer": "readonly"
+ },
+ "parserOptions": {
+ "ecmaVersion": 11,
+ "sourceType": "module"
+ },
+ "rules": {
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": "error"
+ }
+}
diff --git a/libs/remix-ui/editor-context-view/README.md b/libs/remix-ui/editor-context-view/README.md
new file mode 100644
index 0000000000..0b9719fedb
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/README.md
@@ -0,0 +1,7 @@
+# remix-ui-editor-context-view
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test remix-ui-editor-context-view` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/remix-ui/editor-context-view/src/index.ts b/libs/remix-ui/editor-context-view/src/index.ts
new file mode 100644
index 0000000000..b595accc41
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/src/index.ts
@@ -0,0 +1 @@
+export * from './lib/remix-ui-editor-context-view';
diff --git a/apps/remix-ide/src/app/editor/styles/contextView-styles.js b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.css
similarity index 70%
rename from apps/remix-ide/src/app/editor/styles/contextView-styles.js
rename to libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.css
index 5303a356fa..f57c88f78f 100644
--- a/apps/remix-ide/src/app/editor/styles/contextView-styles.js
+++ b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.css
@@ -1,17 +1,9 @@
-var csjs = require('csjs-inject')
-var css = csjs`
- .contextview {
- opacity : 1;
- position : relative;
- height : 25px;
- }
- .container {
+ .container-context-view {
padding : 1px 15px;
}
.line {
display : flex;
- justify-content : flex-end;
align-items : center;
text-overflow : ellipsis;
overflow : hidden;
@@ -48,12 +40,4 @@ var css = csjs`
z-index : 50;
border-radius : 1px;
border : 2px solid var(--secondary);
- }
- .contextviewcontainer{
- z-index : 50;
- border-radius : 1px;
- border : 2px solid var(--secondary);
- }
-`
-
-module.exports = css
+ }
\ No newline at end of file
diff --git a/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx
new file mode 100644
index 0000000000..51b6f6bf9d
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx
@@ -0,0 +1,159 @@
+import React, { useEffect, useState, useRef } from 'react' // eslint-disable-line
+import { sourceMappingDecoder } from '@remix-project/remix-debug'
+
+import './remix-ui-editor-context-view.css';
+
+/* eslint-disable-next-line */
+export interface RemixUiEditorContextViewProps {
+ hide: boolean,
+ contextualListener: any,
+ gotoLine: (line: number, column: number) => void,
+ openFile: (fileName: string) => void,
+ getLastCompilationResult: () => any,
+ offsetToLineColumn: (position: any, file: any, sources: any, asts: any) => any,
+ getCurrentFileName: () => String
+}
+
+function isDefinition (node: any) {
+ return node.nodeType === 'ContractDefinition' ||
+ node.nodeType === 'FunctionDefinition' ||
+ node.nodeType === 'ModifierDefinition' ||
+ node.nodeType === 'VariableDeclaration' ||
+ node.nodeType === 'StructDefinition' ||
+ node.nodeType === 'EventDefinition'
+}
+
+type astNode = {
+ name: string,
+ id: number,
+ children: Array,
+ typeDescriptions: any,
+ nodeType: String,
+ src: any,
+ nodeId: any,
+ position: any
+}
+
+type nullableAstNode = astNode | null
+
+export function RemixUiEditorContextView(props: RemixUiEditorContextViewProps) {
+ const nodesRef = useRef>([])
+ /*
+ gotoLineDisableRef is used to temporarily disable the update of the view.
+ e.g when the user ask the component to "gotoLine" we don't want to rerender the component (but just to put the mouse on the desired line)
+ */
+ const gotoLineDisableRef = useRef(false)
+ const [nodesState, setNode] = useState>([])
+ const contextualListener = props.contextualListener
+
+ useEffect(() => {
+ contextualListener.on('contextualListener', 'contextChanged', (nodes: Array) => {
+ if (gotoLineDisableRef.current) {
+ gotoLineDisableRef.current = false
+ return
+ }
+ nodesRef.current = nodes
+ setNode(nodes)
+ })
+ }, [])
+
+ const _render = (node: nullableAstNode) => {
+ if (!node) return ()
+ let references = contextualListener.referencesOf(node)
+ const type = node.typeDescriptions && node.typeDescriptions.typeString ? node.typeDescriptions.typeString : node.nodeType
+ references = `${references ? references.length : '0'} reference(s)`
+
+ let ref = 0
+ const nodes: Array = contextualListener.getActiveHighlights()
+
+ /*
+ * show gas estimation
+ */
+ const gasEstimation = () => {
+ if (node.nodeType === 'FunctionDefinition') {
+ const result = contextualListener.gasEstimation(node)
+ const executionCost = ' Execution cost: ' + result.executionCost + ' gas'
+ const codeDepositCost = 'Code deposit cost: ' + result.codeDepositCost + ' gas'
+ const estimatedGas = result.codeDepositCost ? `${codeDepositCost}, ${executionCost}` : `${executionCost}`
+ return (
+
+
+ {estimatedGas}
+
+ )
+ } else {
+ return ()
+ }
+ }
+
+ /*
+ * onClick jump to ast node in the editor
+ */
+ const _jumpToInternal = async (position: any) => {
+ const jumpToLine = async (fileName: string, lineColumn: any) => {
+ if (fileName !== await props.getCurrentFileName()) {
+ await props.openFile(fileName)
+ }
+ if (lineColumn.start && lineColumn.start.line && lineColumn.start.column) {
+ gotoLineDisableRef.current = true
+ props.gotoLine(lineColumn.start.line, lineColumn.end.column + 1)
+ }
+ }
+ const lastCompilationResult = await props.getLastCompilationResult()
+ if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
+ const lineColumn = await props.offsetToLineColumn(
+ position,
+ position.file,
+ lastCompilationResult.getSourceCode().sources,
+ lastCompilationResult.getAsts())
+ const filename = lastCompilationResult.getSourceName(position.file)
+ // TODO: refactor with rendererAPI.errorClick
+ jumpToLine(filename, lineColumn)
+ }
+ }
+
+ const jumpTo = () => {
+ if (node && node.src) {
+ const position = sourceMappingDecoder.decode(node.src)
+ if (position) {
+ _jumpToInternal(position)
+ }
+ }
+ }
+
+ // JUMP BETWEEN REFERENCES
+ const jump = (e: any) => {
+ e.target.dataset.action === 'next' ? ref++ : ref--
+ if (ref < 0) ref = nodes.length - 1
+ if (ref >= nodes.length) ref = 0
+ _jumpToInternal(nodes[ref].position)
+ }
+
+ return (
+ {gasEstimation()}
+
{type}
+
{node.name}
+
+
{references}
+
+
+
+ )
+ }
+
+ let last: nullableAstNode = null
+ if (!props.hide && nodesRef.current && nodesRef.current.length) {
+ last = nodesRef.current[nodesRef.current.length - 1]
+ if (!isDefinition(last)) {
+ last = contextualListener.declarationOf(last)
+ }
+ }
+
+ return (
+ !props.hide &&
+ {_render(last)}
+
+ );
+}
+
+export default RemixUiEditorContextView
diff --git a/libs/remix-ui/editor-context-view/tsconfig.json b/libs/remix-ui/editor-context-view/tsconfig.json
new file mode 100644
index 0000000000..d52e31ad74
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "jsx": "react",
+ "allowJs": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ }
+ ]
+}
diff --git a/libs/remix-ui/editor-context-view/tsconfig.lib.json b/libs/remix-ui/editor-context-view/tsconfig.lib.json
new file mode 100644
index 0000000000..b560bc4dec
--- /dev/null
+++ b/libs/remix-ui/editor-context-view/tsconfig.lib.json
@@ -0,0 +1,13 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "types": ["node"]
+ },
+ "files": [
+ "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
+ "../../../node_modules/@nrwl/react/typings/image.d.ts"
+ ],
+ "exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
+ "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
+}
diff --git a/nx.json b/nx.json
index 8bc14e4e9d..a1a7131ff6 100644
--- a/nx.json
+++ b/nx.json
@@ -147,6 +147,9 @@
},
"remix-ui-theme-module": {
"tags": []
+ },
+ "remix-ui-editor-context-view": {
+ "tags": []
}
},
"targetDependencies": {
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 3633434dce..68e1ae5175 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -69,10 +69,9 @@
"@remix-ui/tabs": ["libs/remix-ui/tabs/src/index.ts"],
"@remix-ui/helper": ["libs/remix-ui/helper/src/index.ts"],
"@remix-ui/app": ["libs/remix-ui/app/src/index.ts"],
- "@remix-ui/vertical-icons-panel": [
- "libs/remix-ui/vertical-icons-panel/src/index.ts"
- ],
- "@remix-ui/theme-module": ["libs/remix-ui/theme-module/src/index.ts"]
+ "@remix-ui/vertical-icons-panel": ["libs/remix-ui/vertical-icons-panel/src/index.ts"],
+ "@remix-ui/theme-module": ["libs/remix-ui/theme-module/src/index.ts"],
+ "@remix-ui/editor-context-view": ["libs/remix-ui/editor-context-view/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]
diff --git a/workspace.json b/workspace.json
index 8ce5f9f9ce..8850066499 100644
--- a/workspace.json
+++ b/workspace.json
@@ -1116,80 +1116,48 @@
}
}
}
+ },
+ "remix-ui-editor-context-view": {
+ "root": "libs/remix-ui/editor-context-view",
+ "sourceRoot": "libs/remix-ui/editor-context-view/src",
+ "projectType": "library",
+ "architect": {
+ "lint": {
+ "builder": "@nrwl/linter:lint",
+ "options": {
+ "linter": "eslint",
+ "tsConfig": ["libs/remix-ui/vertical-icons-panel/tsconfig.lib.json"],
+ "exclude": ["**/node_modules/**", "!libs/remix-ui/vertical-icons-panel/**/*"]
+ }
+ }
+ }
}
},
- "remix-ui-editor": {
- "root": "libs/remix-ui/editor",
- "sourceRoot": "libs/remix-ui/editor/src",
- "projectType": "library",
- "schematics": {},
- "architect": {
- "lint": {
- "builder": "@nrwl/linter:lint",
- "options": {
- "linter": "eslint",
- "babel": true
- },
- "component": {
- "style": "css"
- },
- "library": {
- "style": "css",
- "linter": "eslint"
- }
- },
+ "cli": {
+ "defaultCollection": "@nrwl/react"
+ },
+ "schematics": {
+ "@nrwl/workspace": {
"library": {
"linter": "eslint"
}
},
- "@nrwl/nx-plugin": {
- "plugin": {
+ "@nrwl/cypress": {
+ "cypress-project": {
"linter": "eslint"
}
},
- "@nrwl/web": {
- "application": {
- "linter": "eslint"
- }
- },
- "@nrwl/node": {
- "application": {
- "linter": "eslint"
- },
- "library": {
- "linter": "eslint"
- }
- }
- },
- "cli": {
- "defaultCollection": "@nrwl/react"
- },
- "schematics": {
- "@nrwl/workspace": {
- "library": {
- "linter": "eslint"
- }
- },
- "@nrwl/cypress": {
- "cypress-project": {
- "linter": "eslint"
- }
+ "@nrwl/react": {
+ "application": {
+ "style": "css",
+ "linter": "eslint",
+ "babel": true
},
- "@nrwl/react": {
- "application": {
- "style": "css",
- "linter": "eslint",
- "babel": true
- },
- "component": {
- "style": "css"
- },
- "library": {
- "style": "css",
- "linter": "eslint"
- }
+ "component": {
+ "style": "css"
},
"library": {
+ "style": "css",
"linter": "eslint"
}
},
@@ -1199,4 +1167,6 @@
}
},
"defaultProject": "remix-ide"
-}
\ No newline at end of file
+ }
+}
+