some improvements in the scripting part

This commit is contained in:
Michael Zanetti 2019-11-28 20:51:49 +01:00
parent 59047704ae
commit e160ad9665
6 changed files with 75 additions and 26 deletions

View File

@ -15,6 +15,11 @@ CodeCompletion::CodeCompletion(QObject *parent):
m_classes.insert("DeviceState", ClassInfo("DeviceState", {"id", "deviceId", "stateTypeId", "stateName", "value"}, {}, {"onValueChanged"})); m_classes.insert("DeviceState", ClassInfo("DeviceState", {"id", "deviceId", "stateTypeId", "stateName", "value"}, {}, {"onValueChanged"}));
m_classes.insert("DeviceEvent", ClassInfo("DeviceEvent", {"id", "deviceId", "eventTypeId", "eventName"}, {}, {"onTriggered"})); m_classes.insert("DeviceEvent", ClassInfo("DeviceEvent", {"id", "deviceId", "eventTypeId", "eventName"}, {}, {"onTriggered"}));
m_classes.insert("Timer", ClassInfo("Timer", {"id", "interval", "running", "repeat"}, {"start", "stop"}, {"onTriggered"})); m_classes.insert("Timer", ClassInfo("Timer", {"id", "interval", "running", "repeat"}, {"start", "stop"}, {"onTriggered"}));
m_classes.insert("PropertyAnimation", ClassInfo("PropertyAnimation", {"id", "target", "targets", "property", "properties", "value", "from", "to", "easing", "exclude", "duration", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"}));
m_classes.insert("ColorAnimation", ClassInfo("ColorAnimation", {"id", "target", "targets", "property", "properties", "value", "from", "to", "easing", "exclude", "duration", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"}));
m_classes.insert("SequentialAnimation", ClassInfo("SequentialAnimation", {"id", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"}));
m_classes.insert("ParallelAnimation", ClassInfo("ParallelAnimation", {"id", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"}));
m_classes.insert("PauseAnimation", ClassInfo("PauseAnimation", {"id", "duration", "alwaysRunToEnd", "loops", "paused", "running"}, {"start", "stop", "pause", "resume", "complete"}, {"onStarted", "onStopped", "onFinished", "onRunningChanged"}));
m_attachedClasses.insert("Component", ClassInfo("Component", {}, {}, {"onCompleted", "onDestruction", "onDestroyed"})); m_attachedClasses.insert("Component", ClassInfo("Component", {}, {}, {"onCompleted", "onDestruction", "onDestroyed"}));
@ -27,6 +32,7 @@ CodeCompletion::CodeCompletion(QObject *parent):
m_genericJsSyntax.insert("do", "do "); m_genericJsSyntax.insert("do", "do ");
m_genericJsSyntax.insert("if", "if "); m_genericJsSyntax.insert("if", "if ");
m_genericJsSyntax.insert("else", "else "); m_genericJsSyntax.insert("else", "else ");
m_genericJsSyntax.insert("print", "print");
m_jsClasses.insert("console", ClassInfo("console", {}, {"log", "warn"})); m_jsClasses.insert("console", ClassInfo("console", {}, {"log", "warn"}));
m_jsClasses.insert("JSON", ClassInfo("JSON", {}, {"stringify", "parse", "hasOwnProperty", "isPrototypeOf", "toString", "valueOf", "toLocaleString", "propertyIsEnumerable"})); m_jsClasses.insert("JSON", ClassInfo("JSON", {}, {"stringify", "parse", "hasOwnProperty", "isPrototypeOf", "toString", "valueOf", "toLocaleString", "propertyIsEnumerable"}));
@ -157,8 +163,10 @@ void CodeCompletion::update()
} }
QRegExp stateNameExp(".*stateName: \"[a-zA-Z0-9-]*"); QRegExp stateNameExp(".*stateName: \"[a-zA-Z0-9-]*");
qDebug() << "block text" << blockText << stateNameExp.exactMatch(blockText);
if (stateNameExp.exactMatch(blockText)) { if (stateNameExp.exactMatch(blockText)) {
BlockInfo info = getBlockInfo(m_cursor.position()); BlockInfo info = getBlockInfo(m_cursor.position());
qDebug() << "stateName block info" << info.name << info.properties;
if (!info.properties.contains("deviceId")) { if (!info.properties.contains("deviceId")) {
return; return;
} }
@ -456,7 +464,6 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
info.end = m_document->textDocument()->find("}", position).position(); info.end = m_document->textDocument()->find("}", position).position();
info.valid = true; info.valid = true;
qDebug() << "Block strats at" << blockStart.position();
info.name = blockStart.block().text(); info.name = blockStart.block().text();
info.name.remove(QRegExp(" *\\{")); info.name.remove(QRegExp(" *\\{"));
@ -464,25 +471,27 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
info.name.remove(QRegExp(".* ")); info.name.remove(QRegExp(".* "));
} }
qDebug() << "Block starts at" << blockStart.position() << "contents:" << blockStart.block().text();
int childBlocks = 0; int childBlocks = 0;
while (!blockStart.isNull() && blockStart.position() < position) { while (!blockStart.isNull() && blockStart.position() < position) {
QTextCursor tmp = m_document->textDocument()->find(QRegExp("[{}\n]"), blockStart); QString line = blockStart.block().text();
if (tmp.selectedText() == "{") { if (line.endsWith("{")) {
blockStart = tmp; blockStart.movePosition(QTextCursor::NextBlock);
childBlocks++; childBlocks++;
continue; continue;
} }
if (tmp.selectedText() == "}") { if (line.trimmed().startsWith("}")) {
blockStart = tmp; blockStart.movePosition(QTextCursor::NextBlock);
childBlocks--; childBlocks--;
continue; continue;
} }
// \n // \n
if (childBlocks > 0) { // Skip all stuff in child blocks if (childBlocks > 1) { // Skip all stuff in child blocks
blockStart = tmp; blockStart.movePosition(QTextCursor::NextBlock);
continue; continue;
} }
foreach (const QString &statement, blockStart.block().text().split(";")) { foreach (const QString &statement, blockStart.block().text().split(";")) {
qDebug() << "Have statement" << statement;
QStringList parts = statement.split(":"); QStringList parts = statement.split(":");
if (parts.length() != 2) { if (parts.length() != 2) {
continue; continue;
@ -492,7 +501,7 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
qDebug() << "inserting:" << propName << "->" << propValue; qDebug() << "inserting:" << propName << "->" << propValue;
info.properties.insert(propName, propValue); info.properties.insert(propName, propValue);
} }
blockStart = tmp; blockStart.movePosition(QTextCursor::NextBlock);
} }
return info; return info;

View File

@ -124,11 +124,16 @@ void ScriptManager::onNotificationReceived(const QVariantMap &params)
Script *script = new Script(scriptMap.value("id").toUuid()); Script *script = new Script(scriptMap.value("id").toUuid());
script->setName(scriptMap.value("name").toString()); script->setName(scriptMap.value("name").toString());
m_scripts->addScript(script); m_scripts->addScript(script);
emit addScriptReply(params.value("id").toInt(),
params.value("params").toMap().value("scriptError").toString(),
params.value("params").toMap().value("scriptId").toUuid(),
params.value("params").toMap().value("errors").toStringList());
} }
else if (params.value("notification").toString() == "Scripts.ScriptRemoved") { else if (params.value("notification").toString() == "Scripts.ScriptRemoved") {
QUuid id = params.value("params").toMap().value("scriptId").toUuid(); QUuid id = params.value("params").toMap().value("id").toUuid();
m_scripts->removeScript(id); m_scripts->removeScript(id);
emit removeScriptReply(params.value("id").toInt(), params.value("params").toMap().value("scriptError").toString());
} }
else if (params.value("notification").toString() == "Scripts.ScriptChanged") { else if (params.value("notification").toString() == "Scripts.ScriptChanged") {

View File

@ -28,7 +28,7 @@ Page {
RuleTemplatesFilterModel { RuleTemplatesFilterModel {
id: ruleTemplatesModel id: ruleTemplatesModel
ruleTemplates: RuleTemplates {} ruleTemplates: RuleTemplates {}
readonly property var deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(root.device.deviceClassId) : null readonly property var deviceClass: root.device ? engine.deviceManager.deviceClasses.getDeviceClass(root.device.deviceClassId) : null
filterByDevices: DevicesProxy { engine: _engine } filterByDevices: DevicesProxy { engine: _engine }
filterInterfaceNames: deviceClass ? deviceClass.interfaces : [] filterInterfaceNames: deviceClass ? deviceClass.interfaces : []
} }

View File

@ -47,7 +47,7 @@ Page {
HeaderButton { HeaderButton {
imageSource: "../images/save.svg" imageSource: "../images/save.svg"
enabled: d.script.name !== nameTextField.text || d.oldContent !== scriptEdit.text enabled: d.script && d.script.name !== nameTextField.text || d.oldContent !== scriptEdit.text
color: enabled ? app.accentColor : keyColor color: enabled ? app.accentColor : keyColor
hoverEnabled: true hoverEnabled: true
ToolTip.text: qsTr("Deploy script") ToolTip.text: qsTr("Deploy script")
@ -90,6 +90,7 @@ Page {
d.callId = -1; d.callId = -1;
if (scriptError == "ScriptErrorNoError") { if (scriptError == "ScriptErrorNoError") {
d.scriptId = scriptId; d.scriptId = scriptId;
d.oldContent = scriptEdit.text;
} }
errorModel.update(errors); errorModel.update(errors);
} }
@ -97,8 +98,10 @@ Page {
onEditScriptReply: { onEditScriptReply: {
print("edit reply", id, d.callId) print("edit reply", id, d.callId)
if (id == d.callId) { if (id == d.callId) {
d.oldContent = scriptEdit.text;
d.callId = -1; d.callId = -1;
if (scriptError == "ScriptErrorNoError") {
d.oldContent = scriptEdit.text;
}
errorModel.update(errors) errorModel.update(errors)
} }
} }
@ -141,6 +144,7 @@ Page {
LineNumbers { LineNumbers {
id: lineNumbers id: lineNumbers
textArea: scriptEdit
} }
TextArea.flickable: TextArea { TextArea.flickable: TextArea {
@ -217,6 +221,20 @@ Page {
completionBox.show(); completionBox.show();
event.accepted = true; event.accepted = true;
return; return;
case Qt.Key_Plus:
if (event.modifiers & Qt.ControlModifier) {
scriptEdit.font.pixelSize++;
event.accepted = true;
return;
}
break;
case Qt.Key_Minus:
if (event.modifiers & Qt.ControlModifier) {
scriptEdit.font.pixelSize--;
event.accepted = true;
return;
}
} }
// Things to do only when we're autocompleting // Things to do only when we're autocompleting
@ -289,8 +307,7 @@ Page {
delegate: Label { delegate: Label {
width: parent.width width: parent.width
text: model.line + ":" + model.column + ": " + model.message text: model.line + ":" + model.column + ": " + model.message
font.pixelSize: app.extraSmallFont font: scriptEdit.font
font.family: "Monospace"
} }
} }
} }
@ -324,8 +341,7 @@ Page {
delegate: Label { delegate: Label {
width: parent.width width: parent.width
text: model.message text: model.message
font.pixelSize: app.extraSmallFont font: scriptEdit.font
font.family: "Monospace"
color: model.type === "ScriptMessageTypeWarning" ? "red" : app.foregroundColor color: model.type === "ScriptMessageTypeWarning" ? "red" : app.foregroundColor
} }
} }

View File

@ -24,7 +24,7 @@ Page {
Connections { Connections {
target: engine.scriptManager target: engine.scriptManager
onRemovScriptReply: { onRemoveScriptReply: {
if (id == d.pendingAction) { if (id == d.pendingAction) {
d.pendingAction = -1; d.pendingAction = -1;
} }
@ -48,6 +48,17 @@ Page {
d.pendingAction = engine.scriptManager.removeScript(model.id); d.pendingAction = engine.scriptManager.removeScript(model.id);
} }
} }
EmptyViewPlaceholder {
anchors.centerIn: parent
title: qsTr("No scripts are installed yet.")
text: qsTr("Press \"Add script\" to get started.")
imageSource: "../images/script.svg"
buttonText: qsTr("Add script")
onButtonClicked: {
pageStack.push("ScriptEditor.qml");
}
}
} }
BusyOverlay { BusyOverlay {

View File

@ -1,13 +1,21 @@
import QtQuick 2.2 import QtQuick 2.4
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
Rectangle { Rectangle {
id: lineNumbers id: root
property TextArea textArea: null
FontMetrics {
id: fontMetrics
font: textArea.font
}
width: { width: {
var ret = 10; var ret = fontMetrics.maximumCharacterWidth * 2;
var tmp = scriptEdit.lineCount var tmp = scriptEdit.lineCount
while (tmp >= 10) { while (tmp >= 10) {
ret += 10; ret += fontMetrics.maximumCharacterWidth;
tmp /= 10; tmp /= 10;
} }
return ret; return ret;
@ -25,11 +33,11 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.topMargin: 8 anchors.topMargin: 8
Repeater { Repeater {
model: scriptEdit.lineCount model: root.textArea.lineCount
delegate: Rectangle { delegate: Rectangle {
id: lineNumberDelegate id: lineNumberDelegate
width: parent.width width: parent.width
height: scriptEdit.contentHeight / scriptEdit.lineCount height: root.textArea.contentHeight / root.textArea.lineCount
color: hasError ? "#FF0000" : "transparent" color: hasError ? "#FF0000" : "transparent"
readonly property bool hasError: errorModel.errorLines.indexOf(index + 1) >= 0 readonly property bool hasError: errorModel.errorLines.indexOf(index + 1) >= 0
Label { Label {
@ -38,8 +46,8 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 3 anchors.rightMargin: 3
text: index + 1 text: index + 1
font.pixelSize: scriptEdit.font.pixelSize font.pixelSize: root.textArea.font.pixelSize
font.family: scriptEdit.font.family font.family: root.textArea.font.family
font.weight: Font.Light font.weight: Font.Light
color: lineNumberDelegate.hasError ? "#FFFFFF" : "#808080" color: lineNumberDelegate.hasError ? "#FFFFFF" : "#808080"
} }