Commit 0e122406 authored by Christian Kamm's avatar Christian Kamm
Browse files

QmlJS: Completion for enums.

Done-with: Erik Verbruggen
parent a48032b6
......@@ -34,6 +34,9 @@ SOURCES += \
$$PWD/qmljslineinfo.cpp \
$$PWD/qmljscompletioncontextfinder.cpp
OTHER_FILES += \
$$PWD/parser/qmljs.g
contains(QT, gui) {
SOURCES += $$PWD/qmljsindenter.cpp
HEADERS += $$PWD/qmljsindenter.h
......
......@@ -92,6 +92,8 @@ void CompletionContextFinder::checkBinding()
int colonCount = 0;
bool delimiterFound = false;
bool firstToken = true;
bool identifierExpected = false;
bool dotExpected = false;
while (!delimiterFound) {
if (i < 0) {
if (!readLine())
......@@ -113,14 +115,36 @@ void CompletionContextFinder::checkBinding()
case Token::Colon:
++colonCount;
identifierExpected = true;
dotExpected = false;
m_bindingPropertyName.clear();
break;
case Token::Identifier:
if (firstToken && yyLine->midRef(token.begin(), token.length) == QLatin1String("on"))
case Token::Identifier: {
QStringRef tokenString = yyLine->midRef(token.begin(), token.length);
if (firstToken && tokenString == QLatin1String("on")) {
m_behaviorBinding = true;
} else if (identifierExpected) {
m_bindingPropertyName.prepend(tokenString.toString());
identifierExpected = false;
dotExpected = true;
} else {
dotExpected = false;
}
} break;
case Token::Dot:
if (dotExpected) {
dotExpected = false;
identifierExpected = true;
} else {
identifierExpected = false;
}
break;
default:
dotExpected = false;
identifierExpected = false;
break;
}
......@@ -150,7 +174,12 @@ bool CompletionContextFinder::isInLhsOfBinding() const
bool CompletionContextFinder::isInRhsOfBinding() const
{
return isInQmlContext() && m_colonCount == 1;
return isInQmlContext() && m_colonCount >= 1;
}
QStringList CompletionContextFinder::bindingPropertyName() const
{
return m_bindingPropertyName;
}
/*!
......
......@@ -24,6 +24,7 @@ public:
bool isInRhsOfBinding() const;
bool isAfterOnInLhsOfBinding() const;
QStringList bindingPropertyName() const;
private:
int findOpeningBrace(int startTokenIndex);
......@@ -32,7 +33,7 @@ private:
QTextCursor m_cursor;
QStringList m_qmlObjectTypeName;
QStringList m_bindingPropertyName;
int m_startTokenIndex;
int m_colonCount;
bool m_behaviorBinding;
......
......@@ -123,6 +123,9 @@ public:
int keyCount() const
{ return m_keys.size(); }
QStringList keys() const
{ return m_keys; }
};
class FakeMetaMethod {
......@@ -854,6 +857,13 @@ const Value *QmlObjectValue::propertyValue(const FakeMetaProperty &prop) const
value = object;
}
// might be an enum
int enumIndex = _metaObject->enumeratorIndex(prop.typeName());
if (enumIndex != -1) {
const FakeMetaEnum &metaEnum = _metaObject->enumerator(enumIndex);
value = new QmlEnumValue(metaEnum, engine());
}
return value;
}
......@@ -916,6 +926,28 @@ bool QmlObjectValue::isDerivedFrom(const FakeMetaObject *base) const
return false;
}
QmlEnumValue::QmlEnumValue(const FakeMetaEnum &metaEnum, Engine *engine)
: NumberValue(),
_metaEnum(new FakeMetaEnum(metaEnum))
{
engine->registerValue(this);
}
QmlEnumValue::~QmlEnumValue()
{
delete _metaEnum;
}
QString QmlEnumValue::name() const
{
return _metaEnum->name();
}
QStringList QmlEnumValue::keys() const
{
return _metaEnum->keys();
}
namespace {
////////////////////////////////////////////////////////////////////////////////
......
......@@ -69,6 +69,7 @@ typedef QList<const Value *> ValueList;
class FakeMetaObject;
class FakeMetaMethod;
class FakeMetaProperty;
class FakeMetaEnum;
////////////////////////////////////////////////////////////////////////////////
// Value visitor
......@@ -435,6 +436,19 @@ private:
mutable QHash<int, const Value *> _metaSignature;
};
class QMLJS_EXPORT QmlEnumValue: public NumberValue
{
public:
QmlEnumValue(const FakeMetaEnum &metaEnum, Engine *engine);
virtual ~QmlEnumValue();
QString name() const;
QStringList keys() const;
private:
FakeMetaEnum *_metaEnum;
};
class QMLJS_EXPORT Activation
{
public:
......
......@@ -157,12 +157,14 @@ class EnumerateProperties: private Interpreter::MemberProcessor
QSet<const Interpreter::ObjectValue *> _processed;
QHash<QString, const Interpreter::Value *> _properties;
bool _globalCompletion;
bool _enumerateGeneratedSlots;
Interpreter::Context *_context;
const Interpreter::ObjectValue *_currentObject;
public:
EnumerateProperties(Interpreter::Context *context)
: _globalCompletion(false),
_enumerateGeneratedSlots(false),
_context(context),
_currentObject(0)
{
......@@ -173,6 +175,11 @@ public:
_globalCompletion = globalCompletion;
}
void setEnumerateGeneratedSlots(bool enumerate)
{
_enumerateGeneratedSlots = enumerate;
}
QHash<QString, const Interpreter::Value *> operator ()(const Interpreter::Value *value)
{
_processed.clear();
......@@ -231,7 +238,7 @@ private:
virtual bool processGeneratedSlot(const QString &name, const Interpreter::Value *value)
{
if (_globalCompletion || (_currentObject && _currentObject->className().endsWith(QLatin1String("Keys")))) {
if (_enumerateGeneratedSlots || (_currentObject && _currentObject->className().endsWith(QLatin1String("Keys")))) {
// ### FIXME: add support for attached properties.
insertProperty(name, value);
}
......@@ -640,6 +647,10 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
(completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
bool doGlobalCompletion = true;
bool doQmlKeywordCompletion = true;
bool doJsKeywordCompletion = true;
QTextCursor startPositionCursor(edit->document());
startPositionCursor.setPosition(m_startPosition);
CompletionContextFinder contextFinder(startPositionCursor);
......@@ -648,11 +659,13 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
if (contextFinder.isInQmlContext())
qmlScopeType = context.lookupType(document.data(), contextFinder.qmlObjectTypeName());
bool doGlobalCompletion = true;
if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
doGlobalCompletion = false;
doJsKeywordCompletion = false;
EnumerateProperties enumerateProperties(&context);
enumerateProperties.setGlobalCompletion(true);
enumerateProperties.setEnumerateGeneratedSlots(true);
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(qmlScopeType));
while (it.hasNext()) {
......@@ -660,9 +673,6 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
TextEditor::CompletionItem item(this);
item.text = it.key();
item.data = it.key();
if (!contextFinder.isAfterOnInLhsOfBinding())
item.data = QString(item.data.toString() + QLatin1String(": "));
item.icon = symbolIcon;
m_completions.append(item);
}
......@@ -678,6 +688,35 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
}
}
if (contextFinder.isInRhsOfBinding() && qmlScopeType) {
doQmlKeywordCompletion = false;
qDebug() << "property name: " << contextFinder.bindingPropertyName();
if (!contextFinder.bindingPropertyName().isEmpty()) {
const Interpreter::Value *value = qmlScopeType;
foreach (const QString &name, contextFinder.bindingPropertyName()) {
if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
value = objectValue->property(name, &context);
if (!value)
break;
} else {
value = 0;
break;
}
}
if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) {
foreach (const QString &key, enumValue->keys()) {
TextEditor::CompletionItem item(this);
item.text = key;
item.data = QString("\"%1\"").arg(key);
item.icon = symbolIcon;
m_completions.append(item);
}
}
}
}
if (doGlobalCompletion) {
// It's a global completion.
EnumerateProperties enumerateProperties(&context);
......@@ -691,7 +730,9 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
item.icon = symbolIcon;
m_completions.append(item);
}
}
if (doJsKeywordCompletion) {
// add js keywords
foreach (const QString &word, Scanner::keywords()) {
TextEditor::CompletionItem item(this);
......@@ -702,7 +743,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
}
// add qml extra words
if (isQmlFile) {
if (doQmlKeywordCompletion && isQmlFile) {
static QStringList qmlWords;
if (qmlWords.isEmpty()) {
......@@ -718,6 +759,22 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
item.icon = keywordIcon;
m_completions.append(item);
}
if (!doJsKeywordCompletion) {
{
TextEditor::CompletionItem item(this);
item.text = QLatin1String("default");
item.icon = keywordIcon;
m_completions.append(item);
}
{
TextEditor::CompletionItem item(this);
item.text = QLatin1String("function");
item.icon = keywordIcon;
m_completions.append(item);
}
}
}
}
......
......@@ -227,6 +227,8 @@ QString HoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS:
if (! baseClasses->isEmpty())
return baseClasses->first();
} else if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) {
return enumValue->name();
}
QString typeId = context->engine()->typeId(value);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment