forked from qt-creator/qt-creator
QmlJS: Improve handling of user defined enums
Improves handling of Qml based enums inside qml documents. * completion of enums * follow the enum * highlighting values inside the declaration * displaying the enum declaration inside the outline * minor static checks Task-number: QTCREATORBUG-19226 Change-Id: Ia07fd9a8b7fa3106f2ea53198bfdcc50eecb7307 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
@@ -333,6 +333,16 @@ bool Bind::visit(UiInlineComponent *ast)
|
|||||||
return true;
|
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)
|
bool Bind::visit(AST::TemplateLiteral *ast)
|
||||||
{
|
{
|
||||||
Node::accept(ast->expression, this);
|
Node::accept(ast->expression, this);
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ protected:
|
|||||||
bool visit(AST::UiScriptBinding *ast) override;
|
bool visit(AST::UiScriptBinding *ast) override;
|
||||||
bool visit(AST::UiArrayBinding *ast) override;
|
bool visit(AST::UiArrayBinding *ast) override;
|
||||||
bool visit(AST::UiInlineComponent *ast) override;
|
bool visit(AST::UiInlineComponent *ast) override;
|
||||||
|
bool visit(AST::UiEnumDeclaration *ast) override;
|
||||||
|
|
||||||
// QML/JS
|
// QML/JS
|
||||||
bool visit(AST::TemplateLiteral *ast) override;
|
bool visit(AST::TemplateLiteral *ast) override;
|
||||||
|
|||||||
@@ -846,6 +846,30 @@ bool Check::visit(UiObjectInitializer *)
|
|||||||
return true;
|
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)
|
bool Check::visit(AST::TemplateLiteral *ast)
|
||||||
{
|
{
|
||||||
Node::accept(ast->expression, this);
|
Node::accept(ast->expression, this);
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ protected:
|
|||||||
bool visit(AST::FunctionDeclaration *ast) override;
|
bool visit(AST::FunctionDeclaration *ast) override;
|
||||||
bool visit(AST::FunctionExpression *ast) override;
|
bool visit(AST::FunctionExpression *ast) override;
|
||||||
bool visit(AST::UiObjectInitializer *) 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::TemplateLiteral *ast) override;
|
||||||
bool visit(AST::BinaryExpression *ast) override;
|
bool visit(AST::BinaryExpression *ast) override;
|
||||||
|
|||||||
@@ -120,3 +120,8 @@ QIcon Icons::functionDeclarationIcon()
|
|||||||
{
|
{
|
||||||
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::FuncPublic);
|
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::FuncPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QIcon Icons::enumMemberIcon()
|
||||||
|
{
|
||||||
|
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Enum);
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public:
|
|||||||
static QIcon scriptBindingIcon();
|
static QIcon scriptBindingIcon();
|
||||||
static QIcon publicMemberIcon();
|
static QIcon publicMemberIcon();
|
||||||
static QIcon functionDeclarationIcon();
|
static QIcon functionDeclarationIcon();
|
||||||
|
static QIcon enumMemberIcon();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Icons();
|
Icons();
|
||||||
|
|||||||
@@ -635,6 +635,52 @@ const CppComponentValue *QmlEnumValue::owner() const
|
|||||||
return m_owner;
|
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
|
// ValueVisitor
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -791,6 +837,11 @@ const QmlEnumValue *Value::asQmlEnumValue() const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UiEnumValue *Value::asUiEnumValue() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const QmlPrototypeReference *Value::asQmlPrototypeReference() const
|
const QmlPrototypeReference *Value::asQmlPrototypeReference() const
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class NumberValue;
|
|||||||
class ModuleApiInfo;
|
class ModuleApiInfo;
|
||||||
class ObjectValue;
|
class ObjectValue;
|
||||||
class QmlEnumValue;
|
class QmlEnumValue;
|
||||||
|
class UiEnumValue;
|
||||||
class QmlPrototypeReference;
|
class QmlPrototypeReference;
|
||||||
class RealValue;
|
class RealValue;
|
||||||
class Reference;
|
class Reference;
|
||||||
@@ -115,6 +116,7 @@ public:
|
|||||||
virtual const CppComponentValue *asCppComponentValue() const;
|
virtual const CppComponentValue *asCppComponentValue() const;
|
||||||
virtual const ASTObjectValue *asAstObjectValue() const;
|
virtual const ASTObjectValue *asAstObjectValue() const;
|
||||||
virtual const QmlEnumValue *asQmlEnumValue() const;
|
virtual const QmlEnumValue *asQmlEnumValue() const;
|
||||||
|
virtual const UiEnumValue *asUiEnumValue() const;
|
||||||
virtual const QmlPrototypeReference *asQmlPrototypeReference() const;
|
virtual const QmlPrototypeReference *asQmlPrototypeReference() const;
|
||||||
virtual const ASTPropertyReference *asAstPropertyReference() const;
|
virtual const ASTPropertyReference *asAstPropertyReference() const;
|
||||||
virtual const ASTVariableReference *asAstVariableReference() const;
|
virtual const ASTVariableReference *asAstVariableReference() const;
|
||||||
@@ -246,6 +248,12 @@ template <> Q_INLINE_TEMPLATE const QmlEnumValue *value_cast(const Value *v)
|
|||||||
else return nullptr;
|
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)
|
template <> Q_INLINE_TEMPLATE const QmlPrototypeReference *value_cast(const Value *v)
|
||||||
{
|
{
|
||||||
if (v) return v->asQmlPrototypeReference();
|
if (v) return v->asQmlPrototypeReference();
|
||||||
@@ -559,6 +567,27 @@ private:
|
|||||||
int m_enumIndex;
|
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<int> m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// A ObjectValue based on a FakeMetaObject.
|
// A ObjectValue based on a FakeMetaObject.
|
||||||
// May only have other CppComponentValue as ancestors.
|
// May only have other CppComponentValue as ancestors.
|
||||||
|
|||||||
@@ -599,6 +599,17 @@ protected:
|
|||||||
return true;
|
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
|
bool visit(FunctionDeclaration *node) override
|
||||||
{
|
{
|
||||||
return visit(static_cast<FunctionExpression *>(node));
|
return visit(static_cast<FunctionExpression *>(node));
|
||||||
|
|||||||
@@ -108,10 +108,6 @@ void QmlJSHighlighter::highlightBlock(const QString &text)
|
|||||||
break;
|
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)) {
|
} else if (index > 0 && maybeQmlBuiltinType(spell)) {
|
||||||
const Token &previousToken = tokens.at(index - 1);
|
const Token &previousToken = tokens.at(index - 1);
|
||||||
if (previousToken.is(Token::Identifier)
|
if (previousToken.is(Token::Identifier)
|
||||||
|
|||||||
@@ -348,6 +348,13 @@ protected:
|
|||||||
return visit(static_cast<FunctionExpression *>(ast));
|
return visit(static_cast<FunctionExpression *>(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
|
bool visit(PatternElement *ast) override
|
||||||
{
|
{
|
||||||
if (ast->isVariableDeclaration())
|
if (ast->isVariableDeclaration())
|
||||||
|
|||||||
@@ -233,6 +233,19 @@ private:
|
|||||||
m_model->leavePublicMember();
|
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
|
bool visit(AST::FunctionDeclaration *functionDeclaration) override
|
||||||
{
|
{
|
||||||
QModelIndex index = m_model->enterFunctionDeclaration(functionDeclaration);
|
QModelIndex index = m_model->enterFunctionDeclaration(functionDeclaration);
|
||||||
@@ -576,6 +589,33 @@ void QmlOutlineModel::leavePublicMember()
|
|||||||
leaveNode();
|
leaveNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QModelIndex QmlOutlineModel::enterEnumDeclaration(AST::UiEnumDeclaration *enumDecl)
|
||||||
|
{
|
||||||
|
QMap<int, QVariant> 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<int, QVariant> 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)
|
static QString functionDisplayName(QStringView name, AST::FormalParameterList *formals)
|
||||||
{
|
{
|
||||||
QString display;
|
QString display;
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ private:
|
|||||||
QModelIndex enterPublicMember(QmlJS::AST::UiPublicMember *publicMember);
|
QModelIndex enterPublicMember(QmlJS::AST::UiPublicMember *publicMember);
|
||||||
void leavePublicMember();
|
void leavePublicMember();
|
||||||
|
|
||||||
|
QModelIndex enterEnumDeclaration(QmlJS::AST::UiEnumDeclaration *enumDecl);
|
||||||
|
void leaveEnumDeclaration();
|
||||||
|
|
||||||
QModelIndex enterFunctionDeclaration(QmlJS::AST::FunctionDeclaration *functionDeclaration);
|
QModelIndex enterFunctionDeclaration(QmlJS::AST::FunctionDeclaration *functionDeclaration);
|
||||||
void leaveFunctionDeclaration();
|
void leaveFunctionDeclaration();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user