add sourceHighlighter

pull/1/head
yann300 9 years ago
parent ec86fd504f
commit c33a671148
  1. 6
      assets/css/browser-solidity.css
  2. 17
      src/app.js
  3. 3
      src/app/debugger.js
  4. 8
      src/app/editor.js
  5. 206
      src/app/sourceHighlighter.js

@ -434,3 +434,9 @@ input[readonly] {
input[type="file"] {
display: none;
}
.highlightcode {
position:absolute;
z-index:20;
background-color:#F4B9B7;
}

@ -18,6 +18,7 @@ var UniversalDApp = require('./universal-dapp.js');
var Debugger = require('./app/debugger');
var FormalVerification = require('./app/formalVerification');
var EventManager = require('./lib/eventManager');
var SourceHighlighter = require('./app/sourceHighlighter');
// The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event.
@ -233,10 +234,15 @@ var run = function () {
}
return false;
});
function swicthToFile (file) {
editor.setCacheFile(utils.fileKey(file));
updateFiles();
}
function showFileHandler (ev) {
ev.preventDefault();
editor.setCacheFile(utils.fileKey($(this).find('.name').text()));
swicthToFile($(this).find('.name').text())
updateFiles();
return false;
}
@ -421,22 +427,21 @@ var run = function () {
transactionDebugger.switchProvider('VM');
transactionDebugger.addProvider('INTERNAL', executionContext.web3());
transactionDebugger.addProvider('EXTERNAL', executionContext.web3());
transactionDebugger.onDebugRequested = function () {
selectTab($('ul#options li.debugView'));
};
var udapp = new UniversalDApp(executionContext, {
removable: false,
removable_instances: true
}, transactionDebugger);
udapp.event.register('debugRequested', this, function (data) {
transactionDebugger.debug(data);
udapp.event.register('debugRequested', this, function (txResult) {
transactionDebugger.debug(txResult);
selectTab($('ul#options li.debugView'));
});
var compiler = new Compiler(editor, queryParams, handleGithubCall, updateFiles);
var formalVerification = new FormalVerification($('#verificationView'), compiler.event);
var renderer = new Renderer(editor, executionContext.web3(), updateFiles, udapp, executionContext, formalVerification.event, compiler.event); // eslint-disable-line
var sourceHighlighter = new SourceHighlighter(editor, transactionDebugger.debugger, compiler.event, this.event, swicthToFile) // eslint-disable-line
executionContext.event.register('contextChanged', this, function (context) {
compiler.compile();

@ -15,8 +15,7 @@ function Debugger (id, executionContextEvent) {
}
Debugger.prototype.debug = function (receipt) {
if (this.onDebugRequested) this.onDebugRequested();
var self = this;
var self = this
this.debugger.web3().eth.getTransaction(receipt.transactionHash, function (error, tx) {
if (!error) {
self.debugger.debug(tx);

@ -15,6 +15,14 @@ function Editor (loadingFromGist, storage) {
setupStuff(getFiles());
this.addMarker = function (range, cssClass) {
return editor.session.addMarker(range, cssClass)
}
this.removeMarker = function (markerId) {
editor.session.removeMarker(markerId)
}
this.newFile = function () {
var untitledCount = '';
while (storage.exists(SOL_CACHE_UNTITLED + untitledCount)) {

@ -0,0 +1,206 @@
var utils = require('./utils')
var remix = require('ethereum-remix')
var ace = require('brace')
var Range = ace.acequire('ace/range').Range
/*
Provides source highlighting when debugging a transaction
*/
function SourceHighlighter (editor, txdebugger, compilerEvent, appEvent, switchToFile) {
this.switchToFile = switchToFile
this.editor = editor
this.txdebugger = txdebugger
this.sourceMappingDecoder = new remix.util.SourceMappingDecoder()
this.compilationData
this.currentSourceMap
this.currentLineColumnLayout // used to retrieve line/column from char/length
this.currentRange
this.currentMarker
this.currentExecutingAddress
this.currentFile
this.isDebugging = false
var self = this
/* hide/show marker when debugger does not have the focus */
appEvent.register('tabChanged', function (tab) {
if (tab !== 'debugView') {
self.editor.removeMarker(self.currentMarker)
} else {
if (self.currentRange) {
self.currentMarker = self.editor.addMarker(self.currentRange, 'highlightcode')
}
}
})
/* update compilation data */
compilerEvent.register('compilationFinished', this, function (success, data, source) {
if (!self.isDebugging) {
self.reset()
self.compilationData = success ? data : null
}
})
/* update marker, switch file if necessary */
txdebugger.codeManager.register('changed', this, function (code, address, index) {
if (!this.currentExecutingAddress !== address) {
// context changed, need to update srcmap
this.currentExecutingAddress = address
self.loadContext(txdebugger.tx, function (error, ctrName, srcmap) {
if (!error) {
self.currentSourceMap = srcmap
self.currentContractName = ctrName
self.highlightSource(index)
}
})
} else {
self.highlightSource(index)
}
})
txdebugger.register('newTraceLoaded', this, function () {
self.isDebugging = true
})
txdebugger.register('traceUnloaded', this, function () {
self.reset()
self.isDebugging = false
})
}
/*
* Load a new debugging context. A context is define by a new transaction.
* it:
* - builds the line/column layout from the source code.
* - retrieves the source map
*
* @param {Integer} index - the tx which defined the new debugging context
* @param {Function} callback - returns the decompressed source mapping for the given index {start, length, file, jump}
*/
SourceHighlighter.prototype.loadContext = function (tx, cb) {
var self = this
contractName(self.currentExecutingAddress, self, function (error, ctrName) {
if (!error) {
var srcmap = sourceMap(isContractCreation(self.currentExecutingAddress), ctrName, self.compilationData)
cb(null, ctrName, srcmap)
}
})
}
/*
* remove the editor marker and init attributes
*/
SourceHighlighter.prototype.reset = function () {
this.currentSourceMap = null
this.currentLineColumnLayout = null
this.removeCurrentMarker()
this.currentRange = null
this.currentMarker = null
this.currentExecutingAddress = null
this.currentFile = null
}
/*
* remove the current highlighted statement
*/
SourceHighlighter.prototype.removeCurrentMarker = function () {
if (this.currentMarker) {
this.editor.removeMarker(this.currentMarker)
this.currentMarker = null
}
}
/*
* highlight the statement with the given @arg index
*
* @param {Integer} index - the index of the assembly item to be highlighted
*/
SourceHighlighter.prototype.highlightSource = function (index) {
var self = this
this.sourceMappingDecoder.decompress(index, self.currentSourceMap, function (error, rawPos) { // retrieve the sourcemap location
if (!error) {
if (self.currentFile !== rawPos.file) { // source file changed, need to update the line/column layout
var file = self.compilationData.sourceList[rawPos.file]
self.sourceMappingDecoder.retrieveLineColumnLayout(self.editor.getFile(file), function (error, result) {
if (!error) {
self.currentLineColumnLayout = result
self.currentFile = rawPos.file
self.sourceMappingDecoder.getLineColumnPosition(rawPos, self.currentLineColumnLayout, function (error, pos) {
if (!error) {
self.highlight(pos, rawPos.file)
}
})
}
})
} else {
self.sourceMappingDecoder.getLineColumnPosition(rawPos, self.currentLineColumnLayout, function (error, pos) {
if (!error) {
self.highlight(pos, rawPos.file)
}
})
}
}
})
}
/*
* highlight the statement with the given @arg position
*
* @param {Object} position - the position to highlight { start: {line, column}, end: {line, column} }
*/
SourceHighlighter.prototype.highlight = function (position, fileIndex) {
var name = utils.fileNameFromKey(this.editor.getCacheFile()) // current opened tab
var source = this.compilationData.sourceList[parseInt(fileIndex)] // auto switch to that tab
this.removeCurrentMarker()
if (name !== source) {
this.switchToFile(source) // command the app to swicth to the curent file
}
this.currentRange = new Range(position.start.line, position.start.column, position.end.line, position.end.column)
this.currentMarker = this.editor.addMarker(this.currentRange, 'highlightcode')
}
function sourceMap (isConstructor, contractName, compilationData) {
if (isConstructor) {
return compilationData.contracts[contractName].srcmap
} else {
return srcmapRuntime(compilationData.contracts[contractName])
}
}
function contractName (executingAddress, self, cb) {
if (isContractCreation(executingAddress)) {
self.txdebugger.traceManager.getContractCreationCode(executingAddress, function (error, creationCode) {
if (!error) {
retrieveByteCode(creationCode, self.compilationData, 'bytecode', cb)
}
})
} else {
self.txdebugger.web3().eth.getCode(executingAddress, function (error, code) {
if (!error) {
retrieveByteCode(code, self.compilationData, 'runtimeBytecode', cb)
}
})
}
}
function retrieveByteCode (code, compilationData, prop, cb) {
for (var k in compilationData.contracts) {
if (code === '0x' + compilationData.contracts[k][prop]) {
cb(null, k)
return
}
cb('unable to retrieve contract name')
}
}
function srcmapRuntime (contract) {
return contract.srcmapRuntime ? contract.srcmapRuntime : contract['srcmap-runtime']
}
function isContractCreation (address) {
return address.indexOf('Contract Creation') !== -1
}
module.exports = SourceHighlighter
Loading…
Cancel
Save