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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -120,3 +120,8 @@ QIcon Icons::functionDeclarationIcon()
|
||||
{
|
||||
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 publicMemberIcon();
|
||||
static QIcon functionDeclarationIcon();
|
||||
static QIcon enumMemberIcon();
|
||||
|
||||
private:
|
||||
Icons();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<int> m_values;
|
||||
};
|
||||
|
||||
|
||||
// A ObjectValue based on a FakeMetaObject.
|
||||
// May only have other CppComponentValue as ancestors.
|
||||
|
||||
@@ -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<FunctionExpression *>(node));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -348,6 +348,13 @@ protected:
|
||||
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
|
||||
{
|
||||
if (ast->isVariableDeclaration())
|
||||
|
||||
@@ -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<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)
|
||||
{
|
||||
QString display;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user