diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 6ccddcbe630..73da878343a 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -333,6 +333,16 @@ bool Bind::visit(UiInlineComponent *ast) return true; } +bool Bind::visit(UiEnumDeclaration *ast) +{ + if (_currentObjectValue) { + UiEnumValue *value = new UiEnumValue(ast, &_valueOwner, _currentObjectValue->originId()); + _qmlObjects.insert(ast, value); + _currentObjectValue->setMember(ast->name, value); + } + return true; +} + bool Bind::visit(AST::TemplateLiteral *ast) { Node::accept(ast->expression, this); diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h index 08a60c17cc1..d7096abdb28 100644 --- a/src/libs/qmljs/qmljsbind.h +++ b/src/libs/qmljs/qmljsbind.h @@ -57,6 +57,7 @@ protected: bool visit(AST::UiScriptBinding *ast) override; bool visit(AST::UiArrayBinding *ast) override; bool visit(AST::UiInlineComponent *ast) override; + bool visit(AST::UiEnumDeclaration *ast) override; // QML/JS bool visit(AST::TemplateLiteral *ast) override; diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 7ceff0e78de..0d21fd4bdae 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -846,6 +846,30 @@ bool Check::visit(UiObjectInitializer *) return true; } +bool Check::visit(AST::UiEnumDeclaration *ast) +{ + const Value *localLookup = _scopeChain.lookup(ast->name.toString()); + Utils::FilePath fp; + int line, column; + if (localLookup->getSourceLocation(&fp, &line, &column)) { + // if it's not "us" we get shadowed by another enum declaration + if (ast->identifierToken.startLine != line || ast->identifierToken.startColumn != column) + addMessage(ErrDuplicateId, SourceLocation(0, 0, line, column)); + } + return true; +} + +bool Check::visit(AST::UiEnumMemberList *ast) +{ + QStringList names; + for (auto it = ast; it; it = it->next) { + if (names.contains(it->member)) // duplicate enum value + addMessage(ErrInvalidEnumValue, it->memberToken); // better a different message? + names.append(it->member.toString()); + } + return true; +} + bool Check::visit(AST::TemplateLiteral *ast) { Node::accept(ast->expression, this); diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index 1fb96d251b5..d7fc6cc4256 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -57,6 +57,8 @@ protected: bool visit(AST::FunctionDeclaration *ast) override; bool visit(AST::FunctionExpression *ast) override; bool visit(AST::UiObjectInitializer *) override; + bool visit(AST::UiEnumDeclaration *ast) override; + bool visit(AST::UiEnumMemberList *ast) override; bool visit(AST::TemplateLiteral *ast) override; bool visit(AST::BinaryExpression *ast) override; diff --git a/src/libs/qmljs/qmljsicons.cpp b/src/libs/qmljs/qmljsicons.cpp index 0086843553b..a0522ca726b 100644 --- a/src/libs/qmljs/qmljsicons.cpp +++ b/src/libs/qmljs/qmljsicons.cpp @@ -120,3 +120,8 @@ QIcon Icons::functionDeclarationIcon() { return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::FuncPublic); } + +QIcon Icons::enumMemberIcon() +{ + return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Enum); +} diff --git a/src/libs/qmljs/qmljsicons.h b/src/libs/qmljs/qmljsicons.h index 4466d07a18e..1565ae5355c 100644 --- a/src/libs/qmljs/qmljsicons.h +++ b/src/libs/qmljs/qmljsicons.h @@ -28,6 +28,7 @@ public: static QIcon scriptBindingIcon(); static QIcon publicMemberIcon(); static QIcon functionDeclarationIcon(); + static QIcon enumMemberIcon(); private: Icons(); diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 5d687d5cd74..09bc6aecdb8 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -635,6 +635,52 @@ const CppComponentValue *QmlEnumValue::owner() const return m_owner; } +UiEnumValue::UiEnumValue(AST::UiEnumDeclaration *ast, + ValueOwner *valueOwner, const QString &originId) + : ObjectValue(valueOwner, originId) + , m_name(ast->name.toString()) +{ + setClassName("enum"); + m_path = Utils::FilePath::fromUserInput(originId); + m_line = ast->identifierToken.startLine; + m_column = ast->identifierToken.startColumn; + + for (auto it = ast->members; it; it = it->next) { + const QString name = it->member.toString(); + setMember(name, valueOwner->intValue()); + setPropertyInfo(name, PropertyInfo(PropertyInfo::Readable|PropertyInfo::ValueType)); + m_keys.append(name); + m_values.append(it->value); + } +} + +UiEnumValue::~UiEnumValue() +{ +} + +const UiEnumValue *UiEnumValue::asUiEnumValue() const +{ + return this; +} + +bool UiEnumValue::getSourceLocation(Utils::FilePath *path, int *line, int *column) const +{ + *path = m_path; + *line = m_line; + *column = m_column; + return true; +} + +QString UiEnumValue::name() const +{ + return m_name; +} + +QStringList UiEnumValue::keys() const +{ + return m_keys; +} + //////////////////////////////////////////////////////////////////////////////// // ValueVisitor //////////////////////////////////////////////////////////////////////////////// @@ -791,6 +837,11 @@ const QmlEnumValue *Value::asQmlEnumValue() const return nullptr; } +const UiEnumValue *Value::asUiEnumValue() const +{ + return nullptr; +} + const QmlPrototypeReference *Value::asQmlPrototypeReference() const { return nullptr; diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 8651196aefa..aeb3c9e4389 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -44,6 +44,7 @@ class NumberValue; class ModuleApiInfo; class ObjectValue; class QmlEnumValue; +class UiEnumValue; class QmlPrototypeReference; class RealValue; class Reference; @@ -115,6 +116,7 @@ public: virtual const CppComponentValue *asCppComponentValue() const; virtual const ASTObjectValue *asAstObjectValue() const; virtual const QmlEnumValue *asQmlEnumValue() const; + virtual const UiEnumValue *asUiEnumValue() const; virtual const QmlPrototypeReference *asQmlPrototypeReference() const; virtual const ASTPropertyReference *asAstPropertyReference() const; virtual const ASTVariableReference *asAstVariableReference() const; @@ -246,6 +248,12 @@ template <> Q_INLINE_TEMPLATE const QmlEnumValue *value_cast(const Value *v) else return nullptr; } +template <> Q_INLINE_TEMPLATE const UiEnumValue *value_cast(const Value *v) +{ + if (v) return v->asUiEnumValue(); + else return nullptr; +} + template <> Q_INLINE_TEMPLATE const QmlPrototypeReference *value_cast(const Value *v) { if (v) return v->asQmlPrototypeReference(); @@ -559,6 +567,27 @@ private: int m_enumIndex; }; +class QMLJS_EXPORT UiEnumValue : public ObjectValue +{ +public: + UiEnumValue(AST::UiEnumDeclaration *uiEnum, ValueOwner *valueOwner, + const QString &originId); + ~UiEnumValue(); + + const UiEnumValue *asUiEnumValue() const override; + bool getSourceLocation(Utils::FilePath *path, int *line, int *column) const override; + QString name() const; + QStringList keys() const; + +private: + const QString m_name; + Utils::FilePath m_path; + int m_line; + int m_column; + QStringList m_keys; + QList m_values; +}; + // A ObjectValue based on a FakeMetaObject. // May only have other CppComponentValue as ancestors. diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 3f5d53d60f5..8934865783d 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -599,6 +599,17 @@ protected: return true; } + bool visit(UiEnumDeclaration *node) override + { + if (containsOffset(node->identifierToken)) { + _name = node->name.toString(); + _scope = _doc->bind()->findQmlObject(_objectNode); + _targetValue = _scopeChain->context()->lookupType(_doc.data(), QStringList(_name)); + return false; + } + return true; + } + bool visit(FunctionDeclaration *node) override { return visit(static_cast(node)); diff --git a/src/plugins/qmljseditor/qmljshighlighter.cpp b/src/plugins/qmljseditor/qmljshighlighter.cpp index 4fd1da274ee..8000baf57f8 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.cpp +++ b/src/plugins/qmljseditor/qmljshighlighter.cpp @@ -108,10 +108,6 @@ void QmlJSHighlighter::highlightBlock(const QString &text) break; } } - if (QStringView(text).mid(token.offset, token.length) == QLatin1String("enum")) { - setFormat(token.offset, token.length, formatForCategory(C_KEYWORD)); - break; - } } else if (index > 0 && maybeQmlBuiltinType(spell)) { const Token &previousToken = tokens.at(index - 1); if (previousToken.is(Token::Identifier) diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp index 67e14dfbcd5..c8666b96053 100644 --- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp +++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp @@ -348,6 +348,13 @@ protected: return visit(static_cast(ast)); } + bool visit(UiEnumMemberList *ast) override + { + for (auto it = ast; it; it = it->next) + addUse(it->memberToken, SemanticHighlighter::FieldType); + return true; + } + bool visit(PatternElement *ast) override { if (ast->isVariableDeclaration()) diff --git a/src/plugins/qmljseditor/qmloutlinemodel.cpp b/src/plugins/qmljseditor/qmloutlinemodel.cpp index 015e8a41eb1..456789f645b 100644 --- a/src/plugins/qmljseditor/qmloutlinemodel.cpp +++ b/src/plugins/qmljseditor/qmloutlinemodel.cpp @@ -233,6 +233,19 @@ private: m_model->leavePublicMember(); } + bool visit(AST::UiEnumDeclaration *enumDecl) override + { + QModelIndex index = m_model->enterEnumDeclaration(enumDecl); + m_nodeToIndex.insert(enumDecl, index); + + return true; + } + + void endVisit(AST::UiEnumDeclaration *) override + { + m_model->leavePublicMember(); + } + bool visit(AST::FunctionDeclaration *functionDeclaration) override { QModelIndex index = m_model->enterFunctionDeclaration(functionDeclaration); @@ -576,6 +589,33 @@ void QmlOutlineModel::leavePublicMember() leaveNode(); } +QModelIndex QmlOutlineModel::enterEnumDeclaration(AST::UiEnumDeclaration *enumDecl) +{ + QMap objectData; + if (!enumDecl->name.isEmpty()) + objectData.insert(Qt::DisplayRole, enumDecl->name.toString()); + objectData.insert(ItemTypeRole, ElementBindingType); + + QmlOutlineItem *item = enterNode(objectData, enumDecl, nullptr, Icons::enumMemberIcon()); + + for (auto member = enumDecl->members; member; member = member->next) { + QMap memberData; + if (!member->member.isEmpty()) + memberData.insert(Qt::DisplayRole, member->member.toString()); + memberData.insert(ItemTypeRole, ElementBindingType); + memberData.insert(AnnotationRole, QString::number(member->value)); + enterNode(memberData, member, nullptr, Icons::publicMemberIcon()); + leaveNode(); + } + + return item->index(); +} + +void QmlOutlineModel::leaveEnumDeclaration() +{ + leaveNode(); +} + static QString functionDisplayName(QStringView name, AST::FormalParameterList *formals) { QString display; diff --git a/src/plugins/qmljseditor/qmloutlinemodel.h b/src/plugins/qmljseditor/qmloutlinemodel.h index 886d1986e59..bacb041714d 100644 --- a/src/plugins/qmljseditor/qmloutlinemodel.h +++ b/src/plugins/qmljseditor/qmloutlinemodel.h @@ -91,6 +91,9 @@ private: QModelIndex enterPublicMember(QmlJS::AST::UiPublicMember *publicMember); void leavePublicMember(); + QModelIndex enterEnumDeclaration(QmlJS::AST::UiEnumDeclaration *enumDecl); + void leaveEnumDeclaration(); + QModelIndex enterFunctionDeclaration(QmlJS::AST::FunctionDeclaration *functionDeclaration); void leaveFunctionDeclaration();