Merge PR #499: Some improvements in the Script autocompletion

pull/505/head
Jenkins nymea 2021-01-09 12:54:18 +01:00
commit 3a7f828f20
5 changed files with 71 additions and 44 deletions

View File

@ -427,28 +427,40 @@ void CodeCompletion::update()
QString id = blockText;
id.remove(QRegExp(".* ")).remove(QRegExp("\\.[a-zA-Z0-9]*"));
QString type = getIdTypes().value(id);
int blockPosition = getBlockPosition(id);
BlockInfo blockInfo = getBlockInfo(blockPosition);
qDebug() << "dot expression:" << id << type;
// Classes
qDebug() << "lvalue info:" << blockInfo.properties.keys() << blockInfo.properties.value("actionName") << blockInfo.properties.value("actionTypeId");
// Append properties of the type
foreach (const QString &property, m_classes.value(type).properties) {
entries.append(CompletionModel::Entry(property, property, "property"));
}
// Append user-defined properties of the type
foreach (const QString &property, blockInfo.properties.keys()) {
CompletionModel::Entry entry(property, property, "property");
if (!entries.contains(entry)) {
entries.append(entry);
}
}
// Append methods of the item
foreach (const QString &method, m_classes.value(type).methods) {
QString paramString;
// If it's an execute() call, also autocomplete the params
if (method == "execute") {
int blockposition = getBlockPosition(id);
qDebug() << "Blockposition:" << blockposition;
if (blockposition >= 0) {
BlockInfo info = getBlockInfo(blockposition);
qDebug() << "actionType" << info.properties.keys() << info.properties.value("actionName") << info.properties.value("actionTypeId");
if (info.valid) {
QString thingId = info.properties.value("thingId");
qDebug() << "Blockposition:" << blockPosition;
if (blockPosition >= 0) {
if (blockInfo.valid) {
QString thingId = blockInfo.properties.value("thingId");
Device *d = m_engine->thingManager()->things()->getDevice(QUuid(thingId));
if (d) {
ActionType *at = nullptr;
if (info.properties.contains("actionTypeId")) {
at = d->thingClass()->actionTypes()->getActionType(info.properties.value("actionTypeId"));
} else if (info.properties.contains("actionName")) {
at = d->thingClass()->actionTypes()->findByName(info.properties.value("actionName"));
if (blockInfo.properties.contains("actionTypeId")) {
at = d->thingClass()->actionTypes()->getActionType(blockInfo.properties.value("actionTypeId"));
} else if (blockInfo.properties.contains("actionName")) {
at = d->thingClass()->actionTypes()->findByName(blockInfo.properties.value("actionName"));
}
if (at) {
QStringList params;
@ -466,6 +478,10 @@ void CodeCompletion::update()
}
entries.append(CompletionModel::Entry(method + "(", method, "method", "", paramString + ")"));
}
// User-defined functions of the item
foreach (const QString &function, blockInfo.functions) {
entries.append(CompletionModel::Entry(function + "(", function, "method", "", ")"));
}
// Attached classes/properties
foreach (const QString &property, m_attachedClasses.value(id).properties) {
entries.append(CompletionModel::Entry(property, property, "property"));
@ -595,8 +611,9 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
}
info.start = blockStart.position();
info.end = blockEnd.position(); //m_document->textDocument()->find("}", position).position();
info.end = blockEnd.position();
info.valid = true;
// qDebug() << "Block start:" << info.start << "end:" << info.end;
info.name = blockStart.block().text();
info.name.remove(QRegExp(" *\\{ *"));
@ -607,14 +624,11 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
int childBlocks = 0;
while (!blockStart.isNull() && blockStart.position() < info.end) {
QString line = blockStart.block().text();
if (line.endsWith("{")) {
// qDebug() << "line:" << line;
if (line.contains("{") && !line.contains("}")) {
childBlocks++;
if (!blockStart.movePosition(QTextCursor::NextBlock)) {
break;
}
continue;
}
if (line.trimmed().startsWith("}")) {
if (line.contains("}") && !line.contains("{")) {
childBlocks--;
if (!blockStart.movePosition(QTextCursor::NextBlock)) {
break;
@ -622,18 +636,30 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
continue;
}
// \n
if (childBlocks > 1) { // Skip all stuff in child blocks
if (childBlocks > 1 && !(childBlocks == 2 && line.trimmed().startsWith("function"))) { // Skip all stuff in child blocks
// qDebug() << "skipping line in child block" << childBlocks;
blockStart.movePosition(QTextCursor::NextBlock);
continue;
}
foreach (const QString &statement, blockStart.block().text().split(";")) {
// qDebug() << "analysing statement:" << statement;
QStringList parts = statement.split(":");
if (parts.length() != 2) {
continue;
if (parts.length() == 2) { // Properties must be "foo: bar"
QString propName = parts.first().trimmed();
if (propName.split(" ").count() > 1) { // trim modifiers e.g. "property bool foo: bar"
propName = propName.split(" ").last();
}
if (propName.contains(".")) { // skip attached properties e.g. "Component.onCompleted: ..."
continue;
}
QString propValue = parts.last().split("//").first().trimmed().remove("\"");
info.properties.insert(propName, propValue);
}
parts = statement.trimmed().split(" ");
if (parts.count() >= 2 && parts.first().trimmed() == "function") {
QString functionHeader = parts.at(1).trimmed();
info.functions.append(functionHeader.split("(").first());
}
QString propName = parts.first().trimmed();
QString propValue = parts.last().split("//").first().trimmed().remove("\"");
info.properties.insert(propName, propValue);
}
if (!blockStart.movePosition(QTextCursor::NextBlock)) {
break;

View File

@ -101,6 +101,7 @@ private:
bool valid = false;
QString name;
QHash<QString, QString> properties;
QStringList functions;
int start = -1;
int end = -1;
};

View File

@ -81,6 +81,7 @@ CompletionModel::Entry CompletionModel::get(int index)
return m_list.at(index);
}
//************************************************
// CompletionProxyModel
//************************************************

View File

@ -49,6 +49,9 @@ public:
QString decoration;
QString decorationProperty;
QString trailingText;
bool operator==(const Entry &other) const {
return text == other.text && displayText == other.displayText;
}
};
CompletionModel(QObject *parent = nullptr);

View File

@ -38,6 +38,7 @@
#include <QMetaObject>
#include <QTextDocumentFragment>
#include <QQuickItem>
#include <QRegularExpression>
class ScriptSyntaxHighlighterPrivate: public QSyntaxHighlighter
{
@ -62,7 +63,7 @@ private:
};
struct HighlightingRule
{
QRegExp pattern;
QRegularExpression pattern;
QTextCharFormat format;
};
QVector<HighlightingRule> highlightingRules;
@ -118,19 +119,19 @@ void ScriptSyntaxHighlighterPrivate::update(bool dark)
// ClassNames
format.setForeground(dark ? QColor("#55fc49") : QColor("#800080"));
rule.pattern = QRegExp("\\b[A-Z][a-zA-Z0-9_]+\\b");
rule.pattern = QRegularExpression("\\b[A-Z][a-zA-Z0-9_]+\\b");
rule.format = format;
highlightingRules.append(rule);
// Property bindings
format.setForeground(dark ? QColor("#ff5555") : QColor("#800000"));
rule.pattern = QRegExp("[a-zA-Z][a-zA-Z0-9_.]+:");
rule.pattern = QRegularExpression("[a-zA-Z][a-zA-Z0-9_.]+:");
rule.format = format;
highlightingRules.append(rule);
// imports
format.clearForeground();
rule.pattern = QRegExp("import .*$");
rule.pattern = QRegularExpression("import .*$");
rule.format = format;
highlightingRules.append(rule);
@ -158,13 +159,14 @@ void ScriptSyntaxHighlighterPrivate::update(bool dark)
"\\bbool\\b",
"\\bint\\b",
"\\breal\\b",
"\\bdouble\\b",
"\\bdate\\b",
"\\btrue\\b",
"\\bfalse\\b",
};
format.setForeground(dark ? Qt::yellow : QColor("#80831a"));
foreach (const QString &pattern, keywordPatterns) {
rule.pattern = QRegExp(pattern);
rule.pattern = QRegularExpression(pattern);
rule.format = format;
highlightingRules.append(rule);
}
@ -172,17 +174,15 @@ void ScriptSyntaxHighlighterPrivate::update(bool dark)
// String literals
format.setForeground(dark ? QColor("#e64ad7") : Qt::darkGreen);
rule.format = format;
rule.pattern = QRegExp("\".[^\"]*\"");
highlightingRules.append(rule);
rule.pattern = QRegExp("'.[^']*'");
rule.pattern = QRegularExpression(R"**((?<!\\)([\"'])(.*?)(?<!\\)\1)**", QRegularExpression::DotMatchesEverythingOption | QRegularExpression::MultilineOption);
highlightingRules.append(rule);
// comments
format.setForeground(dark ? Qt::cyan : Qt::darkGray);
rule.format = format;
rule.pattern = QRegExp("//.*$");
rule.pattern = QRegularExpression("//.*$");
highlightingRules.append(rule);
rule.pattern = QRegExp("/*.*\\*/");
rule.pattern = QRegularExpression("/*.*\\*/");
highlightingRules.append(rule);
}
@ -191,15 +191,11 @@ void ScriptSyntaxHighlighterPrivate::highlightBlock(const QString &text)
// qDebug() << "hightlightBlock called for" << text << previousBlockState() << currentBlock().text();
foreach(const HighlightingRule &rule, highlightingRules){
QRegExp expression(rule.pattern);
int index = expression.indexIn(text);
while (index >= 0) {
int length = expression.matchedLength();
if (text.mid(index, length).endsWith(':')) {
length--;
}
setFormat(index, length, rule.format);
index = expression.indexIn(text, index + length);
QRegularExpression expression(rule.pattern);
QRegularExpressionMatchIterator matches = expression.globalMatch(text);
while (matches.hasNext()) {
QRegularExpressionMatch match = matches.next();
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
}
}
if (text.trimmed().startsWith("import")) {