diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 516b7f7a3..358750b24 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -40,7 +40,7 @@ Rectangle { var errorInfo = ErrorLocationFormater.extractErrorInfo(compilationErrorMessage, false); errorLocation.text = errorInfo.errorLocation; errorDetail.text = errorInfo.errorDetail; - errorLine.text = errorInfo.errorLine; + errorLine.text = errorInfo.line; } function update(data, giveFocus) @@ -242,7 +242,6 @@ Rectangle { height: 30 buttonShortcut: "Ctrl+Shift+F11" buttonTooltip: qsTr("Step Out Back") - visible: false } StepActionImage @@ -315,9 +314,8 @@ Rectangle { height: 30 buttonShortcut: "Ctrl+F5" buttonTooltip: qsTr("Run Forward") + visible: false } - - } } diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 5cf786d5f..7e5945fc7 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 import QtWebEngine 1.0 import QtWebEngine.experimental 1.0 +import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { signal editorTextChanged @@ -83,7 +84,22 @@ Item { runJavaScript("getTextChanged()", function(result) { }); pollTimer.running = true; syncClipboard(); + if (currentMode === "solidity") + { + codeModel.onCompilationComplete.connect(function(){ + runJavaScript("compilationComplete()", function(result) { }); + }); + + codeModel.onCompilationError.connect(function(error){ + var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); + if (errorInfo.line && errorInfo.column) + runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + else + runJavaScript("compilationComplete()", function(result) { }); + }); + } parent.changeGeneration(); + } } diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js new file mode 100644 index 000000000..03e36c927 --- /dev/null +++ b/mix/qml/html/cm/errorannotation.js @@ -0,0 +1,46 @@ +function ErrorAnnotation(editor, line, column, content) +{ + this.opened = false; + this.line = line; + this.column = column; + this.content = content.replace("Contract Error:", ""); + this.editor = editor; + this.errorMark = null; + this.lineWidget = null; + this.init(); + this.open(); +} + +ErrorAnnotation.prototype.init = function() +{ + var separators = [';', ',', '\\\(', '\\\{', '\\\}', '\\\)', ':']; + var errorPart = editor.getLine(this.line).substring(this.column); + var incrMark = this.column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; + if (incrMark === this.column) + incrMark = this.column + 1; + this.errorMark = editor.markText({ line: this.line, ch: this.column }, { line: this.line, ch: incrMark }, { className: "CodeMirror-errorannotation", inclusiveRight: true }); +} + +ErrorAnnotation.prototype.open = function() +{ + var node = document.createElement("div"); + node.id = "annotation" + node.innerHTML = this.content; + node.className = "CodeMirror-errorannotation-context"; + this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false }); + this.opened = true; +} + +ErrorAnnotation.prototype.close = function() +{ + this.lineWidget.clear(); + this.opened = false; +} + +ErrorAnnotation.prototype.destroy = function() +{ + if (this.opened) + this.close(); + if (this.errorMark) + this.errorMark.clear(); +} diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index f07b8b43b..ece98a95c 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -169,3 +169,16 @@ view-port background: rgba(255, 255, 255, 0.10); } +/* Error annotation */ +.CodeMirror-errorannotation { + border-bottom: 1px solid #b58900; +} + +.CodeMirror-errorannotation-context { + font-family: monospace; + font-size: small; + color: #586e75; + background: #b58900; + padding: 2px; +} + diff --git a/mix/qml/html/codeeditor.html b/mix/qml/html/codeeditor.html index 28a01c9a5..eec13f109 100644 --- a/mix/qml/html/codeeditor.html +++ b/mix/qml/html/codeeditor.html @@ -22,6 +22,7 @@ + diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index b739e2ed5..daf1286d8 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -22,23 +22,23 @@ editor.on("change", function(eMirror, object) { var mac = /Mac/.test(navigator.platform); if (mac === true) { -editor.setOption("extraKeys", { - "Cmd-V": function(cm) { - cm.replaceSelection(clipboard); - }, - "Cmd-X": function(cm) { - window.document.execCommand("cut"); - }, - "Cmd-C": function(cm) { - window.document.execCommand("copy"); - }}); + editor.setOption("extraKeys", { + "Cmd-V": function(cm) { + cm.replaceSelection(clipboard); + }, + "Cmd-X": function(cm) { + window.document.execCommand("cut"); + }, + "Cmd-C": function(cm) { + window.document.execCommand("copy"); + }}); } makeMarker = function() { - var marker = document.createElement("div"); - marker.style.color = "#822"; - marker.innerHTML = "●"; - return marker; + var marker = document.createElement("div"); + marker.style.color = "#822"; + marker.innerHTML = "●"; + return marker; }; toggleBreakpointLine = function(n) { @@ -77,9 +77,9 @@ getBreakpoints = function() { if (line.gutterMarkers && line.gutterMarkers["breakpoints"]) { var l = doc.getLineNumber(line); locations.push({ - start: editor.indexFromPos({ line: l, ch: 0}), - end: editor.indexFromPos({ line: l + 1, ch: 0}) - });; + start: editor.indexFromPos({ line: l, ch: 0}), + end: editor.indexFromPos({ line: l + 1, ch: 0}) + });; } }); return locations; @@ -116,7 +116,7 @@ setMode = function(mode) { else if (mode === "solidity") { CodeMirror.commands.autocomplete = function(cm) { - CodeMirror.showHint(cm, CodeMirror.hint.anyword); + CodeMirror.showHint(cm, CodeMirror.hint.anyword); } editor.setOption("extraKeys", { "Ctrl-Space": "autocomplete" @@ -132,6 +132,8 @@ var executionMark; highlightExecution = function(start, end) { if (executionMark) executionMark.clear(); + if (start === 0 && end + 1 === editor.getValue().length) + return; // Do not hightlight the whole document. executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" }); } @@ -145,3 +147,38 @@ isClean = function() { return editor.isClean(changeId); } + +var annotation = null; +var compilationCompleteBool = true; +compilationError = function(line, column, content) +{ + compilationCompleteBool = false; + window.setTimeout(function(){ + if (compilationCompleteBool) + return; + line = parseInt(line); + column = parseInt(column); + if (line > 0) + line = line - 1; + if (column > 0) + column = column - 1; + + if (annotation == null) + annotation = new ErrorAnnotation(editor, line, column, content); + else if (annotation.line !== line || annotation.column !== column || annotation.content !== content) + { + annotation.destroy(); + annotation = new ErrorAnnotation(editor, line, column, content); + } + }, 500) +} + +compilationComplete = function() +{ + if (annotation !== null) + { + annotation.destroy(); + annotation = null; + } + compilationCompleteBool = true; +} diff --git a/mix/qml/js/ErrorLocationFormater.js b/mix/qml/js/ErrorLocationFormater.js index 8c83e6b15..cb92f298b 100644 --- a/mix/qml/js/ErrorLocationFormater.js +++ b/mix/qml/js/ErrorLocationFormater.js @@ -16,12 +16,15 @@ function extractErrorInfo(raw, shortMessage) { _return.errorLocation = ErrorLocationFormater.formatLocation(reg[0], shortMessage); _return.errorDetail = detail.replace(reg[0], ""); + _return.line = reg[0].split(':')[1]; + _return.column = reg[0].split(':')[2]; } else { _return.errorLocation = ""; _return.errorDetail = detail; + _return.line = ""; + _return.column = ""; } - _return.errorLine = raw.split('\n')[1]; return _return; } diff --git a/mix/web.qrc b/mix/web.qrc index 499ad5746..aa5b38a67 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -27,6 +27,7 @@ qml/html/cm/closebrackets.js qml/html/cm/solidityToken.js qml/html/cm/javascript-hint.js + qml/html/cm/errorannotation.js qml/html/cm/tern.js qml/html/cm/ecma5spec.js qml/html/cm/comment.js