Some improvements in the Script autocompletion

pull/499/head
Michael Zanetti 2021-01-09 00:38:08 +01:00
parent f865481b03
commit 828231c625
5 changed files with 71 additions and 44 deletions

View File

@ -427,28 +427,40 @@ void CodeCompletion::update()
QString id = blockText; QString id = blockText;
id.remove(QRegExp(".* ")).remove(QRegExp("\\.[a-zA-Z0-9]*")); id.remove(QRegExp(".* ")).remove(QRegExp("\\.[a-zA-Z0-9]*"));
QString type = getIdTypes().value(id); QString type = getIdTypes().value(id);
int blockPosition = getBlockPosition(id);
BlockInfo blockInfo = getBlockInfo(blockPosition);
qDebug() << "dot expression:" << id << type; 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) { foreach (const QString &property, m_classes.value(type).properties) {
entries.append(CompletionModel::Entry(property, property, "property")); 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) { foreach (const QString &method, m_classes.value(type).methods) {
QString paramString; QString paramString;
// If it's an execute() call, also autocomplete the params
if (method == "execute") { if (method == "execute") {
int blockposition = getBlockPosition(id); qDebug() << "Blockposition:" << blockPosition;
qDebug() << "Blockposition:" << blockposition; if (blockPosition >= 0) {
if (blockposition >= 0) { if (blockInfo.valid) {
BlockInfo info = getBlockInfo(blockposition); QString thingId = blockInfo.properties.value("thingId");
qDebug() << "actionType" << info.properties.keys() << info.properties.value("actionName") << info.properties.value("actionTypeId");
if (info.valid) {
QString thingId = info.properties.value("thingId");
Device *d = m_engine->thingManager()->things()->getDevice(QUuid(thingId)); Device *d = m_engine->thingManager()->things()->getDevice(QUuid(thingId));
if (d) { if (d) {
ActionType *at = nullptr; ActionType *at = nullptr;
if (info.properties.contains("actionTypeId")) { if (blockInfo.properties.contains("actionTypeId")) {
at = d->thingClass()->actionTypes()->getActionType(info.properties.value("actionTypeId")); at = d->thingClass()->actionTypes()->getActionType(blockInfo.properties.value("actionTypeId"));
} else if (info.properties.contains("actionName")) { } else if (blockInfo.properties.contains("actionName")) {
at = d->thingClass()->actionTypes()->findByName(info.properties.value("actionName")); at = d->thingClass()->actionTypes()->findByName(blockInfo.properties.value("actionName"));
} }
if (at) { if (at) {
QStringList params; QStringList params;
@ -466,6 +478,10 @@ void CodeCompletion::update()
} }
entries.append(CompletionModel::Entry(method + "(", method, "method", "", paramString + ")")); 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 // Attached classes/properties
foreach (const QString &property, m_attachedClasses.value(id).properties) { foreach (const QString &property, m_attachedClasses.value(id).properties) {
entries.append(CompletionModel::Entry(property, property, "property")); entries.append(CompletionModel::Entry(property, property, "property"));
@ -595,8 +611,9 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
} }
info.start = blockStart.position(); info.start = blockStart.position();
info.end = blockEnd.position(); //m_document->textDocument()->find("}", position).position(); info.end = blockEnd.position();
info.valid = true; info.valid = true;
// qDebug() << "Block start:" << info.start << "end:" << info.end;
info.name = blockStart.block().text(); info.name = blockStart.block().text();
info.name.remove(QRegExp(" *\\{ *")); info.name.remove(QRegExp(" *\\{ *"));
@ -607,14 +624,11 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
int childBlocks = 0; int childBlocks = 0;
while (!blockStart.isNull() && blockStart.position() < info.end) { while (!blockStart.isNull() && blockStart.position() < info.end) {
QString line = blockStart.block().text(); QString line = blockStart.block().text();
if (line.endsWith("{")) { // qDebug() << "line:" << line;
if (line.contains("{") && !line.contains("}")) {
childBlocks++; childBlocks++;
if (!blockStart.movePosition(QTextCursor::NextBlock)) {
break;
}
continue;
} }
if (line.trimmed().startsWith("}")) { if (line.contains("}") && !line.contains("{")) {
childBlocks--; childBlocks--;
if (!blockStart.movePosition(QTextCursor::NextBlock)) { if (!blockStart.movePosition(QTextCursor::NextBlock)) {
break; break;
@ -622,18 +636,30 @@ CodeCompletion::BlockInfo CodeCompletion::getBlockInfo(int position) const
continue; continue;
} }
// \n // \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); blockStart.movePosition(QTextCursor::NextBlock);
continue; continue;
} }
foreach (const QString &statement, blockStart.block().text().split(";")) { foreach (const QString &statement, blockStart.block().text().split(";")) {
// qDebug() << "analysing statement:" << statement;
QStringList parts = statement.split(":"); QStringList parts = statement.split(":");
if (parts.length() != 2) { if (parts.length() == 2) { // Properties must be "foo: bar"
continue; 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)) { if (!blockStart.movePosition(QTextCursor::NextBlock)) {
break; break;

View File

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

View File

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

View File

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

View File

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